Bài học 3

Разработка игры TicTacToe на Tezos

Мир игр на блокчейне изобилует возможностями для разработчиков. Он предоставляет уникальный и инновационный способ интеграции децентрализованных и прозрачных механизмов в игры. Разрабатывая игры на блокчейне, мы можем включить в них такие функции, как безопасные и прозрачные транзакции, право собственности на внутриигровые активы и многое другое. В этом уроке мы сделаем шаг в блокчейн-игры, разработав классическую игру TicTacToe на блокчейне Tezos. Наша цель в этом уроке - понять динамику игровой логики и управления состояниями в игре на основе блокчейна.

Давайте начнем с изучения нашего контракта на игру TicTacToe:

Структура контракта

Python
# TicTacToe - Пример только для иллюстративных целей.

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 и params.i < 3
 assert params.j >= 0 и 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 и line[0] == line[1] и line[0] == line[2]:
 winner_ = line[0]
 return winner_

 # Добавить функцию сброса игры
 @sp.entrypoint
 def confirm_and_reset(self):
 assert self.data.winner != 0 или self.data.draw
 self.__init__()

# Тесты
if "templates" not in __name__:

   @sp.add_test(name="TicTacToe")
 def test():
 scenario = sp.test_scenario(main)
        scenario.h1("Tic-Tac-Toe")
        # определите контракт
 c1 = main.TicTacToe()

 # покажите его представление
 scenario.h2("A Последовательность взаимодействий с победителем")
 scenario += c1
 scenario.h2("Message выполнение")
 scenario.h3("A первый ход в центр")
 c1.play(i=1, j=1, move=1)
 scenario.h3("A запрещенный ход")
 c1.play(i=1, j=1, move=2).run(valid=False)
        scenario.h3("A второй ход")
 c1.play(i=1, j=2, move=2)
 scenario.h3("Другое 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 выиграл")
 c1.play(i=0, j=0, move=2).run(valid=False)

        c2 = main.TicTacToe()
 scenario.h2("A Последовательность взаимодействий с жеребьевкой")
 scenario += c2
 scenario.h2("Message выполнение")
 scenario.h3("A первый ход в центр")
 c2.play(i=1, j=1, move=1)
 scenario.h3("A запрещенный ход")
 c2.play(i=1, j=1, move=2).run(valid=False)
        scenario.h3("A второй ход")
 c2.play(i=1, j=2, move=2)
 scenario.h3("Другое 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)

 # Добавьте тесты для перезагрузки игры
 scenario.h2("Testing game reset")
 scenario.p("Winner или ничья подтверждена, теперь сбрасываем игру")
 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)

Контракт для нашей игры TicTacToe на Tezos написан на языке SmartPy. Он состоит из двух основных частей: состояние контракта и логика игры.

Состояние контракта

Состояние контракта инициализируется в функции**init. Он включает в себя:

  • nbMoves: Это счетчик количества ходов, сделанных в игре. Она начинается с нуля.
  • winner (победитель): Здесь ведется учет победителя игры. Первоначально он равен нулю, что означает отсутствие победителя.
  • draw (ничья): Это флаг, указывающий, закончилась ли игра вничью. Первоначально это Ложь.
  • Колода: Это сетка 3x3, представляющая собой доску TicTacToe. Все места на доске изначально пусты, представлены нулями.
  • nextPlayer (следующий игрок): Это указывает, чья очередь играть. Игра начинается с игроком 1, поэтому первоначально установлено значение 1.

Логика игры

Логика игры заключена в функции play. Он выполняет несколько проверок, чтобы убедиться в правильности хода:

  • Он проверяет, что ни один игрок еще не выиграл и что игра не является ничейной.
  • Она проверяет, что индексы места сетки, выбранного игроком, находятся в границах сетки.
  • Он проверяет, чтобы игрок, делающий ход, совпадал с nextPlayer.
  • Это гарантирует, что выбранное место на сетке будет пустым.
    Как только ход сделан, логика игры увеличивает значение nbMoves, переключает следующего игрока (nextPlayer) и проверяет, привел ли этот ход к победе или ничьей.

