Created
February 20, 2018 07:25
-
-
Save mgdigital/6012160ac4e9138a229779bb156bbc1d to your computer and use it in GitHub Desktop.
Ethereum smart contract for Draughts game
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.18; | |
contract Draughts { | |
enum Colour { Black, White } | |
enum CellContent { Empty, Black, White, BlackKing, WhiteKing } | |
uint constant public size = 10; | |
uint constant public turnLimitTime = 60 * 60; // Seconds to allow for turn | |
CellContent[size][size] gridState; | |
address blackAddr; | |
address whiteAddr; | |
address initiator; | |
address currentTurn; | |
uint public moveCount = 0; | |
uint lastX; | |
uint lastY; | |
uint public turnStartTime; | |
bool public won = false; | |
event Moved(Colour colour, uint fromX, uint fromY, uint toX, uint toY); | |
event Captured(Colour capturedColour, uint x, uint Y, bool king); | |
event Yielded(Colour toColour); | |
event Won(Colour colour); | |
function Draughts(address _opponent, Colour _myColour) public | |
{ | |
require(msg.sender != _opponent); | |
if (_myColour == Colour.Black) { | |
blackAddr = msg.sender; | |
whiteAddr = _opponent; | |
} else { | |
blackAddr = _opponent; | |
whiteAddr = msg.sender; | |
} | |
for (uint colourIndex = 0; colourIndex <= 1; colourIndex++) { | |
Colour thisColour = Colour(colourIndex); | |
int yInc = _getYInc(thisColour); | |
uint yStart = _getYStart(thisColour); | |
for (uint i = 0; i < 3; i++) { | |
uint y = uint(int(yStart) + (int(i) * yInc)); | |
for (uint j = 0; j < size; j += 2) { | |
uint x = j + ((i + colourIndex + 1) % 2); | |
_setCellContent(x, y, thisColour == Colour.Black ? CellContent.Black : CellContent.White); | |
} | |
} | |
} | |
initiator = msg.sender; | |
currentTurn = msg.sender; | |
turnStartTime = now; | |
} | |
function move(uint _fromX, uint _fromY, uint _toX, uint _toY, bool _yieldTurn) public payable { | |
require(isMyTurn()); | |
require(isCellOwnedBy(_fromX, _fromY, getMyColour())); | |
require(isCellEmpty(_toX, _toY)); | |
require(moveCount == 0 || (_fromX == lastX && _fromY == lastY)); | |
CellContent pieceToMove = getCellContent(_fromX, _fromY); | |
bool hasCaptured = false; | |
bool becameKing = false; | |
int advX = int(_toX) - int(_fromX); | |
int advY = int(_toY) - int(_fromY); | |
if (_abs(advY) == 1) { // First check if moving by one cell | |
require(_abs(advX) == 1); | |
require(_isContentKing(pieceToMove) || advY == _getYInc(getMyColour())); | |
} else if (_abs(advY) == 2) { // Now check if capturing opponent's piece | |
require(_abs(advX) == 2); | |
require(_isContentKing(pieceToMove) || advY == _getYInc(getMyColour()) * 2); | |
uint oppX = uint(int(_fromX) + (advX / 2)); | |
uint oppY = uint(int(_fromY) + (advY / 2)); | |
require(isCellOwnedBy(oppX, oppY, getOpponentColour())); | |
require(_isContentKing(pieceToMove) || !isCellKing(oppX, oppY)); | |
Captured(getOpponentColour(), oppX, oppY, isCellKing(oppX, oppY)); | |
_setCellContent(oppX, oppY, CellContent.Empty); | |
hasCaptured = true; | |
} else { | |
require(false); | |
} | |
if (_toY == _getYStart(getOpponentColour())) { | |
becameKing = !_isContentKing(pieceToMove); | |
pieceToMove = getMyColour() == Colour.Black ? CellContent.BlackKing : CellContent.WhiteKing; | |
} | |
_setCellContent(_fromX, _fromY, CellContent.Empty); | |
_setCellContent(_toX, _toY, pieceToMove); | |
Moved(getMyColour(), _fromX, _fromY, _toX, _toY); | |
moveCount++; | |
lastX = _toX; | |
lastY = _toY; | |
if (hasCaptured && getPieceCount(getOpponentColour()) == 0) { | |
Won(getMyColour()); | |
won = true; | |
} else if (_yieldTurn || (!hasCaptured && !becameKing)) { | |
yieldTurn(); | |
} | |
} | |
function yieldTurn() public payable { | |
require(isMyTurn() && moveCount >= 1); | |
Yielded(getOpponentColour()); | |
currentTurn = getMyColour() == Colour.Black ? whiteAddr : blackAddr; | |
moveCount = 0; | |
lastX = 0; | |
lastY = 0; | |
turnStartTime = now; | |
} | |
function claimGame() public payable { | |
require(!isMyTurn()); | |
require(isTurnPastTimeLimit()); | |
currentTurn = msg.sender; | |
won = true; | |
Won(getMyColour()); | |
} | |
function isTurnPastTimeLimit() public view returns (bool) { | |
return getTurnLength() > turnLimitTime; | |
} | |
function getTurnLength() public view returns (uint) { | |
return now - turnStartTime; | |
} | |
function isMyTurn() public view returns (bool) { | |
return isAddressTurn(msg.sender); | |
} | |
function isAddressTurn(address _addr) public view returns (bool) { | |
require(!won); | |
require(_addr == blackAddr || _addr == whiteAddr); | |
return _addr == currentTurn; | |
} | |
function isColourTurn(Colour _colour) public view returns (bool) { | |
require(!won); | |
return getAddressColour(currentTurn) == _colour; | |
} | |
function getAddressColour(address _addr) public view returns (Colour) { | |
require(_addr == blackAddr || _addr == whiteAddr); | |
return _addr == blackAddr ? Colour.Black : Colour.White; | |
} | |
function getMyColour() public view returns (Colour) { | |
return getAddressColour(msg.sender); | |
} | |
function getOpponentColour() public view returns (Colour) { | |
return getMyColour() == Colour.Black ? Colour.White : Colour.Black; | |
} | |
function isCellOwnedBy(uint _x, uint _y, Colour _colour) public view returns (bool) { | |
CellContent content = getCellContent(_x, _y); | |
if (_colour == Colour.Black) { | |
return content == CellContent.Black || content == CellContent.BlackKing; | |
} else { | |
return content == CellContent.White || content == CellContent.WhiteKing; | |
} | |
} | |
function isCellEmpty(uint _x, uint _y) public view returns (bool) { | |
return getCellContent(_x, _y) == CellContent.Empty; | |
} | |
function isCellKing(uint _x, uint _y) public view returns (bool) { | |
return _isContentKing(getCellContent(_x, _y)); | |
} | |
function _isContentKing(CellContent _content) private pure returns (bool) { | |
return _content == CellContent.BlackKing || _content == CellContent.WhiteKing; | |
} | |
function getCellColour(uint _x, uint _y) public pure returns (Colour) { | |
require(_validateCoords(_x, _y)); | |
return Colour((_x + _y + 1) % 2); | |
} | |
function getCellContent(uint _x, uint _y) public view returns (CellContent) { | |
require(_validateCoords(_x, _y)); | |
return gridState[_x][_y]; | |
} | |
function getPieceCount(Colour _colour) public view returns (uint) { | |
uint c = 0; | |
for (uint x = 0; x < size; x++) { | |
for (uint y = 0; y < size; y++) { | |
if (isCellOwnedBy(x, y, _colour)) { | |
c++; | |
} | |
} | |
} | |
return c; | |
} | |
function getKingCount(Colour _colour) public view returns (uint) { | |
uint c = 0; | |
for (uint x = 0; x < size; x++) { | |
for (uint y = 0; y < size; y++) { | |
if (isCellOwnedBy(x, y, _colour) && isCellKing(x, y)) { | |
c++; | |
} | |
} | |
} | |
return c; | |
} | |
function getWinningColour() public view returns (Colour) { | |
return getAddressColour(getWinningAddress()); | |
} | |
function getWinningAddress() public view returns (address) { | |
require(won); | |
return currentTurn; | |
} | |
function _setCellContent(uint _x, uint _y, CellContent _content) private { | |
assert(getCellColour(_x, _y) == Colour.Black); | |
gridState[_x][_y] = _content; | |
} | |
function _validateCoords(uint _x, uint _y) private pure returns (bool) { | |
return _x < size && _y < size; | |
} | |
function _getYStart(Colour _colour) private pure returns (uint) { | |
return _colour == Colour.Black ? 0 : size - 1; | |
} | |
function _getYInc(Colour _colour) private pure returns (int) { | |
return _colour == Colour.Black ? int(1) : -1; | |
} | |
function _abs(int _int) private pure returns (uint) { | |
return uint(_int < 0 ? _int * -1 : _int); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment