Smart Contract
Gomoku
A.a10dde51240c1ec7.Gomoku
1import MatchContract from 0xa10dde51240c1ec7
2import FungibleToken from 0xf233dcee88fe0abe
3import FlowToken from 0x1654653399040a61
4
5import GomokuIdentity from 0xa10dde51240c1ec7
6import GomokuResult from 0xa10dde51240c1ec7
7import GomokuType from 0xa10dde51240c1ec7
8
9pub contract Gomoku {
10
11 // Paths
12 pub let AdminStoragePath: StoragePath
13
14 pub let CollectionStoragePath: StoragePath
15 pub let CollectionPublicPath: PublicPath
16
17 // Bets
18 access(account) let hostOpeningBetMap: @{ UInt32: FlowToken.Vault }
19 access(account) let challengerOpeningBetMap: @{ UInt32: FlowToken.Vault }
20
21 access(account) let hostRaisedBetMap: @{ UInt32: FlowToken.Vault }
22 access(account) let challengerRaisedBetMap: @{ UInt32: FlowToken.Vault }
23
24 // Bets value when finalize
25 access(account) let hostFinalizedOpeningBetMap: { UInt32: UFix64 }
26 access(account) let challengerFinalizedOpeningBetMap: { UInt32: UFix64 }
27
28 access(account) let hostFinalizedRaisedBetMap: { UInt32: UFix64 }
29 access(account) let challengerFinalizedRaisedBetMap: { UInt32: UFix64 }
30
31 // Events
32 // Event be emitted when the composition is created
33 pub event HostOpeningBet(balance: UFix64)
34
35 // Event be emitted when the contract is created
36 pub event CompositionMatched(
37 host: Address,
38 challenger: Address,
39 currency: String,
40 openingBet: UFix64)
41
42 pub event CompositionCreated(
43 host: Address,
44 currency: String)
45 pub event CollectionCreated()
46 pub event Withdraw(id: UInt32, from: Address?)
47 pub event Deposit(id: UInt32, to: Address?)
48 pub event Surrender(id: UInt32, from: Address)
49
50 pub event CollectionNotFound(type: Type, path: Path, address: Address)
51 pub event ResourceNotFound(id: UInt32, type: Type, address: Address)
52
53 pub event MakeMove(
54 compositionId: UInt32,
55 locationX: Int8,
56 locationY: Int8,
57 stoneColor: UInt8)
58
59 pub event RoundSwitch(
60 compositionId: UInt32,
61 previous: UInt8,
62 next: UInt8
63 )
64
65 pub resource Admin {
66
67 pub fun manualFinalizeByTimeout(index: UInt32) {
68 }
69
70 pub fun recycleBets() {
71
72 let flowReceiverReference = Gomoku.account
73 .getCapability<&FlowToken.Vault{FungibleToken.Receiver}>(/public/flowTokenReceiver)
74 .borrow() ?? panic("Could not borrow a reference to the Flow token receiver capability")
75
76 for key in Gomoku.hostOpeningBetMap.keys {
77 // let hostOpeningBet <-! Gomoku.hostOpeningBetMap[key] not working: cannot move nested resource
78
79 // let placeholderVault: @FlowToken.Vault? <- FlowToken.createEmptyVault() as! @FlowToken.Vault
80
81 // let mapRef: &{UInt32: FlowToken.Vault} = &Gomoku.hostOpeningBetMap as &{UInt32: FlowToken.Vault}
82 // let vault = &mapRef[key] as &FlowToken.Vault?
83 // let flowBet <- vault!.withdraw(amount: vault!.balance)
84
85 // // let hostOpeningBet <- Gomoku.hostOpeningBetMap[key] <- placeholderVault
86 // flowReceiverReference.deposit(from: <- flowBet)
87 // let emptyVault <- Gomoku.hostOpeningBetMap.remove(key: key)
88 // destroy emptyVault
89 let tokenVault <- Gomoku.hostOpeningBetMap.remove(key: key)
90 if let vault <- tokenVault {
91 flowReceiverReference.deposit(from: <- (vault as! @FungibleToken.Vault))
92 } else {
93 destroy tokenVault
94 }
95 }
96
97 for key in Gomoku.challengerOpeningBetMap.keys {
98 // var challengerOpeningBet: @FlowToken.Vault? <- FlowToken.createEmptyVault() as! @FlowToken.Vault
99 // Gomoku.challengerOpeningBetMap[key] <-> challengerOpeningBet
100 // flowReceiverReference.deposit(from: <- (challengerOpeningBet as! @FungibleToken.Vault))
101 // let emptyVault <- Gomoku.challengerOpeningBetMap.remove(key: key)
102 // destroy emptyVault
103 let tokenVault <- Gomoku.challengerOpeningBetMap.remove(key: key)
104 if let vault <- tokenVault {
105 flowReceiverReference.deposit(from: <- (vault as! @FungibleToken.Vault))
106 } else {
107 destroy tokenVault
108 }
109 }
110
111 for key in Gomoku.hostRaisedBetMap.keys {
112 // var hostRaisedBet: @FlowToken.Vault? <- FlowToken.createEmptyVault() as! @FlowToken.Vault
113 // Gomoku.hostRaisedBetMap[key] <-> hostRaisedBet
114 // flowReceiverReference.deposit(from: <- (hostRaisedBet as! @FungibleToken.Vault))
115 // let emptyVault <- Gomoku.hostRaisedBetMap.remove(key: key)
116 // destroy emptyVault
117 let tokenVault <- Gomoku.hostRaisedBetMap.remove(key: key)
118 if let vault <- tokenVault {
119 flowReceiverReference.deposit(from: <- (vault as! @FungibleToken.Vault))
120 } else {
121 destroy tokenVault
122 }
123 }
124
125 for key in Gomoku.challengerRaisedBetMap.keys {
126 // var challengerRaisedBet: @FlowToken.Vault? <- FlowToken.createEmptyVault() as! @FlowToken.Vault
127 // Gomoku.challengerRaisedBetMap[key] <-> challengerRaisedBet
128 // flowReceiverReference.deposit(from: <- (challengerRaisedBet as! @FungibleToken.Vault))
129 // let emptyVault <- Gomoku.challengerRaisedBetMap.remove(key: key)
130 // destroy emptyVault
131 let tokenVault <- Gomoku.challengerRaisedBetMap.remove(key: key)
132 if let vault <- tokenVault {
133 flowReceiverReference.deposit(from: <- (vault as! @FungibleToken.Vault))
134 } else {
135 destroy tokenVault
136 }
137 }
138 }
139
140 pub fun getValue(): UFix64 {
141 var total = 0.0
142 for key in Gomoku.hostOpeningBetMap.keys {
143 total = total + (Gomoku.hostOpeningBetMap[key]?.balance ?? 0.0)
144 }
145 return total
146 }
147 }
148
149 init() {
150 self.CollectionStoragePath = /storage/gomokuCompositionCollectionV1
151 self.CollectionPublicPath = /public/gomokuCompositionCollectionV1
152
153 self.hostOpeningBetMap <- {}
154 self.challengerOpeningBetMap <- {}
155 self.hostRaisedBetMap <- {}
156 self.challengerRaisedBetMap <- {}
157
158 self.hostFinalizedOpeningBetMap = {}
159 self.challengerFinalizedOpeningBetMap = {}
160 self.hostFinalizedRaisedBetMap = {}
161 self.challengerFinalizedRaisedBetMap = {}
162
163 self.AdminStoragePath = /storage/gomokuAdminV1
164 let admin <- create Admin()
165 self.account.save(<- admin, to: self.AdminStoragePath)
166
167 if self.account.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault) == nil {
168 let flowVault <- FlowToken.createEmptyVault()
169 self.account.save(<- flowVault, to: /storage/flowTokenVault)
170 }
171
172 if self.account.getCapability<&FlowToken.Vault{FungibleToken.Receiver}>(/public/flowTokenReceiver) == nil {
173 // Create a public capability to the stored Vault that only exposes
174 // the `deposit` method through the `Receiver` interface
175 self.account.link<&FlowToken.Vault{FungibleToken.Receiver}>(
176 /public/flowTokenReceiver,
177 target: /storage/flowTokenVault
178 )
179 }
180
181 if self.account.getCapability<&FlowToken.Vault{FungibleToken.Balance}>(/public/flowTokenBalance) == nil {
182 // Create a public capability to the stored Vault that only exposes
183 // the `balance` field through the `Balance` interface
184 self.account.link<&FlowToken.Vault{FungibleToken.Balance}>(
185 /public/flowTokenBalance,
186 target: /storage/flowTokenVault
187 )
188 }
189 }
190
191 pub resource Composition {
192
193 pub let id: UInt32
194
195 pub let boardSize: UInt8
196 pub let totalRound: UInt8
197 pub var currentRound: UInt8
198
199 // timeout of block height
200 pub var latestBlockHeight: UInt64
201 pub var blockHeightTimeout: UInt64
202
203 access(account) var destroyable: Bool
204
205 priv var winner: GomokuType.Role?
206
207 priv var host: Address
208 priv var challenger: Address?
209 priv var roundWinners: [GomokuType.Result]
210 priv var steps: @[[Stone]]
211 priv var locationStoneMaps: [{String: GomokuType.StoneColor}]
212
213 init(
214 id: UInt32,
215 host: Address,
216 boardSize: UInt8,
217 totalRound: UInt8
218 ) {
219 pre {
220 totalRound >= 2: "Total round should be 2 to take turns to make first move (black stone) for fairness."
221 totalRound % 2 == 0: "Total round should be event number to take turns to make first move (black stone) for fairness."
222 }
223
224 self.id = id
225 self.host = host
226 self.boardSize = boardSize
227 self.challenger = nil
228 self.totalRound = totalRound
229 self.currentRound = 0
230 self.winner = nil
231 self.roundWinners = []
232
233 self.steps <- []
234 self.locationStoneMaps = []
235 var stepIndex = 0
236 // while totalRound > stepIndex {
237 // self.steps.append(<- [])
238 // self.locationStoneMaps.append({})
239 // stepIndex = stepIndex + 1
240 // }
241
242 self.latestBlockHeight = getCurrentBlock().height
243 self.blockHeightTimeout = 60 * 60 * 24 * 7
244 self.destroyable = false
245
246 // emit CompositionCreated(
247 // host: host,
248 // currency: Type<FlowToken>().identifier)
249 }
250
251 destroy() {
252 if self.destroyable == false {
253 panic("You can't destory this composition by yourself!")
254 }
255 destroy self.steps
256 }
257
258 }
259
260 pub resource CompositionCollection {
261
262 pub let StoragePath: StoragePath
263 pub let PublicPath: PublicPath
264
265 priv var ownedCompositionMap: @{UInt32: Gomoku.Composition}
266 priv var destroyable: Bool
267
268 init () {
269 self.ownedCompositionMap <- {}
270 self.destroyable = false
271 self.StoragePath = /storage/gomokuCollectionV1
272 self.PublicPath = /public/gomokuCollectionV1
273 }
274
275 access(account) fun withdraw(by id: UInt32): @Gomoku.Composition {
276 let token <- self.ownedCompositionMap.remove(key: id) ?? panic("missing Composition")
277 emit Withdraw(id: token.id, from: self.owner?.address)
278 if self.ownedCompositionMap.keys.length == 0 {
279 self.destroyable = true
280 }
281 return <- token
282 }
283
284 access(account) fun deposit(token: @Gomoku.Composition) {
285 let id: UInt32 = token.id
286 let oldToken <- self.ownedCompositionMap[id] <- token
287 emit Deposit(id: id, to: self.owner?.address)
288 self.destroyable = false
289 destroy oldToken
290 }
291
292 pub fun getIds(): [UInt32] {
293 return self.ownedCompositionMap.keys
294 }
295
296 pub fun getBalance(): Int {
297 return self.ownedCompositionMap.keys.length
298 }
299
300 pub fun borrow(id: UInt32): &Gomoku.Composition? {
301 return &self.ownedCompositionMap[id] as &Gomoku.Composition?
302 }
303
304 destroy() {
305 destroy self.ownedCompositionMap
306 if self.destroyable == false {
307 panic("Ha Ha! Got you! You can't destory this collection if there are Gomoku Composition!")
308 }
309 }
310 }
311
312 pub resource Stone: GomokuType.Stoning {
313 pub let color: GomokuType.StoneColor
314 pub let location: GomokuType.StoneLocation
315
316 pub init(
317 color: GomokuType.StoneColor,
318 location: GomokuType.StoneLocation
319 ) {
320 self.color = color
321 self.location = location
322 }
323
324 pub fun key(): String {
325 return self.location.key()
326 }
327
328 pub fun convertToData(): GomokuType.StoneData {
329 return GomokuType.StoneData(
330 color: self.color,
331 location: self.location
332 )
333 }
334 }
335
336 pub struct StoneData: GomokuType.StoneDataing {
337 pub let color: GomokuType.StoneColor
338 pub let location: GomokuType.StoneLocation
339
340 init(
341 color: GomokuType.StoneColor,
342 location: GomokuType.StoneLocation
343 ) {
344 self.color = color
345 self.location = location
346 }
347 }
348}