Library of the game tic-tac-toe + AI. Experimental functionality with dynamic parameters of the game field
Install the latest stable version of titato | PYPI:
pip install titato
- Unlimited number of players in one game
- Creating a playing field of any size:
-
- With size parameters required: row, column and winning combination
- Artificial Intelligence algorithm works with any game settings
# Visualization of dynamic settings of the playing field
10 x 10 player vs player vs player
+-----+---+---+---+---+---+---+---+---+---+---+
6 x 6 player vs player | ↓/→ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+-----+---+---+---+---+---+---+ +-----+---+---+---+---+---+---+---+---+---+---+
3 x 3 player vs player | ↓/→ | 0 | 1 | 2 | 3 | 4 | 5 | | 0: | * | * | * | * | * | * | * | * | * | * |
+-----+---+---+---+ +-----+---+---+---+---+---+---+ | 1: | X | * | * | * | * | * | * | O | * | * |
| ↓/→ | 0 | 1 | 2 | | 0: | * | * | * | * | X | * | | 2: | * | X | * | * | * | * | O | * | * | * |
+-----+---+---+---+ | 1: | * | * | * | * | O | * | | 3: | * | * | P | * | * | P | * | * | * | * |
| 0: | O | * | X | | 2: | * | * | * | * | O | * | | 4: | * | * | * | X | O | * | * | * | * | * |
| 1: | * | O | * | | 3: | X | X | X | X | O | X | | 5: | * | * | * | O | X | * | * | * | * | * |
| 2: | X | * | O | | 4: | * | * | * | * | O | * | | 6: | * | * | O | * | * | X | * | * | * | * |
+-----+---+---+---+ | 5: | * | * | * | * | O | * | | 7: | * | O | * | * | * | * | X | * | O | * |
+-----+---+---+---+---+---+---+ | 8: | O | * | * | * | * | * | * | * | * | * |
| 9: | * | * | X | P | P | P | P | O | P | P |
+-----+---+---+---+---+---+---+---+---+---+---+
Start a quick game in the console
Translations into other languages:
Demo game in console
🤖 Expand
We can set any size for the game table, and many players for the game
- AI and the list of winning combinations for players are adjusted automatically
We will use this. Instead of the classic 3×3 table — let's create 7×7, and 3 players Let the bots play with each other this time. Let's look at it.
from titato.client import GameConsole
from titato.core.player import Player, Players, Symbol
from titato.core.table import Table, TableParam
if __name__ == "__main__":
p1 = Player(name="PETROS_ANDROID:1", symbol=Symbol('X'), role=Player.Role.ANDROID)
p2 = Player(name="AMIGOS_ANDROID:2", symbol=Symbol('O'), role=Player.Role.ANDROID)
p3 = Player(name="GENTOS_ANDROID:3", symbol=Symbol('K'), role=Player.Role.ANDROID)
# p4 = Player(name="PLAYER", symbol=Symbol('P'), role=Player.Role.USER)
players = Players(players=[p1, p2, p3])
table = Table(param=TableParam(ROW=7, COLUMN=7, COMBINATION=5))
game = GameConsole(players=players, table=table)
game.start_game()
Attempt #1
WIN: PETROS_ANDROID:1 < X > | COMB: < ((1, 1), (2, 2), (3, 3), (4, 4), (5, 5)) >
+-----+---+---+---+---+---+---+---+
| ↓/→ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+-----+---+---+---+---+---+---+---+
| 0: | O | * | * | * | K | X | K |
| 1: | K | X | * | * | O | * | X |
| 2: | X | K | X | O | K | O | X |
| 3: | K | O | K | X | X | K | O |
| 4: | K | O | X | X | X | O | X |
| 5: | O | O | K | O | O | X | X |
| 6: | * | * | O | K | * | K | K |
+-----+---+---+---+---+---+---+---+
Attempt #2
PEACE: ALL USED CELLS
+-----+---+---+---+---+---+---+---+
| ↓/→ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+-----+---+---+---+---+---+---+---+
| 0: | X | K | K | O | O | O | X |
| 1: | K | X | X | K | X | O | K |
| 2: | X | K | O | O | O | X | K |
| 3: | O | X | K | K | O | K | X |
| 4: | X | O | K | O | O | X | O |
| 5: | X | X | O | X | X | K | K |
| 6: | K | K | X | O | K | O | X |
+-----+---+---+---+---+---+---+---+
Attempt #3
WIN: AMIGOS_ANDROID:2 < O > | COMB: < ((3, 1), (3, 2), (3, 3), (3, 4), (3, 5)) >
+-----+---+---+---+---+---+---+---+
| ↓/→ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+-----+---+---+---+---+---+---+---+
| 0: | * | * | * | * | K | * | * |
| 1: | * | * | * | * | K | * | * |
| 2: | * | X | * | * | * | * | * |
| 3: | * | O | O | O | O | O | X |
| 4: | * | * | * | * | K | * | * |
| 5: | * | * | * | * | * | * | * |
| 6: | K | X | X | X | O | X | K |
+-----+---+---+---+---+---+---+---+
* Brief description of GameConsole
The .start_game
method activates a while a loop with an exit condition,
if the game will be logically finished (There is a win / All cells are occupied == game_console.game_state.is_finished
)
- For queued players that return True for the
player.is_android
method, automatic cell search is applied, players who return True forplayer.is_user
will be prompted to enter indexes in the console
If you want to load the processor with a hundred bots in a 1000×1000 field — no one will interfere!
Let's go further.
from titato.core.game import Game
from titato.core.player import Player, Players, Symbol
from titato.core.table import Table, TableParam
p1 = Player(name="VERA_ANDROID", symbol=Symbol('X'), role=Player.Role.ANDROID)
p2 = Player(name="BOGDAN_PLAYER", symbol=Symbol('O'), role=Player.Role.USER)
players = Players(players=[p1, p2])
table = TableDefault(param=TableParam(ROW=3, COLUMN=3, COMBINATION=3))
game = Game(players=players, table=table)
- These variables will be used when describing the methods
Main. The process of the game, the processing of moves, the issuing of the result.
📂 Expand
def step(self, index_row: int, index_column: int, player: PlayerBase)
# input
game.step(index_row=1, index_column=0, player=p2)
game.step(index_row=1, index_column=2, player=p2)
game.step(index_row=1, index_column=1, player=p2)
# output
+-----+---+---+---+ -> +-----+---+---+---+ -> +-----+---+---+---+
| ↓/→ | 0 | 1 | 2 | -> | ↓/→ | 0 | 1 | 2 | -> | ↓/→ | 0 | 1 | 2 |
+-----+---+---+---+ -> +-----+---+---+---+ -> +-----+---+---+---+
| 0: | * | * | * | -> | 0: | * | * | * | -> | 0: | * | * | * |
| 1: | O | * | * | -> | 1: | O | * | O | -> | 1: | O | O | O |
| 2: | * | * | * | -> | 2: | * | * | * | -> | 2: | * | * | * |
+-----+---+---+---+ -> +-----+---+---+---+ -> +-----+---+---+---+
The function sets the player symbol player.symbol
in the cell at the specified indices.
After successful installation, the player.count_steps
counter is incremented by +1,
and game.table.count_free_cells
is decreased by -1
Note:
- If the transferred indexes do not match the possible ones in the table — an error
TableIndexError
- If you try to set a new symbol on an already occupied cell — error
CellAlreadyUsedError
def result(self, player: PlayerBase) -> GameStateT
Let's check the result of our previous 3 steps (in the block above), we expect winnings
# input
res = game.result(player=p2)
match res.code:
case ResultCode.NO_RESULT:
print('STATUS: NO RESULT')
case ResultCode.WINNER:
print(f'STATUS: WINNER. Player: {res.win_player.name}, Win comb: {res.win_combination}')
case ResultCode.ALL_CELLS_USED:
print('STATUS: DRAW')
# output
STATUS: WINNER. Player: BOGDAN_PLAYER, Win comb: ((1, 0), (1, 1), (1, 2))
For a given player, the function performs 2 checks:
- Check for winnings. Compares player moves with winning combinations
game.table.combinations
- Checking for a tie. Checks with free cell count
game.table.count_free_cells
When one of the two probabilities is valid, the game.set_winner
or game.set_draw
method is automatically called
After checks and possible modifications — returns the object: game.game_state
Note:
assert res == game.game_state # True
- To check that one of the triggers that logically ends the game worked — call the game_state method:
.is_finished
, if True — you have a win or a draw. You can also use.is_winner
or.is_draw
.
For more details, see GameState section
def step_result(self, index_row: int, index_column: int, player: PlayerBase) -> GameStateT
- Unifying function. Replaces the successive call of
game.step
andgame.result
, returns the result
def ai_get_step(self, player: PlayerBase) -> CellIndex
AI returns a tuple with the two indices (index_row: int, index_column: int
) of the cell
- For more details, see AI section
def ai_step(self, player: PlayerBase)
- Unifying function. Replaces the successive call of
game.ai_get_step
andgame.step
def ai_step_result(self, player: PlayerBase) -> GameStateT
- Unifying function. Replaces the successive call of
game.ai_get_step
andgame.step_result
, returns the result
def set_winner(self, player: PlayerBase, win_combination)
Updates the game result in the game.game_state
object, replacing game.game_state.code
with
ResultCode.WINNER
, and
adds the winning result to the game.game_state.win_player
and game.game_state.win_combination
fields
Note:
- This method is automatically called in the
game.result
method if the win trigger fires - The result is updated via the
game.game_result.update
method
def set_draw(self)
Updates the game result in the game.game_state
, object, replacing game.game_state.code
with ResultCode.ALL_CELLS_USED
Note:
- This method is automatically called in the
game.result
method if a draw trigger fires - The result is updated via the
game.game_result.update
method
Sets moves, combinations for the game board
📂 Expand
table = game.table
@property
def game_field(self) -> GameFieldType
Returns a 2D list of the playing field
Note:
- It is also available in:
game.game_field
@property
def combinations(self) -> CombsType
Returns a list of all winning combinations for this table
- Combinations are created automatically according to the parameters of the table, or manually passed to the constructor of the Table class instance
@property
def count_free_cells(self) -> int
Returns the number of free cells in the game filed
@property
def set_symbol_cell(self, index_row: int, index_column: int, symbol: SymbolBase)
Sets the passed character at the specified indices of the playing field. Decreases the number of free cells by -1
Note:
- It is this method that is automatically called in
game.step
List of players and queue
📂 Expand
players = game.players
@property
def players_list(self) -> list[PlayerT]:
Returns a list of all players
Note:
- This list changes after using the
players.shuffle_players
method
@property
def current_player(self) -> PlayerT
Returns the current player from the queue
def set_get_next_player(self) -> PlayerT
Replaces the current player with the next player in the queue and returns it
Note:
- After that, this player is available in the `players.current_player' method
def shuffle_players(self)
Shuffles the player list and replaces the current queue with a new one.
Note:
- The first player from the new queue will be set as the current one, and is available in
players.current_player
The player, his date
📂 Expand
# titato.core.player.player.py
class Role(Enum):
USER = 1
ANDROID = 2
player = game.current_player
@property
def role(self) -> Role
@property
def symbol(self) -> SymbolBase
Returns an object of class Symbol of the player
@property
def count_steps(self) -> int
Returns the number of steps taken by the player
@property
def is_android(self) -> bool
Returns True if the player has role Role.ANDROID
Otherwise — False
@property
def is_user(self) -> bool
Returns True if the player has role Role.USER
Otherwise — False
def add_count_step(self)
Adds +1 to the player's step counter
Note:
- This method is automatically called in
table.set_symbol_cell
Current game information
📂 Expand
# titato.core.game.result.py
class ResultCode(Enum):
NO_RESULT = 0
ALL_CELLS_USED = 1
WINNER = 2
game_state = game.game_state
@property
def code(self) -> ResultCode
Returns the status code of the game
Note:
- The initial value is set to
Result Code.NO_RESULT
@property
def win_player(self) -> Optional[PlayerBase]
Returns the winning player if it was added by the game_result.update
method
@property
def win_combination(self) -> Optional[CombType]
Returns the winning combination if it was added by the game_result.update
method
@property
def is_finished(self) -> bool
Returns True if game_result.code
is ResultCode.ALL_CELLS_USED
or ResultCode.WINNER
Otherwise — False
@property
def is_continues(self) -> bool
Returns True if game_result.code
is ResultCode.NO_RESULT
Otherwise — False
@property
def is_winner(self) -> bool
Returns True if game_result.code
is ResultCode.WINNER
Otherwise — False
@property
def is_draw(self) -> bool
Returns True if game_result.code
is ResultCode.ALL_CELLS_USED
Otherwise — False
def update(self,
code: Optional[ResultCode] = None,
win_player: Optional[PlayerBase] = None,
win_combination: Optional[CombType] = None)
Updates data about the current game result.
Note:
game_result.code
- automatically updated when thegame.set_draw
orgame.set_winner
method is usedgame_result.win_player
andgame_result.win_combination
-
automatically updated when thegame.set_winner
method is used
For more details, see section Game, methods:game.set_draw
andgame.set_winner
A short work example:
📂 Expand
p1 = Player(name="PLAYER", symbol=Symbol('X'))
p2 = Player(name="ANDROID", symbol=Symbol('O'))
...
game.step(2, 2, player=p1)
game.step(0, 0, player=p1)
game.ai_step(p2) # result in second table
+-----+---+---+---+ -> +-----+---+---+---+
| ↓/→ | 0 | 1 | 2 | -> | ↓/→ | 0 | 1 | 2 |
+-----+---+---+---+ -> +-----+---+---+---+
| 0: | X | * | * | -> | 0: | X | * | * |
| 1: | * | * | * | -> | 1: | * | O | * |
| 2: | * | * | X | -> | 2: | * | * | X |
+-----+---+---+---+ -> +-----+---+---+---+
- The AI algorithm understands that the opponent's next move is likely to collect a winning combination, so it blocks it
Let's consider the second situation
game.step(0, 0, player=p1) # X
game.step(2, 0, player=p1) # X
game.step(0, 2, player=p2) # O
game.step(2, 2, player=p2) # O
game.ai_step(p2) # result in second table
+-----+---+---+---+ -> +-----+---+---+---+
| ↓/→ | 0 | 1 | 2 | -> | ↓/→ | 0 | 1 | 2 |
+-----+---+---+---+ -> +-----+---+---+---+
| 0: | X | * | O | -> | 0: | X | * | O |
| 1: | * | * | * | -> | 1: | * | * | O |
| 2: | X | * | O | -> | 2: | X | * | O |
+-----+---+---+---+ -> +-----+---+---+---+
- The AI algorithm prioritizes its own winnings, with the understanding that there will be no next winning move for the opponent
UA
Демо гра в консолі
🤖 Розгорнути
Ми можемо задати будь-який розмір для ігрової таблиці та безліч гравців для гри
- AI й перелік виграшних комбінацій для гравців підлаштуються автоматично
Скористаємося цим. Замість класичної таблиці 3х3 - створимо 7х7, та 3 гравці
Цього разу боти хай грають один з одним. Поглянемо на це
from titato.client import GameConsole
from titato.core.player import Player, Players, Symbol
from titato.core.table import Table, TableParam
if __name__ == "__main__":
p1 = Player(name="PETROS_ANDROID:1", symbol=Symbol('X'), role=Player.Role.ANDROID)
p2 = Player(name="AMIGOS_ANDROID:2", symbol=Symbol('O'), role=Player.Role.ANDROID)
p3 = Player(name="GENTOS_ANDROID:3", symbol=Symbol('K'), role=Player.Role.ANDROID)
# p4 = Player(name="PLAYER", symbol=Symbol('P'), role=Player.Role.USER)
players = Players(players=[p1, p2, p3])
table = Table(param=TableParam(ROW=7, COLUMN=7, COMBINATION=5))
game = GameConsole(players=players, table=table)
game.start_game()
Спроба №1
WIN: PETROS_ANDROID:1 < X > | COMB: < ((1, 1), (2, 2), (3, 3), (4, 4), (5, 5)) >
+-----+---+---+---+---+---+---+---+
| ↓/→ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+-----+---+---+---+---+---+---+---+
| 0: | O | * | * | * | K | X | K |
| 1: | K | X | * | * | O | * | X |
| 2: | X | K | X | O | K | O | X |
| 3: | K | O | K | X | X | K | O |
| 4: | K | O | X | X | X | O | X |
| 5: | O | O | K | O | O | X | X |
| 6: | * | * | O | K | * | K | K |
+-----+---+---+---+---+---+---+---+
Спроба №2
PEACE: ALL USED CELLS
+-----+---+---+---+---+---+---+---+
| ↓/→ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+-----+---+---+---+---+---+---+---+
| 0: | X | K | K | O | O | O | X |
| 1: | K | X | X | K | X | O | K |
| 2: | X | K | O | O | O | X | K |
| 3: | O | X | K | K | O | K | X |
| 4: | X | O | K | O | O | X | O |
| 5: | X | X | O | X | X | K | K |
| 6: | K | K | X | O | K | O | X |
+-----+---+---+---+---+---+---+---+
Спроба №3
WIN: AMIGOS_ANDROID:2 < O > | COMB: < ((3, 1), (3, 2), (3, 3), (3, 4), (3, 5)) >
+-----+---+---+---+---+---+---+---+
| ↓/→ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+-----+---+---+---+---+---+---+---+
| 0: | * | * | * | * | K | * | * |
| 1: | * | * | * | * | K | * | * |
| 2: | * | X | * | * | * | * | * |
| 3: | * | O | O | O | O | O | X |
| 4: | * | * | * | * | K | * | * |
| 5: | * | * | * | * | * | * | * |
| 6: | K | X | X | X | O | X | K |
+-----+---+---+---+---+---+---+---+
* Короткий опис GameConsole
Метод .start_game
активує цикл while з умовою виходу,
якщо гра буде логічно закінчено (Є виграш / Всі клітинки зайняті == game_console.game_state.is_finished
)
- Для гравців в черзі, які повертають True для методу
player.is_android
застосовуються автоматичний пошук клітинки, а для гравців які повернуть True дляplayer.is_user
буде запропоновано ввести індекси в консолі
Як захочете нагрузити процесор сотнею ботів в 1000х1000 полі — ніхто не завадить! Підемо далі
from titato.core.game import Game
from titato.core.player import Player, Players, Symbol
from titato.core.table import Table, TableParam
p1 = Player(name="VERA_ANDROID", symbol=Symbol('X'), role=Player.Role.ANDROID)
p2 = Player(name="BOGDAN_PLAYER", symbol=Symbol('O'), role=Player.Role.USER)
players = Players(players=[p1, p2])
table = TableDefault(param=TableParam(ROW=3, COLUMN=3, COMBINATION=3))
game = Game(players=players, table=table)
- Ці змінні будуть використовуватись при описі методів
Головний. Процес гри, обробка ходів, видача результату.
📂 Розгорнути
def step(self, index_row: int, index_column: int, player: PlayerBase)
# input
game.step(index_row=1, index_column=0, player=p2)
game.step(index_row=1, index_column=2, player=p2)
game.step(index_row=1, index_column=1, player=p2)
# output
+-----+---+---+---+ -> +-----+---+---+---+ -> +-----+---+---+---+
| ↓/→ | 0 | 1 | 2 | -> | ↓/→ | 0 | 1 | 2 | -> | ↓/→ | 0 | 1 | 2 |
+-----+---+---+---+ -> +-----+---+---+---+ -> +-----+---+---+---+
| 0: | * | * | * | -> | 0: | * | * | * | -> | 0: | * | * | * |
| 1: | O | * | * | -> | 1: | O | * | O | -> | 1: | O | O | O |
| 2: | * | * | * | -> | 2: | * | * | * | -> | 2: | * | * | * |
+-----+---+---+---+ -> +-----+---+---+---+ -> +-----+---+---+---+
Функція встановлює символ гравця player.symbol
в клітинку за вказаними індексами.
Після успішного встановлення лічильник player.count_steps
збільшується на +1,
а game.table.count_free_cells
зменшується на -1
Примітка:
- Якщо передані індекси не збігаються з можливими в таблиці - помилка
TableIndexError
- Якщо ви намагаєтесь встановити новий символ на вже зайняту клітинку - помилка
CellAlreadyUsedError
def result(self, player: PlayerBase) -> GameStateT
Перевіримо результат наших попередніх 3-ох кроків (в блоці вище), очікуємо виграш
# input
res = game.result(player=p2)
match res.code:
case ResultCode.NO_RESULT:
print('STATUS: NO RESULT')
case ResultCode.WINNER:
print(f'STATUS: WINNER. Player: {res.win_player.name}, Win comb: {res.win_combination}')
case ResultCode.ALL_CELLS_USED:
print('STATUS: DRAW')
# output
STATUS: WINNER. Player: BOGDAN_PLAYER, Win comb: ((1, 0), (1, 1), (1, 2))
Для заданого гравця функція проводить 2 перевірки:
- Пошуку виграшу. Звіряється з виграшними комбінаціями
game.table.combinations
- Перевірка на нічию. Звіряється з показником вільних клітинок
game.table.count_free_cells
Коли одна з двох вірогідностей дійсна, автоматично викликається метод game.set_winner
або game.set_draw
Після перевірок та можливих модифікацій — повертає об'єкт: game.game_state
Примітка:
assert res == game.game_state # True
- Щоб перевірити що одна з тригерів які логічно завершує гру спрацювала — викликаємо в game_state метод:
.is_finished
, якщо True - в нас є виграш або нічия. Також можете використати.is_winner
або.is_draw
.
Детальніше див. розділ GameState
def step_result(self, index_row: int, index_column: int, player: PlayerBase) -> GameStateT
- Об'єднувальний метод. Заміняє почерговий виклик
game.step
іgame.result
, повертає результат останнього
def ai_get_step(self, player: PlayerBase) -> CellIndex
AI повертає кортеж з двома індексами (index_row: int, index_column: int
) клітинки
- Детальніше див. розділ AI
def ai_step(self, player: PlayerBase)
- Об'єднувальний метод. Заміняє почерговий виклик.
game.ai_get_step
іgame.step
def ai_step_result(self, player: PlayerBase) -> GameStateT
- Об'єднувальний метод. Заміняє почерговий виклик
game.ai_get_step
іgame.step_result
, повертає результат останнього
def set_winner(self, player: PlayerBase, win_combination)
Оновлює результат гри в об'єкті game.game_state
, замінюючи game.game_state.code
на ResultCode.WINNER
, і
додає результат виграшу в поля game.game_state.win_player
і game.game_state.win_combination
Примітка:
- Цей метод автоматично викликається в роботі методу
game.result
, якщо спрацьовує тригер перемоги - Оновлення результату виконується через метод
game.game_result.update
def set_draw(self)
Оновлює результат гри в об'єкті game.game_state
, замінюючи game.game_state.code
на ResultCode.ALL_CELLS_USED
,
Примітка:
- Цей метод автоматично викликається в роботі методу
game.result
, якщо спрацьовує тригер нічиєї - Оновлення результату виконується через метод
game.game_result.update
Виставляння ходів, комбінації для таблиці
📂 Розгорнути
table = game.table
@property
def game_field(self) -> GameFieldType
Повертає двовимірний список ігрового поля
Примітка:
- Також доступний в
game.game_field
@property
def combinations(self) -> CombsType
Повертає список всіх виграшних комбінацій для цієї таблиці
- Комбінації створюються автоматично за параметрами таблиці, або передаються вручну в конструктор екземпляра класу Table
@property
def count_free_cells(self) -> int
Повертає кількість вільних клітинок в таблиці
@property
def set_symbol_cell(self, index_row: int, index_column: int, symbol: SymbolBase)
Встановлює переданий символ за вказаними індексами ігрового поля.
Зменшує рахунок вільних клітинок на -1
Примітка:
- Цей метод автоматично викликається в
game.step
Список гравців і черга
📂 Розгорнути
players = game.players
@property
def players_list(self) -> list[PlayerT]:
Повертає список всіх гравців
Примітка:
- Цей список змінюється після застосування методу
players.shuffle_players
@property
def current_player(self) -> PlayerT
Повертає поточного гравця з черги
def set_get_next_player(self) -> PlayerT
Заміняє поточного гравця на наступного з черги й повертає його
Примітка:
- Після цього цей гравець доступний в методі
players.current_player
def shuffle_players(self)
Перемішує список гравців й заміняє чинну чергу на нову.
Примітка:
- Перший гравець з нової черги буде встановлений як теперішній, і доступний в
players.current_player
Гравець, його данні
📂 Розгорнути
# titato.core.player.player.py
class Role(Enum):
USER = 1
ANDROID = 2
player = game.current_player
@property
def role(self) -> Role
Повертає роль гравця
@property
def symbol(self) -> SymbolBase
Повертає об'єкт класу Symbol гравця
@property
def count_steps(self) -> int
Повертає кількість зроблених кроків гравця
@property
def is_android(self) -> bool
Повертає True якщо гравець з роллю Role.ANDROID
Інакше - False
@property
def is_user(self) -> bool
Повертає True якщо гравець з роллю Role.USER
Інакше - False
def add_count_step(self)
Додає +1 до лічильника кроків гравця
Примітка:
- Цей метод автоматично викликається в
table.set_symbol_cell
Поточний результат гри
📂 Розгорнути
# titato.core.game.result.py
class ResultCode(Enum):
NO_RESULT = 0
ALL_CELLS_USED = 1
WINNER = 2
game_state = game.game_state
@property
def code(self) -> ResultCode
Повертає статус код гри:
Примітка:
- Початкове значення встановлене як
ResultCode.NO_RESULT
@property
def win_player(self) -> Optional[PlayerBase]
Повертає виграшного гравця, якщо він був доданий методом game_result.update
@property
def win_combination(self) -> Optional[CombType]
Повертає виграшну комбінацію, якщо вона була доданий методом game_result.update
@property
def is_finished(self) -> bool
Повертає True якщо game_result.code
має значення ResultCode.ALL_CELLS_USED
або ResultCode.WINNER
Інакше - False
@property
def is_continues(self) -> bool
Повертає True якщо game_result.code
має значення ResultCode.NO_RESULT
Інакше - False
@property
def is_winner(self) -> bool
Повертає True якщо game_result.code
має значення ResultCode.WINNER
Інакше - False
@property
def is_draw(self) -> bool
Повертає True якщо game_result.code
має значення ResultCode.ALL_CELLS_USED
Інакше - False
def update(self,
code: Optional[ResultCode] = None,
win_player: Optional[PlayerBase] = None,
win_combination: Optional[CombType] = None)
Оновлює дані про поточний результат гри.
Примітка:
game_result.code
- автоматично оновлюється коли застосовується методgame.set_draw
абоgame.set_winner
game_result.win_player
іgame_result.win_combination
-
автоматично оновлюється коли застосовується методgame.set_winner
Детальніше див. розділ Game, методи:game.set_draw
іgame.set_winner
Короткий приклад роботи
📂 Розгорнути
p1 = Player(name="PLAYER", symbol=Symbol('X'))
p2 = Player(name="ANDROID", symbol=Symbol('O'))
...
game.step(2, 2, player=p1)
game.step(0, 0, player=p1)
game.ai_step(p2) # result in second table
+-----+---+---+---+ -> +-----+---+---+---+
| ↓/→ | 0 | 1 | 2 | -> | ↓/→ | 0 | 1 | 2 |
+-----+---+---+---+ -> +-----+---+---+---+
| 0: | X | * | * | -> | 0: | X | * | * |
| 1: | * | * | * | -> | 1: | * | O | * |
| 2: | * | * | X | -> | 2: | * | * | X |
+-----+---+---+---+ -> +-----+---+---+---+
- AI алгоритм розуміє, що наступний хід для суперника ймовірно збере виграшну комбінацію, тому перекриває його
Розглянемо другу ситуацію
game.step(0, 0, player=p1) # X
game.step(2, 0, player=p1) # X
game.step(0, 2, player=p2) # O
game.step(2, 2, player=p2) # O
game.ai_step(p2) # result in second table
+-----+---+---+---+ -> +-----+---+---+---+
| ↓/→ | 0 | 1 | 2 | -> | ↓/→ | 0 | 1 | 2 |
+-----+---+---+---+ -> +-----+---+---+---+
| 0: | X | * | O | -> | 0: | X | * | O |
| 1: | * | * | * | -> | 1: | * | * | O |
| 2: | X | * | O | -> | 2: | X | * | O |
+-----+---+---+---+ -> +-----+---+---+---+
- AI алгоритм ставить в пріоритет свій виграш, розуміючи що наступного виграшного ходу для суперника — вже не буде