{"id":4369,"date":"2020-05-08T15:03:15","date_gmt":"2020-05-08T19:03:15","guid":{"rendered":"https:\/\/blogs.mathworks.com\/deep-learning\/?p=4369"},"modified":"2025-06-26T21:56:46","modified_gmt":"2025-06-27T01:56:46","slug":"raspberry-pi-poker-player","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/deep-learning\/2020\/05\/08\/raspberry-pi-poker-player\/","title":{"rendered":"Raspberry Pi Poker Player"},"content":{"rendered":"<em>Today, I\u2019d like to introduce Dan Doherty from technical marketing to talk about using Deep Learning with Raspberry Pi using MATLAB.<\/em>\r\n<h6><\/h6>\r\nA 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.\r\n<h6><\/h6>\r\nThe poker player performs 3 main steps: (1) identify cards, (2) identify hands, and (3) make bets.\u00a0 I will show how you can use MATLAB to program these steps and then deploy the algorithm onto a Raspberry Pi. <a href=\"https:\/\/github.com\/matlab-deep-learning\/Deep_Learning_Poker_Player_using_MATLAB_and_Raspberry_Pi\">Here<\/a> is the repository with the tools you need to design a poker player using MATLAB, Deep Learning, and Raspberry Pi.\r\n<h6><\/h6>\r\n<h1>Hardware Used<\/h1>\r\n<ul>\r\n \t<li>Raspberry Pi: the brains of the project.*<\/li>\r\n \t<li>USB webcam: to read images of the cards.<\/li>\r\n \t<li>Raspberry Pi Display: to assist in visualization.<\/li>\r\n \t<li>Push buttons: to call\/fold bets.<\/li>\r\n<\/ul>\r\n<div id=\"attachment_4373\" style=\"width: 310px\" class=\"wp-caption alignnone\"><img aria-describedby=\"caption-attachment-4373\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-4373 size-medium\" src=\"https:\/\/blogs.mathworks.com\/deep-learning\/files\/2020\/05\/fig1_res-300x221.png\" alt=\"\" width=\"300\" height=\"221\" \/><p id=\"caption-attachment-4373\" class=\"wp-caption-text\">Fig 1: Hardware used in project; shown is Raspberry Pi 3B+<\/p><\/div>\r\n<h6><\/h6>\r\n*Keeping performance issues in mind, it's recommended to use RPi 3B+ and higher\r\n<h6><\/h6>\r\n<h1>Identify Cards<\/h1>\r\n<div id=\"attachment_4375\" style=\"width: 710px\" class=\"wp-caption alignnone\"><img aria-describedby=\"caption-attachment-4375\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-4375 size-full\" src=\"https:\/\/blogs.mathworks.com\/deep-learning\/files\/2020\/05\/fig2_res.png\" alt=\"\" width=\"700\" height=\"130\" \/><p id=\"caption-attachment-4375\" class=\"wp-caption-text\">Fig 2: Webcam images are used as the input to the neural network that identifies cards<\/p><\/div>\r\n<h6><\/h6>\r\nThe 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\u00a0<a href=\"https:\/\/www.mathworks.com\/help\/deeplearning\/ref\/squeezenet.html\">SqueezeNet<\/a>,\u00a0<a href=\"https:\/\/www.mathworks.com\/help\/deeplearning\/ref\/alexnet.html\">AlexNet<\/a>\u00a0and\u00a0<a href=\"https:\/\/www.mathworks.com\/help\/deeplearning\/ref\/googlenet.html\">GoogLeNet<\/a>. SqueezeNet was chosen as it is sufficiently small to be deployed to a Raspberry Pi and it has relatively high prediction accuracy.\u00a0<a href=\"https:\/\/www.mathworks.com\/help\/deeplearning\/examples\/transfer-learning-using-alexnet.html\">Here<\/a>\u00a0is 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.\r\n\r\n<div id=\"attachment_4377\" style=\"width: 615px\" class=\"wp-caption alignnone\"><img aria-describedby=\"caption-attachment-4377\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-4377 size-full\" src=\"https:\/\/blogs.mathworks.com\/deep-learning\/files\/2020\/05\/fig3-1.png\" alt=\"\" width=\"605\" height=\"273\" \/><p id=\"caption-attachment-4377\" class=\"wp-caption-text\">Fig 3: Collect data for transfer learning<\/p><\/div>\r\n\r\nTo 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.\r\n<h6><\/h6>\r\nThe code for generating training data is fairly straightforward, and the full code is in\u00a0<span style=\"font-family: courier;\">generateCardData.m<\/span>\r\n<h6><\/h6>\r\n<pre><span class=\"comment\">%% Initialize variables<\/span>\r\nNumImages = 200; % Max = 1000\r\nsuits     = ['D' 'C' 'H' 'S'];\r\nrank      = ['A' '2' '3' '4' '5' '6' '7' '8' '9' 'T' 'J' 'Q' 'K'];\r\n\r\n<span class=\"comment\">%% Get input from command line and validate<\/span>\r\ndisp('Show the card to the webcam and enter the card name in short form. ex: \"2D\" for Diamond-Two, AC for Club-Ace');\r\ncardname = input('Enter card name in short form:','s');\r\nif ~ischar(cardname) || (numel(cardname) ~= 2) || ~contains(rank,cardname(1)) || ~contains(suits,cardname(2))\r\n    error('Invalid cardname');\r\nend\r\n\r\n...\r\n<span class=\"comment\">% Capture input from webcam and save to file.<\/span>\r\n\r\nfor i = 1:1000\r\n    waitbar(i\/1000,waitBarHandle,['Saving image: ' num2str(i)]);\r\n    im = snapshot(cam);\r\n    if mod(i,2)\r\n        im = imrotate(im,180);\r\n    end\r\n    if i &lt;= NumImages\r\n        imwrite(im,fullfile(cardsDirectory,[cardname,num2str(i) '.png']));\r\n    else\r\n        imwrite(im,fullfile(unusedImgsDirectory,[cardname,num2str(i) '.png']));\r\n    end\r\nend\r\nwaitbar(1,waitBarHandle,['Capturing images for ',cardname,' completed.']);<\/pre>\r\n<h6><\/h6>\r\nWe 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\u00a0<span style=\"font-family: courier;\">imageDatastore<\/span>\u00a0which let us work with the overall collection of images. The function\u00a0<span style=\"font-family: courier;\">augmentedImageDatastore<\/span>\u00a0was 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.\r\n<h6><\/h6>\r\n<pre>net = squeezenet;\r\ninputSize = net.Layers(1).InputSize;\r\naudsTrain = augmentedImageDatastore(inputSize(1:2),imdsTrain);\r\naugimdsValidation = augmentedImageDatastore(inputSize(1:2),imdsValidation);\r\n<\/pre>\r\n<h6><\/h6>\r\nAfter applying transfer learning using these images, our network was able to effectively identify cards for all test images.\r\n<h6><\/h6>\r\n<h1>Identify hands and make bets<\/h1>\r\nAfter identifying individual cards, the Pi poker player is ready to identify hands. The flowchart in Figure 4 describes how this is implemented.\r\n<h6><\/h6>\r\n<div id=\"attachment_4379\" style=\"width: 510px\" class=\"wp-caption alignnone\"><img aria-describedby=\"caption-attachment-4379\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-4379 size-full\" src=\"https:\/\/blogs.mathworks.com\/deep-learning\/files\/2020\/05\/fig4_res.png\" alt=\"\" width=\"500\" height=\"505\" \/><p id=\"caption-attachment-4379\" class=\"wp-caption-text\">Fig 4: Flowchart to identify hands<\/p><\/div>\r\n<h6><\/h6>\r\nLet\u2019s 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.\r\n<h6><\/h6>\r\n<table width=\"624\">\r\n<tbody>\r\n<tr style=\"border: 1px solid black; background: #f1f1f1; padding: 5px;\">\r\n<td colspan=\"4\" width=\"624\"><strong><span style=\"font-size: 14px; padding: 5px;\">Example hands<\/span><\/strong><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong>Hand<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\">\u00a0Player 1<\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\">\u00a0Player 2<\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\">\u00a0Player 3<\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong>Unique cards<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\">4C, 4D<\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\">6D, JC<\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\">JH, JS<\/td>\r\n<\/tr>\r\n<tr style=\"border: 1px solid black;\">\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong>Shared community cards<\/strong><\/td>\r\n<td style=\"padding: 5px;\" width=\"108\"><\/td>\r\n<td style=\"padding: 5px;\" width=\"120\">8S, 4H, 9S, TS, QS<\/td>\r\n<td style=\"padding: 5px;\" width=\"126\"><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; background: #f1f1f1; padding: 5px;\" colspan=\"4\" width=\"624\"><strong>Step 1. Separate into Rank and Suit<\/strong><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong>Rank<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\"><span style=\"font-family: courier;\">44849TQ<\/span><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\"><span style=\"font-family: courier;\">6J849TQ<\/span><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\"><span style=\"font-family: courier;\">JJ849TQ<\/span><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong>Suit<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\"><span style=\"font-family: courier;\">CDSHSSS<\/span><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\"><span style=\"font-family: courier;\">DCSHSSS<\/span><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\"><span style=\"font-family: courier;\">HSSHSSS<\/span><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; background: #f1f1f1; padding: 5px;\" colspan=\"4\" width=\"624\"><strong>Step 2. Sort both rank and suit<\/strong><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong>Sorted rank<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\"><span style=\"font-family: courier;\">44489TQ<\/span><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\"><span style=\"font-family: courier;\">4689TJQ<\/span><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\"><span style=\"font-family: courier;\">489TJJQ<\/span><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong>Sorted suit<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\"><span style=\"font-family: courier;\">CDHSSSS<\/span><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\"><span style=\"font-family: courier;\">CDHSSSS<\/span><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\"><span style=\"font-family: courier;\">HHSSSSS<\/span><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; background: #f1f1f1; padding: 5px;\" colspan=\"4\" width=\"624\"><strong>Step 3. Difference between subsequent ranks and suits<\/strong><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong>Diff in sorted rank<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\">004111<\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\">221111<\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\">411101<\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong>Diff in sorted suit (binary)<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\">111000<\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\">111000<\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\">010000<\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; background: #f1f1f1; padding: 5px;\" colspan=\"4\" width=\"624\"><strong>Step 4. Count and decide<\/strong><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong># of zeros in rank (from Step 3)<\/strong>\r\n<h6><\/h6>\r\n0 = Highcard 1 = Pair 2 (in sequence) = 3 of a kind 2 (not in sequence) = 2 pair 3 (in sequence) = 4 of a kind &gt;=3 (with 2 in sequence) = Full house<\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\">Count: 2 (in sequence)\r\n<h6><\/h6>\r\n<strong>3 of a kind<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\">Count: 0\r\n<h6><\/h6>\r\n<strong>Highcard<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\">Count: 1\u00a0<strong>\u00a0<\/strong>\r\n<h6><\/h6>\r\n<strong>Pair<\/strong><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong># of ones in rank (from Step 3)<\/strong>\r\n<h6><\/h6>\r\n&lt;4 = Not a straight &gt;=4 = Straight<\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\">Count: 3\r\n<h6><\/h6>\r\n<strong>Not a straight<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\">Count: 4\r\n<h6><\/h6>\r\n<strong>Straight<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\">Count: 4\r\n<h6><\/h6>\r\n<strong>Straight<\/strong><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong># of zeros in suit (from Step 3)<\/strong>\r\n<h6><\/h6>\r\n&lt;4 = Not any flush &gt;=4 = Flush<\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\">Count: 3\r\n<h6><\/h6>\r\n<strong>Not a flush<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\">Count: 3\r\n<h6><\/h6>\r\n<strong>Not a flush<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\">Count: 5\r\n<h6><\/h6>\r\n<strong>Flush<\/strong><\/td>\r\n<\/tr>\r\n<tr>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"270\"><strong>Hand<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"108\"><strong>3 of a kind<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"120\"><strong>Straight<\/strong><\/td>\r\n<td style=\"border: 1px solid black; padding: 5px;\" width=\"126\"><strong>Straight flush<\/strong><\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n&nbsp;\r\n<h6><\/h6>\r\nAfter identifying cards and hands, the (Pi) Poker Player is ready to place bets.\u00a0 The betting algorithm is very simple and based solely on the identified hand.\u00a0 It starts at 1 USD for a Highcard and increases by 1 USD for each subsequent\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Texas_hold_%27em#Hand_values\">ranked hand<\/a>\u00a0up 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\u2019re feeling.\r\n<h6><\/h6>\r\n<h1><span lang=\"EN\">Embed algorithm onto Raspberry Pi<\/span><\/h1>\r\nWe generate C++ code from our MATLAB algorithms and embed it onto the Raspberry Pi using\u00a0<a href=\"https:\/\/www.mathworks.com\/products\/matlab-coder.html\">MATLAB Coder<\/a>.\u00a0 We wrote a function <span style=\"font-family: courier;\">main_poker_player.cpp<\/span> 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 \u2013 we just need to plug in a camera and run the .exe file.\r\n<h6><\/h6>\r\n<div id=\"attachment_4371\" style=\"width: 810px\" class=\"wp-caption alignnone\"><img aria-describedby=\"caption-attachment-4371\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-4371 size-full\" src=\"https:\/\/blogs.mathworks.com\/deep-learning\/files\/2020\/05\/deploySnapshot_resize.jpg\" alt=\"\" width=\"800\" height=\"627\" \/><p id=\"caption-attachment-4371\" class=\"wp-caption-text\">Fig 5: Generate code and deploy to the Raspberry Pi using MATLAB Coder<\/p><\/div>\r\n<h6><\/h6>\r\nTo see this workflow in more detail and find additional resources, refer to the video\u00a0<a href=\"https:\/\/www.mathworks.com\/videos\/deep-learning-with-raspberry-pi-and-matlab-1567791721457.html\">here<\/a>.\r\n<h6><\/h6>\r\n<h1>Conclusion<\/h1>\r\nWith 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.\r\n<h6><\/h6>\r\nThe code is available on\u00a0<a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/75018-deep_learning_poker_player_using_matlab_and_raspberry_pi\">FileExchange<\/a>\u00a0and\u00a0<a href=\"https:\/\/github.com\/mathworks\/Deep_Learning_Poker_Player_Using_MATLAB_and_Raspberry_Pi\">GitHub<\/a>. Share your comments below and ideas on how to improve the poker player!\r\n<h6><\/h6>\r\nTo request help with Raspberry Pi and MATLAB for your specific application, contact raspberrypi@mathworks.com.\r\n<h6><\/h6>","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img decoding=\"async\"  class=\"img-responsive\" src=\"https:\/\/blogs.mathworks.com\/deep-learning\/files\/2020\/05\/fig1_res-300x221.png\" onError=\"this.style.display ='none';\" \/><\/div><p>Today, I\u2019d like to introduce Dan Doherty from technical marketing to talk about using Deep Learning with Raspberry Pi using MATLAB.\r\n\r\nA few members of our development team are self-proclaimed... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/deep-learning\/2020\/05\/08\/raspberry-pi-poker-player\/\">read more >><\/a><\/p>","protected":false},"author":156,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[9],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/deep-learning\/wp-json\/wp\/v2\/posts\/4369"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/deep-learning\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/deep-learning\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/deep-learning\/wp-json\/wp\/v2\/users\/156"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/deep-learning\/wp-json\/wp\/v2\/comments?post=4369"}],"version-history":[{"count":26,"href":"https:\/\/blogs.mathworks.com\/deep-learning\/wp-json\/wp\/v2\/posts\/4369\/revisions"}],"predecessor-version":[{"id":17679,"href":"https:\/\/blogs.mathworks.com\/deep-learning\/wp-json\/wp\/v2\/posts\/4369\/revisions\/17679"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/deep-learning\/wp-json\/wp\/v2\/media?parent=4369"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/deep-learning\/wp-json\/wp\/v2\/categories?post=4369"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/deep-learning\/wp-json\/wp\/v2\/tags?post=4369"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}