# Counting Cards (#152)

**URL:** <https://www.ruby-forum.com/t/counting-cards-152/126397>\
**Category:** Ruby\
**Created:** [January 17, 2008, 11:22pm UTC](https://www.ruby-forum.com/t/counting-cards-152/126397 "2008-01-17T23:22:39Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [January 17, 2008, 11:22pm UTC](https://www.ruby-forum.com/t/counting-cards-152/126397/1 "2008-01-17T23:22:39Z")

</div>

Denis H. 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:

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:

 \<%= javascript\_include\_tag :defaults %\> Practice Card Counting \<% form\_tag :action =\> 'practice' do %\> Number of Decks in Shoe: \<%= text\_field\_tag :decks, '1' %\>  
 Deal between \<%= text\_field\_tag :min, '1', :size =\> 1%\> and \<%= text\_field\_tag :max, '3', :size =\> 1 %\> cards per hand.  
 Deal cards every \<%= text\_field\_tag :delay, '5', :size =\> 1%\> seconds.  
 \<%= submit\_tag("Start") %\> \<% end %\> 

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:

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
# 1=Ac,2=As,3=Ah,4=Ad,5=Kc and so on
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:

 \<%= javascript\_include\_tag :defaults %\> Practice Card Counting Pause

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

```

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…

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [January 18, 2008, 12:33am UTC](https://www.ruby-forum.com/t/counting-cards-152/126397/2 "2008-01-18T00:33:41Z")

</div>

On 17 Jan 2008, at 22:17, Ruby Q. wrote:

> the code is just a longhand form of @shoe.sort\_by { rand }).

That’s sweet. Time to go re-read Enumerable…

/dh

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [January 18, 2008, 10:20am UTC](https://www.ruby-forum.com/t/counting-cards-152/126397/3 "2008-01-18T10:20:19Z")

</div>

Ruby Q. \<james [grayproductions.net](http://grayproductions.net)\> writes:

> ```
> size.times do |i|
> j = rand(size)
> @shoe[i],@shoe[j] = @shoe[j],@shoe[i]
> end
> 
> ```
> 
> (the code is just a longhand form of @shoe.sort\_by { rand }).

Easy to think that, but this is the exact same definition of a “naive  
algorithm”  
demonstrated recently and very well summed up by Jeff Atwood:

[http://www.codinghorror.com/blog/archives/001015.html](http://www.codinghorror.com/blog/archives/001015.html)

Gareth

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [January 18, 2008, 1:15pm UTC](https://www.ruby-forum.com/t/counting-cards-152/126397/4 "2008-01-18T13:15:10Z")

</div>

On 18 Jan 2008, at 09:19, Gareth A. wrote:

> demonstrated recently and very well summed up by Jeff Atwood:
> 
> [http://www.codinghorror.com/blog/archives/001015.html](http://www.codinghorror.com/blog/archives/001015.html)
> 
> Gareth

That’s very interesting. The core of the bug is the the original  
algorithm generates 52_52 possible outcomes, instead of 52! outcomes.  
The non-randomness occurs because 52_52 is not evenly dividable by 52!.

Also interesting that you can learn new things by reading C# code…

/dh
