Navigation
- Useful Links and Updates
- Background
- Notation
- Blocks
- Textual Input Language
- The Commands
- Output
- Running The Program
- Your Task
- Some Notes/Hints About The Skeleton
- Advice
- Checkpoint Grading Details
- Grading Details
- Extra Credit
Useful Links and Updates
- Ataxx Board.java Intro Video
- Ataxx Skeleton Overview Video
- Ataxx Intro Slides
- Ataxx AI.java Intro Video
- Unit Testing
- Acceptance Testing
- Acceptance Testing Slides
- Conceptual Understanding Spec Quiz
- Undo Mechanics Review Slides
- Undo Mechanics Review Recording
- Tips, Q & A Recording
- Tips, Q & A Presentation Slides
Background
Ataxx is a two-person game played with red and blue pieces on a 7-by-7 board. As illustrated below, there are two possible kinds of moves:
- Extending - you can extend from a piece of your own color by laying down a new piece of your color in an empty square next to that existing piece (horizontally, vertically, or diagonally).
- Jumping - you can jump by moving a piece of your own color to an empty, non-adjacent square that is no more than two rows and no more than two columns distant.
In either case, all opposing pieces that are next to the previously empty destination square are replaced by pieces of your color. Here is an example of a starting position (in the middle) and two possible moves with the same piece. Squares marked $\times$ show red's other possible jumps with that piece:

At the beginning of the game, we start with pieces in all four corners:

The red player goes first, and the two players alternate until there are no more moves possible or until there have been 25 consecutive jumps between both players. You are allowed to skip a move (we will call this a pass) only if you have at least one piece on the board, but no legal move. The winner is the player who has the most pieces at the end of the game; thus, you don't automatically lose just because you can't move. The one exception to this condition is if you have no pieces left on the board. In this case, you have no way of winning, so you will automatically lose. It is possible also to tie, when both sides end up with the same number of pieces on the board and:
- neither player can make a move, or
- the maximum number of jumps (25 consecutive across both players) has been reached.
Notation
We'll denote columns with
letters a--g from the left and rows with numerals 1--7 from the
bottom, as shown in Figure 2.
An ordinary move consists of
two positions separated by a
hyphen in the format $c_0r_0-c_1r_1$ (e.g., g1-f2
).
The first position gives a piece owned by the current
player, and the second gives an empty square to which the piece
jumps or extends.
A single hyphen denotes a case where one
player has no move and must pass (it is only legal when there is no
other legal move).
Blocks
To make things even more interesting, you can place a set of blocks symmetrically about the center of the board before playing. These are pre-filled squares that may never be moved to (the blocks themselves never move). Setting blocks is only possible at the start of the game. The illustration below is an example of a starting configuration with 10 blocks. Each block is always reflected across the middle row and the middle column. In other words, there is symmetry both horizontally and vertically for any block that is placed. No block may be placed in the four corner squares, since the initial configuration has pieces there.
For example, the configuration below may be described by listing three squares: "b2", "c3", and "c4"; reflecting these three gives in addition "f2", "e3", "e4", "b6", "c5", "f6", and "e5". Alternatively, the same configuration may be described as "f2", "c3", and "e4".

