Skip to content

Instantly share code, notes, and snippets.

@yoshirahh
Created April 11, 2019 01:49
Show Gist options
  • Save yoshirahh/c903b1010a0a73c1b7bb41d3d3335780 to your computer and use it in GitHub Desktop.
Save yoshirahh/c903b1010a0a73c1b7bb41d3d3335780 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.5.6+commit.b259423e.js&optimize=false&gist=
pragma solidity 0.5.6;
contract RockPaperScissors {
address payable player1;
address payable player2;
uint stake;
bytes32 p1Move;
string p2Move;
uint p1Timer;
uint p2Timer;
constructor() public payable {
// 1 (a) Constructor that sets creator as player 1.
player1 = msg.sender;
}
function p1_commit(bytes32 _commitment) public payable {
// (b) only callable by player 1
require(msg.sender == player1);
// (d) amount must be strictly greater than 0.
require(msg.value > 0 ether);
p1Move = _commitment;
p1Timer = block.timestamp;
// (c) the amount of ethereum sent by player 1 defines the stake of the game
stake = msg.value;
}
function p2_join(string calldata _play) external payable {
// (a) should do nothing if there are already two players
if(player2 == address(0) && player1 != msg.sender) {
// (b) play is either “Rock”, “Paper”, or “Scissors”
require(valid_play(_play));
// (c) must be at least enough ethereum as the stake of the game.
require(msg.value >= stake);
// (d) whoever sends the message is player 2, assuming all the above hold
player2 = msg.sender;
p2Move = _play;
p2Timer = block.timestamp;
// This adds fairness - p1 doesn't have to worry about p2 joining and immediately cashing out based on p1Timer
// (i.e. if p1 is waiting longer than 30s for a second player)
p1Timer = block.timestamp;
}
}
function p1_reveal(string calldata _play, string calldata _salt) external {
// (c) called by player 1 only
require(msg.sender == player1);
// (a) play must be either “Rock”, “Paper”, or “Scissors”
require(valid_play(_play));
// (b) keccak256( play, salt) should be equal to the commitment provided when the contract was created
require(salt_string(_play, _salt) == p1Move);
// (d) if no other player has joined, cancel the contract, refunding the stored ether to player 1.
if(player2 == address(0)) {
selfdestruct(player1);
} else {
uint8 winner = check_moves(_play, p2Move);
// (e) if the game is a tie, erase both players’ moves (but not player identities!)
if(winner == 0) {
p1Move = "";
p2Move = "";
p1Timer = block.timestamp;
p2Timer = p1Timer;
} else {
// (f) If not a tie, kill the contract and give the balance to player that won.
if(winner == 1) selfdestruct(player1);
else selfdestruct(player2);
}
}
// (g) if player 2 has not made a move in 30 seconds, cancel the contract, giving all stored ether to player 1.
if((block.timestamp - p2Timer) > 30) selfdestruct(player2);
}
function p2_payout() external {
require(msg.sender == player2);
// (a) if it’s been at least 30 seconds since player 1 has made a move, kill the contract and send the balance to player 2
if((block.timestamp - p1Timer) > 30) selfdestruct(player2);
}
function p1_replay(bytes32 _commitment) external {
// (a) player 1 only
require(msg.sender == player1);
// (b) only if player 1 has no valid play stored (because a tie was declared)
require(p1Move == "");
// (c) sets player 1’s commitment to commitment
p1Move = _commitment;
p1Timer = block.timestamp;
}
function p2_replay(string calldata _play) external {
// (a) player 2 only
require(msg.sender == player2);
// (b) only if player 2 has no valid play stored (because a tie was declared)
require(str_eq(p2Move, ""));
// (c) stores play as player 2’s play (should be “Rock”, “Paper”, or “Scissors”)
require(valid_play(_play));
p2Move = _play;
p2Timer = block.timestamp;
}
function salt_string(string memory _str, string memory _salt) public pure returns (bytes32){
return keccak256(abi.encodePacked(_str, _salt));
}
function str_eq (string memory a, string memory b) private pure returns(bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
function valid_play(string memory _play) private pure returns (bool) {
return str_eq(_play, "Rock") || str_eq(_play, "Paper") || str_eq(_play, "Scissors");
}
function check_moves(string memory _p1, string memory _p2) private pure returns (uint8) {
if(str_eq(_p1, _p2)) {
return 0;
} else if(str_eq(_p1, "Rock")) {
if (str_eq(_p2, "Scissors")) return 1;
else return 2;
} else if(str_eq(_p1, "Paper")) {
if (str_eq(_p2, "Rock")) return 1;
else return 2;
} else if(str_eq(_p1, "Scissors")) {
if (str_eq(_p2, "Paper")) return 1;
else return 2;
}
}
}
pragma solidity 0.5.6;
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "./RPS.sol";
// file name has to end with '_test.sol'
contract RPSTest {
RockPaperScissors testRPS;
function beforeAll() public {
// here should instantiate tested contract
testRPS = new RockPaperScissors();
}
function checkP1Commit() public {
testRPS.
Assert.equal(testRPS.stake, uint(1), "error message");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment