To compile the Amazons bot, type make. gcc will probably yield the best results (I also tested with clang and icc but the resulting executables were slower). Now execute
src/hippolyta -tto run the automated test suite. If any of the tests fails, email me.
Assuming all the tests have passed, you can try out the bot by running this command:
src/hippolyta 0001001000000000000000000000001000000001000000000000000000002000000002000000000000000000000002002000It should think for a while and then output three squares (source, destination, target). Hopefully it has chosen the move d1-d7/g7 or g1-g7/d7.
The Amazons bot takes as an argument one string of 100 characters. This encodes the state of the board from square a1 to square j10: empty squares are represented by 0, arrows by 3, and Amazons by 1 or 2. When the number of arrows is even, it is player 1's turn; when odd, it is player 2's turn.
The output of the program is the names of three squares, one per line. If the program wants to resign, it will instead print "resign" and then two blank lines. (There is actually a fourth line of output that comes after the move; the fourth is used for the messages that say "I count XX territory for me versus XX for you.")
The program is invoked once for each move that is generated; the program prints its move and then exits. This is different from how (for example) chess engines work, but it is simple and easy to work with.
To see all the bot's options, run
src/hippolyta -h
You can set the thinking time in seconds by using the -s option. This time is only a suggestion, though; near the beginning of the game, the bot will usually take several minutes to pick its move.
This part is complicated and needs to be done carefully! I suggest using Firefox for this, because Firefox provides easy access to browser cookies.
This process is a little annoying, but the good news is that after you do it once, you should never have to do it again. My bot's cookie is still working after four years!
The bot should now be running. To confirm this, challenge it to a game of Amazons. The bot should accept your challenge within 5 to 10 minutes.
This script is called periodically to play turns and answer invitations. As written, it accepts Amazons invitations and rejects all others. To modify this behavior, modify the function answerInvitation.
To support a new game, you must modify the function handleGame to interpret the board for that game. Then (if you program your bots the same way I did) pass the game state to your program and listen for the move that the bot generates. Look at the handleXXXGame functions for examples. (If you add support for a new game, remember to allow your bot to answer invitations for that game!)
bits128.c: Operations on 128-bit integers. The engine represents the board as a set of 128-bit bitfield. Bit 0 corresponds to a1, bit 1 corresponds to a2, etc., but bit 11 corresponds to b1. The engine pretends that there is an eleventh column and an eleventh row, and these are filled with arrows when the position is created. This is done to speed up the logic in several places throughout the program. For instance, to determine whether two squares are adjacent, we figure out whether the absolute value of their difference is 1, 10, 11, or 12. (This system is somewhat reminiscent of the 0x88 board representation used in older chess programs.) This board representation requires 121 bits, which conveniently fits into our 128-bit bitfields.
Our 128-bit ``integers'' are actually composed of two 64-bit integers. The logical bit operations are defined in a straightforward way. The shiftUp/shiftDown operation is crucial to the evaluator, as it is used to assign the squares on the board to each player's territory. If you are running on a 32-bit system, gcc will synthesize the 64-bit operations in terms of 32-bit integers, so the program will still work for you (albeit with a speed penalty). In fact, I am still running on 32-bit, so the program will probably run a lot faster for you than it does for me!
hamilton.c: Hamiltonian move search. When the main search procedure decides to move in a territory that is sealed off and contains only one Amazon, this code will be called to try to find a safe path. The code searches for a path of adjacent squares, starting with the Amazon's current square, that fills the entire territory. (Such a path is called a Hamiltonian path in graph theory.) If such a path is found, the engine moves its Amazon to the next square in the path and fires an arrow onto the Amazon's original square; in this way, we are sure that the entire territory can be filled. If no such path is found in the alloted search time (one-sixth of the main search time) then the original move is used instead.
tests.c: These are automated tests that you can run by passing -t to the amazons program. It is recommended that you run these before trying to use the bot. The tests should catch compiler problems (no 64-bit type, insufficient stack space) or any bugs that you might introduce when modifying the position generation logic.
Have fun! Ask me for help if you get stuck.