game.py
#!/usr/bin/env python3
 
"""Sample implementation for Python Workshop at the Krautspace."""
 
from itertools import chain
from copy import deepcopy
 
SYMBOLS = (" ", "X", "O")
PLAYER1 = 1
PLAYER2 = 2
PLAYERS = (PLAYER1, PLAYER2)
 
def game_to_str(game):
    """Convert a game state into a string."""
    lines = ("|".join(SYMBOLS[pos] for pos in row)
             for row in game)
    lines = reversed(tuple("{} {}".format(num + 1, line)
                      for num, line in enumerate(lines)))
    return "\n  -----\n".join(lines) + "\n  a b c"
 
def winner(game):
    """Determine winner of game. Returns 0 if there is no winner."""
    diagonals = (tuple(row[i] for i, row in enumerate(game)),
                 tuple(list(reversed(row))[i] for i, row in enumerate(game)))
    lanes = chain(game, zip(*game), diagonals)
    for lane in lanes:
        for player in (PLAYER1, PLAYER2):
            if all(x == player for x in lane):
                return player
    return 0
 
def _domove(game, move, player):
    """Update game state with a move."""
    try:
        column = ord(move[0]) - 97
        row = int(move[1]) - 1
    except (IndexError, ValueError):
        print("Unable to parse move.")
        return False
    try:
        if game[row][column]:
            print("Allready taken.")
            return False
    except IndexError:
        print("Outside of board.")
        return False
    game[row][column] = player
    return True
 
def tictactoe(player1, player2, output=True):
    """Orchestrate the game by alternatingly invoking the players."""
    game = [[0 for _ in range(3)] for _ in range(3)]
    moves = 0
    players = (player1, player2)
    while not winner(game) and moves < 9:
        tries = 0
        while tries < 42:
            tmp = deepcopy(game)
            player = PLAYERS[moves % 2]
            move = players[moves % 2](tmp, player)
            if _domove(game, move, player):
                break
            tries += 1
        else:
            raise RuntimeError("Unable to make valid move.")
        moves += 1
    if output:
        print("Final board is:")
        print(game_to_str(game))
        if winner(game):
            print("Player {} has won!".format(SYMBOLS[winner(game)]))
        else:
            print("Draw!")
    return winner(game)
 
def human_player(game, player):
    """Get moves from terminal."""
    print("Current board is:")
    print(game_to_str(game))
    return input("Your move as {}: ".format(SYMBOLS[player]))
 
def compete(game, ai1, ai2, runs=1000):
    """Benchmark to AIs against each other."""
    results12 = [0, 0]
    results21 = [0, 0]
    for _ in range(runs):
        res = game(ai1, ai2, output=False)
        if res:
            results12[res - 1] += 1
    for _ in range(runs):
        res = game(ai2, ai1, output=False)
        if res:
            results21[res - 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__":
    tictactoe(human_player, human_player)