amoba
This commit is contained in:
166
ora6/amoba.py
Normal file
166
ora6/amoba.py
Normal file
@@ -0,0 +1,166 @@
|
||||
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
|
||||
dy, dx = 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(1):
|
||||
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))
|
||||
|
||||
59
ora6/game_search_algorithms.py
Normal file
59
ora6/game_search_algorithms.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# SEARCHES
|
||||
# Minimax search
|
||||
def minimax_search(state, game):
|
||||
player = game.next(state)
|
||||
|
||||
# define labels on each level of the tree
|
||||
def max_value(state):
|
||||
if game.is_leaf(state):
|
||||
return game.goodness(state, player)
|
||||
return max([min_value(s) for (_, s) in game.next_state(state)])
|
||||
|
||||
def min_value(state):
|
||||
if game.is_leaf(state):
|
||||
return game.goodness(state, player)
|
||||
return min([max_value(s) for (_, s) in game.next_state(state)])
|
||||
|
||||
# minimax method
|
||||
children_values = [(a, min_value(s)) for (a, s) in game.next_state(state)]
|
||||
step, value = max(children_values, key=lambda a_s: a_s[1])
|
||||
return step
|
||||
|
||||
|
||||
def alfabeta_search(state, game, d=4, cut_test=None, expand=None):
|
||||
"""Search game tree until defined depth"""
|
||||
player = game.next(state)
|
||||
|
||||
def max_value(state, alfa, beta, depth):
|
||||
if cut_test(state, depth):
|
||||
return expand(state)
|
||||
v = float("-inf")
|
||||
for (a, s) in game.next_state(state):
|
||||
v = max(v, min_value(s, alfa, beta, depth+1))
|
||||
if v >= beta:
|
||||
return v
|
||||
alfa = max(alfa, v)
|
||||
return v
|
||||
|
||||
def min_value(state, alfa, beta, depth):
|
||||
if cut_test(state, depth):
|
||||
return expand(state)
|
||||
v = float("inf")
|
||||
for (a, s) in game.next_state(state):
|
||||
v = min(v, max_value(s, alfa, beta, depth+1))
|
||||
if v <= alfa:
|
||||
return v
|
||||
beta = min(beta, v)
|
||||
return v
|
||||
|
||||
# Alfabeta search
|
||||
cut_test = cut_test or (lambda state, depth: depth > d or game.is_leaf(state))
|
||||
expand = expand or (lambda state: game.goodness(state, player))
|
||||
alfa = float("-inf")
|
||||
best_step = None
|
||||
for a, s in game.next_state(state):
|
||||
v = min_value(s, alfa, float("inf"), 0)
|
||||
if v > alfa:
|
||||
alfa = v
|
||||
best_step = a
|
||||
return best_step
|
||||
160
ora6/tictactoe.py
Normal file
160
ora6/tictactoe.py
Normal file
@@ -0,0 +1,160 @@
|
||||
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__
|
||||
|
||||
|
||||
# TIC-TAC TOE CLASS
|
||||
class TicTacToe(Game):
|
||||
"""3x3 version."""
|
||||
|
||||
def __init__(self, h=3, v=3, k=3):
|
||||
"""Base of the game"""
|
||||
self.h = h
|
||||
self.v = v
|
||||
self.k = k
|
||||
steps = [(x, y) for x in range(1, h + 1) for y in range(1, v + 1)]
|
||||
print(steps)
|
||||
print(type(steps))
|
||||
print(steps[0])
|
||||
self.initial = {'next': 'X',
|
||||
'result': 0,
|
||||
'board': {},
|
||||
'steps': steps}
|
||||
|
||||
def legal_steps(self, state):
|
||||
"""We can step on every empty cell"""
|
||||
return state['steps']
|
||||
|
||||
def take_step(self, step, state):
|
||||
"""Effect of the step"""
|
||||
if step not in state['steps']:
|
||||
return state
|
||||
board = state['board'].copy()
|
||||
board[step] = state['next']
|
||||
steps = list(state['steps'])
|
||||
steps.remove(step)
|
||||
return {
|
||||
'next': 'X' if state['next'] == 'O' else 'O',
|
||||
'result': self.result(board, step, state['next']), # need to change
|
||||
'board': board,
|
||||
'steps': steps
|
||||
}
|
||||
|
||||
def result(self, board, step, player):
|
||||
"""If X wins with this step then return 1. If O wins with this then return -1. Else return 0."""
|
||||
if (self.check_triples(board, step, player, (0, 1)) or self.check_triples(board, step, player, (1, 0)) or
|
||||
self.check_triples(board, step, player, (1, -1)) or self.check_triples(board, step, player, (1, 1))):
|
||||
return 1 if player == 'X' else -1
|
||||
return 0
|
||||
|
||||
def check_triples(self, board, step, player, direction):
|
||||
"""Check for triples in a direction."""
|
||||
delta_x, delta_y = direction
|
||||
x, y = step
|
||||
n = 0
|
||||
while board.get((x, y)) == player:
|
||||
n += 1
|
||||
x, y = x + delta_x, y + delta_y
|
||||
x, y = step
|
||||
while board.get((x, y)) == player:
|
||||
n += 1
|
||||
x, y = x - delta_x, y - delta_y
|
||||
n -= 1
|
||||
return n >= self.k
|
||||
|
||||
def goodness(self, state, player):
|
||||
"""X goodness: 1, if it wins; -1, if it loses, 0 if draw."""
|
||||
return state['result'] if player == "X" else -state['result']
|
||||
|
||||
def is_leaf(self, state):
|
||||
"""If someone won or the table is full it will be the end of the game."""
|
||||
return state['result'] != 0 or len(state['steps']) == 0
|
||||
|
||||
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()
|
||||
|
||||
|
||||
# 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(1):
|
||||
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))
|
||||
Reference in New Issue
Block a user