Building a Card Playing Twitter Bot: storing and retrieving game state to/from AWS DynamoDB

I recently built a Twitter Bot that plays Blackjack. Here’s my previous posts so far if you are catching up:

Since interaction with the Bot is via Twitter, there will be an unknown length of time between when a player choses to reply and interact with the bot which could be seconds, minutes or days. We therefore need to store the gameplay state for each player, retrieve it on the next interaction from the user and store it again after the response from the bot while we wait for the next player interaction.

The bot can support any number of players in parallel. For each bot/player interaction we need to store the following:

  • the player’s twitter handle
  • current cards remaining in the deck
  • cards in the player’s hand
  • cards in the bot’s hand
  • who’s turn is it next

In a ‘traditional’ data model we could easily model this with some relational tables, but with DynamoDB as a key/value / document based datastore, since we only need to store one interaction state per user, we can store this whole data structure in a single row keyed by the user’s Twitter handle.

DynamoDB’s supported types of scalar values (number, string), and set types (collections of scalar types) allow us to store everything we need in the game state (I did consider the document type, persisting JSON documents), but for retrieving game state values in an easy to use format this didn’t appear as useful and straightforward as scalar types and sets).

Browsing the table in DynamoDB using the AWS console, here’s what the schema currently looks like for a completed game:

AWS DynamoDB offers 3 different APIs for interacting with tables and your data, described in the docs here: low level, document, high level object mapper. With the AWS Java SDK APIs, using Java POJO classes to represent your data together with the DynamoDB Object Mapper APIs is probably the simplest of the 3 approaches, and will feel familiar if you’ve used JPA, Hibernate or other object/relational type mappers before.

Similar to JPA, the AWS DynamoDB APIs provide a number of annotations to map Java Pojos to your DynamoDB tables and columns. Here’s the Pojo class that represents the game state:

[code language=”Java”]
@DynamoDBTable(tableName = "twitterbot-blackjack")
public class PlayerGameState {

private String twitterhandle;
private Hand playerHand;
private Hand botHand;
private Deck deck;

}
[/code]

twitterhandle is the key, the annotation for the key column looks like this:

[code language=”Java”]

@DynamoDBHashKey
public String getTwitterhandle() {
return this.twitterhandle;
}

[/code]

deck, playerHand and botHand are all collections of Card. As a ‘sub-document’ type used by each of these other collections of Cards, the type is annotated with @DynamoDBDocument (instead of @DynamoDBTable):

[code language=”Java”]

@DynamoDBDocument
public class Card {

private Suit suit;
private CardName name;
private int pointsValue;


}
[/code]

DynamoDB supports maps of scalar values, so these fit well for representing a deck of cards, and the player’s hands of cards. If a single Card is a map of values, a collection of Cards is a map of Cards, so a map of maps. To map these more complex structures, a DynamoDB Type Converter is needed to tell the DynamoDB api how to map the structure to the table and back:

[code language=”Java”]
@DynamoDBDocument
public class Hand {

private List<Card> cards = new ArrayList<>();

@DynamoDBTypeConverted(converter = ListOfCardsToMapOfMapsConverter.class)
public List<Card> getCards(){
return this.cards;
}

}
[/code]

Next up I’ll describe how these Type Converters are used in more detail, and we’ll look at storing an retrieving from a DynamoDB table.

Slides from RCARCS 7/3/18 meeting: Intro to FT8 Digital Mode

I presented an overview of the incredibly popular FT8 digital mode at the River City ARCS club meeting on 7/3/18.

Here’s a copy of my slides:

Instead of disassembling my HF station and taking it into the meeting, I tried something different and demo’d using the mode (to receive) using WebSDR, and to transmit using a remote station provided by www.remotehamradio.com . We operated the W1/Chaplain, CT station on the East coast, and worked 3 stations in Europe – HA1RB, DL2LDE and DG6YID during the meeting. From this East coast station the 40m section where FT8 is operated on 7.074Mhz was completely packed from edge to edge on the waterfall!

