-
Notifications
You must be signed in to change notification settings - Fork 1
/
Game.sol
224 lines (185 loc) · 6.25 KB
/
Game.sol
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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract Game is ERC721 {
uint256 public totalSupply; // total amount of Mons available
uint8 constant WATER = 0;
uint8 constant AIR = 1;
uint8 constant FIRE = 2;
uint8 constant DECK_SIZE = 3;
uint256 private nonce = 123; // nonce used by pseudo-random generator
// a Mon has 4 attributes
struct Mon {
uint8 water;
uint8 air;
uint8 fire;
uint8 speed;
}
// a mapping from ids to Mons
mapping(uint256 => Mon) public mons;
// a mapping from players to their decks
mapping(address => uint256[DECK_SIZE]) public decks;
// a mapping from ids of Mons to booleans - if true, the Mon is for sale
mapping(uint256 => bool) public forSale;
// address of the flag holder
address public flagHolder;
constructor() ERC721("Hats Game 1", "HG1") {
// create an unbeatable superdeck for the deployer
Mon memory superMon = Mon(9,9,9,9);
flagHolder = msg.sender;
for (uint8 i; i < DECK_SIZE; i++) {
decks[flagHolder][i] = _mintMon(flagHolder, superMon);
}
}
// join the game and receive `DECK_SIZE` random Mons
function join() external returns (uint256[DECK_SIZE] memory deck) {
address newPlayer = msg.sender;
require(balanceOf(newPlayer) == 0, "player already joined");
// give the new player DECK_SIZE pseudorandom Mons
deck[0] = _mintMon(newPlayer);
deck[1] = _mintMon(newPlayer);
deck[2] = _mintMon(newPlayer);
decks[newPlayer] = deck;
}
// fight the flagHolder with your deck
function fight() external {
address attacker = msg.sender;
address opponent = flagHolder;
uint256[DECK_SIZE] memory deck0 = decks[attacker];
uint256[DECK_SIZE] memory deck1 = decks[opponent];
for (uint8 i = 0; i < DECK_SIZE; i++) {
uint8 element = randomGen(3);
// if the first player wins, burn the Mon of the second player
if (_fight(deck0[i], deck1[i], element)) {
_burn(deck1[i]);
} else {
_burn(deck0[i]);
}
}
// winner is the player with most Mons left
if (balanceOf(attacker) > balanceOf(opponent)) {
flagHolder = attacker;
}
// replenish balance of both players so they can play again
uint256[DECK_SIZE] memory deckAttacker = decks[attacker];
uint256[DECK_SIZE] memory deckOpponent = decks[opponent];
for (uint i; i < DECK_SIZE; i++) {
if (!_exists(deckAttacker[i])) {
deckAttacker[i] = _mintMon(attacker);
}
if (!_exists(deckOpponent[i])) {
deckOpponent[i] = _mintMon(opponent);
}
}
decks[attacker] = deckAttacker;
decks[opponent] = deckOpponent;
}
// fight _mon0 againts _mon1 in element _element
function _fight(uint256 _mon0, uint256 _mon1, uint8 _element) internal view returns(bool) {
assert(_element < 3);
Mon memory mon0;
Mon memory mon1;
mon0 = mons[_mon0];
mon1 = mons[_mon1];
if (_element == WATER) {
if (mon0.water > mon1.water) {
return true;
} else if (mon0.water < mon1.water) {
return false;
} else {
return mon0.speed > mon1.speed;
}
} else if (_element == AIR) {
if (mon0.air > mon1.air) {
return true;
} else if (mon0.air < mon1.air) {
return false;
} else {
return mon0.speed > mon1.speed;
}
} else if (_element == FIRE) {
if (mon0.fire > mon1.fire) {
return true;
} else if (mon0.fire < mon1.fire) {
return false;
} else {
return mon0.speed > mon1.speed;
}
}
}
// put a mon up for sale
function putUpForSale(uint256 _monId) external {
require(ownerOf(_monId) == msg.sender, "Can only put your own mons up for sale");
forSale[_monId] = true;
}
// swap your mon with _monId1 for a mon with _monId2 that is for sale and owned by _to
function swap(address _to, uint256 _monId1, uint256 _monId2) external {
address swapper = msg.sender;
require(forSale[_monId2], "Cannot swap a Mon that is not for sale");
require(swapper != _to, "Cannot swap a Mon with yourself");
_safeTransfer(swapper, _to, _monId1, "");
_safeTransfer(_to, swapper, _monId2, "");
// update the decks
uint256 idx1 = indexInDeck(swapper, _monId1);
uint256 idx2 = indexInDeck(_to, _monId2);
decks[swapper][idx1] = _monId2;
decks[_to][idx2] = _monId1;
}
function indexInDeck(address _owner, uint256 _monId) internal view returns(uint256 idx) {
for (uint256 i; i < DECK_SIZE; i++) {
if (decks[_owner][i] == _monId) {
idx = i;
}
}
}
function swapForNewMon(uint256 _monId) external {
address swapper = msg.sender;
require(ownerOf(_monId) == swapper, "Can only swap your own Mon for a new Mon");
uint256 idx = indexInDeck(swapper, _monId);
_burn(_monId);
decks[swapper][idx] = _mintMon(swapper);
}
function _mintMon(address _to, Mon memory mon) internal returns(uint256) {
uint256 tokenId = totalSupply;
totalSupply += 1;
mons[tokenId] = mon;
_mint(_to, tokenId);
return tokenId;
}
function _mintMon(address _to) internal returns(uint256) {
Mon memory newMon = genMon();
return _mintMon(_to, newMon);
}
// generate a new Mon
function genMon() private returns (Mon memory newMon) {
// generate a new Mon
uint8 fire = randomGen(10);
uint8 water = randomGen(10);
uint8 air = randomGen(10);
uint8 speed = randomGen(10);
newMon = Mon(fire, water, air, speed);
}
// function that generates pseudorandom numbers
function randomGen(uint256 i) private returns (uint8) {
uint8 x = uint8(uint256(keccak256(abi.encodePacked(block.number, msg.sender, nonce))) % i);
nonce++;
return x;
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public override {
// disable transferFrom - the only way to obtain a new Mon is by swapping
require(false, "transfers of Mons are disabled");
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) public override {
// disable transferFrom - the only way to obtain a new Mon is by swapping
require(false, "transfers of Mons are disabled");
}
}