From f9fd2a042af10a86c69dc82c9f1659533e1a74eb Mon Sep 17 00:00:00 2001
From: Valentin Gehrke <madmaurice@zom.bi>
Date: Fri, 12 Sep 2014 16:35:57 +0200
Subject: [PATCH] TicTacToe AI

---
 tictactoe.py | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 157 insertions(+)
 create mode 100644 tictactoe.py

diff --git a/tictactoe.py b/tictactoe.py
new file mode 100644
index 0000000..c8cd073
--- /dev/null
+++ b/tictactoe.py
@@ -0,0 +1,157 @@
+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 win
+            if boardcopy.get_winner() == self:
+                return (x,y)
+
+            # Check for chance
+            chance = self.count_win_chances(boardcopy) - enemy.count_win_chances(boardcopy)
+
+            moves.append( (chance,(x,y)) )
+
+
+            # Check if enemy win
+            boardcopy.set(x,y,enemy)
+
+            if boardcopy.get_winner() == enemy:
+                return (x,y)
+
+            boardcopy.set(x,y,Board.empty)
+
+        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:")
+            try:
+                (x,y) = map(int, s.split(","))
+                return (x,y)
+            except Exception:
+                print("Try again?")
+
+def rotate(l):
+    return l[1:] + l[:1]
+
+if __name__ == "__main__":
+    board = Board(size=6)
+    players = [ AI("X"), AI("O") ]
+
+    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) )
+
+