Advertisement
Guest User

Untitled

a guest
May 21st, 2019
118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.21 KB | None | 0 0
  1. import copy
  2.  
  3.  
  4. class ExamplePlayer:
  5.  
  6. __FINISHING_HEXES = {
  7. 'r': {(3,-3), (3,-2), (3,-1), (3,0)},
  8. 'g': {(-3,3), (-2,3), (-1,3), (0,3)},
  9. 'b': {(-3,0),(-2,-1),(-1,-2),(0,-3)},
  10. }
  11.  
  12. def __init__(self, colour):
  13. """
  14. This method is called once at the beginning of the game to initialise
  15. your player. You should use this opportunity to set up your own internal
  16. representation of the game state, and any other information about the
  17. game state you would like to maintain for the duration of the game.
  18.  
  19. The parameter colour will be a string representing the player your
  20. program will play as (Red, Green or Blue). The value will be one of the
  21. strings "red", "green", or "blue" correspondingly.
  22. """
  23. # TODO: Set up state representation.
  24.  
  25. # To let AI know the colour they are playing and the board infomation
  26.  
  27. #The colour of the AI
  28. self.my_colour = colour[0]
  29.  
  30. #The starting board information
  31. self._BOARD = {
  32. 'r': {(-3,3), (-3,2), (-3,1), (-3,0)},
  33. 'g': {(0,-3), (1,-3), (2,-3), (3,-3)},
  34. 'b': {(3, 0), (2, 1), (1, 2), (0, 3)},
  35. }
  36.  
  37. #The information with the number of exits of all players
  38. self._EXITS = {
  39. 'r': 0,
  40. 'g': 0,
  41. 'b': 0,
  42. }
  43.  
  44. def action(self):
  45. """
  46. This method is called at the beginning of each of your turns to request
  47. a choice of action from your program.
  48.  
  49. Based on the current state of the game, your player should select and
  50. return an allowed action to play on this turn. If there are no allowed
  51. actions, your player must return a pass instead. The action (or pass)
  52. must be represented based on the above instructions for representing
  53. actions.
  54. """
  55.  
  56. # AI will only Exit if we have enough pieces to win otherwise
  57. # it will not exit the piece
  58. # e.g. we already exit two pieces, there is only on on the board,
  59. # we will try not to exit and try eat others to have enough pieces to win
  60. if(self._EXITS[self.my_colour] + len(self._BOARD[self.my_colour]) >= 4):
  61. for piece in self._BOARD[self.my_colour]:
  62. if piece in self.__FINISHING_HEXES[self.my_colour]:
  63. return ('EXIT',piece)
  64.  
  65. #calculating the best move using minimax with depth of 2
  66. myaction = self.minimax(self._BOARD,self.my_colour,2,True)
  67.  
  68. # re-format the best action to provide to the referee
  69. if(myaction[0][0] == 'PASS'):
  70. return ("PASS", None)
  71. if(myaction[0][1] == 'EXIT'):
  72. return ('EXIT',myaction[0][0])
  73.  
  74. if(abs(myaction[0][1][0] - myaction[0][0][0]) <= 1 and abs(myaction[0][1][1] - myaction[0][0][1]) <= 1):
  75. return(('MOVE',myaction[0]))
  76. else:
  77. return(('JUMP',myaction[0]))
  78.  
  79.  
  80. def update(self, colour, action):
  81.  
  82. """
  83. This method is called at the end of every turn (including your player’s
  84. turns) to inform your player about the most recent action. You should
  85. use this opportunity to maintain your internal representation of the
  86. game state and any other information about the game you are storing.
  87.  
  88. The parameter colour will be a string representing the player whose turn
  89. it is (Red, Green or Blue). The value will be one of the strings "red",
  90. "green", or "blue" correspondingly.
  91.  
  92. The parameter action is a representation of the most recent action (or
  93. pass) conforming to the above in- structions for representing actions.
  94.  
  95. You may assume that action will always correspond to an allowed action
  96. (or pass) for the player colour (your method does not need to validate
  97. the action/pass against the game rules).
  98. """
  99. # TODO: Update state representation in response to action.
  100.  
  101. #update the inforamtion given to the AI's board
  102.  
  103. if action[0] != 'PASS':
  104. if colour == 'red':
  105. if action[0] == 'EXIT':
  106. self._BOARD['r'].remove(action[1])
  107. self._EXITS['r'] += 1
  108. elif action[0] == 'JUMP':
  109. self._BOARD['r'].remove(action[1][0])
  110. self._BOARD['r'].add(action[1][1])
  111. #setting the peice that is jumped
  112. pos = self.find_middle(action[1][0],action[1][1])
  113. for key in self._BOARD:
  114. if(pos in self._BOARD[key]):
  115. self._BOARD[key].remove(pos)
  116. self._BOARD['r'].add(pos)
  117. else:
  118. self._BOARD['r'].add(action[1][1])
  119. self._BOARD['r'].remove(action[1][0])
  120. elif colour == 'green':
  121. if action[0] == 'EXIT':
  122. self._BOARD['g'].remove(action[1])
  123. self._EXITS['g'] += 1
  124. elif action[0] == 'JUMP':
  125. self._BOARD['g'].remove(action[1][0])
  126. self._BOARD['g'].add(action[1][1])
  127. #setting the peice that is jumped
  128. pos = self.find_middle(action[1][0],action[1][1])
  129. for key in self._BOARD:
  130. if(pos in self._BOARD[key]):
  131. self._BOARD[key].remove(pos)
  132. self._BOARD['g'].add(pos)
  133.  
  134. else:
  135. self._BOARD['g'].remove(action[1][0])
  136. self._BOARD['g'].add(action[1][1])
  137. else:
  138. if action[0] == 'EXIT':
  139. self._BOARD['b'].remove(action[1])
  140. self._EXITS['b'] += 1
  141. elif action[0] == 'JUMP':
  142. self._BOARD['b'].remove(action[1][0])
  143. self._BOARD['b'].add(action[1][1])
  144. #setting the peice that is jumped
  145. pos = self.find_middle(action[1][0],action[1][1])
  146. for key in self._BOARD:
  147. if(pos in self._BOARD[key]):
  148. self._BOARD[key].remove(pos)
  149. self._BOARD['b'].add(pos)
  150.  
  151. else:
  152. self._BOARD['b'].remove(action[1][0])
  153. self._BOARD['b'].add(action[1][1])
  154.  
  155.  
  156.  
  157.  
  158. #currentpos means the position we are currently at
  159. #pos refers to where we are going to and return all the surroundings for analysis
  160. def get_surr(self,currentpos,pos,show_all_pos):
  161. hex = []
  162. if self.on_board(pos[0],pos[1] - 1):
  163. hex.append((pos[0],pos[1] - 1))
  164. if self.on_board(pos[0] + 1,pos[1] - 1):
  165. hex.append((pos[0] + 1,pos[1] - 1))
  166. if self.on_board(pos[0] + 1,pos[1]):
  167. hex.append((pos[0] + 1,pos[1]))
  168. if self.on_board(pos[0],pos[1] + 1):
  169. hex.append((pos[0],pos[1] + 1))
  170. if self.on_board(pos[0] - 1,pos[1] + 1):
  171. hex.append((pos[0] - 1,pos[1] + 1))
  172. if self.on_board(pos[0] - 1,pos[1]):
  173. hex.append((pos[0] - 1,pos[1]))
  174. if not show_all_pos:
  175. hex.remove(currentpos)
  176. return hex
  177.  
  178. # Check if the coordinate is inside the board
  179. def on_board(self,x, y):
  180. if x == 0 and -3 <= y <= 3:
  181. return True
  182. elif x == -1 and -2 <= y <= 3:
  183. return True
  184. elif x == -2 and -1 <= y <= 3:
  185. return True
  186. elif x == -3 and 0 <= y <= 3:
  187. return True
  188. elif x == 1 and -3 <= y <= 2:
  189. return True
  190. elif x == 2 and -3 <= y <= 1:
  191. return True
  192. elif x == 3 and -3 <= y <= 0:
  193. return True
  194. else:
  195. return False
  196.  
  197. #get hex distance from a (x,y) to b (x,y)
  198. def get_hex_distance(self,a,b):
  199. return max(abs(a[0] - b[0]),abs(a[1] - b[1]), abs((-1*a[0]-a[1]) - (-1*b[0]-b[1])))
  200.  
  201.  
  202.  
  203. #calulating the shortest hex distance to the goals best (1 of 4)
  204. def get_shortest_hex_distance(self,a,colour):
  205. min = 37
  206. for items in self.__FINISHING_HEXES[colour]:
  207. new_dis = self.get_hex_distance(a,items)
  208. if(new_dis < min):
  209. min = new_dis
  210. return min
  211.  
  212.  
  213. #calulating the shortest hex distance to the other pieces
  214. def get_shortest_att_distance(self,a,colour,board):
  215. min = 37
  216. pieces = set()
  217. for co in ('r','b','g'):
  218. if co != colour:
  219. pieces.update(board[co])
  220. for items in pieces:
  221. new_dis = self.get_hex_distance(a,items)
  222. if(new_dis < min):
  223. min = new_dis
  224. return min
  225.  
  226. # find the middle coordinate between two coordinates A:(x,y) and B:(x,y)
  227. def find_middle(self, a,b):
  228. x = a[0]
  229. y = a[1]
  230. if b[0]-a[0] == 2:
  231. x = a[0] + 1
  232. elif b[0]-a[0] == -2:
  233. x = a[0] - 1
  234. if b[1]-a[1] == 2:
  235. y = a[1] + 1
  236. elif b[1]-a[1] == -2:
  237. y = a[1] - 1
  238. return (x,y)
  239.  
  240. #to check if A:(x,y) can jump over B:(x,y)
  241. def can_be_jump(self,board,a,b):
  242. new_pos = (b[0] + (b[0] - a[0]),b[1] + (b[1] - a[1]))
  243. if self.on_board(new_pos[0],new_pos[1]):
  244. for key in board:
  245. if new_pos in board[key]:
  246. return False
  247. return True
  248. else:
  249. return False
  250.  
  251.  
  252. #Evaluation function of the board used by the minimax
  253. def get_total_score(self,board,colour):
  254. final = []
  255. #creating all the position of other pieces
  256. pieces = set()
  257. for key in board:
  258. if key != colour:
  259. pieces.update(board[key])
  260.  
  261. #calulating the scaore of each piece of ours
  262. for i in board[colour]:
  263. total = 0
  264. # The steps to the goals with exiting move
  265. total += -1000*(self.get_shortest_hex_distance(i,colour)+1)
  266.  
  267. for sur in self.get_surr(i,i,True):
  268. if sur in board[colour]:
  269. #score for the own colour near this piece
  270. total += 250
  271. if (sur[0] + (sur[0] - i[0]),sur[1] + (sur[1] - i[1])) in pieces:
  272. # score for protecting our own piece, in case of get eaten by other piece
  273. total += 1000
  274. if sur in pieces:
  275. if self.can_be_jump(board, sur, i):
  276. # we may get eat by other colour
  277. total -= 2000
  278. if (sur[0] + (sur[0] - i[0]),sur[1] + (sur[1] - i[1])) in pieces:
  279. # we block others way from jumping
  280. total += 500
  281. final.append(total)
  282.  
  283.  
  284. # only calulate the best four score of our piece that is needed to win,
  285. #since we only need to win exiting four pieces
  286.  
  287. final_score = 0
  288. index = 0
  289. for i in sorted(final, reverse = True):
  290. if index < len(self._BOARD[colour]):
  291. final_score += i
  292. index += 1
  293.  
  294. # the score of each piece
  295. # the more piece we have , the higher score we got
  296. final_score += 3000*(len(self._BOARD[colour])+self._EXITS[colour])
  297.  
  298. return final_score
  299.  
  300.  
  301. # check if B:(x,y) is closer to the goal of certain colour A:(x,y)
  302. def is_closer(self,a,b,colour):
  303. return self.get_shortest_hex_distance(a,colour) < self.get_shortest_hex_distance(b,colour)
  304.  
  305. # change the given board information
  306. # e.g. make the move
  307. def make_move(self,board,a,b):
  308. for co in ('r','g','b'):
  309. if a in board[co]:
  310. if b == "EXIT":
  311. board[co].remove(a)
  312. return
  313. else:
  314. board[co].remove(a)
  315. board[co].add(b)
  316. return
  317.  
  318. # geting all the possiable move of a colour return the list with the move
  319. # e.g. [(a,b),(a,c)] NOTE: a can move to b and c
  320. def get_valid_move(self,board, colour):
  321. vaild_move = []
  322. pieces = set()
  323. #put all the pieces in pieces for checking the position
  324. for key in board:
  325. pieces.update(board[key])
  326. for items in board[colour]:
  327. #if we can exit
  328. # if items in self.__FINISHING_HEXES[colour]:
  329. # vaild_move.append((items,'EXIT'))
  330. sur = self.get_surr(items,items,True)
  331. for pos in sur:
  332. # check if they can move to this position
  333. if pos not in pieces:
  334. vaild_move.append((items,pos))
  335. #CHECK if they can jump over
  336. elif (pos[0]+(pos[0]-items[0]),pos[1]+(pos[1] - items[1])) not in pieces and self.on_board(pos[0]+(pos[0]-items[0]),pos[1]+(pos[1] - items[1])):
  337. vaild_move.append((items,(pos[0]+(pos[0]-items[0]),pos[1]+(pos[1] - items[1]))))
  338. return vaild_move
  339.  
  340. # our minimax function
  341. def minimax(self,board, colour, depth, maximizing):
  342. vaild_move = self.get_valid_move(board, colour)
  343. others_move = []
  344. colours = ['r','b','g']
  345. colours.remove(colour)
  346. temp_move = self.get_valid_move(board,colours[0])
  347. temp_colour = colours[0]
  348. if len(temp_move) == 0 and len(colours) > 1:
  349. temp_move = self.get_valid_move(board,colours[1])
  350. temp_colour = colours[1]
  351. others_move += temp_move
  352. colours.remove(temp_colour)
  353. unmoved_colour = colours[0]
  354.  
  355.  
  356. # Need to add a or condition for terminal state
  357. if depth == 0:
  358. #Termonal state
  359. #if AI(me) is winning return large value
  360. #if other is winning return low value
  361. #else
  362. #game over return 0
  363.  
  364. return (None,self.get_total_score(board,colour))
  365.  
  366.  
  367. #maximizing part
  368. if maximizing:
  369. value = -999999
  370. move = ('PASS',None)
  371. for moves in vaild_move:
  372. temp_board = copy.deepcopy(board)
  373. self.make_move(temp_board,moves[0],moves[1])
  374. new_score = self.minimax(temp_board,colour,depth-1,False)[1]
  375. if new_score > value:
  376. value = new_score
  377. move = moves
  378. return (move,value)
  379. # minimizing part
  380. else:
  381. value = 999999
  382. move = ('PASS',None)
  383. for moves in others_move:
  384. temp_board = copy.deepcopy(board)
  385. self.make_move(temp_board,moves[0],moves[1])
  386. unmoved_colour_moves = self.get_valid_move(temp_board, unmoved_colour)
  387. for sec_moves in unmoved_colour_moves:
  388. temp_board_2 = copy.deepcopy(temp_board)
  389. self.make_move(temp_board_2,sec_moves[0],sec_moves[1])
  390. new_score = self.minimax(temp_board_2,colour,depth-1,True)[1]
  391. if new_score < value:
  392. value = new_score
  393. move = moves
  394. return (move,value)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement