game.py
#!/usr/bin/env python3
 
from itertools import chain
 
class Game:
    """Abstract base class for games"""
    def finished(self):
        """Returns True if no more moves are to be played, False
        otherwise."""
        raise NotImplementedError()
 
    def current_player(self):
        """Return an int corresponding to the player who has to do the next
        move. Returns 0 or 1 for player1 or player2 respectively."""
        raise NotImplementedError()
 
    def move(self, move):
        """Make the move denoted by the string 'move'. Return True if the
        move was valid and False otherwise. In most cases the moves should
        look like the following: 'e17'."""
        raise NotImplementedError()
 
    def eval(self):
        """Return the result of the game as integer: 0 ... draw; >0 player1
        won; <0 player2 won. This may only be called if self.finished() is
        True."""
        raise NotImplementedError()
 
    def __str__(self):
        raise NotImplementedError()
 
class Player:
    """Abstract base class for players"""
    def compute_move(self, game):
        """Create a move for the game 'game' and return it. Moves are strings which
        look like 'e17' most of the time."""
        raise NotImplementedError()
 
class TicTacToe(Game):
    """The constants are 0, 1 and 2 for empty and the two players
    respectively. """
    def __init__(self):
        self.board = [[0 for _ in range(3)] for _ in range(3)]
        self.winner = 0
        self.moves = 0
 
    def __str__(self):
        lines = ["|".join([" ", "X", "O"][pos] for pos in row)
                 for row in self.board]
        lines = reversed(["{} {}".format(num + 1, line)
                          for num, line in enumerate(lines)])
        return "\n  -----\n".join(lines) + "\n  a b c"
 
    def current_player(self):
        return self.moves % 2
 
    def determine_winner(self):
        """Internal function to find out whether there is a winner. Returns
        0, 1 or 2."""
        lanes = chain(self.board, zip(*self.board),
                      [[row[i] for i, row in enumerate(self.board)],
                       [list(reversed(row))[i] for i, row in enumerate(self.board)]])
        for lane in lanes:
            for player in [1, 2]:
                if all(x == player for x in lane):
                    return player
        return 0
 
    def finished(self):
        return self.moves >= 9 or self.winner
 
    def move(self, move):
        try:
            column = ord(move[0]) - 97
            row = int(move[1]) - 1
        except (IndexError, ValueError):
            print("Unable to parse move.")
            return False
        try:
            if self.board[row][column]:
                print("Allready taken.")
                return False
        except (IndexError):
            print("Outside of board.")
            return False
        self.board[row][column] = self.current_player() + 1
        self.moves += 1
        self.winner = self.determine_winner()
        return True
 
    def eval(self):
        if not self.finished():
            raise RuntimeError("Game hasn't finished yet.")
        translate = {0:0, 1:1, 2:-1}
        return translate[self.winner]
 
class HumanPlayer(Player):
    def compute_move(self, game):
        print(game)
        return input("Your move: ")
 
def match(game, player1, player2, quiet=False):
    """Play a match of 'game' between 'player1' and 'player2'."""
    players = [player1, player2]
    while not game.finished():
        game.move(players[game.current_player()].compute_move(game))
    if not quiet:
        print(game)
    winner = game.eval()
    if not quiet:
        if not winner:
            print("Draw")
        elif winner > 0:
            print("Player1 won the game with {} point(s)".format(winner))
        else:
            print("Player2 won the game with {} point(s)".format(-winner))
    return winner
 
def compete(gamecls, ai1, ai2, runs=1000):
    results12 = [0, 0]
    results21 = [0, 0]
    for _ in range(runs):
        res = match(gamecls(), ai1, ai2, quiet = True)
        if res:
            if res > 0:
                results12[0] += 1
            else:
                results12[1] += 1
    for _ in range(runs):
        res = match(gamecls(), ai2, ai1, quiet = True)
        if res:
            if res > 0:
                results21[0] += 1
            else:
                results21[1] += 1
    print("In {} runs the result is {}:{} for AI1 starting and {}:{} for AI2 starting".format(runs, results12[0], results12[1], results21[0], results21[1]))
 
if __name__ == "__main__":
    match(TicTacToe(), HumanPlayer(), HumanPlayer())