Piping audio between applications: Configuring ham radio apps on Mac OS using SoundFlower (virtual audio cables)

You’re running some digital mode software like WSJT-X on your Mac. Normally you would use a physical audio cable between your radio to your Mac, either via a soundcard interface like a Rigblaster, or even a direct USB connection to your Mac and your radio. What happens though if you want to route your audio from one application to another? For example, can you pipe the audio from a Web SDR running in your browser straight into WSJT-X (or any other digital mode software)? What you need are ‘virtual audio cables’.

On Windows you have a product called VB-Cable (the approach for Windows is similar to what’s described here). On MacOS you have a couple of options. There’s a commercial product called Loopback from Rogue Amoeba, or an open source alternative called Soundflower.

Follow the instructions to download and install. Once installed, you’ll find a couple of extra sound devices in your System Preferences:

Think of the Soundflower device as your cable. Instead of configuring Speakers for output and Mic for input, if you configure the input for one app as Soundflower (one end of the virtual cable) and the output for another app also as Soundflower (the other end of the cable), and sound output from one app is now directed into input of the other.

Let’s give this a go to connect the output from a WebSDR with the input to WSJT-X.

First, from System Preferences, select the Output to be Soundflower (shown above).

Start up a browser and pick a Web SDR station from http://websdr.org/

Here’s KFS and we’re tuned in to 7.074Mhz USB to receive some FT8:

Next, start up WSJT-X and go to Preferences, Audio:

Note that with Input = Soundflower we’re routing the Output audio from the WebSDR running in the browser into the Soundflower virtual cable. From WSJT-X we’re then taking the audio from this virtual cable as the input into WSJT-X, effectively routing the audio from the web browser into WSJT-X.

Also note that with Output = Soundflower in WSJT-X, if we transmit on WSJT-X the audio will also go out on the virtual cable. With WebSDR we can’t obviously transmit, but if you have access to a remote rig like remotehamradio.com, you can route the audio from WSJT-X into the remote rig app. More on that coming next.

You might note that with this current configuration there’s no actual audio coming out of your speakers. With some virtual cables you have the option to monitor the audio passing over the virtual cable. On MacOS you also have the ability to create composite audio devices using the Audio MIDI Setup app:

This shows a ‘Multi-Output Device’ comprising both the regular built-in audio (your speakers) and Soundflower. Now you’ve got the best of both worlds. More on this next step, and also configuring to use remotehamradio.com with WSJT-X coming up next.

Eclipse Oxygen with Atlassian Connector plugin for accessing Jira issues

I’ve been kicking the tires in my local dev setup running my own Jira and GitLab installations. I’ve been meaning to take a look at how to access Jira tickets from within Eclipse, and then the next logical step is to look at the Jira to GitLab integration.

First up, let’s look at accessing Jira tickets in Eclipse. Docs on the Atlassian Connector are here: https://confluence.atlassian.com/ideplugin/atlassian-connector-for-eclipse. The installation guide gives an Eclipse Update Sites for Eclipse version up to Luna but not more recent versions (Mars, Neon, Oxygen), but questions online (e.g. here) suggest the Luna version still installs and works with Oxygen (using update site: http://update.atlassian.com/atlassian-eclipse-plugin/rest/e3.7)

Integration within Eclipse is using Mylyn. After installing the plugin by adding the update site above, open the Mylyn/Task Repository view:

and select the Jira option:

Press Next and enter the URL for your Jira server:

When prompted to create a new Query, select Yes – this is what retrieves your assigned tasks from Jira:

Press Next for the next dialog. There’s a lot of options here (continues below the area in this screenshot), but in this case I’m interested just in the issues logged for my Blackjack Twitterbot project, so I selected this specific project:

In my Task List view I can now see a list of my assigned tasks, open and competed:

Double-clicking any of these opens the ticket in Eclipse:

This is pretty typical of any issue ticket tracking support in Eclipse. At this point I can edit and update the tickets.

Next up, I’ll look at Jira and GitHub integration.