minimax.lisp,
which contains the game-independent implementation of the minimax
algorithm, and connect-4.lisp,
which contains the plugins to minimax for the connect-4 game. The
version used by the server employs arrays to represent the game-board,
which is the most efficient way, however you may find it interesting
and educational to compare this to the nearly identical code that uses
lists to represent a board, which can be found in connect-4-lists.lisp.
The code in the files above provides the following definitions that you may find useful:
*max* |
[Constant] |
*min* |
[Constant] |
*debug* |
[Variable] |
nil, the minimax function will output
information about each node in the state tree it examines. When
nil, minimax will output nothing. Useful for debugging
your heuristic.
state |
[Structure] |
board:The game board this state represents.
This is the most important slot in the structure for the purposes of
your heuristic. The individual positions on the board can be accessed
with the get-posn function, described below.
bottom: This slot is non-nil if the state is a
end of game state (board full or someone won), nil
otherwise. If non-nil, the value must be one of the
following: BLK if the state represents a victory for
Black, RED if the state represents a victory for Red, or
DRAW if the state represents a full board.
You should use this slot - your heuristic should return
*max* if the value is RED,
*min* if the value is BLK,
and 0 if the value is DRAW.
parent:The state which is the parent of this state in
the search tree. If nil, this is the top of the search.
You can probably ignore this slot, though you may use it if you want.
children:A list of states which are the children of
this state. If nil, it means either than this state has
not yet been expanded, or that the state has no children (i.e. it
represents a full board, or a win/loss). You can ignore this slot, as
it should be nil when your function is called.
heuristic:The value of the heuristic for this
state. You can ignore this slot, the minimax function will not call
your heuristic function unless this slot is empty.
cost: The cost of this state. This is used by the
minimax function for pruning, you can ignore this slot.
max: Max is t if the state represents
the computer's move, and nil if the state represents the
opponent's move. You can ignore this slot as well, your heuristic
function should always return a value that is from the computer's
perspective (RED), the minimax algorithm takes care of alternating min
and max.
move: This slot indicates the column that was most
recently filled. You can probably ignore this slot as well.
below: The number of nodes that have been expanded
below this one, used to gather stats about the search for display
purposes. You can ignore this slot as it should be 0 when your
heuristic function is called.
The following function is provided for you to access individual positions on the game board (the board slot of a state):
get-posn board row col |
[Function] |
BLK, RED, or
NIL, which should be the contents of the board at
position row, col. The indices begin at zero, with the top
left position being (0,0), For example, if state
represented the winning board shown in the examples, then:
> (get-posn (state-board state) 0 0) ; top left corner NIL > (get-posn (state-board state) 5 0) ; bottom left corner RED > (get-posn (state-board state) 5 6) ; bottom right corner RED > (get-posn (state-board state) 0 6) ; Top right corner RED > (get-posn (state-board state) 3 3) ; fourth column, fourth spot from the top. BLK
You are only required to provide one function, although this function may well call a series of other functions you define as well. You should give the function a name that reflects your own name:
your-name state |
[Function] |
Your function must return *max* if, and
only if, state represents a winning position for Red (the
computer always plays as Red), *min* if, and only
if, state represents a winning position for Black, and
0 if the state represents a draw (full board). You can
get this information immediately from the bottom slot of
the state (see above). The value it
returns for non-winning boards must be within this range, with, in
general, negative numbers indicating advantages for Black, positive
numbers indicating advantages for Red, and 0 representing a purely
neutral state of the game. Your function should always evaluate a
state from the perspective of the Red player.
Your function may call any number of other support functions to do its job, including any of the support functions in the two LISP files that were not explicitly documented here.
The file your heuristic is in should have the same name as your
heuristic function, with a ".lisp" extension. In
addition, the first line of your file must be: (in-package
:cl-user)
The main part of the project will be playing against your heuristic, trying to analyze its performance, and improving it. A web interface has been provided for this, and you will have to submit your heuristic to the web server to use it. First, however, you must test your code to be sure it works. Play a few games against it to be sure it runs without errors before you submit it to the server. To test your code, you have to download the support code and make use of the ASCII text interface for playing a game. Follow these instructions:
minimax.lisp,
and
connect-4.lisp.
There is code for the default heuristic, which basically just checks
for win/loss, in default.lisp.
You might find this useful in getting started.
When the files are all loaded (in MCL or KCL), you can play a text-based game by using the following functions:
start-text-game heuristic ply |
[Function] |
play move |
[Function] |
After a game is over, you should not call play again, unless
you call the function start-text-game.
Deliverables
You are required to hand in two things: your heuristic code, and a
writeup. Before you hand in your heuristic, you should test it to be
sure it works.
There will be a tournament amongst all the heuristics. The winner
will receive a prize.
For your writeup, consider that the point of this project is for you to experiment with your heuristic. Instead of spending your time implementing all of minimax, you only have to implement a heuristic, and then play. Play a lot of games against it. For each game, see if you can figure out why it makes each move. Watch in particular for "don't care" moves, which tend to be in the left column, and see if you can determine why it makes these moves and iteratively improve your heuristic (a good player never makes a "don't care" move).
You should get a feel for conceptualizing a game-playing strategy as an algorithm that returns a number. Perhaps you can develop several and play them against each other.
Your writeup should include a description of what your heuristic is supposed to be doing. In other words, describe in English the strategy your heuristic implements. Then discuss your analysis of its strengths and weaknesses from your game-playing observations. Consider how prone it is to the horizon effect, and discuss ways you might (or have) overcome them.
Get an early start, this is way more than it looks.
The writeup should be submitted by email.
To submit your heuristic code, follow these steps on a mac connected to the network:
Please test your heuristic to be sure it doesn't generate errors before submitting it.