From 38c54ba2374a790e2a4d48dd790fd109a8c92850 Mon Sep 17 00:00:00 2001
From: Sean Paeglis <s.paeglis@student.unimelb.edu.au>
Date: Thu, 16 May 2019 15:36:32 +1000
Subject: [PATCH] fa

---
 AI_partb_bits.py                           |  40 ++++++
 AI_partb_bits2.py                          | 137 +++++++++++++++++++++
 Snek/player/Player.py                      | 112 ++++++++++++++++-
 Snek/util/Board.py                         |  62 +++++++++-
 Snek/util/__pycache__/Board.cpython-36.pyc | Bin 4355 -> 4355 bytes
 5 files changed, 348 insertions(+), 3 deletions(-)
 create mode 100644 AI_partb_bits.py
 create mode 100644 AI_partb_bits2.py

diff --git a/AI_partb_bits.py b/AI_partb_bits.py
new file mode 100644
index 0000000..e5579c5
--- /dev/null
+++ b/AI_partb_bits.py
@@ -0,0 +1,40 @@
+def recursive_move_finder(state_array, depth, max_depth):
+    if(depth == max_depth):
+        return evaluation_fn(stat_array[depth])
+    moves = state_array[depth].get_moves(board.colours((depth-1)%3))
+    if(depth==1):
+        expected_eval = -100000
+        for move in moves:
+            array_apply_move(state_array, depth, move)
+            new_eval = recursive_move_finder( state_array, depth+1, max_depth)
+            if(new_eval > expected_eval):
+                expected_eval = new_eval
+                best_move = move
+            array_undo_move(state_array, depth, move)
+        return best_move
+    if((depth-1)%3==0):
+        expected_eval = -100000
+        for move in moves:
+            array_apply_move(state_array, depth, move)
+            new_eval = recursive_move_finder( state_array, depth+1, max_depth)
+            if(new_eval > expected_eval):
+                expected_eval = new_eval
+            array_undo_move(state_array, depth, move)
+        return expected_eval
+    else:
+        expected_eval = 100000
+        for move in moves:
+            array_apply_move(state_array, depth, move)
+            new_eval = recursive_move_finder( state_array, depth+1, max_depth)
+            if(new_eval < expected_eval):
+                expected_eval = new_eval
+            array_undo_move(state_array, depth, move)
+        return expected_eval
+
+def array_apply_move(state_array, depth, move):
+    for i in range(len(state_array)-depth):
+        state_array[depth+i].move(move)
+
+def array_undo_move(state_array, depth, move):
+    for i in range(len(state_array)-depth):
+        state_array[depth+i].undo_move(move)
diff --git a/AI_partb_bits2.py b/AI_partb_bits2.py
new file mode 100644
index 0000000..232476c
--- /dev/null
+++ b/AI_partb_bits2.py
@@ -0,0 +1,137 @@
+def on_board(self, q, r):
+    if q >= 0 and q <= 6 and r >= 0 and r <= 6:
+        return True
+    else:
+        return False
+
+
+
+def getMoves(self, colour):
+    valid_moves = []
+    for piece in self.pieces[colour]:
+
+        q = piece.pos[0]
+        r = piece.pos[1]
+
+        # Otherwise, try all moves and return valid ones
+        for direction in self.directions:
+            q_moved = q + direction[0]
+            r_moved = r + direction[1]
+            if(on_board(q_moved, r_moved) && self.tiles[q_moved][r_moved] is None):
+                valid_moves.append(('MOVE',(q, r),(q_moved, r_moved)))
+            else
+                q_jumped = q_moved + direction[0]
+                r_jumped = r_moved + direction[1]
+                if(on_board(q_jumped, r_jumped) && self.tiles[q_jumped][r_jumped] is None):
+                    valid_moves.append(('JUMP',(q, r),(q_jumped, r_jumped)))
+
+        if piece.pos in self.exit_tiles[colour]:
+            valid_moves.append(('EXIT',(q, r)))
+
+    if len(valid_moves) == 0:
+        valid_moves.append(('PASS',))
+
+    return valid_moves
+
+
+
+
+def recursive_move_finder(state_array, depth, max_depth):
+    if(depth == max_depth):
+        return evaluation_fn(stat_array[depth])
+    moves = state_array[depth].get_moves(board.colours((depth-1)%3))
+    if(depth==1):
+        expected_eval = -100000
+        for move in moves:
+            array_apply_move(state_array, depth, move)
+            new_eval = recursive_move_finder( state_array, depth+1, max_depth)
+            if(new_eval > expected_eval):
+                expected_eval = new_eval
+                best_move = move
+            array_undo_move(state_array, depth, move)
+        return best_move
+    if((depth-1)%3==0):
+        expected_eval = -100000
+        for move in moves:
+            array_apply_move(state_array, depth, move)
+            new_eval = recursive_move_finder( state_array, depth+1, max_depth)
+            if(new_eval > expected_eval):
+                expected_eval = new_eval
+            array_undo_move(state_array, depth, move)
+        return expected_eval
+    else:
+        expected_eval = 100000
+        for move in moves:
+            array_apply_move(state_array, depth, move)
+            new_eval = recursive_move_finder( state_array, depth+1, max_depth)
+            if(new_eval < expected_eval):
+                expected_eval = new_eval
+            array_undo_move(state_array, depth, move)
+        return expected_eval
+
+def array_apply_move(state_array, depth, move):
+    for i in range(len(state_array)-depth):
+        state_array[depth+i].move(move)
+
+def array_undo_move(state_array, depth, move):
+    for i in range(len(state_array)-depth):
+        state_array[depth+i].undo_move(move)
+
+def move(self, move):
+
+        move_from = move[1][0]
+        move_to = move[1][1]
+
+		if move[0] ==  'MOVE':
+			shift_piece(move_from, move_to)
+
+		elif move[0] == 'JUMP':
+			piece = self.tiles[move_from[1]][move_from[0]]
+			direction = move_direction(move_from,move_to)
+			mid_tile = (piece.pos[0] + direction[0], piece.pos[1] + direction[1])
+			capture_piece(mid_tile, piece.colour)
+			shift_piece(move_from, move_to)
+
+		elif move[0] == 'EXIT':
+			piece = self.tiles[move[1][1]][move[1][0]]
+			self.tiles[move[1][1]][move[1][0]] = None
+			self.pieces[piece.colour].remove(piece)
+
+def undo_move(self, move, colour):
+
+        move_from = move[1][1]
+        back_to = move[1][0]
+
+		if move[0] ==  'MOVE':
+			shift_piece(move_from, move_to)
+
+		elif move[0] == 'JUMP':
+			piece = self.tiles[move_from[1]][move_from[0]]
+			direction = move_direction(move_from,move_to)
+			mid_tile = (piece.pos[0] + direction[0], piece.pos[1] + direction[1])
+			capture_piece(mid_tile, self.tiles[mid_tile[1]][mid_tile[0]])
+			shift_piece(move_from, move_to)
+
+		elif move[0] == 'EXIT':
+			piece = Piece.Piece(move[1], colour)
+			self.tiles[move[1][1]][move[1][0]] = piece
+			self.pieces[piece.colour].append(piece)
+
+def capture_piece(self, pos, colour):
+    taken_piece = self.tiles[pos[1]][pos[0]]
+    for i in range(len(self.pieces[taken_piece.colour])):
+        if self.pieces[taken_piece.colour][i] is taken_piece:
+            self.pieces[taken_piece.colour].pop(i)
+            self.pieces[colour].append(taken_piece)
+            taken_piece.colour = colour
+            break
+
+def shift_piece(self, t_from, t_to):
+    piece = self.tiles[t_from[1]][t_from[0]]
+    self.tiles[t_from[1]][t_from[0]] = None
+    self.tiles[t_to[1]][t_to[0]] = piece
+    piece.pos = t_to
+
+def move_direction(move_from, move_to):
+    direction = (move_to[0]-move_from[0], move_to[1]-move_from[1])
+    return (int(direction[0]/2), int(direction[1]/2))
diff --git a/Snek/player/Player.py b/Snek/player/Player.py
index 291fbb6..663fb42 100644
--- a/Snek/player/Player.py
+++ b/Snek/player/Player.py
@@ -1,10 +1,14 @@
 from Snek.util import Board
