An Implementor's Guide to the Ataxx Program Author: Loring Scotty Hoag Version-1.00 ------------------------------------------------------------------------ Table of Contents Use your text editor's/viewer's find function to jump to the correct section. I. Overview II. The Game Board II.A. Representation II.B. Determining Move Validity II.B.1. Determining Individual Move Validity II.B.2. Finding a Player's Valid Moves III. Commands III.A. Reading Commands III.A.1. Input File III.A.2. Mailboxes III.A.3. Opponents III.B. Determining Valid Commands IV. The Main Game Loop and States V. Artificial Intelligence (AI) VI. Playing Remotely VI.A. Hosting a Game VI.B. Joining a Game VII. Graphical User Interface (BETA) VII.A. AtaxxWindow VII.B. GUI Board VII.C. GUI Menu ------------------------------------------------------------------------ ------------------------------------------------------------------------ I. Overview The game ATAXX! was written as a student project for the class "Computer Science 61-B: Data Structures" at UC Berkeley by Loring Scotty Hoag. The purpose of this guide is to guide a person with sufficient understanding of the Java programming language through this version of the game's implementation. ------------------------------------------------------------------------ ------------------------------------------------------------------------ II. The Game Board The board is a self-contained unit where all of the action in the game takes place. ------------------------------------------------------------------------ II.A. Representation The game board is represented by a 7x7 array of spaces that keep track of the state of individual positions on the board. At any time, a position can be in one of four states: 1. The position contains a red piece. 2. The position contains a blue piece. 2. The position contains a block. 2. The position contains nothing. The PieceColor enumerated type has four state variables to account for these conditions; RED, BLUE, BLOCKED, and Empty, respectfully. By storing the matching state variable in the array positions that it describes, the entire board becomes very easy to read and manipulate iteratively through the use of double for-loops. ------------------------------------------------------------------------ II.B. Moves and Validity The Board class handles both checking to see whether a given move is valid and finding lists of valid moves. Being able to find valid moves is necessary for the AI and for checking the validity of passing. ------------------------------------------------------------------------ II.B.1. Determining Individual Move Validity There are six criteria in the Board.legalMove method that must be satisfied for an individual move (c0r0-c1r1) to be considered valid. These exact criteria are: 1. c0r0 and c1r1 are actual positions on the game board. 2. c0r0 and c1r1 are not the same space. 3. c0r0 contains a piece belonging to the player whose turn it is. 4. c1r1 is an empty space. 5. The space to move to is no more than 2 squares away from c0r0. ------------------------------------------------------------------------ II.B.2. Finding a Player's Valid Moves This works well for a single move given to the board, but finding a valid moves is bit different. Finding moves for a particular player given that player's color and assuming it is that player's turn is accomplished by looking at every one of that player's pieces and determining how many moves it can make. While this works well at the beginning of the game when there are relatively few pieces on the board, it becomes inefficient half-way through the game when there are very few empty spaces left on the board. This problem is optimized in the program by looking for all pieces of the opponent's color within move distance of empty spaces rather than empty spaces within move-distance of a player's color. The algorithm in both cases then is: 1. If there are more empty spaces on the board than spaces of the player's color, then look at every piece of the player's color on the board. Empty positions on the board within two spaces of those pieces are valid positions for those pieces to move to. 2. If there are more spaces containing the player's pieces than empty spaces, then look at every empty space on the board instead. Every position within two spaces of each empty space that contains a piece of the player's color can legally move to the empty space. By looking for valid moves based on the empty spaces rather than the player's pieces allows the game to continue efficiently looking for valid moves after the game's half-way point. This has been implemented in the Board.findValidMoves method. ------------------------------------------------------------------------ II.C. Tallying Score. Score is initially set to 2 at the beginning of the game for both players. 1 point is added for extending or changing an opponent's piece to the player's color. A point is taken away for each piece captured by an opponent. Jumping has no effect on a player's score. ------------------------------------------------------------------------ ------------------------------------------------------------------------ III. Commands The program is run via a command line interface. Thus, all of the actions take the form of commands. ------------------------------------------------------------------------ III.A. Reading Commands The commands can come from a variety of sources. Should the the input ever "end", (The game runs out of commands to process) the program immediatly exits by throwing an ExitException(0). By default, the program reads from standard input. ------------------------------------------------------------------------ III.A.1. Input File When the user loads a file, that file is loaded onto a stack. When a file is exhausted of input, it is popped off the stack. So long as the stack contains files, commands will be read from the file on teh top of the stack instead of standard in. ------------------------------------------------------------------------ III.A.2. Mailboxes When playing remotely or with a GUI, mailboxes are implemented for communication. When commands need to be read, the main thread waits until a message appears in its inbox. Over remote connections, the program will time-out after 3 minutes and close the connection. When a GUI is in use, the main thread can wait for instructions indefinitely. ------------------------------------------------------------------------ III.A.3. Opponents The program assumes that input from opponents is valid and will terminate if it determines that the received command not valid. ------------------------------------------------------------------------ III.B. Determining Valid Commands Commands are represented by the enumerated type Command. After parsing the current command line, Command.parseCommand returns the type of command and Command.getArgs returns the arguments as an array of strings. The receiver of the command determines whether the returned command type is valid under the current game circumstances or not. ------------------------------------------------------------------------ ------------------------------------------------------------------------ IV. The Main Game Loop and States The main game cycles through three main states, each of which have their own inner update-loops and behaviours. The flow of the program and the game's states is shown through the flow chart below. .---------------. ( Start Program ) '---------------' | | v -------------------------- | Allocate Object Memory | -------------------------- | | v ------------------------ .--->| Initialize Variables |-----. / ------------------------ \ | | | v | --------------- | | Setup State |--------. | --------------- \ | | ^ | | | | v | | | --------------------------- | | \---| Process Setup Commands | | | --------------------------- | | | | | v | ----------------- | | Playing State |-------. | ----------------- \ | | ^ | | | | v | | | ----------------------------- | | \---| Process Playing Commands | | | ----------------------------- | | | v | ------------------ | | Finished State |-------. | ------------------ \ | | ^ | | | | v | | | ------------------------------ | | \--| Process Finished Commands | | | ------------------------------ | | \ / '----------------------------' As the diagram suggests, the three states have their own ways of precessing commands. This is because there are many commands that are only valid in certain states. Also, there are various ways of accepting commands based on the current state. In setup state, for example, commands are received from the command line or a remote host. This is different from playing state where commands are handled by individual players based on who's turn it currently is. The setup and finished states are handled with while(true)-loops and break when certain commands require state to change like start and clear. The playing state is handled with a while loop that runs until the board indicates that one of its various game-over conditions has occured. ------------------------------------------------------------------------ V. Artificial Intelligence (AI) As of the current version, the AI is as dumb as a sack of bricks that is particularly noteworthy for being even dumber than an average sacks of bricks. The AI requests a list of all of its possible moves from the board and then picks one at random. The randomness is based off of the seed value which can be set by the player. If the list contains no elements, the AI passes. It creates no game tree and does not look into the future at all. Please be nice to him as he doesn't seem to have many friends. ------------------------------------------------------------------------ VI. Playing Remotely Remote connections make extensive use of the ucb package Mailbox construct to exchange information (See "III.A.2. Mailboxes"). In the case of both the client and host, a successful connection means that the opponenet in the playing state will be a remote opponent. ------------------------------------------------------------------------ VI.A. Hosting a Game When the host starts the process of setting up a game in User.hostCmd, said host first creates two new mailbox objects. One mailbox functions as the host's inbox and the client's outbox. The other mailbox plays the opposite roles for each player as the host's outbox and the client's inbox. The host then registers these objects under the game's ID followed by ".IN" and ".OUT" extensions. The register is closed when a problem occurs when the client is trying to connect and when the client connects successfully. Before connecting, all moves that the host makes in setup state are added to a queue. Once a connection is established, the queue sends all of the moves one after the other. Block coordinate options set before connecting are stored as part of a string. Once connected, the string is sent as a single blocks command. After connecting, all move and blocks commands are sent as they occur. ------------------------------------------------------------------------ VI.B. Joining a Game When joining a game through the User.joinCmd method, the client attempts to connect to the host by looking for mailboxes registered at the given address with the extensions ".IN" and ".OUT". If the client does find these, it registers the host's inbox as the clients outbox, and then registers the host's outbox as the client's inbox. ------------------------------------------------------------------------ ------------------------------------------------------------------------ VII. Graphical User Interface (BETA) As of the current version (1.0), all of the commands are implemented at their most basic level of implementation, though some options are more polished than others. the GUI is structured with the following component layout: --------------------------------------------------------- | | | Title Bar | | | --------------------------------------------------------- | | | | | | | | | | | | | Red's | | Blue's | | Sidebar | | Sidebar | | | Game Board | | | | | | | | | | | | | | | | | | | | | | --------------------------------------------------------- | | | Menu Buttons | | | --------------------------------------------------------- ------------------------------------------------------------------------ VII.A. AtaxxWindow The AtaxxWindow class is the link between the GUI and the underlying game. It is an internal class to GUIUser, so it has access to all of the important game variables. the GUI components talk with the AtaxxWindow in order to synchronize with the game and each other as in this diagram: ------------ .----->| Sidebars | ---------------- --------------- / ------------ | Main game | | |<--' ------------- | occuring in |<---->| AtaxxWindow |<------>| GameBoard | | User.process | | |<--. ------------- ---------------- --------------- \ ------------ '----->| Menu | ------------ ------------------------------------------------------------------------ VII.B. GUI Board The GuiBoard is like a simpler version of the Board class. It contains a 7x7 array of buttons that contain information about where they are on the board and the pieces that are on them. Moves are interpreted when pairs of buttons are clicked in GuiBoard.tryToMakeMove. If only one box is clicked, the coordinate is stored until a second one is clicked at which point the completed move is sent to the owner window to send off to the main game. If the user has clicked the box on the menu enabling blocks to be set, then the board will attempt to place blocks on the given buttons. The GuiBoard is redrawn by the main thread at the same intervals as the non-Gui board (After every command). ------------------------------------------------------------------------ VII.C. GUI Menu The menu contains buttons corresponding to most of the game's normal commands. When pressed, they all ultimately either tell the owner window to send a specific command to the main process or set a GUI-specific state variable. ------------------------------------------------------------------------ VII.D. Sidebars The sidebars are where most of the fun things in the GUI happen! There are three main components of the sidebars: A button that displays the symbol of the character currently chosen to represent that player, a yellow text field that updates the score for that player, and a background image specific to that player's character and state. the paintComponent method is overwritten here to allow the background image to be drawn over the entire panel behind the score and button. Each sidebar takes in a set of Nationality objects that contain the art assets for each character. Changing characters cycles the art assets and sends the player's new playing piece icon to the BoardButtons. Also, the characters' background images change depending on the player's performance as determined in determineState and updateImages. The GuiSidebar.update method calls repaint to keep the background image updated as the user's GUI specific state chages. ------------------------------------------------------------------------