-
Notifications
You must be signed in to change notification settings - Fork 0
/
Rock Paper Scissor
225 lines (167 loc) · 11.2 KB
/
Rock Paper Scissor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;
import "./ERC20.sol";
import "./SafeMath.sol";
contract RockPaperScissor is ERC20 {
using SafeMath for uint;
uint8 constant SCISSOR = 1;
uint8 constant ROCK = 2;
uint8 constant PAPER = 3;
uint8 constant NO_VOTE = 0;
constructor (string memory name_, string memory symbol_) ERC20(name_, symbol_) {
_mint(msg.sender, 100 );
}
struct Rounds {
uint256 id;
bool moveAlreadyMadebyChallenged;
uint256 value;
uint8 playerOneVote;
uint8 playerTwoVote;
address challenger;
address challenged;
bool playerTwoEnrolled;
bool roundCancelled;
}
Rounds[] newRound;
//This is a mapping to check what move has been made by each players
mapping (address => uint256) voteNumberByPlayer;
//How much token is available on the selected address
mapping (address => uint256) tokenBalance;
//This checks if Rock, Paper, or Scissor has already been selected by the players on each rounds
mapping (address => mapping(uint256 => bool)) moveAlreadyMade;
//This shows which roundId's the selected address has
mapping (address => uint256[]) roundsByPlayer;
//Shows once a round is created, whom will be the players, also the amount the challenger has bid
event roundCreated(address challenger, address challenged, uint256 value);
//Notifies that the challenged person had made his move
event challengedMadeHisMove(uint256 forRoundId);
//Shows that the challenger changed his mind/don't want to wait more for the selected round, he cancelled it
event challengerCancelledRound(uint256 forRoundId);
function addBalance() payable external {
tokenBalance[msg.sender]+= msg.value; //To add balance to the selected address's wallet if somebody wants to bid some tokens to play
}
function createRound(address challenged, uint256 _value, uint8 RPS) payable external {
require(msg.sender != challenged); // the challenger cannot be the challenged person, otherwise the same person should play with himself
require(RPS == SCISSOR || RPS == ROCK || RPS == PAPER, "Your given element is not Rock, Paper or Scissor"); //only 3 values are available to use here, otherwise error
require(tokenBalance[msg.sender]>= _value, "Balance is not enough to enroll with this amount");
tokenBalance[msg.sender] -= _value;
Rounds memory currentRound = Rounds(newRound.length, false, _value, RPS, NO_VOTE, msg.sender, challenged, false, false);
newRound.push(currentRound); //create the array for the Round
roundsByPlayer[msg.sender].push(currentRound.id); //Mapping that saves which roundId's belong to the challenger
roundsByPlayer[challenged].push(currentRound.id); //same mapping just for the challenged
voteNumberByPlayer[msg.sender] = RPS; //save this to the mapping which move the player has made
}
function playAsChallenged(uint256 _id, uint8 RPS, uint256 _value) external {
require(newRound[_id].value <= tokenBalance[msg.sender], "balance is not enough to play in this game");
require(RPS == SCISSOR || RPS == ROCK || RPS == PAPER, "Your given element is not Rock, Paper or Scissor"); //only 3 values are availble here
require(_value == newRound[_id].value, "you need to bid the same amount of token as the challenger did"); //checks if the same has been set to bid
require(msg.sender == newRound[_id].challenged); //challenged needs to be challenged person which is saved in the array
_enrollAsChallenged(_id, _value); // executes the _enrollAsChallenged function
require(moveAlreadyMade[msg.sender][_id] == false); // Checks if the challenged has already made a move before
newRound[_id].moveAlreadyMadebyChallenged = true; // if no move has been made, this sets the boolean to true
newRound[_id].playerTwoVote = RPS; //sets this to the amount the challenger has chosen, either Rock Paper or Scissor
voteNumberByPlayer[msg.sender] = RPS; //save this to the mapping which move the player has made
if(newRound[_id].playerOneVote > 0 && newRound[_id].playerTwoVote > 0){
_calculateResults(newRound[_id].challenger, newRound[_id].challenged, newRound[_id].value);
} // If both the challenger and challenged has made his move, executes the _calculateResults function
emit challengedMadeHisMove(_id);
}
function cancelRound(uint256 _id) external {
require(newRound[_id].playerTwoEnrolled == false, "This match has already been played out, so you cannot cancel it");
require(newRound[_id].roundCancelled == false, "This round has already been cancelled"); // checks if the round is not cancelled, if it is, reverts
// checks if the playerTwoEnrolled or made his move, if he did, the round cannot be cancelled anymore as it has been finished already
require(newRound[_id].challenger == msg.sender); // only the challenger can cancel the round
uint256 lastId = roundsByPlayer[msg.sender][roundsByPlayer[msg.sender].length-1];
for (uint i = 0; i < roundsByPlayer[msg.sender].length; i++) { // looping through the array's length
if (roundsByPlayer[msg.sender][i] == _id) { // if i equals with the Id the challenger wants to delete
roundsByPlayer[msg.sender][i] = lastId; // the last element of the array becomes the id the challenger wants to remove
roundsByPlayer[msg.sender].pop(); // removes the last element of the array as the last id is appearing twice this time
} //
}
newRound[_id].roundCancelled = true; // set the boolean to true
emit challengerCancelledRound(_id); // notification that the round has been cancelled for the specific Id
}
function _calculateResults(address challenger, address challenged, uint256 value) private {
// If challenger chooses 1 and challenged 2 => playerTwo wins, which is the challenged, also the tokenTransfer executes to challenged's address
if(voteNumberByPlayer[challenger] == SCISSOR && voteNumberByPlayer[challenged] == ROCK){
tokenBalance[challenged] += value;
// If Challenger chooses 1 and challenged 3 =>Player one wins, scissor beats paper
}
else if(voteNumberByPlayer[challenger] == SCISSOR && voteNumberByPlayer[challenged] == PAPER){
tokenBalance[challenger] += value;
}
//If challenger chooses 2 and challenged 1, challenger wins as Rock beats Scissor
else if(voteNumberByPlayer[challenger] == ROCK && voteNumberByPlayer[challenged] == SCISSOR){
tokenBalance[challenger] += value;
}
//If challenger chooses 2 and challenged 3 => player two wins as Paper beats rock
else if(voteNumberByPlayer[challenger] == ROCK && voteNumberByPlayer[challenged] == PAPER){
tokenBalance[challenged] += value;
}
// If challenger chooses 3 and challenged 1 => challenged wins as scissor beats paper
else if(voteNumberByPlayer[challenger] == PAPER && voteNumberByPlayer[challenged] == SCISSOR){
tokenBalance[challenged] += value;
}
// If challenger chooses 3 and challenged 2 => challenger wins as Paper beats Rock
else if(voteNumberByPlayer[challenger] == PAPER && voteNumberByPlayer[challenged] == ROCK){
tokenBalance[challenger] += value;
}
// Every other situation is a draw, so both player gets their value/2 back which was their own bets
else {
tokenBalance[challenger] += value/2;
tokenBalance[challenged] += value/2;
}
}
function _enrollAsChallenged(uint256 _id, uint256 _value) private {
require(newRound[_id].roundCancelled == false); // Round cannot be cancelled otherwise it would not be possible for the challenged to be enrolled
require(msg.sender == newRound[_id].challenged, "challenger and challenged cannot have the same address");
require(newRound[_id].playerTwoEnrolled == false, "you already made your move to this round");
newRound[_id].playerTwoEnrolled = true; // set the boolean to true
newRound[_id].value += _value; // adds the value to the newround.value
tokenBalance[msg.sender] -= _value; // deducts the amount the challenged has made from his Address
}
function sortFinishedGames(uint256 _id) public { // This function deletes every id where the game has already finished
require(newRound[_id].playerTwoEnrolled == true, "this game has not been finished yet so it cannot be deleted");
uint256 lastId = roundsByPlayer[msg.sender][roundsByPlayer[msg.sender].length-1];
for(uint i = 0; i < roundsByPlayer[msg.sender].length; i++){
if(roundsByPlayer[msg.sender][i] == _id){
roundsByPlayer[msg.sender][i] = lastId;
roundsByPlayer[msg.sender].pop();
}
}
}
function _cancelRound(uint256 _id) private { // This function deletes the id for the players when the challenger decided to cancel the round instead
require(newRound[_id].roundCancelled == true);
uint256 lastId = roundsByPlayer[msg.sender][roundsByPlayer[msg.sender].length-1];
for(uint i = 0; i < roundsByPlayer[msg.sender].length; i++){
if(roundsByPlayer[msg.sender][i] == _id){
roundsByPlayer[msg.sender][i] = lastId;
roundsByPlayer[msg.sender].pop();
}
}
}
function getRoundbyId(uint256 _id) external view returns (
// If somebody would like to check for a round, he can do it here, this shows the status of the round, who made his move, if it is cancelled, etc
bool moveAlreadyMadebyChallenged,
uint256 tokenNumberInRound,
address Challenger,
address Challenged,
bool playerTwoEnrolled,
bool roundCancelled){
return ( newRound[_id].moveAlreadyMadebyChallenged, newRound[_id].value,
newRound[_id].challenger, newRound[_id].challenged, newRound[_id].playerTwoEnrolled, newRound[_id].roundCancelled);
}
function getAllRoundbyOwner() external view returns(uint256[] memory roundIds){
return roundsByPlayer[msg.sender]; // this shows that which Id's belongs to the selected Address
}
function getBalance() external view returns (uint256){
return tokenBalance[msg.sender]; // This shows the amount of balance the msg.sender has
}
function _mint(address account, uint256 amount) internal override virtual { // mints 100 tokens when the contract is deployed
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
tokenBalance[account] += amount;
emit Transfer(address(0), account, amount);
}
}