Условие выигрыша проверяется по строке и колонке последнего хода, а также по двум диагоналям.

Если все места на доске заполнены и ни один игрок не выиграл (т.е. nbMoves равно 9, а победитель все еще 0), игра объявляется ничейной.

Проверка на выигрыш

Функция checkLine используется для проверки того, выиграл ли какой-либо игрок. Она проверяет, все ли места в линии (которая может быть строкой, колонкой или диагональю) заполнены одним и тем же игроком. Если да, то этот игрок объявляется победителем.

Взаимодействие с контрактом

Взаимодействия с контрактом представляются в виде транзакций. Когда игрок делает ход, вызывая функцию play, он генерирует транзакцию. Эта транзакция записывается в журнал и видна на правой панели SmartPy IDE:

Неудачное или недействительное перемещение также генерирует транзакцию, но с индикацией ошибки:

Второй ход и далее

Первый ход в нашей игре TicTacToe относительно прост, поскольку игровое поле пусто. Однако все становится более интересным со вторым ходом и последующими ходами. Эти ходы не только добавляют фигуры на игровое поле, но и вызывают логику игры для проверки потенциальных победителей.

После первого хода значение nextPlayer переключается на игрока 2. Теперь функция play проверяет ход игрока 2. Аналогичные проверки выполняются для того, чтобы убедиться, что перемещение является действительным, т.е. выбранный участок сетки находится в пределах границ и не пуст.

По мере того, как каждый игрок делает ход, состояние игры изменяется. nbMoves увеличивается, nextPlayer переключается, и колода обновляется. Кроме того, после каждого хода контракт проверяет, есть ли победитель или это ничья.

Например, после того, как первый игрок сделает ход в центре доски в точке i=1, j=1, второй игрок может сыграть в другой точке, скажем, i=1, j=2. Оба этих хода будут подтверждены и успешно выполнены, с генерированием соответствующих транзакций.

Другие движения и развитие игры

Последующие перемещения продолжаются аналогичным образом. Каждый игрок играет по очереди, выбирая свободное место на доске. После каждого хода контракт проверяет наличие выигрышного состояния. Если игрок заполняет своим символом всю строку, колонку или диагональ, игра заканчивается, и этот игрок объявляется победителем. Переменная winner в состоянии контракта будет обновлена соответствующим образом.

Важно отметить, что как только игрок выиграл, никакие дальнейшие ходы не действительны. Любая попытка сделать ход после окончания игры будет считаться недействительной, и соответствующая операция будет провалена.

Сценарий розыгрыша

В некоторых играх возможно, что ни один игрок не достигнет выигрышного состояния даже после того, как будет заполнено все игровое поле. В результате получается ничья. Контракт был разработан таким образом, чтобы справиться и с этой ситуацией.

Если все места на доске заполнены(nbMoves равно 9) и ни один игрок не выиграл(победитель остается в 0), игра объявляется ничейной. Флаг ничьей в состоянии контракта устанавливается в True, указывая на то, что игра закончилась вничью. И снова, после этого момента никакие дальнейшие ходы не действительны. Любые попытки сделать ход после ничьей также будут безуспешными.

Вторая часть тестового сценария контракта TicTacToe демонстрирует этот сценарий розыгрыша. Он моделирует серию ходов, приводящих к ничьей, и проверяет, правильно ли контракт ее обрабатывает.

Tuyên bố từ chối trách nhiệm
* Đầu tư tiền điện tử liên quan đến rủi ro đáng kể. Hãy tiến hành một cách thận trọng. Khóa học không nhằm mục đích tư vấn đầu tư.
* Khóa học được tạo bởi tác giả đã tham gia Gate Learn. Mọi ý kiến chia sẻ của tác giả không đại diện cho Gate Learn.
Danh mục
Bài học 3