+import pickle
 
 class Player:
 
 	def __init__(self, colour):
 		self.colour = colour
-		self.board = Board.Board(self.colour)
+		#self.board = Board.Board(self.colour)
+		states = []
+		for i in range(6):
+			states.append(Board.Board(self.colour))
 
 	def on_board(self, piece, move):
 		if piece.pos[0] + move[0] < -3 or piece.pos[0] + move[0] > 3:
@@ -16,11 +20,115 @@ class Player:
 		return True
 
 	def action(self):
+		"""
 		moves = self.board.get_moves(self.colour)
 		moves[0] = self.board.convert_coords_ref(moves[0])
 		print(moves[0])
 		return(moves[0])
+		"""
+
+
 
 	def update(self, colour, action):
 		action = self.board.convert_coords_local(action)
-		self.board.move(action)
+		self..move(action)
+
+	def recursive_move_finder(self, state_array, depth, max_depth):
+	    if(depth == max_depth):
+	        return evaluation_fn(stat_array[depth], Board.colours[(depth-1)%3])
+	    moves = state_array[depth].get_moves(board.colours[(depth-1)%3])
+	    if(depth==1):
+	        expected_eval = -100000
+	        for move in moves:
+	            array_apply_move(state_array, depth, move)
+	            new_eval = recursive_move_finder( state_array, depth+1, max_depth)
+	            if(new_eval > expected_eval):
+	                expected_eval = new_eval
+	                best_move = move
+	            array_undo_move(state_array, depth, move, board.colours[(depth-1)%3])
+	        return best_move
+	    if((depth-1)%3==0):
+	        expected_eval = -100000
+	        for move in moves:
+	            array_apply_move(state_array, depth, move)
+	            new_eval = recursive_move_finder( state_array, depth+1, max_depth)
+	            if(new_eval > expected_eval):
+	                expected_eval = new_eval
+	            array_undo_move(state_array, depth, move, board.colours[(depth-1)%3])
+	        return expected_eval
+	    else:
+	        expected_eval = 100000
+	        for move in moves:
+	            array_apply_move(state_array, depth, move)
+	            new_eval = recursive_move_finder( state_array, depth+1, max_depth)
+	            if(new_eval < expected_eval):
+	                expected_eval = new_eval
+	            array_undo_move(state_array, depth, move, board.colours[(depth-1)%3])
+	        return expected_eval
+
+	def array_apply_move(self, state_array, depth, move):
+	    for i in range(len(state_array)-depth):
+	        state_array[depth+i].move(move)
+
+	def array_undo_move(self, state_array, depth, move):
+	    for i in range(len(state_array)-depth):
+	        state_array[depth+i].undo_move(move)
+
+	def evaluation_fn(self, state):
+		evl = 0
+		for piece in state.pieces[self.board.colour]:
+			evl += ((min_dist(piece)-position_value(node, piece))/2 + 1)
+		return evl
+
+	def min_dist(self, piece):
+		min_dist = 10
+	    for tile in self.board.exit_tiles[piece.colour]:
+	        if self.board.distance(piece.pos, tile) <= min_dist:
+	            min_dist = board.distance(piece.pos, tile)
+	    return min_dist
+
+	# Calculates a "score" for a position based on the number of available jumps
+	# that get you closer to the goal state
+	def position_value(node, piece):
+	    adjacent_piece = pickle.loads(pickle.dumps(piece))
+	    value = 0
+	    if piece.pos in self.board.exit_tiles[piece.colour]:
+	        return value
+	    for move in node.moves:
+	        adjacent_piece.pos = (piece.pos[0] + move[0], piece.pos[1] + move[1])
+	        adjacent_min = min_dist(adjacent_piece)
+	        current_min = min_dist(piece)
+	        if checkJump(node, piece.pos, move) and on_board(adjacent_piece.pos):
+	            if adjacent_min < current_min:
+	                value += 0.5
+	            if adjacent_min == current_min:
+	                value += 0.25
+
+	            if adjacent_min == current_min+1:
+	                value += 0.15
+	        if (
+	                on_board(adjacent_piece.pos) and
+	                type(node.tiles[adjacent_piece.pos[1]+3][adjacent_piece.pos[0]+3]) == Piece.Piece
+	            ):
+	            value += 0.1
+	    return value
+
+	# Check if a jump is possible from a position in a given direction
+	def checkJump(node, position, move):
+	    new = (position[0] + move[0], position[1] + move[1])
+	    new_jump = (position[0] + 2*move[0], position[1] + 2*move[1])
+	    if on_board(new) and on_board(new_jump):
+	        if (
+	                type(board.tiles[new[0]+3][new[1]+3]) == Piece.Piece and 
+	                type(board.tiles[new_jump[0]+3][new_jump[1]+3]) == None
+	            ):
+	            return True
+	        else:
+	            return False
+
+	# Check if a position is on the board
+	def on_board(position):
+	    if position[0] >= -3 and position[0] <= 3 and position[1] >= -3 and position[1] <= 3:
+	        return True
+	    else:
+	        return False
diff --git a/Snek/util/Board.py b/Snek/util/Board.py
index 425e13c..b724044 100644
--- a/Snek/util/Board.py
+++ b/Snek/util/Board.py
@@ -8,6 +8,17 @@ class Board:
 
 		self.colour = colour
 
