166 lines
5.0 KiB
Python
166 lines
5.0 KiB
Python
import random
|
|
from game_search_algorithms import alfabeta_search, minimax_search
|
|
|
|
# GAME BASE CLASS
|
|
class Game:
|
|
def legal_steps(self, state: dict[str, any]):
|
|
"""Steps that can be made in given state."""
|
|
raise NotImplementedError()
|
|
|
|
def take_step(self, step, state): # NOQA
|
|
"""Result of taking step in given state."""
|
|
raise NotImplementedError
|
|
|
|
def goodness(self, state, player):
|
|
"""Goodness measure of the state for the player."""
|
|
raise NotImplementedError()
|
|
|
|
def is_leaf(self, state):
|
|
"""Is the node a terminal node."""
|
|
return not self.legal_steps(state)
|
|
|
|
def next(self, state):
|
|
"""Return next player."""
|
|
return state['next']
|
|
|
|
def print(self, state):
|
|
"""Print current state."""
|
|
print(state)
|
|
|
|
def next_state(self, state):
|
|
"""Return next (step, state) list."""
|
|
return [(step, self.take_step(step, state))
|
|
for step in self.legal_steps(state)]
|
|
|
|
def __repr__(self):
|
|
"""Print the name of the game."""
|
|
return '<%s>' % self.__class__.__name__
|
|
|
|
|
|
|
|
|
|
|
|
class TicTacToe(Game):
|
|
def __init__(self, h=3, v=3, k=3):
|
|
#super().__init__()
|
|
self.h = h
|
|
self.v = v
|
|
self.k = k
|
|
|
|
steps = [(i,j) for i in range(1, h+1) for j in range(1, v+1)]
|
|
print(steps)
|
|
|
|
self.initial = {
|
|
#ki kezd
|
|
'next': 'X',
|
|
'steps': steps,
|
|
'result': 0,
|
|
'board': dict()
|
|
}
|
|
|
|
def print(self, state):
|
|
"""Let's see the current state."""
|
|
board = state['board']
|
|
for x in range(1, self.h + 1):
|
|
for y in range(1, self.v + 1):
|
|
print(board.get((x, y), '.'), end=" ")
|
|
print()
|
|
print('Result of the game: ', state['result'])
|
|
print()
|
|
|
|
def legal_steps(self, state):
|
|
return state['steps']
|
|
|
|
def take_step(self, step, state):
|
|
if step not in state['steps']:
|
|
return state #ha galiba van ujra lephetunk (??????)
|
|
|
|
board = state['board'].copy()
|
|
board[step] = self.next(state) #adot pozba beirjuk a jatekost
|
|
#modositjuk a lepeseket
|
|
steps = state['steps'].copy()
|
|
steps.remove(step) #ezzel nem tud foglalt helyre lepni
|
|
|
|
return {
|
|
'next': 'X' if state['next'] == 'O' else 'O',
|
|
'steps': steps,
|
|
'result': self.result(board, step, state['next']),
|
|
'board': board
|
|
}
|
|
|
|
def result(self, board, step, player):
|
|
if (self.check_triplets(board, step, player, (0,1)) or self.check_triplets(board, step, player, (1,0)) or
|
|
self.check_triplets(board, step, player, (1,1)) or self.check_triplets(board, step, player, (1,-1))):
|
|
if player == 'X':
|
|
return 1
|
|
else:
|
|
return -1
|
|
return 0
|
|
|
|
def check_triplets(self, board, step, player, direction):
|
|
n = -1
|
|
dx, dy = direction
|
|
|
|
x,y = step
|
|
while board.get((x,y)) == player:
|
|
x,y = dx + x,dy + y
|
|
n += 1
|
|
|
|
x,y = step
|
|
while board.get((x,y)) == player:
|
|
x,y = x - dx,y - dy
|
|
n += 1
|
|
|
|
return n >= self.k
|
|
|
|
def goodness(self, state, player):
|
|
return state["result"] if player == 'X' else -state['result']
|
|
|
|
def is_leaf(self, state):
|
|
return state['steps'] == [] or state['result'] != 0 #elfogytak a lepesek vagy valaki nyert
|
|
|
|
|
|
|
|
# PLAYERS
|
|
def random_player(game, state):
|
|
"""Randomly choose between options"""
|
|
return random.choice(game.legal_steps(state))
|
|
|
|
|
|
def alfabeta_player(game, state):
|
|
"""Search in game tree"""
|
|
return alfabeta_search(state, game)
|
|
|
|
|
|
def minimax_player(game, state):
|
|
"""Search in game tree"""
|
|
return minimax_search(state, game)
|
|
|
|
|
|
def play_game(game, *players):
|
|
state = game.initial
|
|
game.print(state)
|
|
while True:
|
|
for player in players:
|
|
step = player(game, state)
|
|
state = game.take_step(step, state)
|
|
game.print(state)
|
|
if game.is_leaf(state):
|
|
end_result = game.goodness(state, 'X')
|
|
return "X wins" if end_result == 1 else "O wins" if end_result == -1 else "Draw"
|
|
|
|
|
|
tto = TicTacToe()
|
|
|
|
# Test if playing works
|
|
play_game(tto, random_player, random_player)
|
|
|
|
# Demonstrate the power of the search algorithms
|
|
# you can comment out the game.print(state) lines in the play_game function for this
|
|
for i in range(0):
|
|
#print(play_game(tto, random_player, random_player)) # outcome will be random (starting player has the edge)
|
|
#print(play_game(tto, alfabeta_player, random_player)) # X will always win
|
|
print(play_game(tto, minimax_player, alfabeta_player)) # O will most likely win
|
|
#print(play_game(tto, alfabeta_player, alfabeta_player)) # game will always end in draw
|
|
#print(play_game(tto, alfabeta_player, minimax_player))
|
|
|