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