sample_players.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. """This file contains a collection of player classes for comparison with your
  2. own agent and example heuristic functions.
  3. """
  4. from random import randint
  5. def null_score(game, player):
  6. """This heuristic presumes no knowledge for non-terminal states, and
  7. returns the same uninformative value for all other states.
  8. Parameters
  9. ----------
  10. game : `isolation.Board`
  11. An instance of `isolation.Board` encoding the current state of the
  12. game (e.g., player locations and blocked cells).
  13. player : hashable
  14. One of the objects registered by the game object as a valid player.
  15. (i.e., `player` should be either game.__player_1__ or
  16. game.__player_2__).
  17. Returns
  18. ----------
  19. float
  20. The heuristic value of the current game state.
  21. """
  22. if game.is_loser(player):
  23. return float("-inf")
  24. if game.is_winner(player):
  25. return float("inf")
  26. return 0.
  27. def open_move_score(game, player):
  28. """The basic evaluation function described in lecture that outputs a score
  29. equal to the number of moves open for your computer player on the board.
  30. Parameters
  31. ----------
  32. game : `isolation.Board`
  33. An instance of `isolation.Board` encoding the current state of the
  34. game (e.g., player locations and blocked cells).
  35. player : hashable
  36. One of the objects registered by the game object as a valid player.
  37. (i.e., `player` should be either game.__player_1__ or
  38. game.__player_2__).
  39. Returns
  40. ----------
  41. float
  42. The heuristic value of the current game state
  43. """
  44. if game.is_loser(player):
  45. return float("-inf")
  46. if game.is_winner(player):
  47. return float("inf")
  48. return float(len(game.get_legal_moves(player)))
  49. def improved_score(game, player):
  50. """The "Improved" evaluation function discussed in lecture that outputs a
  51. score equal to the difference in the number of moves available to the
  52. two players.
  53. Parameters
  54. ----------
  55. game : `isolation.Board`
  56. An instance of `isolation.Board` encoding the current state of the
  57. game (e.g., player locations and blocked cells).
  58. player : hashable
  59. One of the objects registered by the game object as a valid player.
  60. (i.e., `player` should be either game.__player_1__ or
  61. game.__player_2__).
  62. Returns
  63. ----------
  64. float
  65. The heuristic value of the current game state
  66. """
  67. if game.is_loser(player):
  68. return float("-inf")
  69. if game.is_winner(player):
  70. return float("inf")
  71. own_moves = len(game.get_legal_moves(player))
  72. opp_moves = len(game.get_legal_moves(game.get_opponent(player)))
  73. return float(own_moves - opp_moves)
  74. class RandomPlayer():
  75. """Player that chooses a move randomly."""
  76. def get_move(self, game, legal_moves, time_left):
  77. """Randomly select a move from the available legal moves.
  78. Parameters
  79. ----------
  80. game : `isolation.Board`
  81. An instance of `isolation.Board` encoding the current state of the
  82. game (e.g., player locations and blocked cells).
  83. legal_moves : list<(int, int)>
  84. A list containing legal moves. Moves are encoded as tuples of pairs
  85. of ints defining the next (row, col) for the agent to occupy.
  86. time_left : callable
  87. A function that returns the number of milliseconds left in the
  88. current turn. Returning with any less than 0 ms remaining forfeits
  89. the game.
  90. Returns
  91. ----------
  92. (int, int)
  93. A randomly selected legal move; may return (-1, -1) if there are
  94. no available legal moves.
  95. """
  96. if not legal_moves:
  97. return (-1, -1)
  98. return legal_moves[randint(0, len(legal_moves) - 1)]
  99. class GreedyPlayer():
  100. """Player that chooses next move to maximize heuristic score. This is
  101. equivalent to a minimax search agent with a search depth of one.
  102. """
  103. def __init__(self, score_fn=open_move_score):
  104. self.score = score_fn
  105. def get_move(self, game, legal_moves, time_left):
  106. """Select the move from the available legal moves with the highest
  107. heuristic score.
  108. Parameters
  109. ----------
  110. game : `isolation.Board`
  111. An instance of `isolation.Board` encoding the current state of the
  112. game (e.g., player locations and blocked cells).
  113. legal_moves : list<(int, int)>
  114. A list containing legal moves. Moves are encoded as tuples of pairs
  115. of ints defining the next (row, col) for the agent to occupy.
  116. time_left : callable
  117. A function that returns the number of milliseconds left in the
  118. current turn. Returning with any less than 0 ms remaining forfeits
  119. the game.
  120. Returns
  121. ----------
  122. (int, int)
  123. The move in the legal moves list with the highest heuristic score
  124. for the current game state; may return (-1, -1) if there are no
  125. legal moves.
  126. """
  127. if not legal_moves:
  128. return (-1, -1)
  129. _, move = max([(self.score(game.forecast_move(m), self), m) for m in legal_moves])
  130. return move
  131. class HumanPlayer():
  132. """Player that chooses a move according to user's input."""
  133. def get_move(self, game, legal_moves, time_left):
  134. """
  135. Select a move from the available legal moves based on user input at the
  136. terminal.
  137. **********************************************************************
  138. NOTE: If testing with this player, remember to disable move timeout in
  139. the call to `Board.play()`.
  140. **********************************************************************
  141. Parameters
  142. ----------
  143. game : `isolation.Board`
  144. An instance of `isolation.Board` encoding the current state of the
  145. game (e.g., player locations and blocked cells).
  146. legal_moves : list<(int, int)>
  147. A list containing legal moves. Moves are encoded as tuples of pairs
  148. of ints defining the next (row, col) for the agent to occupy.
  149. time_left : callable
  150. A function that returns the number of milliseconds left in the
  151. current turn. Returning with any less than 0 ms remaining forfeits
  152. the game.
  153. Returns
  154. ----------
  155. (int, int)
  156. The move in the legal moves list selected by the user through the
  157. terminal prompt; automatically return (-1, -1) if there are no
  158. legal moves
  159. """
  160. if not legal_moves:
  161. return (-1, -1)
  162. print(('\t'.join(['[%d] %s' % (i, str(move)) for i, move in enumerate(legal_moves)])))
  163. valid_choice = False
  164. while not valid_choice:
  165. try:
  166. index = int(input('Select move index:'))
  167. valid_choice = 0 <= index < len(legal_moves)
  168. if not valid_choice:
  169. print('Illegal move! Try again.')
  170. except ValueError:
  171. print('Invalid index! Try again.')
  172. return legal_moves[index]
  173. if __name__ == "__main__":
  174. from isolation import Board
  175. # create an isolation board (by default 7x7)
  176. player1 = RandomPlayer()
  177. player2 = GreedyPlayer()
  178. game = Board(player1, player2)
  179. # place player 1 on the board at row 2, column 3, then place player 2 on
  180. # the board at row 0, column 5; display the resulting board state. Note
  181. # that .apply_move() changes the calling object
  182. game.apply_move((2, 3))
  183. game.apply_move((0, 5))
  184. print(game.to_string())
  185. # players take turns moving on the board, so player1 should be next to move
  186. assert(player1 == game.active_player)
  187. # get a list of the legal moves available to the active player
  188. print(game.get_legal_moves())
  189. # get a successor of the current state by making a copy of the board and
  190. # applying a move. Notice that this does NOT change the calling object
  191. # (unlike .apply_move()).
  192. new_game = game.forecast_move((1, 1))
  193. assert(new_game.to_string() != game.to_string())
  194. print("\nOld state:\n{}".format(game.to_string()))
  195. print("\nNew state:\n{}".format(new_game.to_string()))
  196. # play the remainder of the game automatically -- outcome can be "illegal
  197. # move" or "timeout"; it should _always_ be "illegal move" in this example
  198. winner, history, outcome = game.play()
  199. print("\nWinner: {}\nOutcome: {}".format(winner, outcome))
  200. print(game.to_string())
  201. print("Move history:\n{!s}".format(history))