Разработка игры TicTacToe на Tezos

Мир игр на блокчейне изобилует возможностями для разработчиков. Он предоставляет уникальный и инновационный способ интеграции децентрализованных и прозрачных механизмов в игры. Разрабатывая игры на блокчейне, мы можем включить в них такие функции, как безопасные и прозрачные транзакции, право собственности на внутриигровые активы и многое другое. В этом уроке мы сделаем шаг в блокчейн-игры, разработав классическую игру TicTacToe на блокчейне Tezos. Наша цель в этом уроке - понять динамику игровой логики и управления состояниями в игре на основе блокчейна.

Давайте начнем с изучения нашего контракта на игру TicTacToe:

Структура контракта

Python
# TicTacToe - Пример только для иллюстративных целей.

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 и params.i < 3
 assert params.j >= 0 и 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 и line[0] == line[1] и line[0] == line[2]:
 winner_ = line[0]
 return winner_

 # Добавить функцию сброса игры
 @sp.entrypoint
 def confirm_and_reset(self):
 assert self.data.winner != 0 или self.data.draw
 self.__init__()

# Тесты
if "templates" not in __name__:

   @sp.add_test(name="TicTacToe")
 def test():
 scenario = sp.test_scenario(main)
        scenario.h1("Tic-Tac-Toe")
        # определите контракт
 c1 = main.TicTacToe()

 # покажите его представление
 scenario.h2("A Последовательность взаимодействий с победителем")
 scenario += c1
 scenario.h2("Message выполнение")
 scenario.h3("A первый ход в центр")
 c1.play(i=1, j=1, move=1)
 scenario.h3("A запрещенный ход")
 c1.play(i=1, j=1, move=2).run(valid=False)
        scenario.h3("A второй ход")
 c1.play(i=1, j=2, move=2)
 scenario.h3("Другое 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 выиграл")
 c1.play(i=0, j=0, move=2).run(valid=False)

        c2 = main.TicTacToe()
 scenario.h2("A Последовательность взаимодействий с жеребьевкой")
 scenario += c2
 scenario.h2("Message выполнение")
 scenario.h3("A первый ход в центр")
 c2.play(i=1, j=1, move=1)
 scenario.h3("A запрещенный ход")
 c2.play(i=1, j=1, move=2).run(valid=False)
        scenario.h3("A второй ход")
 c2.play(i=1, j=2, move=2)
 scenario.h3("Другое 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)

 # Добавьте тесты для перезагрузки игры
 scenario.h2("Testing game reset")
 scenario.p("Winner или ничья подтверждена, теперь сбрасываем игру")
 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)

Контракт для нашей игры TicTacToe на Tezos написан на языке SmartPy. Он состоит из двух основных частей: состояние контракта и логика игры.

Состояние контракта

Состояние контракта инициализируется в функции**init. Он включает в себя:

  • nbMoves: Это счетчик количества ходов, сделанных в игре. Она начинается с нуля.
  • winner (победитель): Здесь ведется учет победителя игры. Первоначально он равен нулю, что означает отсутствие победителя.
  • draw (ничья): Это флаг, указывающий, закончилась ли игра вничью. Первоначально это Ложь.
  • Колода: Это сетка 3x3, представляющая собой доску TicTacToe. Все места на доске изначально пусты, представлены нулями.
  • nextPlayer (следующий игрок): Это указывает, чья очередь играть. Игра начинается с игроком 1, поэтому первоначально установлено значение 1.

Логика игры

Логика игры заключена в функции play. Он выполняет несколько проверок, чтобы убедиться в правильности хода:

  • Он проверяет, что ни один игрок еще не выиграл и что игра не является ничейной.
  • Она проверяет, что индексы места сетки, выбранного игроком, находятся в границах сетки.
  • Он проверяет, чтобы игрок, делающий ход, совпадал с nextPlayer.
  • Это гарантирует, что выбранное место на сетке будет пустым.
    Как только ход сделан, логика игры увеличивает значение nbMoves, переключает следующего игрока (nextPlayer) и проверяет, привел ли этот ход к победе или ничьей.

