Card tricks with Ruby


#1

So suppose you have a deck of cards, that might have some number of
cards.

First the cards are shuffled, in effect placed in random order.

Then the deck is split, ie. some number of cards are lifted from the top
of the deck and placed under the remaining cards.

How would one implement this? I was thinking of using an array, but how
to
shuffle the deck, and how to split the deck?

grrr


#2

On Mar 16, 2006, at 2:53 PM, grrr wrote:

how to shuffle the deck

cards = %w{A 2 3 4 5 6 7 8 9 T J Q K A}
=> [“A”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “T”, “J”, “Q”, “K”,
“A”]

cards = cards.sort_by { rand } # shuffle
=> [“6”, “T”, “Q”, “5”, “A”, “J”, “2”, “7”, “4”, “8”, “A”, “9”, “K”,
“3”]

and how to split the deck?

cards = cards.values_at(3…-1, 0…2) # cut
=> [“5”, “A”, “J”, “2”, “7”, “4”, “8”, “A”, “9”, “K”, “3”, “6”, “T”,
“Q”]

Hope that gives you some new ideas.

James Edward G. II


#3

grrr wrote:

So suppose you have a deck of cards, that might have some number of
cards.

First the cards are shuffled, in effect placed in random order.

Then the deck is split, ie. some number of cards are lifted from the top
of the deck and placed under the remaining cards.

How would one implement this? I was thinking of using an array, but how
to
shuffle the deck, and how to split the deck?

grrr

class Array
def shuffle
self.sort_by{rand}
end

def cut(index)
(self-self[0…index]).push(*self[0…index])
end
end


#4

Hi –

On Fri, 17 Mar 2006, grrr wrote:

So suppose you have a deck of cards, that might have some number of cards.

First the cards are shuffled, in effect placed in random order.

Then the deck is split, ie. some number of cards are lifted from the top
of the deck and placed under the remaining cards.

How would one implement this? I was thinking of using an array, but how to
shuffle the deck, and how to split the deck?

You could do:

def shuffle
sort_by { rand }
end

(Disclaimer: I haven’t really followed the discussions of how random
this and other techniques for shuffling are.)

Here’s a demo of how you might cut the deck (using a small deck here):

irb(main):015:0> deck = *1…10
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):016:0> deck.concat(deck.slice!(0,5))
=> [6, 7, 8, 9, 10, 1, 2, 3, 4, 5]

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” chapters now available
from Manning Early Access Program! http://www.manning.com/books/black


#5

On 3/16/06, grrr removed_email_address@domain.invalid wrote:

So suppose you have a deck of cards, that might have some number of cards.

First the cards are shuffled, in effect placed in random order.

Then the deck is split, ie. some number of cards are lifted from the top
of the deck and placed under the remaining cards.

How would one implement this? I was thinking of using an array, but how to
shuffle the deck, and how to split the deck?

How about this as a starting point?

class Card
attr_reader :card_id
def initialize(card_id)
@card_id = card_id
end
end

class Deck
attr_reader :cards
def initialize(card_count = 52)
@cards = []
card_count.times {|i| @cards << Card.new(i)}
shuffle
end
def shuffle
@cards.sort! {rand}
end
def cut(offset = nil)
top = @cards[0,(offset || @cards.length / 2)]
@cards = (@cards - top) + top
end
end

deck.cut defaults to cutting the deck in half. deck.cut(5) takes the
top 5 cards and puts them on the bottom of the deck.
In real life, the attr_reader is bad form, because it exposes the
implementation of ‘@cards’ to clients. You don’t really want people
shuffling the deck without calling Deck#shuffle.
To fix that, you could make custom accessors, like:
each_card, card_at(position), deal_card, etc.


#6

cards = cards.values_at(3…-1, 0…2) # cut
=> [“5”, “A”, “J”, “2”, “7”, “4”, “8”, “A”, “9”, “K”, “3”, “6”,
“T”, “Q”]

:slight_smile: Heh, cool - I didn’t know values_at did that. Nice :slight_smile:

Hope that gives you some new ideas.

:slight_smile: I’m sure it’ll be useful!


#7

Matthew M. wrote:

deck = deck[x…-1] + deck[0…x] # notice differing amounts of dots

I find myself constantly pointing this out too… is anyone else bothered
by it? Has Matz weighed in on this in the past?

It seems error-prone to me. Extraordinarily more error-prone than other
“stuff” in Ruby.

–Steve


#8

cards = cards.sort_by { rand }

How does this work??
I thought the code block should yield either -1, 0 or +1, but rand
yields a number between 0 and 1. Or am I mistaken?

Best regards,
Francis


#9

deck = (1…52).to_a

shuffle

deck = deck.sort_by { rand }

cut

x = rand(52)
deck = deck[x…-1] + deck[0…x] # notice differing amounts of dots


#10

removed_email_address@domain.invalid wrote:

cards = cards.sort_by { rand }

How does this work??
I thought the code block should yield either -1, 0 or +1, but rand
yields a number between 0 and 1. Or am I mistaken?

You are thing of the normal sort method which looks like this:

cards.sort { |a, b|
(a.suit <=> b.suit).nonzero? || (a.value <=> b.value)
}

With sort_by, you need to return a key value that will be used as the
comparison key for that particular value.

cards.sort_by { |card| [card.suit, card.value] }

Sort_by actually does a “Schwartzian Transform”[1] on the array to
perform the sorting.


– Jim W.

[1] A trick developed by Randall Schwartz to speed sorting in Perl. See
http://en.wikipedia.org/wiki/Schwartzian_Transform