Textual Input Language
The players (we'll simply call them Red and Blue) can each be either a manual player, entering moves as input, or an automated player (we'll call it an AI). Manual players can talk to the program using either a textual interface, described in this section, or (for extra credit) a GUI.
Your program should respond to the following textual commands (you may
add others). There is one command per line, but otherwise, whitespace
may precede and follow command names and operands freely. Empty lines
have no effect, and everything from a #
character to the end
of a line is ignored as a comment.
The Commands:
Starting and Ending Games
new
: Abandons the current game (if one is in progress), clears the board to its initial configuration, and starts a new game.quit
: Abandons any current game and exits the program. The end of input has the same effect.
Commands to Set Parameters
The following commands set various game parameters.
auto <COLOR>
: Sets up the program so that player C (Red or Blue) is an AI. By default, and after aclear
command, Red is a manual player and Blue is an AI. Thus, the commandauto Red
(without any other specification for Blue) causes both Red and Blue to be AIs, and the machine will play a game against itself.manual <COLOR>
: Sets up the program so that player C (Red or Blue) is a manual player. Thus, the commandmanual Blue
causes both Red and Blue to be manual players, if ran immediately after aclear()
call or at the beginning of the game. The two players would then presumably alternate entering moves on a terminal).block CR
: Sets a block at square CR (e.g.block a1
) and all reflections of that square around the middle row and middle column, as described in Blocks. It is an error to place a block (or one of its reflections) on a previously set up piece. Blocks may not be placed after a piece move, until the board is cleared again. In case of any errors, the command has no effect.seed N
: If your program's AIs use pseudo-random numbers to choose moves, this command sets the random seed to N (a long integer). This command has no effect if there is no random component to your automated players (or if you don't use them in a particular game). It doesn't matter exactly how you use N as long as your automated player(s) behave(s) identically in response to any given sequence of moves. In the absence of aseed
command, do what you want to seed your generator.
Entering and Undoing Moves
The first and then every other move is for the red player, the second and then every other is for blue, and the normal legality rules apply to all moves.
c0r0-c1r1
: Makes a move for the current player from location(c0, r0)
to location(c1, r1)
. This should automatically detect if the move was a jump or an extend and behave accordingly (i.e. creating a new piece if the move was an extend or moving an existing piece if it was a jump).undo
: Undoes the last move made (if there was one) and, if that makes it an automated player's turn, undo a second move as well.
Your program must reject illegal moves from any player (they have no effect on the board; the program should tell the user that there is an error and request another move). An AI must not make illegal moves.
Miscellaneous Commands
help
Print a brief summary of the commands.dump
This command is especially for testing and debugging. It prints the board out in exactly the following format:=== r - - - - - b - - - - - - - - - X - X - - - - - - - - - - - X - X - - - - - - - - - b - - - - - r ===
Here,
-
indicates an empty square,r
indicates a red square,b
indicates a blue square, andX
indicates a block. Don't use===
markers anywhere else in your output. This gives the autograder a way to determine the state of your game board at any point. It does not change any of the state of the program.
Optional (Helpful) Commands To Add
You might (optionally) consider a couple of other useful commands that you will find in the staff solution. These may be useful in debugging and improving the overall experience of the game:
board
Prints out a board in something nicer than the dump format. The staff's version, for example, prints row and column designations along the sides of the board.verbose
Causes the board to get printed (as for board) after each move.quiet
Turns off verbose mode, so that the board no longer gets printed after each move.
Output
When either player enters a winning move, the program should print one of the lines
* Red wins.
* Blue wins.
* Draw.
as appropriate. Use exactly those phrases, alone on their line. Don't use these phrases in any other situation.
Whenever a player is an AI, it should print out each move that it makes, using one of the formats
* Red moves -.
* Blue moves -.
* Red moves C0R0-C1R1.
* Blue moves C0R0-C1R1.
(replacing C0R0-C1R1
with an actual move, of course.) Again, use exactly
those phrases, alone on their line.
Other than this, you may produce any additional user-friendly output you choose, subject to a few restrictions:
- Do not use the "
===
" marker except in the specified output from thedump
command. - Do not output any lines containing the words "passes", "wins", "Draw", or "moves", except in the specified cases described above.
Running the Program
As usual, you'll run the program with the command
java -ea ataxx.Main
Your AIs must make only legal moves. They are required to be somewhat smart: they should be able to find a forced win that is within four moves of the current position. A forced win is a situation where you maneuver the opponent into a position in which they have no choice other than to make losing moves; in other words, you force your opponent into losing the game. Your AI should be able to look up to 4 moves ahead and find a forced win, if one is available. Finally, there will be an (optional) tournament at the end of the project so that you can challenge your AI against AIs that other 61B students came up with!
We've provided an executable staff-ataxx
in cs61b-software
, which allows
you to run the staff's solution and AI.
You can run staff-ataxx
as follows:
staff-ataxx
After running the above line, you can begin entering the textual input commands in the terminal to play the game. If you'd like to run the staff solution with the GUI enabled, you can use:
staff-ataxx --display
To get this executable, cd
into your cs61b-software
directory and run
git pull origin master
Be aware, however, that the staff-ataxx
program
does not define what you are supposed to do; this document does.
If you find a discrepancy between this specification and our program, tell us.
Please don't assume that our program defines how your program is supposed to work.
Testing
Check out these testing videos for some explanations about the Ataxx testing framework!
As with Projects 0 and 1, the command
make unit
will run all the unit tests, and the command
make acceptance
will run all the acceptance tests. If you try this on the skeleton, you'll see that, unsurprisingly, it fails all these tests, giving you an indication of what needs to be changed. The unit and acceptance tests provided with the skeleton are nowhere near comprehensive. Passing the unit and acceptance tests included with the skeleton is not an indication that your code is correct, you should write additional tests to ensure correctness. Use the IntelliJ debugger to assist in passing these test cases.
Usually, you will want to use the command make check
for testing,
which runs both the unit and acceptance tests.
By the time you finish the project,
make check
should report no errors. If your
python command is python
rather than python3
you will need to use
make PYTHON=python check
to run the acceptance tests.
Important: For information on the testing commands you can use in an acceptance test,
refer to the comments in the test-ataxx
file that was given as part of
the skeleton.
Furthermore, to run individual acceptance tests, you can run the following
from the proj2
directory:
python3 testing/tester.py --show=1 testing/<testX>.in
where <testX>.in
is an input file (e.g test01-1.in
). The --show=1
option
here shows error output for the test that you are running. Finally, you can also run
the above command with the --keep
option:
python3 testing/tester.py --show=1 --keep testing/<testX>.in
This will create two new files in the proj2
directory:
<testX>.out
: This file contains the output generated by the input file that you provided to the test.<testX>.err
: This file contains any errors/exceptions that were thrown during the execution of this test.
Acceptance tests used in make check
are stored under the
proj2/testing
folder. If you would like to run an acceptance test
in IntelliJ so that you can use the debugger, use the following steps:
- Navigate to the Main file in IntelliJ and open it.
- Click on Run > Edit Configurations...
- In the left sidebar of the pop-up window, under "Application", select Main.
- For "Program arguments", put in
testing/<test_file>
where<test_file>
is the name of the file of the acceptance test you would like to run.
For example, if you would like to run acceptance test test01-1.in
,
you should have testing/test01-1.in
as your program arguments.
Now you can click Run > Run 'Main' or Run > Debug 'Main' to run and use the
debugger on the acceptance tests. Remember to set a breakpoint if you're debugging!
Some tests play two programs against each other (the tests with a testX-2.in
file).
When running these tests, you'll have to pass both files as arguments.
Your Task
Your job is to write a program that allows you to play a game of Ataxx, and also
an AI that you can play the game against. This will involve editing the functions/locations
labeled FIXME
in Move.java
, Board.java
, and AI.java
.
To obtain the skeleton files (and set up an initial entry for your project in the repository), you can use the usual command sequence:
git fetch shared
git merge shared/proj2 -m "Get proj2 skeleton"
from your Git working directory. Should we update the skeleton, you can use exactly the same sequence to update your project with the same changes.
Be sure to include tests of your program (this is part of the
grade, see Grading Details). The makefile we provide has
convenient targets for running unit and acceptance tests, and a
Python testing script for acceptance testing. See the testing/README
file for a description of the specification language used by this script.
Our skeleton directory
contains some trivial tests, but these do not constitute
an adequate set of tests!
Make up your tests ahead of time and update your makefile to run them.
To help with testing and debugging, we will provide our own version of
the program, so that you can test your program against ours (we'll be
on the lookout for illegal moves). More details will follow.
Your AI should be able to find a forced win that is within four moves of a given position. It must not use more than 10 seconds to make any given move.
Some Notes and Hints About the Skeleton
- The
_notifier
instance variable and theannounce()
method that you see used throughoutBoard.java
is a mechanism for the GUI (see the Extra Credit section). If you aren't working on the GUI, you can ignore this mechanism entirely. - Our usage of the
EXTENDED_SIDE
class constant may confuse you; if you don't understand the motivation for it, please read the lengthy comment above the_board
instance variable at the bottom ofBoard.java
. - The
unrecordedSet()
method inBoard.java
is a way for us to set the PieceColor of a square without marking the action as a move that can be undone. Theset()
method is supposed to set a square and mark the move as "undoable"; this may pose a problem when we'd like to make certain changes to the board that aren't supposed to be undone by a player. - You shouldn't be calling the Move constructor (it's private!) whenever you
need to create a new Move object; instead, use the factory static method
Move.move()
to get a Move object containing the move that you'd like to represent. This method is optimized (see the Javadoc comment above the method) in order for your AIs to run even faster, so you should use it whenever needed. You'll likely find that you'll need to do some character arithmetic while completing this project. Since chars are just integers underneath the hood, we can do addition with them as follows:
'a' + 3 = 'd' 'g' - 4 = 'c' '1' + 5 = '6' 'b'++ is equivalent to 'c'
"Linearized indices" is just a fancy way of stating that we converted the standard 2D array indexing with a row and column index into 1D array indexing. As a visual example, in the 3x3 table below we translate square (0, 0) to index 0, square (1, 0) to index 1, square (2, 0) to index 2, so on and so forth. Then, we can store all the squares in the Board as a 1D array and refer to each square by its new 1D index.
0 1 2 3 4 5 6 7 8
Advice
You are not required to use any part of our skeleton as long as your program behaves as described in this document. However, don't throw anything away without understanding it; there's something to learn from our code even if you choose not to use it.
We suggest working first on the classes Move
and Board
, which allow you
to make moves and interact with the board.
Then you can tackle writing a machine player in AI.java
.
For the AI, start with something really simple (perhaps choosing a legal move at
random) and introduce strategy only when you get everything working
properly. Random play will not reliably satisfy the requirement of finding
forced wins, so you will eventually have to do a real search.
Checkpoint Grading Details
By Friday, 3/18 at 11:59PM you must have completed the following deliverables in order to receive 4 points. The checkpoint is not extra credit, and will be factored into your final project grade.
- Pass all unit tests in
MoveTest.java
. - Pass all unit tests in
BoardTest.java
. - Pass acceptance tests 1-4 (a correct Move and Board implementation will be able to pass these 4).
You will be able to submit to the checkpoint autograder by committing it and tagging it as follows (the tag is proj2a not proj2):
git tag proj2a-0 # Or proj2a-1, etc.
git push
git push --tags
On release, you will only be able to submit to the checkpoint autograder once every 6 hours, and full error logs will be displayed. Starting Monday 3/14, you will be able to submit once every 3 hours. Finally, starting at 9:00PM PST on 3/18, you will be able to submit as many times as you want.
Grading Details
Project 2 is due 4/1 at 11:59 PM PT. The project is worth 24 points (the total score for this project is 28 points, with the checkpoint included). Your score on Gradescope will be your final score for the assignment.
The release date of the full Project 2 autograder will be announced soon. The tests included in the skeleton code are not the full suite of tests that will be run on your submission. The intention is that you write tests for your code, and do not rely on autograder submissions to debug problems. To encourage this, you will only be able to submit to the autograder once every 6 hours, and limited error output will be displayed.
Starting 3/18, the autograder will show more information about why you are failing some tests, and what the tests that you are failing contain. Starting on 4/1, you will be able to submit to the grader once every 3 hours, and in the last hour before the deadline, you will be able to submit 4 times to account for any technical difficulties or Git issues. Starting 4/2 (this is after the official project deadline!), you will be able to submit as many times as you want.
As a warning, you will not be able to complete the project if you wait to start or test your code 3 days before the deadline You should plan to start early, and write your own extensive tests for this assignment. You will be required to write and pass at least 1 extra acceptance test on top of the ones given to you.
Extra Credit
Due to high demand for course resources, we will not provide help in Office Hours or answer Gitbugs for questions on the extra credit.
Extra credit grading details will be released on Piazza closer to the assignment deadline.
For extra credit, you can provide a GUI (after all, what computer board game is complete without a GUI?). By default, the command shown above should not use a GUI. Instead, you indicate explicitly that you want it:
java -ea ataxx.Main --display
If you choose not
to implement a GUI, make sure that your program terminates with an
error exit code of 2
when given the --display
parameter.
Otherwise, your program should exit
with exit code 0, as usual, even if someone entered invalid commands during a
session (and simply print
error messages in response to the errors).
We do not recommend doing the extra credit before finishing the main part of the assignment.