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)