Files
tictactoecss/bot.py

106 lines
3.4 KiB
Python

from functools import lru_cache
from collections import namedtuple
def get_sudoku_moves():
PLAYER_1 = "X"
PLAYER_2 = "O"
EMPTY = "_"
Move = namedtuple("Move", ["ps", "n"])
Position = namedtuple("Position", ["t", "i"])
def all_rows(field):
""" Returns all rows of a Sudoku field. """
f = field
return [
f[0:3], f[3:6], f[6:9], # horizontals
f[0:7:3], f[1:8:3], f[2:9:3], # verticals
f[0:9:4], f[2:7:2] # diagonals
]
assert(all_rows(list(range(9))) == [
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6]])
def all_equal_to(row, value):
""" Returns True if all elements in row are
equal to value. Returns False otherwise. """
for e in row:
if e != value:
return False
return True
def player_won(field, player):
for row in all_rows(field):
if all_equal_to(row, player):
return True
return False
def player_lost(field, player):
other_player = PLAYER_2 if player == PLAYER_1 else PLAYER_1
return player_won(field, other_player)
def game_over(field):
for f in field:
if f == EMPTY:
return False
return True
def possible_moves(field):
return [i for i in range(len(field)) if field[i] == EMPTY]
@lru_cache(maxsize=2**16)
def get_equity(field, player):
if player_won(field, player):
return 1
elif player_lost(field, player):
return -1
elif game_over(field):
return 0
other_player = PLAYER_2 if player == PLAYER_1 else PLAYER_1
equities = []
for possible_move in possible_moves(field):
new_field = list(field)
new_field[possible_move] = player
new_field = tuple(new_field)
equity = get_equity(new_field, other_player)
equities.append(equity)
return min(equities) * -1
def get_moves(field, turn, moves, moves_acc, bots_turn):
if player_won(field, PLAYER_1) or player_won(field, PLAYER_2):
return
if game_over(field):
return
if not bots_turn:
for move in possible_moves(field):
new_field = list(field)
new_field[move] = PLAYER_1
new_field = tuple(new_field)
new_moves = Move(moves.ps + [Position(turn, move)], None)
get_moves(new_field, turn + 1, new_moves, moves_acc, True)
else:
equities = []
for move in possible_moves(field):
new_field = list(field)
new_field[move] = PLAYER_2
new_field = tuple(new_field)
equity = get_equity(new_field, PLAYER_2)
equities.append((equity, move))
move = max(equities)[1]
new_field = list(field)
new_field[move] = PLAYER_2
new_field = tuple(new_field)
bot_move = Move(moves.ps, Position(turn, move))
moves_acc.append(bot_move)
get_moves(new_field, turn + 1, moves, moves_acc, False)
return moves_acc
EMPTY_FIELD = tuple([EMPTY for _ in range(9)])
return get_moves(EMPTY_FIELD, 0, Move([], None), [], False)