import random def rotate(l): return l[1:] + l[:1] class Player(object): def __init__(self,rep): self.rep = rep def __repr__(self): return self.rep def count_win_chances(self, board): chances = 0 for line in board.get_all_lines(): chances += self.win_chance_line(line) return chances def win_chance_line(self,line): c = 1 for p in line: if p == self: c += 1 elif p != Board.empty: return 0 return c class Board(object): draw = object() empty = Player(" ") def __init__(self,size=3): self.board = [ [Board.empty] * size for i in range(size) ] self.size = size def set(self, x, y, value): if x < 0 or x >= self.size or y < 0 or y >= self.size: raise Exception("Out of bounds!") if value == Board.empty or isinstance(value,Player): self.board[x][y] = value else: raise Exception("Invalid value %s" % str(value)) def get(self, x, y): if x < 0 or x >= self.size or y < 0 or y >= self.size: raise Exception("Out of bounds!") else: return self.board[x][y] def get_all_lines(self): lines = [] #diagonals lines.append( [ self.board[xy][xy] for xy in range(self.size) ] ) lines.append( [ self.board[x][self.size-x-1] for x in range(self.size) ] ) #rows lines += self.board #cols lines += [ [ self.board[x][y] for x in range(self.size) ] for y in range(self.size) ] return lines def get_empty_fields(self): for x in range(self.size): for y in range(self.size): if self.board[x][y] == Board.empty: yield (x,y) def get_winner(self): for line in self.get_all_lines(): if line[0] != self.empty and all(map(lambda v: v == line[0],line)): return line[0] for field in self.get_empty_fields(): return None return Board.draw def copy(self): c = Board(size=self.size) for x in range(self.size): for y in range(self.size): c.set(x,y, self.get(x,y)) return c def __str__(self): return ("\n-"+("*-"*(self.size-1)) +"\n").join( [ "|".join( [ str(pos) for pos in line ] ) for line in self.board ] ) class AI(Player): def play(self,enemy, board): if board.get_winner() is not None: raise Exception("Game over!") boardcopy = board.copy() moves = [] #Check for win chance for x,y in board.get_empty_fields(): boardcopy.set(x,y,self) # Check for chance chance = self.count_win_chances(boardcopy) - enemy.count_win_chances(boardcopy) # Check for win if boardcopy.get_winner() == self: chance += 1000 # Check if enemy win boardcopy.set(x,y,enemy) if boardcopy.get_winner() == enemy: chance += 500 boardcopy.set(x,y,Board.empty) moves.append( (chance,(x,y)) ) moves.sort(key=lambda x: -x[0]) best_move = moves[0] good_moves = [] for move in moves: if move[0] == best_move[0]: good_moves.append(move) return random.choice(good_moves)[1] class Human(Player): def play(self, enemy, board): while True: s = input("Player %s's turn:" % self.rep) try: (x,y) = map(int, s.split(",")) return (x,y) except Exception: print("Try again?") class Random(Player): def play(self, enemy, board): fields = list(board.get_empty_fields()) return random.choice(fields) class Game(object): def __init__(self, players, size=3): self.size = size self.players = players self.reset() def reset(self): self.board = Board(size=self.size) self.winner = None def run(self,out=True): if out: print(str(self.board)) while self.board.get_winner() is None: (x,y) = self.players[0].play(self.players[1], self.board) while self.board.get(x,y) != Board.empty: print("Fail") (x,y) = self.players[0].play(players[1], self.board) self.board.set(x,y,self.players[0]) self.players = rotate(self.players) if out: print() print(str(self.board)) return self.board.get_winner() def menu(prompt,choices): print("> %s" % prompt) for i,choice in enumerate(choices): print("%d. %s" % (i,choice)) return int(input("Your choice: ")) if __name__ == "__main__": c = menu("Playmode",["Ai vs Ai","Ai vs Random","Ai vs Player","Player vs Player"]) if c == 0: players = [ AI("X"), AI("O") ] elif c == 1: players = [ AI("X"), Random("O") ] elif c == 2: players = [ Human("X"), AI("O") ] elif c == 3: players = [ Human("X"), Human("O") ] else: import sys sys.exit(0) game = Game(players) winner = game.run() if winner == Board.draw: print("Round draw!") else: print("%s has won!" % str(winner) )