Last wednesday we were given the opportunity to present a graphical version of our Hanabi work to the faculty at the university. Our setup involved 2 human players and two AI players.
For the demo, we had a little work that needed to be done. The code was originally designed with network play in mind (to felicitate easy running of competitions using the codebase and sandboxed AIs) but we hadn’t written any network code for the project so we quickly implemented a working network client and server. This wasn’t quite as easy as we had hoped as we needed to add a few messages for human players that the AIs did not need to care about. To do this we wrapped the messages and send them as serializable java objects over the network using ObjectStreams. This means the network code isn’t portable to clients written in other languages but for our purposes it works well (and the only client we had to care about was the one we had written).
The server only sends events the agent should be aware of to each agent, so the hidden information aspect of the game is maintained and the agents are not able to cheat by accessing the network messages that are sent to them directly.
Before we had AI players, we had basic text interfaces for human players. As the codebase changed this interface was no longer used. The text based UI was eventually removed from the codebase after it no longer worked correctly with some API changes we made. In order to allow for human play, we needed to re-create a basic text user interface that would work with our current API as a starting point.
Our recreated UI was terminal based and involved inputting the number of the move that you wished to make. This was cumbersome even for players who are experienced and while useful for debugging its not very useful for non-technical users or users that are not familiar with the game.
The original terminal interface we had allowed users to input arbitrary moves (PLAY 1, TELL 1 REDS, TELL 1 1). The re-created interface uses code created for the random agent to enumerate possible legal moves and present these as a list of actions. The user then inputs a number corresponding to the move that they wish to make and the game then evaluates the move. This makes it impossible for the player to input an illegal move but does require them to scan though the list of legal actions to find the move they wish to make.
The way in which this code was implemented is fairly similar to the way the AI controlled agents work – the player is implemented as an agent that when prompted for a move outputs the current state and then prompts the user for a move. The agent blocks until a move is provided, with some basic error handling for when the user enters an invalid move.
From this we enhanced the UI with a basic zones for each type of information present in the game, the gameplay still had to be handled by the CLI interface as there was no way to input moves into this UI. This UI was incredibly basic but helped us figure out what kind of changes we would need to make to for this kind of UI to work.
This UI is implemented as a wrapper around the agent that is playing the game. This allows it to be used with any agent (including the terminal agent mentioned above). The player creates a JFrame when the game tells it what player it is and renders a JComponent that contains the information about the game inside it. When the player receives an event the UI is repainted using Swing’s repaint functionality.
All the basic parts of the UI are present: information tokens (blue circles), life tokens (red circles), the cards for each player represented as 1 player per row and the current table value (presented at the bottom of view).
From this we expanded the UI further into a more polished form that would allow for user input. The code for this version of the UI is a little messy but it was coded quite quickly to get it working for the demo.
Each player’s hand is represented as a box, the player’s hand is presented at the bottom of the screen so they can easily identify it. When testing this UI we noticed it was hard for the player to figure out who’s turn it was. To fix this we added a subtle highlighting to the currently active player’s hand (ours in the screenshot).This highlighting helps to indicate to the player who is currently making a move.
Each move is represented as 2 buttons under the cards. For other players, these are the “tell colour” and “tell number” actions and for the player’s own hand these are the play and discard actions. Next to each card, the current card’s information from that player’s perspective are displayed. As information about the card becomes known to that player, the colour and number boxes on either side hide the impossible colours or numbers. The player may have more information than this in reality – as our own hand and the cards left in the deck may help to eliminate some of these options.
In order to help demonstrate the effect of the tell actions to novice players, when highlighting over a tell action, all cards that would be told are highlighted (notice the light red border in the image below when hovering over the first “tell colour” button).
The information about each card is displayed in it’s corresponding indicators. This indicates what the player who owns the card has been told about that card (either directly or though negative information). For example, we have been told that the 2nd card is a 1, and as no other card was pointed out we know that no other card in our hand is a 1. As a result, the 1 indicators for these cards are removed.
For our own hand, we can also make use of our cards left in the deck and the cards we can see to reduce the possible options. In order to see possible combinations, the player can mouse over one of the squares for their own card. The corresponding numbers that can be eliminated based on information we can calculate are then hidden. For example, We can see both blue threes, so it is impossible for a card to be a blue three. Mousing over the blue indicator makes the corresponding 3 in the indicator disappear indicating this combination is impossible for this card.
Our network code works well for our needs. It’s been made available on the FOSSGalaxy git server along with the Hanabi codebase. The UI does not depend on the network functionality and so is in the main Hanabi codebase. Our GUI is quite good and we feel we have made a lot of the information displayed quite concisely. However, there are still a few things that are not as clear as we would have liked – the effects of the play and discard actions are not overly clear. This was difficult to model as the effects of the play action can be variable (either the card goes on the table pile or gets discarded and a live is removed.
Making the player physically drag the card to the table to play it would be one way around this, revealing the card and the either moving it to the correct place based on the card revealed. A similar hover highlighting could be used to indicate possible outcomes – although this might be confusing due to the two possible outcomes.