diff options
author | Santo Cariotti <santo@dcariotti.me> | 2022-06-17 17:21:25 +0200 |
---|---|---|
committer | Santo Cariotti <santo@dcariotti.me> | 2022-06-17 17:21:25 +0200 |
commit | d6fdeec63a01ba43c5468ecc7c77a37347b0c953 (patch) | |
tree | 7979964f0b88f0ae13a8e173049b214d5d701082 | |
parent | a5e27da469c6224179b6a27ad89d0c6b534b5ae1 (diff) |
Add exercise
-rw-r--r-- | Year_3/Blockchain/RockPaperScissor.sol | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/Year_3/Blockchain/RockPaperScissor.sol b/Year_3/Blockchain/RockPaperScissor.sol new file mode 100644 index 0000000..528bc4c --- /dev/null +++ b/Year_3/Blockchain/RockPaperScissor.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "TrustworthyRockPaperScissorsTournamentSpecs.sol"; + +contract TrustworthyRockPaperScissorsTournament is TrustworthyRockPaperScissorsTournamentSpecs { + enum Move { ROCK, PAPER, SCISSOR } + enum Status { IN_PROGRESS, END } + struct PlayerInfo { + address payable id; + uint8 wins; + Move[] games; + uint fees; + } + + mapping(Player => PlayerInfo) players; + uint8 target; + uint256 matchFee; + Status status; + Player winner; + address payable owner; + + uint8 override public disputedMatches; + + constructor( + address payable firstPlayer, + address payable secondPlayer, + uint8 targetWins, + uint256 singleMatchFee + ) { + require(firstPlayer != secondPlayer, "Players must be two different addresses."); + require(firstPlayer != address(0) && secondPlayer != address(0), "Players address are not valid."); + + players[Player.First].id = firstPlayer; + players[Player.Second].id = secondPlayer; + target = targetWins; + matchFee = singleMatchFee; + status = Status.IN_PROGRESS; + disputedMatches = 0; + owner = payable(msg.sender); + } + + function moveRock() external override payable { + move(Move.ROCK); + } + + function movePaper() external override payable { + move(Move.PAPER); + } + + function moveScissor() external override payable { + move(Move.SCISSOR); + } + + modifier mustBeAPlayer() { + require(msg.sender == players[Player.First].id || msg.sender == players[Player.Second].id, "You're not one of the players."); + _; + } + + function move(Move move_type) internal mustBeAPlayer { + require(status != Status.END, "Game has already finish"); + require(msg.value >= matchFee, "You need to pass the minimum fee to play"); + + Player p = (msg.sender == players[Player.First].id ? Player.First : Player.Second); + players[p].games.push(move_type); + players[p].fees += msg.value; + + checkGame(); + } + + function checkGame() internal { + uint8 n = uint8( + players[Player.First].games.length < players[Player.Second].games.length + ? players[Player.First].games.length + : players[Player.Second].games.length + ); + + for (uint8 i = disputedMatches; i < n; ++i) { + Move p1 = players[Player.First].games[i]; + Move p2 = players[Player.Second].games[i]; + + // Fair play + if (p1 == p2) continue; + + if ( + (p1 == Move.ROCK && p2 == Move.SCISSOR) || + (p1 == Move.SCISSOR && p2 == Move.PAPER) || + (p1 == Move.PAPER && p2 == Move.ROCK) + ) { + players[Player.First].wins++; + emit MatchWonBy(Player.First, i); + } else { + players[Player.Second].wins++; + emit MatchWonBy(Player.Second, i); + } + + disputedMatches++; + + if (players[Player.First].wins == target) { + emit TournamentWonBy(Player.First); + winner = Player.First; + endGame(); + } else if (players[Player.Second].wins == target) { + emit TournamentWonBy(Player.Second); + winner = Player.Second; + endGame(); + } + } + } + + function endGame() internal { + require(status != Status.END, "Game has already finish"); + + assert(players[winner].wins == target); + + uint total = players[Player.First].fees + players[Player.Second].fees; + assert(total == address(this).balance); + players[winner].id.transfer(total); + + status = Status.END; + selfdestruct(owner); + } +} |