首先,我們來具體分析一下這個井字棋游戲合約:
Python
# TicTacToe - Example for illustrative purposes only.
import smartpy as sp
@sp.module
def main():
class TicTacToe(sp.Contract):
def __init__(self):
self.data.nbMoves = 0
self.data.winner = 0
self.data.draw = False
self.data.deck = {
0: {0: 0, 1: 0, 2: 0},
1: {0: 0, 1: 0, 2: 0},
2: {0: 0, 1: 0, 2: 0},
}
self.data.nextPlayer = 1
@sp.entrypoint
def play(self, params):
assert self.data.winner == 0 and not self.data.draw
assert params.i >= 0 and params.i < 3
assert params.j >= 0 and params.j < 3
assert params.move == self.data.nextPlayer
assert self.data.deck[params.i][params.j] == 0
self.data.deck[params.i][params.j] = params.move
self.data.nbMoves += 1
self.data.nextPlayer = 3 - self.data.nextPlayer
self.data.winner = self.checkLine(
sp.record(winner=self.data.winner, line=self.data.deck[params.i])
)
self.data.winner = self.checkLine(
sp.record(
winner=self.data.winner,
line={
0: self.data.deck[0][params.j],
1: self.data.deck[1][params.j],
2: self.data.deck[2][params.j],
},
)
)
self.data.winner = self.checkLine(
sp.record(
winner=self.data.winner,
line={
0: self.data.deck[0][0],
1: self.data.deck[1][1],
2: self.data.deck[2][2],
},
)
)
self.data.winner = self.checkLine(
sp.record(
winner=self.data.winner,
line={
0: self.data.deck[0][2],
1: self.data.deck[1][1],
2: self.data.deck[2][0],
},
)
)
if self.data.nbMoves == 9 and self.data.winner == 0:
self.data.draw = True
@sp.private()
def checkLine(self, winner, line):
winner_ = winner
if line[0] != 0 and line[0] == line[1] and line[0] == line[2]:
winner_ = line[0]
return winner_
# Add a game reset function
@sp.entrypoint
def confirm_and_reset(self):
assert self.data.winner != 0 or self.data.draw
self.__init__()
# Tests
if "templates" not in __name__:
@sp.add_test(name="TicTacToe")
def test():
scenario = sp.test_scenario(main)
scenario.h1("Tic-Tac-Toe")
# define a contract
c1 = main.TicTacToe()
# show its representation
scenario.h2("A sequence of interactions with a winner")
scenario += c1
scenario.h2("Message execution")
scenario.h3("A first move in the center")
c1.play(i=1, j=1, move=1)
scenario.h3("A forbidden move")
c1.play(i=1, j=1, move=2).run(valid=False)
scenario.h3("A second move")
c1.play(i=1, j=2, move=2)
scenario.h3("Other moves")
c1.play(i=2, j=1, move=1)
c1.play(i=2, j=2, move=2)
scenario.verify(c1.data.winner == 0)
c1.play(i=0, j=1, move=1)
scenario.verify(c1.data.winner == 1)
scenario.p("Player1 has won")
c1.play(i=0, j=0, move=2).run(valid=False)
c2 = main.TicTacToe()
scenario.h2("A sequence of interactions with a draw")
scenario += c2
scenario.h2("Message execution")
scenario.h3("A first move in the center")
c2.play(i=1, j=1, move=1)
scenario.h3("A forbidden move")
c2.play(i=1, j=1, move=2).run(valid=False)
scenario.h3("A second move")
c2.play(i=1, j=2, move=2)
scenario.h3("Other moves")
c2.play(i=2, j=1, move=1)
c2.play(i=2, j=2, move=2)
c2.play(i=0, j=0, move=1)
c2.play(i=0, j=1, move=2)
c2.play(i=0, j=2, move=1)
c2.play(i=2, j=0, move=2)
c2.play(i=1, j=0, move=1)
# Add tests for game reset
scenario.h2("Testing game reset")
scenario.p("Winner or draw confirmed, now resetting the game")
c1.confirm_and_reset()
scenario.verify(c1.data.nbMoves == 0)
scenario.verify(c1.data.winner == 0)
scenario.verify(not c1.data.draw)
c2.confirm_and_reset()
scenario.verify(c2.data.nbMoves == 0)
scenario.verify(c2.data.winner == 0)
scenario.verify(not c2.data.draw)
我們在Tezos上的井字棋游戲合約是用SmartPy語言編寫的。它包含兩個主要部分組成:合約狀態和游戲邏輯。
合約的狀態通過**init
函數初始化。它包括:
nbMoves
:這是游戲中移動次數的計數器。初始值爲零。winner
:此變量用於跟蹤游戲的穫勝者。初始值爲零,錶示沒有穫勝者。draw
:指示游戲是否以平局結束的標誌。初始狀態爲False(假)。deck
:這是一個3x3的網格,代錶井字棋棋盤。棋盤上的所有點最初都爲空,用零錶示。nextPlayer
:錶示輪到哪個玩家下棋。游戲從玩家1開始,所以最初設置爲1。游戲邏輯包含在play
函數中。它會執行多項檢查以確保有效的移動:
nextPlayer
匹配。nbMoves
,切換nextPlayer
,併檢查這一步棋是否導緻勝利或平局。勝利條件將在最新棋步所在的行、列以及兩個對角線上進行檢查。
如果棋盤上的所有點都被填滿且沒有玩家穫勝(即nbMoves
等於9且winner
仍然爲0),則宣布游戲爲平局。
checkLine
函數用於檢查是否有玩家穫勝。它檢查一條線(包括行、列或對角線)上的所有點是否由衕一玩家填充。如果是,則宣布該玩家爲穫勝者。
與合約的交互用交易來錶示。當玩家通過調用play
函數進行移動時,會生成一筆交易。這筆交易被記録下來,可以在SmartPy IDE的右側麵闆中看到:
不成功或無效的移動也會生成交易,但帶有錯誤指示:
在我們的井字棋游戲中,第一步相對簡單,因爲棋盤是空的。然而,從第二步開始,棋步將變得比較有趣了,因爲它們不僅會曏棋盤上添加棋子,還會調用游戲邏輯來檢查可能的穫勝者。
在第一步之後,nextPlayer
值切換到玩家2。現在,play
函數會驗證玩家2的棋步。合約會執行類似的檢查以確保棋步是有效的,即所選網格點在邊界內併且爲空。
每個玩家落子後,游戲的狀態會髮生變化。nbMoves
會增加,nextPlayer
會切換, deck
也會更新。此外,在每步棋之後,合約都會檢查是否有穫勝者或是否平局。
例如,第一個玩家在棋盤的中央i=1, j=1
進行了一步棋,第二位玩家可以在不衕的位置進行下一步,如i=1, j=2
。這兩個棋步都會經過驗證併成功執行,併生成相應的交易。
後續的棋步以類似的方式進行。每個玩家選擇棋盤上的一個空點,輪流落子。在每一次落子之後,合約都會檢查是否存在穫勝條件。如果一名玩家用他的棋子填滿一整行、整列或整個對角線,則游戲結束,該玩家穫勝。合約狀態中的winner
變量將相應更新。
需要註意的是,一旦有玩家穫勝,就不再允許繼續落子。在游戲結束後嘗試進行棋步都將被視爲無效,相應的交易也將失敗。
在某些游戲中,即使整個游戲棋盤都被填滿,也有可能沒有玩家達到穫勝條件,這將導緻平局。合約的設計中已經包含了處理這種情況的方案。
如果棋盤上的所有點位都被填滿(nbMoves
等於9)併且沒有玩家穫勝(winner
仍然爲0),則游戲爲平局。合約狀態下的draw
標識爲True(真),錶示游戲以平局結束。衕樣,在此點之後,任何後續棋步都是無效的。
井字棋游戲合約測試場景的第二部分對該平局場景進行了演示。它模擬了一繫列導緻平局的棋步,併驗證了合約是否正確處理它。
首先,我們來具體分析一下這個井字棋游戲合約:
Python
# TicTacToe - Example for illustrative purposes only.
import smartpy as sp
@sp.module
def main():
class TicTacToe(sp.Contract):
def __init__(self):
self.data.nbMoves = 0
self.data.winner = 0
self.data.draw = False
self.data.deck = {
0: {0: 0, 1: 0, 2: 0},
1: {0: 0, 1: 0, 2: 0},
2: {0: 0, 1: 0, 2: 0},
}
self.data.nextPlayer = 1
@sp.entrypoint
def play(self, params):
assert self.data.winner == 0 and not self.data.draw
assert params.i >= 0 and params.i < 3
assert params.j >= 0 and params.j < 3
assert params.move == self.data.nextPlayer
assert self.data.deck[params.i][params.j] == 0
self.data.deck[params.i][params.j] = params.move
self.data.nbMoves += 1
self.data.nextPlayer = 3 - self.data.nextPlayer
self.data.winner = self.checkLine(
sp.record(winner=self.data.winner, line=self.data.deck[params.i])
)
self.data.winner = self.checkLine(
sp.record(
winner=self.data.winner,
line={
0: self.data.deck[0][params.j],
1: self.data.deck[1][params.j],
2: self.data.deck[2][params.j],
},
)
)
self.data.winner = self.checkLine(
sp.record(
winner=self.data.winner,
line={
0: self.data.deck[0][0],
1: self.data.deck[1][1],
2: self.data.deck[2][2],
},
)
)
self.data.winner = self.checkLine(
sp.record(
winner=self.data.winner,
line={
0: self.data.deck[0][2],
1: self.data.deck[1][1],
2: self.data.deck[2][0],
},
)
)
if self.data.nbMoves == 9 and self.data.winner == 0:
self.data.draw = True
@sp.private()
def checkLine(self, winner, line):
winner_ = winner
if line[0] != 0 and line[0] == line[1] and line[0] == line[2]:
winner_ = line[0]
return winner_
# Add a game reset function
@sp.entrypoint
def confirm_and_reset(self):
assert self.data.winner != 0 or self.data.draw
self.__init__()
# Tests
if "templates" not in __name__:
@sp.add_test(name="TicTacToe")
def test():
scenario = sp.test_scenario(main)
scenario.h1("Tic-Tac-Toe")
# define a contract
c1 = main.TicTacToe()
# show its representation
scenario.h2("A sequence of interactions with a winner")
scenario += c1
scenario.h2("Message execution")
scenario.h3("A first move in the center")
c1.play(i=1, j=1, move=1)
scenario.h3("A forbidden move")
c1.play(i=1, j=1, move=2).run(valid=False)
scenario.h3("A second move")
c1.play(i=1, j=2, move=2)
scenario.h3("Other moves")
c1.play(i=2, j=1, move=1)
c1.play(i=2, j=2, move=2)
scenario.verify(c1.data.winner == 0)
c1.play(i=0, j=1, move=1)
scenario.verify(c1.data.winner == 1)
scenario.p("Player1 has won")
c1.play(i=0, j=0, move=2).run(valid=False)
c2 = main.TicTacToe()
scenario.h2("A sequence of interactions with a draw")
scenario += c2
scenario.h2("Message execution")
scenario.h3("A first move in the center")
c2.play(i=1, j=1, move=1)
scenario.h3("A forbidden move")
c2.play(i=1, j=1, move=2).run(valid=False)
scenario.h3("A second move")
c2.play(i=1, j=2, move=2)
scenario.h3("Other moves")
c2.play(i=2, j=1, move=1)
c2.play(i=2, j=2, move=2)
c2.play(i=0, j=0, move=1)
c2.play(i=0, j=1, move=2)
c2.play(i=0, j=2, move=1)
c2.play(i=2, j=0, move=2)
c2.play(i=1, j=0, move=1)
# Add tests for game reset
scenario.h2("Testing game reset")
scenario.p("Winner or draw confirmed, now resetting the game")
c1.confirm_and_reset()
scenario.verify(c1.data.nbMoves == 0)
scenario.verify(c1.data.winner == 0)
scenario.verify(not c1.data.draw)
c2.confirm_and_reset()
scenario.verify(c2.data.nbMoves == 0)
scenario.verify(c2.data.winner == 0)
scenario.verify(not c2.data.draw)
我們在Tezos上的井字棋游戲合約是用SmartPy語言編寫的。它包含兩個主要部分組成:合約狀態和游戲邏輯。
合約的狀態通過**init
函數初始化。它包括:
nbMoves
:這是游戲中移動次數的計數器。初始值爲零。winner
:此變量用於跟蹤游戲的穫勝者。初始值爲零,錶示沒有穫勝者。draw
:指示游戲是否以平局結束的標誌。初始狀態爲False(假)。deck
:這是一個3x3的網格,代錶井字棋棋盤。棋盤上的所有點最初都爲空,用零錶示。nextPlayer
:錶示輪到哪個玩家下棋。游戲從玩家1開始,所以最初設置爲1。游戲邏輯包含在play
函數中。它會執行多項檢查以確保有效的移動:
nextPlayer
匹配。nbMoves
,切換nextPlayer
,併檢查這一步棋是否導緻勝利或平局。勝利條件將在最新棋步所在的行、列以及兩個對角線上進行檢查。
如果棋盤上的所有點都被填滿且沒有玩家穫勝(即nbMoves
等於9且winner
仍然爲0),則宣布游戲爲平局。
checkLine
函數用於檢查是否有玩家穫勝。它檢查一條線(包括行、列或對角線)上的所有點是否由衕一玩家填充。如果是,則宣布該玩家爲穫勝者。
與合約的交互用交易來錶示。當玩家通過調用play
函數進行移動時,會生成一筆交易。這筆交易被記録下來,可以在SmartPy IDE的右側麵闆中看到:
不成功或無效的移動也會生成交易,但帶有錯誤指示:
在我們的井字棋游戲中,第一步相對簡單,因爲棋盤是空的。然而,從第二步開始,棋步將變得比較有趣了,因爲它們不僅會曏棋盤上添加棋子,還會調用游戲邏輯來檢查可能的穫勝者。
在第一步之後,nextPlayer
值切換到玩家2。現在,play
函數會驗證玩家2的棋步。合約會執行類似的檢查以確保棋步是有效的,即所選網格點在邊界內併且爲空。
每個玩家落子後,游戲的狀態會髮生變化。nbMoves
會增加,nextPlayer
會切換, deck
也會更新。此外,在每步棋之後,合約都會檢查是否有穫勝者或是否平局。
例如,第一個玩家在棋盤的中央i=1, j=1
進行了一步棋,第二位玩家可以在不衕的位置進行下一步,如i=1, j=2
。這兩個棋步都會經過驗證併成功執行,併生成相應的交易。
後續的棋步以類似的方式進行。每個玩家選擇棋盤上的一個空點,輪流落子。在每一次落子之後,合約都會檢查是否存在穫勝條件。如果一名玩家用他的棋子填滿一整行、整列或整個對角線,則游戲結束,該玩家穫勝。合約狀態中的winner
變量將相應更新。
需要註意的是,一旦有玩家穫勝,就不再允許繼續落子。在游戲結束後嘗試進行棋步都將被視爲無效,相應的交易也將失敗。
在某些游戲中,即使整個游戲棋盤都被填滿,也有可能沒有玩家達到穫勝條件,這將導緻平局。合約的設計中已經包含了處理這種情況的方案。
如果棋盤上的所有點位都被填滿(nbMoves
等於9)併且沒有玩家穫勝(winner
仍然爲0),則游戲爲平局。合約狀態下的draw
標識爲True(真),錶示游戲以平局結束。衕樣,在此點之後,任何後續棋步都是無效的。
井字棋游戲合約測試場景的第二部分對該平局場景進行了演示。它模擬了一繫列導緻平局的棋步,併驗證了合約是否正確處理它。