In particular, we will be learning how to play the game TicTacToe. After a game is played, each board state along the path from the initial board to the final board will be recorded as a win for the winning player. If the board state has never been encountered before, then a new entry is created in a HashTable with equal win/loss/draw counts, and then updated based on this play. Later board states will be influenced more than earlier board states, to account for the recency of the position and opportunity for alternate play choices.
As more games are recorded, the AI will build up a probabilistic picture of the game tree. The user can elect to train multiple iterations at once with the Train button, which will play random games of the computer vs itself. Using the learned knowledge, the AI Move button will select the move that has shown the highest probability of success.
Besides the HashTable to store board states, another HashTable is used to record a GUI Rectangle for each position on the board. To guide the player, those moves more likely than average to succeed will be shaded green, while those less likely to succeed will be shaded red.
In your code below, you should follow good coding practices, for example, noticing when you are writing essentially the same code twice and abstracting this pattern into a private method.
index
method. When passed a key, it will return the appropriate HashTable index for
that key using the hashcode() of the key modded by the length of the array,
then taking the absolute value. A successful solution for this step
will pass the testIndex
test case.
index
method from Step 1.
If there is no entry at the found location, a new HashNode is created to store the key and value. However, if there already exists a HashNode at that location, we first check to see if the keys match. If a match is found, the new value replaces the old value for that node. If the keys do not match, then a collision has occurred and must be resolved. You can either use Open Addressing with Linear Probing to incrementally walk the array looking for either a matching key or an open spot to add a new HashNode, or Sequential Chaining to walk the linked list of HashNodes at that location, looking for either a matching key or the end of the chain where a new HashNode can be added. Only when a new HashNode is created should the size counter be incremented.
When the load factor of the HashTable, determined by the size of the HashTable
divided by the capacity of the array, exceeds 0.75, the put
method should
double the side of the array and reput all the entries into the new array.
index
method from Step 1. Then, according to the
collision resolution method you chose above, we walk through the HashNodes found, until
either a matching key is identified and the method returns the value of this node, or we encounter
a null position and the method returns Optional.empty().
A successful solution for Steps 2 and 3 will pass testPutGet
,
testCapacityIncrease
, testDuplicate
, and
testVeryBad
.
A successful solution will pass testKeys
.
Feel free to either use paper or a drawing tool such as draw.io to complete your drawings.
Cumulative Progress | Points Earned |
---|---|
Step 1 | 10 |
Steps 2 & 3 | 13 |
Step 4 | 15 |
Step 5 | 17 |
Step 6 | 20 |