tictactoecss/rules.py

121 lines
4.0 KiB
Python

from functools import lru_cache
from collections import namedtuple
def get_tictactoe_rules():
PLAYER_1 = "X"
PLAYER_2 = "O"
UNCHECKED = "_"
Rule = namedtuple("Rule", ["moves", "next_move"])
Move = namedtuple("Move", ["turn", "field"])
def all_rows(board):
""" Returns all rows of a Sudoku field. """
f = board
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 elem in row:
if elem != value:
return False
return True
def player_won(board, player):
for row in all_rows(board):
if all_equal_to(row, player):
return True
return False
def player_lost(board, player):
other_player = PLAYER_2 if player == PLAYER_1 else PLAYER_1
return player_won(board, other_player)
def game_over(board):
for field in board:
if field == UNCHECKED:
return False
return True
def get_unchecked_fields(board):
return [field_index for field_index in range(len(board))
if board[field_index] == UNCHECKED]
def get_new_board(board, field_index, player):
new_board = list(board)
new_board[field_index] = player
return tuple(new_board)
def get_empty_board():
return tuple([UNCHECKED for _ in range(9)])
@lru_cache(maxsize=2**16)
def get_equity(board, player):
if player_won(board, player):
return 1
elif player_lost(board, player):
return -1
elif game_over(board):
return 0
other_player = PLAYER_2 if player == PLAYER_1 else PLAYER_1
equities = []
for field_index in get_unchecked_fields(board):
new_board = get_new_board(board, field_index, player)
equity = get_equity(new_board, other_player)
equities.append(equity)
return min(equities) * -1
bot_move_rules = []
draw_rules = []
win_rules = []
def get_rules(current_board, turn, current_rule, bots_turn):
if player_won(current_board, PLAYER_1):
raise Exception("If we got here our bot screwed up.")
if player_won(current_board, PLAYER_2):
rule = Rule(current_rule.moves, Move(turn, None))
win_rules.append(rule)
return
if game_over(current_board):
rule = Rule(current_rule.moves, Move(turn, None))
draw_rules.append(rule)
return
if not bots_turn:
for field_index in get_unchecked_fields(current_board):
new_board = get_new_board(current_board, field_index, PLAYER_1)
new_moves = current_rule.moves + [Move(turn, field_index)]
new_current_rule = Rule(new_moves, None)
get_rules(new_board, turn + 1, new_current_rule, True)
else:
equities = []
for field_index in get_unchecked_fields(current_board):
new_board = get_new_board(current_board, field_index, PLAYER_2)
equity = get_equity(new_board, PLAYER_1)
equities.append((equity, field_index))
new_index = min(equities)[1]
new_board = get_new_board(current_board, new_index, PLAYER_2)
bot_rule = Rule(current_rule.moves, Move(turn, new_index))
bot_move_rules.append(bot_rule)
get_rules(new_board, turn + 1, current_rule, False)
EMPTY_BOARD = get_empty_board()
get_rules(EMPTY_BOARD, 0, Rule([], None), False)
return (bot_move_rules, draw_rules, win_rules)