setup63
to create a git
repository for the lab. If you want to work alone do:
setup63 labs/04 noneIf you want to work with a partner, then one of you needs to run the following while the other one waits until it finishes.
setup63 labs/04 partnerUsernameOnce the script finishes, the other partner should run it on their account.
cd ~/cs63/labs/04 cp -r ~meeden/public/cs63/labs/04/* .
git add konane.py minimax.py git commit -m "lab4 start" git push
cd ~/cs63/labs/04 git pull
For this lab you will implement the minimax adversarial search algorithm on the game of Konane, also known as Hawaiian Checkers. The game is played on an N by N board of black and white pieces. N may be as small as 4 or as large as 8. The board shown below is 8 by 8, and uses 'B' for black pieces and 'W' for white pieces.
0 1 2 3 4 5 6 7 0 B W B W B W B W 1 W B W B W B W B 2 B W B W B W B W 3 W B W B W B W B 4 B W B W B W B W 5 W B W B W B W B 6 B W B W B W B W 7 W B W B W B W B
In order to play Konane, the black player goes first and removes one black piece from the corner or the center of the board. For an 8 by 8 board, this equates to positions (0,0), (3,3), (4,4), or (7,7). Next the white player removes a white piece adjacent to the space created by the first move. Then the players alternate moves, each jumping one of their own pieces over one horizontally or vertically adjacent opponent's piece, landing in an empty space on the other side, and removing the jumped piece. If desired, this may be continued in a multiple jump, as long as the same piece is moved in a straight line. The game ends when one player can no longer move and that player is considered the loser.
I have provided you with a Python implementation of the Konane game. You will implement minimax and then focus on finding a good evaluation function for estimating the value of a given Konane board. "It should be clear that the performance of a game-playing program depends strongly on the quality of its evaluation function" (Russell and Norvig, page 171).
Begin by playing a game of Konane. When you execute the program konane.py it will run one 6x6 game where you are playing Black versus an automated player that chooses moves randomly playing White. The program will give you a list of all valid moves that you can take on each turn. Simply choose the index of the move, or -1 to concede the game. It is important that you understand the rules of the game before you start trying to implement minimax.
Open the file konane.py. You should not modify this file. However, you should be sure you understand the structure of the classes defined here. There is a Konane class that implements the game. There is a base Player class, and three specializations of this class: HumanPlayer, SimplePlayer, and RandomPlayer. Each of these player definitions inherits from both the Player class and the Konane class. Therefore, the board size must also be specified in the constructors for these classes. You will be writing another specialization of this class, a MinimaxPlayer, in the file minimax.py.
There are several methods of the Konane class that will be useful to you as you work on minimax.
determine number of black moves if there are no black moves return worst score determine number of white moves if there are no white moves return best score return black moves - white movesThis will return positive scores when black has more moves than white, and will return negative scores when white has more moves than black.
boundedMinimax(node) returns an action set values to an empty list moves = generateMoves(state) if no moves return None for each move in moves nextState = successor(state, action) nextNode = node to store the nextState add minValue(nextNode) to values list return move associated with the maximum of values
return (random() * 6) - 3We can use these random values to help us test the current method. Add some print statements to boundedMinimax using the self.verbose flag so that you can ensure your implementation is correct. For example, at the start of the method:
if self.verbose: print "BEGIN boundedMinimax"And then at any point where the function returns:
if self.verbose: print "END boundedMinimax"Also print out the list of values accumulated, the list of possible moves, and the best move found. Ensure that the move being selected is the one that has been assigned the maximum value.
minValue(node) returns value of state in node if node at depth limit return eval(node) moves = generateMoves(state) if no moves return eval(node) v = best possible score for each move in moves nextState = successor(state, move) v = min(v, maxValue(node(nextState))) return v
game = Konane(6) game.playOneGame(MinimaxPlayer(6, 2, True), SimplePlayer(6))Executing the above commands should demonstrate that the minimax player that searches to depth 2 will outperform the simple player that always selects the first move from the list of valid moves.
if self.verbose: print " " * node.depth + "message ..."Update the debugging messages within both minValue and maxValue to include this feature. Then try the following test:
game = Konane(6) game.playOneGame(MinimaxPlayer(6, 4, True), RandomPlayer(6))Your program should now generate debugging output like the following (which was taken from a point near the end of the game):
BEGIN boundedMinimax BEGIN minValue BEGIN maxValue BEGIN minValue END minValue no moves returning 1000 END maxValue, returning 1000 END minValue, returning 1000 BEGIN minValue END minValue no moves returning 1000 BEGIN minValue BEGIN maxValue BEGIN minValue BEGIN maxValue END maxValue at depth limit returning 2 END minValue, returning 2 BEGIN minValue BEGIN maxValue END maxValue at depth limit returning -1000 END minValue, returning -1000 BEGIN minValue BEGIN maxValue END maxValue at depth limit returning 0 END minValue, returning 0 END maxValue, returning 2 END minValue, returning 2 VALUES [1000, 1000, 2] MOVES [[3, 5, 3, 3], [5, 1, 3, 1], [5, 3, 3, 3]] BESTMOVE [3, 5, 3, 3] END boundedMinimax
Once your minimax implementation is working correctly, you should focus on trying to come up with a better static evaluator. For example, if you play multiple games against the random player, on occasion a minimax player using the number of moves static evaluator will lose. Try to come up with a better static evaluator that can show improved performance against a the random player.
For example, you may want to consider safe moves vs unsafe moves. A safe move is when one player's piece has a jump but the other player piece does not. This happens when the jumping player's piece is on the perimeter of the board and the other player's piece is not.
Be sure to give a clear and detailed explanation of your static evaluator in the comments of the eval method. Discuss the various features you considered including as part of the evaluation function.
minimax.py
.
cd ~/cs63/labs/04 git add minimax.py git commit -m "the latest version" git push