# Python TicTacToe AlphaBeta Program

I am looking for a review of my perfect TicTacToe algorithm. It will be split up into 3 parts:

• The function
• The GameState Class
• The game (NOT well designed)

I am mainly looking for tips and comments on the first 2, but I will take comments / tips on the poorly designed game.

### alphabeta.py

#Using wikipedia pseudocode without depth
def alphabeta(game_state, alpha=-2, beta=2, our_turn=True):
if game_state.is_gameover():
return game_state.score(), None
if our_turn:
score = -2 #worst non-possible score. A win, tie, or even a loss will change this
for move in game_state.get_possible_moves():
child = game_state.get_next_state(move, True)
temp_max, _ = alphabeta(child, alpha, beta, False)
if temp_max > score:
score = temp_max
best_move = move
alpha = max(alpha, score)
if beta <= alpha:
break
return score, best_move
else:
score = 2 #worst non-possible score. A win, tie, or even a loss will change this
for move in game_state.get_possible_moves():
child = game_state.get_next_state(move, False)
temp_min, _ = alphabeta(child, alpha, beta, True)
if temp_min < score:
score = temp_min
best_move = move
beta = min(beta, score)
if beta <= alpha:
break
return score, best_move


### gamestate.py

class GameState:
def __init__(self,board,char='X',oppchar='O'):
self.char = char
self.oppchar = oppchar
self.board = board
self.winning_combos = [[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 is_gameover(self):
'''returns if a game_state has been won or filled up'''
if self.board.count(self.char) + self.board.count(self.oppchar) == 9:
return True
for combo in self.winning_combos:
if self.board[combo[0]] == self.char and self.board[combo[1]] == self.char and self.board[combo[2]] == self.char:
return True
elif self.board[combo[0]] == self.oppchar and self.board[combo[1]] == self.oppchar and self.board[combo[2]] == self.oppchar:
return True
return False
def score(self):
'''returns 1, 0, or -1 corresponding to the score (WIN, TIE, LOSS) from the ai's point of view'''
if self.board.count(self.char) + self.board.count(self.oppchar) == 9:
return 0
for combo in self.winning_combos:
if self.board[combo[0]] == self.char and self.board[combo[1]] == self.char and self.board[combo[2]] == self.char:
return 1
elif self.board[combo[0]] == self.oppchar and self.board[combo[1]] == self.oppchar and self.board[combo[2]] == self.oppchar:
return -1

@staticmethod
def return_state(score):
'''Returns a word for each score'''
if score == 0:
return('TIED')
elif score == 1:
return('WON')
elif score == -1:
return('LOST')
else:
return('ERROR')

def pretty_print(self):
'''prints the board joined by spaces'''
print(' '.join(self.board[:3]))
print(' '.join(self.board[3:6]))
print(' '.join(self.board[6:9]))

def get_possible_moves(self):
'''returns all possible squares to place a character'''
return [index for index, square in enumerate(self.board) if square != self.char and square != self.oppchar]

def get_next_state(self, move, our_turn):
'''returns the gamestate with the move filled in'''
copy = self.board[:]
copy[move] = self.char if our_turn else self.oppchar
return GameState(copy, char=self.char, oppchar=self.oppchar)


### game

#!/usr/bin/env python3
import random
from time import sleep
import alphabeta as ab
import gamestate as gs

def get_p_move(game):
while True:
raw_move = input('Your move (1-9) > ')
if raw_move.isdigit():
p_move = int(raw_move) - 1
if p_move > -1 and p_move < 9:
if game.board[p_move] == '_':
break
return p_move

def run_game(game, player_goes_first):
if player_goes_first:
while True:
game.pretty_print()
p_move = get_p_move(game)
game = game.get_next_state(p_move, False)
if game.is_gameover(): break
score, ai_move = ab.alphabeta(game)
game = game.get_next_state(ai_move, True)
if game.is_gameover(): break
else:
while True:
score, ai_move = ab.alphabeta(game)
game = game.get_next_state(ai_move, True)
if game.is_gameover(): break
game.pretty_print()
p_move = get_p_move(game)
game = game.get_next_state(p_move, False)
if game.is_gameover(): break
game.pretty_print()
print('The computer ' + game.return_state(score))

def get_symbols():
human_char = input('Pick your symbol > ')
if len(human_char) > 1 or human_char == '' or human_char == ' ' or human_char == '_':
exit()
elif human_char == 'X' or human_char == 'x':
ai_char = 'O'
else:
ai_char = 'X'
return human_char, ai_char
if __name__ == '__main__':
try:
human_char, ai_char = get_symbols()
start_board = ['_'] * 9
player_goes_first = bool(random.randint(0,2))

input('You go first:{}\nenter to continue\n'.format(player_goes_first))
game = gs.GameState(start_board,char=ai_char, oppchar=human_char)
run_game(game, player_goes_first)
except KeyboardInterrupt:
print('\n')
exit()