首先,我们来具体分析一下这个井字棋游戏合约:
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(真),表示游戏以平局结束。同样,在此点之后,任何后续棋步都是无效的。
井字棋游戏合约测试场景的第二部分对该平局场景进行了演示。它模拟了一系列导致平局的棋步,并验证了合约是否正确处理它。