+		self.colours = []
+
+		if self.colour == 'red':
+			self.colours = ['red', 'green', 'blue']
+
+		elif self.colour == 'green':
+			self.colours = ['green', 'blue', 'red']
+
+		else:
+			self.colours = ['blue', 'red', 'green']
+
 		# List of exit tiles for each colour
 		self.exit_tiles = {'red':[(6,0), (6,1), (6,2), (6,3)], 'green':[(0,6),(1,6),(2,6),(3,6)],'blue':[(3,0),(2,1),(1,2),(0,3)]}
 
@@ -105,8 +116,18 @@ class Board:
 
 		return (action[0], ((action[1][0][0]-3, action[1][0][1]-3), (action[1][1][0]-3, action[1][1][1]-3)))
 
+	def distance(self, start, end):
+        q1 = start[0]
+        q2 = end[0]
+        r1 = start[1]
+        r2 = end[1]
+        return (abs(q1 - q2) + abs(q1 + r1 - q2 - r2) + abs(r1 - r2))/2
+
 	def move(self, action):
 
+		m_from = action[1][0]
+		m_to = action[1][1]
+
 		if action[0] ==  'MOVE':
 			piece = self.tiles[action[1][0][1]][action[1][0][0]]
 			self.tiles[action[1][0][1]][action[1][0][0]] = None
