# Raspberry Pi Poker Player

Today, I’d like to introduce Dan Doherty from technical marketing to talk about using Deep Learning with Raspberry Pi using MATLAB. A few members of our development team are self-proclaimed Raspberry Pi and Texas hold'em nerds, so we decided to build a Raspberry Pi Poker Player to bring to games to help make smart bets. Here is a video of it in action. The poker player performs 3 main steps: (1) identify cards, (2) identify hands, and (3) make bets.  I will show how you can use MATLAB to program these steps and then deploy the algorithm onto a Raspberry Pi.

# Hardware Used

• Raspberry Pi: the brains of the project.*
• USB webcam: to read images of the cards.
• Raspberry Pi Display: to assist in visualization.
• Push buttons: to call/fold bets.

Fig 1: Hardware used in project; shown is Raspberry Pi 3B+

*Keeping performance issues in mind, it's recommended to use RPi 3B+ and higher

# Identify Cards

Fig 2: Webcam images are used as the input to the neural network that identifies cards

The first step is for the Raspberry Pi to identify cards. The webcam connected to the Pi captures images of the cards and a deep learning model identifies them. MATLAB supports several popular deep learning networks including SqueezeNetAlexNet and GoogLeNet. SqueezeNet was chosen as it is sufficiently small to be deployed to a Raspberry Pi and it has relatively high prediction accuracy. Here is a simple doc example that shows how to perform transfer learning using AlexNet, but you could easily swap AlexNet for other (more modern) networks like SqueezeNet or ResNet.

Fig 3: Collect data for transfer learning

To train the deep learning algorithm, we need to collect images of the cards we want to be able to identify. We took a series of snapshots using a webcam making sure to show cards in different lighting and orientations. After manually selecting images that captured the features of each card with minimal blur / distortion, we rotated each image to avoid overfitting the network and to ensure it can recognize cards for different orientations. The code for generating training data is fairly straightforward, and the full code is in generateCardData.m
%% Initialize variables
NumImages = 200; % Max = 1000
suits     = ['D' 'C' 'H' 'S'];
rank      = ['A' '2' '3' '4' '5' '6' '7' '8' '9' 'T' 'J' 'Q' 'K'];

%% Get input from command line and validate
disp('Show the card to the webcam and enter the card name in short form. ex: "2D" for Diamond-Two, AC for Club-Ace');
cardname = input('Enter card name in short form:','s');
if ~ischar(cardname) || (numel(cardname) ~= 2) || ~contains(rank,cardname(1)) || ~contains(suits,cardname(2))
error('Invalid cardname');
end

...
% Capture input from webcam and save to file.

for i = 1:1000
waitbar(i/1000,waitBarHandle,['Saving image: ' num2str(i)]);
im = snapshot(cam);
if mod(i,2)
im = imrotate(im,180);
end
if i <= NumImages
imwrite(im,fullfile(cardsDirectory,[cardname,num2str(i) '.png']));
else
imwrite(im,fullfile(unusedImgsDirectory,[cardname,num2str(i) '.png']));
end
end
waitbar(1,waitBarHandle,['Capturing images for ',cardname,' completed.']);
We ended up with over 10,000 images which were used to train the model. Instead of loading each image file into memory during training, we used imageDatastore which let us work with the overall collection of images. The function augmentedImageDatastore was used to resize the images to the predefined input size of SqueezeNet. This function is efficient at quickly resizing images in one line of code as shown below.
net = squeezenet;
inputSize = net.Layers(1).InputSize;
audsTrain = augmentedImageDatastore(inputSize(1:2),imdsTrain);
augimdsValidation = augmentedImageDatastore(inputSize(1:2),imdsValidation);

After applying transfer learning using these images, our network was able to effectively identify cards for all test images.

# Identify hands and make bets

After identifying individual cards, the Pi poker player is ready to identify hands. The flowchart in Figure 4 describes how this is implemented.

Fig 4: Flowchart to identify hands

Let’s use the example hands in the table below to better understand this flowchart. Note that the final 5 cards in each hand are community cards that are shared among all players.
 Example hands Hand Player 1 Player 2 Player 3 Unique cards 4C, 4D 6D, JC JH, JS Shared community cards 8S, 4H, 9S, TS, QS Step 1. Separate into Rank and Suit Rank 44849TQ 6J849TQ JJ849TQ Suit CDSHSSS DCSHSSS HSSHSSS Step 2. Sort both rank and suit Sorted rank 44489TQ 4689TJQ 489TJJQ Sorted suit CDHSSSS CDHSSSS HHSSSSS Step 3. Difference between subsequent ranks and suits Diff in sorted rank 004111 221111 411101 Diff in sorted suit (binary) 111000 111000 010000 Step 4. Count and decide # of zeros in rank (from Step 3) 0 = Highcard 1 = Pair 2 (in sequence) = 3 of a kind 2 (not in sequence) = 2 pair 3 (in sequence) = 4 of a kind >=3 (with 2 in sequence) = Full house Count: 2 (in sequence) 3 of a kind Count: 0 Highcard Count: 1   Pair # of ones in rank (from Step 3) <4 = Not a straight >=4 = Straight Count: 3 Not a straight Count: 4 Straight Count: 4 Straight # of zeros in suit (from Step 3) <4 = Not any flush >=4 = Flush Count: 3   Not a flush Count: 3   Not a flush Count: 5 Flush Hand 3 of a kind Straight Straight flush
After identifying cards and hands, the (Pi) Poker Player is ready to place bets.  The betting algorithm is very simple and based solely on the identified hand.  It starts at 1 USD for a Highcard and increases by 1 USD for each subsequent ranked hand up to 10 USD for a Royal flush. Of course, if you are working on this algorithm, you can rework the algorithm to be more (or less) aggressive depending on how you’re feeling.

# Embed algorithm onto Raspberry Pi

We generate C++ code from our MATLAB algorithms and embed it onto the Raspberry Pi using MATLAB Coder.  We wrote a function main_poker_player.cpp that serves as the main script of the executable, where we specify which functions to generate code for. The generated code and executable (including modified AlexNet and betting logic algorithm) are downloaded directly to the Pi for standalone execution – we just need to plug in a camera and run the .exe file.

Fig 5: Generate code and deploy to the Raspberry Pi using MATLAB Coder

To see this workflow in more detail and find additional resources, refer to the video here.

# Conclusion

With that we have seen how to build a Raspberry Pi Poker Player in MATLAB. While this is a good start there are certainly areas for improvement, and I encourage you to modify the code to make this even better. For example, you could try to build a more sophisticated betting algorithm that is able to escalate bets or even bluff. The code is available on FileExchange and GitHub. Share your comments below and ideas on how to improve the poker player! To request help with Raspberry Pi and MATLAB for your specific application, contact raspberrypi@mathworks.com.
|