Skip to content

Instantly share code, notes, and snippets.

@mgdigital
Created February 20, 2018 07:25
Show Gist options
  • Save mgdigital/6012160ac4e9138a229779bb156bbc1d to your computer and use it in GitHub Desktop.
Save mgdigital/6012160ac4e9138a229779bb156bbc1d to your computer and use it in GitHub Desktop.
Ethereum smart contract for Draughts game
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