@@ -117,7 +138,6 @@ class Board:
 			piece = self.tiles[action[1][0][1]][action[1][0][0]]
 			direction = (action[1][1][0]-action[1][0][0], action[1][1][1]-action[1][0][1])
 			direction = (int(direction[0]/2), int(direction[1]/2))
-			print("MID: " + str((piece.pos[0] + direction[0]-3, piece.pos[1] + direction[1]-3)))
 			mid_tile = (piece.pos[0] + direction[0], piece.pos[1] + direction[1])
 			taken_piece = self.tiles[mid_tile[1]][mid_tile[0]]
 			print(len(self.pieces[taken_piece.colour]))
@@ -136,3 +156,43 @@ class Board:
 			piece = self.tiles[action[1][1]][action[1][0]]
 			self.tiles[action[1][1]][action[1][0]] = None
 			self.pieces[piece.colour].remove(piece)
+
+
+	def undo_move(self, move, colour):
+
+        move_from = move[1][1]
+        move_to = move[1][0]
+
+		if move[0] ==  'MOVE':
+			shift_piece(move_from, move_to)
+
+		elif move[0] == 'JUMP':
+			piece = self.tiles[move_from[1]][move_from[0]]
+			direction = move_direction(move_from,move_to)
+			mid_tile = (piece.pos[0] + direction[0], piece.pos[1] + direction[1])
+			capture_piece(mid_tile, self.tiles[mid_tile[1]][mid_tile[0]])
+			shift_piece(move_from, move_to)
+
+		elif move[0] == 'EXIT':
+			piece = Piece.Piece(move[1], colour)
+			self.tiles[move[1][1]][move[1][0]] = piece
+			self.pieces[piece.colour].append(piece)
+
+	def capture_piece(self, pos, colour):
+	    taken_piece = self.tiles[pos[1]][pos[0]]
+	    for i in range(len(self.pieces[taken_piece.colour])):
+	        if self.pieces[taken_piece.colour][i] is taken_piece:
+	            self.pieces[taken_piece.colour].pop(i)
+	            self.pieces[colour].append(taken_piece)
+	            taken_piece.colour = colour
+	            break
+
+	def shift_piece(self, t_from, t_to):
+	    piece = self.tiles[t_from[1]][t_from[0]]
+	    self.tiles[t_from[1]][t_from[0]] = None
+	    self.tiles[t_to[1]][t_to[0]] = piece
+	    piece.pos = t_to
+
+	def move_direction(move_from, move_to):
+	    direction = (move_to[0]-move_from[0], move_to[1]-move_from[1])
+	    return (int(direction[0]/2), int(direction[1]/2))
diff --git a/Snek/util/__pycache__/Board.cpython-36.pyc b/Snek/util/__pycache__/Board.cpython-36.pyc
index 0ad5a8382cc8ab13b09b209bc90970a7b5293e00..ca24baad38a3617d61fb58c02b1969f38a365aac 100644
GIT binary patch
delta 15
WcmZoxYF1)1=H=ybc)pSCuK)lb9R!mA

delta 15
WcmZoxYF1)1=H=zOcW)!xUjYCkqy;Dd

-- 
GitLab