Условие выигрыша проверяется по строке и колонке последнего хода, а также по двум диагоналям.

Если все места на доске заполнены и ни один игрок не выиграл (т.е. nbMoves равно 9, а победитель все еще 0), игра объявляется ничейной.

Проверка на выигрыш

Функция checkLine используется для проверки того, выиграл ли какой-либо игрок. Она проверяет, все ли места в линии (которая может быть строкой, колонкой или диагональю) заполнены одним и тем же игроком. Если да, то этот игрок объявляется победителем.

Взаимодействие с контрактом

Взаимодействия с контрактом представляются в виде транзакций. Когда игрок делает ход, вызывая функцию play, он генерирует транзакцию. Эта транзакция записывается в журнал и видна на правой панели SmartPy IDE:

Неудачное или недействительное перемещение также генерирует транзакцию, но с индикацией ошибки:

Второй ход и далее

Первый ход в нашей игре TicTacToe относительно прост, поскольку игровое поле пусто. Однако все становится более интересным со вторым ходом и последующими ходами. Эти ходы не только добавляют фигуры на игровое поле, но и вызывают логику игры для проверки потенциальных победителей.

После первого хода значение nextPlayer переключается на игрока 2. Теперь функция play проверяет ход игрока 2. Аналогичные проверки выполняются для того, чтобы убедиться, что перемещение является действительным, т.е. выбранный участок сетки находится в пределах границ и не пуст.

По мере того, как каждый игрок делает ход, состояние игры изменяется. nbMoves увеличивается, nextPlayer переключается, и колода обновляется. Кроме того, после каждого хода контракт проверяет, есть ли победитель или это ничья.

Например, после того, как первый игрок сделает ход в центре доски в точке i=1, j=1, второй игрок может сыграть в другой точке, скажем, i=1, j=2. Оба этих хода будут подтверждены и успешно выполнены, с генерированием соответствующих транзакций.

Другие движения и развитие игры

Последующие перемещения продолжаются аналогичным образом. Каждый игрок играет по очереди, выбирая свободное место на доске. После каждого хода контракт проверяет наличие выигрышного состояния. Если игрок заполняет своим символом всю строку, колонку или диагональ, игра заканчивается, и этот игрок объявляется победителем. Переменная winner в состоянии контракта будет обновлена соответствующим образом.

Важно отметить, что как только игрок выиграл, никакие дальнейшие ходы не действительны. Любая попытка сделать ход после окончания игры будет считаться недействительной, и соответствующая операция будет провалена.

Сценарий розыгрыша

В некоторых играх возможно, что ни один игрок не достигнет выигрышного состояния даже после того, как будет заполнено все игровое поле. В результате получается ничья. Контракт был разработан таким образом, чтобы справиться и с этой ситуацией.

Если все места на доске заполнены(nbMoves равно 9) и ни один игрок не выиграл(победитель остается в 0), игра объявляется ничейной. Флаг ничьей в состоянии контракта устанавливается в True, указывая на то, что игра закончилась вничью. И снова, после этого момента никакие дальнейшие ходы не действительны. Любые попытки сделать ход после ничьей также будут безуспешными.

Вторая часть тестового сценария контракта TicTacToe демонстрирует этот сценарий розыгрыша. Он моделирует серию ходов, приводящих к ничьей, и проверяет, правильно ли контракт ее обрабатывает.

Tuyên bố từ chối trách nhiệm
* Đầu tư tiền điện tử liên quan đến rủi ro đáng kể. Hãy tiến hành một cách thận trọng. Khóa học không nhằm mục đích tư vấn đầu tư.
* Khóa học được tạo bởi tác giả đã tham gia Gate Learn. Mọi ý kiến chia sẻ của tác giả không đại diện cho Gate Learn.