Counting Cards (#152)

Learning to count cards is much easier than Hollywood or the casinos would have us believe. Some systems only require you to track a single running total in your head.

One such system, called the Knock-out system of card counting, is extra easy. You start your count at 4 - (4 x number_of_decks). That gives us an initial running count of 0, -4, -20, or -28 for the common casino shoe sizes of 1, 2, 6, or 8 decks. From there, you add one each time you see a 2, 3, 4, 5, 6 or 7 and subtract one when you see a 10, jack, queen, king, or ace. The 8 and 9 cards do not affect the count. Once you learn to track the running count, you can make strategy decisions and vary your bets based on the times when the count is in your favor.

That's not a lot to remember, but it does take practice to get fast. You really need to get to where you can count a deck in 20 to 30 seconds if you are going to keep up with those fast moving casinos dealers.

This week's Ruby Quiz is to build a training program for helping you learn to count cards.

The program needs to show you one or more cards at a time, running through a Blackjack shoe. As it goes, the program should track the running count. Have it pause at random intervals, ask you the count, and notify you if you are right or wrong.

Both the time to go through the deck and the number of cards displayed at a time should be configurable. It's important to practice with seeing multiple cards at once because you learn to cancel out pairs of high and low cards. It might even be nice to provide a mixed mode, which varies the number of cards shown at a time.

You can show cards as simple Strings, ASCII art, or full graphics as you prefer. You may wish to make cards fully disappear after their display time though, to make the conditions more like they would be in a casino.

Quiz Summary

Denis Hennessy put it very well when explained that there are two challenges to this quiz. The first was to implement a card counter. That's the easy bit.

Here's the library Denis submitted for counting cards:

ruby
CARDS = %w{A K Q J T 9 8 7 6 5 4 3 2}
SUITS = %w{c s h d}

class Counter
def initialize(decks)
@count = 4 - 4*decks
@shoe = []
decks.times do
CARDS.each do |c|
SUITS.each do |s|
@shoe << c.to_s + s.to_s
end
end
end
size = 52*decks
size.times do |i|
j = rand(size)
@shoe[i],@shoe[j] = @shoe[j],@shoe[i]
end
end

def deal
card = @shoe.pop
@count += 1 if "234567".include? card[0,1].to_s
@count -= 1 if "TJQKA".include? card[0,1].to_s
card
end

def count
@count
end

def size
@shoe.size
end
end

This code is very easy to follow. You construct a Counter from a number of decks and it will determine the starting count, load a shoe full of cards, and shuffle those cards (the code is just a longhand form of @shoe.sort_by { rand }).

The deal() method is the only other one that does any work. It pulls cards from the shoe, but before returning them it makes sure to update the count. Our counting system is easy, so it breaks down into the two simple conditional checks we see here.

That's literally all it takes to setup some cards, deal, and track a count.

The other challenge is how are we going to allow the user to interact with this code. I left the quiz wide open on this front, but the truth is that you will probably desire a GUI interface if you really want to practice. Recognizing the card faces will be import when you are actually sitting at a Blackjack table.

Denis decided to use Rails as a GUI framework. He located some free card images and built a tiny web application to show them. All of the action takes place in a single controller and there are only two views. (The library we've already examined is the only model needed.)

When you first visit the application, it will display a simple form:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<%= javascript_include_tag :defaults %>
<title>Practice Card Counting</title>
<body>
<% form_tag :action => 'practice' do %>
Number of Decks in Shoe: <%= text_field_tag :decks, '1' %><br/>
Deal between
<%= text_field_tag :min, '1', :size => 1%> and
<%= text_field_tag :max, '3', :size => 1 %>
cards per hand.<br/>
Deal cards every
<%= text_field_tag :delay, '5', :size => 1%> seconds.<br/>
<%= submit_tag("Start") %>
<% end %>
</body>
</html>

As you can see, this just collects a few parameters for the application. You can set a deck count, a range of cards that will be dealt at one time, and a delay between new deals.

Submitting this form will kick us into the controller, where the majority of the work is done:

ruby
require 'card_counter'

class CardCounterController < ApplicationController

def practice
session[:counter] = Counter.new params[:decks].to_i
session[:min] = params[:min].to_i
session[:max] = params[:max].to_i
session[:delay] = params[:delay].to_i
end

def deal
min = session[:min]
max = session[:max]
counter = session[:counter]
max = counter.size if counter.size<max
min = max if max < min
count = min + rand(max-min+1)
text = ""
text = "Shoe complete" if count == 0
count.times do
card = session[:counter].deal
text += "<img src='/images/#{card_index(card)}.png' " +
"width='72' height='96'/>\n"
end
text += "<p id='count' style='visibility: hidden'>" +
"Count is #{counter.count}</p>"
render :text => text
end

# Convert card name ("6d", "Qs"...) to image index where
def card_index(card)
c = CARDS.index card[0,1].to_s
s = SUITS.index card[1,1].to_s
c * 4 + s + 1
end
end

The form we just saw dumps us into the practice() method which just stores all of the parameters in your session. Note that the full Counter object (require()d at the top of this file) is constructed at this point and also placed in your session.

The deal() method is the only other action and it's called to replace some page elements via Ajax. It reads your session parameters back, selects a random number of cards to display based on your range and what is left in the shoe, and renders a chunk of HTML text with the images for those cards as well as a hidden paragraph with the current count in it.

The card_index() method is just a helper that constructs image names based on the face and suit.

Just after practice() runs, Rails will render the only other view for this application:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<%= javascript_include_tag :defaults %>
<title>Practice Card Counting</title>
<script type="text/javascript">
//<![CDATA[
paused = false;
function pause() {
paused = !paused;
if (paused) {
\$('count').setStyle('visibility: visible');
\$('pause').update('Continue')
} else {
\$('pause').update('Pause')
}
}
//]]>
</script>
<body>
<a id='pause' href="#" onClick="pause()">Pause</a>
<div id="cards"></div>

<%= periodically_call_remote(
:condition => "paused == false",
:update => "cards",
:frequency => session[:delay],
:url => { :action => "deal" }) %>
</body>
</html>

If you glance down at the body, you will see that there are really only two elements in this page: a link and an initially empty div.

The link controls a Javascript toggle function. When you click "Pause" the function shows the count, switches the link name to "Continue," and stops the periodic Ajax calls. Clicking Continue changes the link name back to Pause and restarts the periodic calls. The next Ajax action will render a (re-)hidden count.

When you put all of these pieces together, you get an effective trainer for card counting with pictures. Do have a look at the other solutions though, for examples of how to handle the event loop in the console.

My thanks to all who were brave enough to risk having themselves labeled a card counter. With any luck, we will train a future generation of super counters.

Tomorrow we will tackle a classic computer science optimization problem...