#11

I think you’re confusing the <=> operator needed to mixin Enumerable.

The block in sort_by just returns a value that the sort_by method
should assign to each element in the array so that they may be
ordered. The returned value itself could be anything as long as it
implements Comparable (or it might just need <=>). So for instance
when you say

deck.sort_by do |card|
card.suite
end

the sort_by method get’s all the values of the suites of the cards in
the deck and then sorts them according to that. The suite could be a
string (“Hearts”) or a numeric value (0=Hearts, 1=Diamonds etc). For
instance

[1,2,3,4].sort_by do |number|
number % 2
end

will return [4,2,3,1]. Because the value of the block can either be 0
or 1 the array is sorted into two ‘chunks’: 4 & 2 (number % 2 ==0) 1
& 3 (number % 2 == 1).

Farrel


#12

Hi –

On Fri, 17 Mar 2006, Stephen W. wrote:

Matthew M. wrote:

deck = deck[x…-1] + deck[0…x] # notice differing amounts of dots

I find myself constantly pointing this out too… is anyone else bothered by
it? Has Matz weighed in on this in the past?

It seems error-prone to me. Extraordinarily more error-prone than other
“stuff” in Ruby.

Maybe I need more caffeine, but what exactly is the problem you’re
referring to?

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” chapters now available
from Manning Early Access Program! http://www.manning.com/books/black


#13

Hi –

On Fri, 17 Mar 2006, grrr wrote:

So suppose you have a deck of cards, that might have some number of cards.

First the cards are shuffled, in effect placed in random order.

Then the deck is split, ie. some number of cards are lifted from the top
of the deck and placed under the remaining cards.

How would one implement this? I was thinking of using an array, but how to
shuffle the deck, and how to split the deck?

I have to say, it only dawned on me just now that it’s kind of funny
to cut the deck when the dealer is a computer. The idea of cutting
the deck is to thwart attempts by the dealer to stack the deck. I
actually kind of love the idea that one has to do this in the case of
a computer :slight_smile:

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” chapters now available
from Manning Early Access Program! http://www.manning.com/books/black


#14

deck = deck[x…-1] + deck[0…x] # notice differing amounts of
dots

I’d never seen this before so i decided to give it a try. Without the
three dots on the righthand side it would be inclusive right? As in
you’d get the xth element twice.

  • Geoff

#15

On Mar 17, 2006, at 7:04 AM, Farrel L. wrote:

I think you’re confusing the <=> operator needed to mixin Enumerable.

Technically, the only method required by Enumerable is each(). You
do need to provide <=>() if you want to use method like sort(), min
(), or max() without a block though.

The Comparable mix-in requires <=>().

James Edward G. II


#16

Hi –

On Fri, 17 Mar 2006, gparsons wrote:

deck = deck[x…-1] + deck[0…x] # notice differing amounts of
dots

I’d never seen this before so i decided to give it a try. Without the
three dots on the righthand side it would be inclusive right? As in
you’d get the xth element twice.

Yes, that’s right.

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” chapters now available
from Manning Early Access Program! http://www.manning.com/books/black


#17

My bad I meant comparable.


#18

I point it out to people who I think may not have seen it before, just
so they don’t think it a typo.

It doesn’t really bother me, because I’ve gotten used to it. And I
regularly make use of it, as deep as I am into C++ standard template
library practice.

Unit testing could catch some errors, but I do agree if something
reasonably simple could replace it syntax-wise, it might be a tad
better. What that something simple is, I don’t know… The simplest
solution would be to use mathematical notation, but that would be a
pita for the compiler to parse: [0…x)


#19

Hi –

On Sat, 18 Mar 2006, Stephen W. wrote:

As an earlier mentioned example, when I’ve taught Ruby, I always feel like I
not perfect.
Can you really stand to look at that? It gives me the same feeling as
putting my shoes on the wrong feet :slight_smile:

I’m not sure what it is about the range operators that bothers you.
Are you finding it hard to remember which is which? My way of
remembering is: every range is the same width, in relation to the
operators. Since the … operator is wider, the end-point gets pushed
outside the range:

     v  v
     a..b
     a...b
     ^  ^

So b is off the map in the second one. This is of course a schematic
way to view it (not proportional to the contents of the range), but I
found it helpful and maybe you will.

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” chapters now available
from Manning Early Access Program! http://www.manning.com/books/black


#20

Matthew M. wrote:

[snip]

Well, it’s definitely one of the (very) few things that has bothered me
about Ruby. For example:

A Rubyist might argue that this is more error prone:

 for(int i=0;i<10;i++)

than this:

 10.times

Which is a valid argument, and the result is elegant. And, we see stuff
like this throughout Ruby. As Why’s guide says often, read the code
aloud.

So yes, … vs … really bugs me. While they are convenient operators,
I also see them as too error prone to fit in Ruby.

As an earlier mentioned example, when I’ve taught Ruby, I always feel
like I have to wave the red flags when I talk about ranges.

Unit testing could catch some errors, but I do agree if something
reasonably simple could replace it syntax-wise, it might be a tad
better. What that something simple is, I don’t know… The simplest
solution would be to use mathematical notation, but that would be a
pita for the compiler to parse: [0…x)

Yah, [x…y], [x…y), (x…y], (x…y) would be sweetness. It’s slightly
less error-prone IMO since it’s following a common (math) notation.
Still, maybe not perfect.

Do any of the Ruby dinosaurs know if Matz has a stance on this issue?

–Steve