import random 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(): if all( map( lambda v: v == self or v == Board.empty, line) ): chances += 1 return chances 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?") def rotate(l): return l[1:] + l[:1] 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 Player","Player vs Player"]) if c == 0: players = [ AI("X"), AI("O") ] elif c == 1: players = [ Human("X"), AI("O") ] elif c == 2: players = [ Human("X"), Human("O") ] else: import sys sys.exit(0) board = Board(size=3) print(str(board)) while board.get_winner() is None: (x,y) = players[0].play(players[1],board) while board.get(x,y) != Board.empty: print("Try again!") (x,y) = players[0].play(players[1],board) board.set(x,y,players[0]) players = rotate(players) print() print(str(board)) winner = board.get_winner() if winner == Board.draw: print("Round draw!") else: print("%s has won!" % str(winner) )