There's more than one way to do things PerlMonks

### What I am missing here

by heatblazer (Scribe)
 on Mar 20, 2012 at 19:49 UTC Need Help??

heatblazer has asked for the wisdom of the Perl Monks concerning the following question:

Hello all, monks.

I was playing with Perl, trying to get a poker game, or a game of any origin with cards probably doing some coding, but I`ve encountered a bizzare problem . Here is some code and I`ll add in later the problem.

Looks like I`ve managed to get it work fine for now

View the screenshoto
```#!/usr/bin/perl
use strict;
use utf8;
use warnings;
########## S U B R O U T I N E S ########################
#deck creator using a really simpla algorithm
#DECK MAKER
sub deck_maker {
my @deck = (); #the deck for returning
my @types = ('spades', 'hearts', 'diamonds', 'clubs');
my @cards = (
[2,3,4,5,6,7,8,9,10,'J','Q','K','A'],
[2,3,4,5,6,7,8,9,10,'J','Q','K','A'],
[2,3,4,5,6,7,8,9,10,'J','Q','K','A'],
[2,3,4,5,6,7,8,9,10,'J','Q','K','A']
);
for ( my \$i=0; \$i < 4; \$i++) {
for ( my \$j=0; \$j < 13; \$j++) {
#pops an element from array
my \$newcard = pop(\$cards[\$i]) . " of " . \$types[\$i];
push(@deck, \$newcard);
}
}
return @deck; #return the new deck
}
#SWAP
sub swap_two {
#really simple swap
(\$_[0], \$_[1]) = (\$_[1], \$_[0]);
}
#SHUFFLE
sub shuffle {
#simple shuffling algorithm
#there are 52 cards always but \$#shuffle+1 can be used too... it`s
+ useless
#unless there are people that don`t know there are 52 cards in tot
+al.
for (my \$i=0; \$i < 52; \$i++) {
my \$card1 = int rand(52);
my \$card2 = int rand(52/2);
&swap_two(\$_[\$card1], \$_[\$card2]);
}
my (@cut1) = @_[0..(\$#\$_+1)/2];
my (@cut2) = @_[(\$#\$_+1)/2..\$#\$_+1];
push(@_ , @cut2);
push(@_, @cut1);
}
#PICK A CARD
sub pick_a_card {
my \$deck = shift;
return pop @\$deck;
}
sub show_my_hand {
print "~~"x40,"\n";
foreach my \$cards (@_) {

print  "[", \$cards, "]";
}
print "\n", "~~"x40,"\n";
}

#################################################
#    THE GAME STARTS BELOW ############################
################################################
# init the deck and shuffle it
my @deck = &deck_maker;
&shuffle(@deck);
# end of deck manipulations
my (@hand) = ();
push (@hand, &pick_a_card(\@deck));
push (@hand, &pick_a_card(\@deck));
push (@hand, &pick_a_card(\@deck));
push (@hand, &pick_a_card(\@deck));
push (@hand, &pick_a_card(\@deck));
&show_my_hand(@hand);
#game loop here

Since the problem is solved, are there any free time monks who wish to make a simple poker game with some Tk or SDL for minimal gui???

Here is a simple output to my code:

```~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~
[K of diamonds][10 of clubs][4 of clubs][5 of clubs][6 of clubs]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~

****************************************
Deck No. 0 has 6 of spades
Deck No. 1 has 8 of spades
Deck No. 2 has J of hearts
Deck No. 3 has Q of spades
Deck No. 4 has 7 of spades
Deck No. 5 has 9 of diamonds
Deck No. 6 has 3 of clubs
Deck No. 7 has 2 of clubs
Deck No. 8 has A of clubs
Deck No. 9 has 5 of spades
Deck No. 10 has 2 of diamonds
Deck No. 11 has 8 of clubs
Deck No. 12 has 9 of spades
Deck No. 13 has 10 of spades
Deck No. 14 has 6 of hearts
Deck No. 15 has 4 of spades
Deck No. 16 has 2 of spades
Deck No. 17 has 8 of hearts
Deck No. 18 has A of hearts
Deck No. 19 has 4 of diamonds
Deck No. 20 has 9 of hearts
Deck No. 21 has 7 of diamonds
Deck No. 22 has J of clubs
Deck No. 23 has 10 of hearts
Deck No. 24 has A of spades
Deck No. 25 has 2 of hearts
Deck No. 26 has A of diamonds
Deck No. 27 has K of hearts
Deck No. 28 has Q of diamonds
Deck No. 29 has J of diamonds
Deck No. 30 has 10 of diamonds
Deck No. 31 has Q of clubs
Deck No. 32 has 8 of diamonds
Deck No. 33 has K of spades
Deck No. 34 has 6 of diamonds
Deck No. 35 has 5 of diamonds
Deck No. 36 has 4 of hearts
Deck No. 37 has 3 of diamonds
Deck No. 38 has J of spades
Deck No. 39 has 7 of hearts
Deck No. 40 has K of clubs
Deck No. 41 has 5 of hearts
Deck No. 42 has Q of hearts
Deck No. 43 has 3 of hearts
Deck No. 44 has 9 of clubs
Deck No. 45 has 3 of spades
Deck No. 46 has 7 of clubs

Replies are listed 'Best First'.
Re: What I am missing here
by Riales (Hermit) on Mar 20, 2012 at 20:01 UTC
When you call pick_a_card, the subroutine is actually getting a copy of @deck. Pass a reference instead:
```sub pick_a_card {
my \$deck = shift;

return pop @\$deck;
}

pick_a_card(\@deck);

Hmm.. so it`s just a C-like error of mine. Sorry, I am still learning Perl, so I didn`t know that a special reference will be needed in the case. Well, thanks a lot, I`ll keep in mind that for the future.

I`ll look into references ( pointers ) asap when I finish the regexp part of perl, which I find a bit confusing. I guess pointers are as powerful as they were in C... I just had no idea there are any since Perl is garbage collected and, dunno... maybe I am a bit noob to this :)

Well, I would say that references are kind of a step up in terms of abstraction from pointers. For example, in C, you can point a pointer at pretty much any arbitrary space in memory; I don't think you can do that with references in Perl. I also don't think you could perform arithmetic on references (not that this is a great idea in C anyways).

In my mind, Perl references are much simpler. They always point to a thing, and you just have to remember to dereference it to use the thing it's pointing to.

Re: What I am missing here
by Marshall (Canon) on Mar 20, 2012 at 20:37 UTC
I looked in my copy of "The Perl Cookbook" a very, very handy critter to have around and highly recommended by me. There are all sorts of solutions to common problems - well worth the money.

First step is a better shuffle algorithm, which is right there in Recipe 14.7, the fisher-yates algorithm. I don't see the need to keep the suits separate, generate an array (or just type it in manually) with all 52 cards, shuffle and away you go!

```#!/usr/bin/perl -w
use strict;

use Data::Dumper;

my @deck = make_deck();
fisher_yates_shuffle(\@deck); #in place shuffle
print Dumper \@deck;

# use pop or shift of @deck to deal a single card
# or perhaps use splice to deal out multiple cards
# the cards are already randomized so it doesn't matter
# how you deal 'em.

my @deal4 = splice(@deck,0,4);
print "4 cards: @deal4\n"; #4 cards: 6_heart 9_heart J_heart Q_spade

# From Perl Cookbook  Recipe 14.7
#    Randomizing an Array with fisher_yates_shuffle

sub fisher_yates_shuffle
{
my \$array = shift;
my \$i;
for (\$i = @\$array; --\$i; )
{
my \$j = int rand (\$i+1);
next if \$i == \$j;
@\$array[\$i,\$j] = @\$array[\$j,\$i];
}
}

sub make_deck   #or just use a fixed array with 52 cards
{
my @cards = ( 2,3,4,5,6,7,8,9,10,'J','Q','K','A');
my @deck;
foreach my \$suit qw(spade diamond heart club)
{
push @deck, map{"\$_"."_\$suit"}@cards;
}
return @deck;
}

I know about that algorithm, better say 'heard' than 'know', actually, but really I think that it`s a bit complex for a simple task like mine, not that is bad or good, it`s just another way to do it. Maybe I would have used it if I know how to implement it, but since I don`t have that cookbook, I just made my own ( simple as it is ). However, thanx for the recipe, if I am going to make a real game ( sallable ) will consider Yates shuffle algorithm

Re: What I am missing here
by druthb (Beadle) on Mar 20, 2012 at 20:08 UTC

Riales is spot-on; using Data::Dumper to dump out @deck at several places will show this quickly enough.

As an aside, I spotted another issue when I was looking at this, and couldn't run it as-shown. There's some needless complexity in sub deck_maker, which I simplified to this:

```#DECK MAKER
sub deck_maker {
my @deck = (); #the deck for returning
my @types = ('spades', 'hearts', 'diamonds', 'clubs');
my @cards = ( 2,3,4,5,6,7,8,9,10,'J','Q','K','A');
for ( my \$i=0; \$i < 4; \$i++) {
for ( my \$j=0; \$j < 13; \$j++) {
my \$newcard = \$cards[\$j] . " of " . \$types[\$i];
push(@deck, \$newcard);
}
}
return @deck; #return the new deck
}

D Ruth Bavousett

Perhaps a little bit easier using some perl idioms...

```#DECK MAKER
sub deck_maker {
my @deck = (); #the deck for returning
my @suits = qw( spades hearts diamonds clubs );
my @cards = ( 2..10, qw(J Q K A) );
for my \$suit ( @suits ) {
for my \$card ( @cards ) {
my \$newcard = "\$card of \$suit";
push(@deck, \$newcard);
}
}
return @deck; #return the new deck
}

--MidLifeXis

Can go simpler...

```sub deck_maker {
map {
my \$suit = \$_;
map { "\$_ of \$suit" } 2..10, qw(J Q K A)
}
perl -E'sub Monkey::do{say\$_,for@_,do{(\$monkey=[caller(0)]->[3])=~s{::}{ }and\$monkey}}"Monkey say"->Monkey::do'

Yes, it is, but I prefer to really be more verbose about my code.

Re: What I am missing here
by druthb (Beadle) on Mar 20, 2012 at 20:37 UTC

You'll also need to dereference that reference, inside sub pick_a_card, of course:

```#PICK A CARD
sub pick_a_card {
my \$deckref = shift;
my \$card=pop(@{\$deckref});
return \$card;
}

D Ruth Bavousett
Re: What I am missing here
by lidden (Curate) on Mar 20, 2012 at 20:46 UTC
I think your shuffle is buggy. Use the built in shuffle instead.

```use List::Util;

# ... snip ...

@deck = List::Util::shuffle @deck;
This is slightly different that the fisher_yates_shuffle algorithm.

At the root of the matter, rand() is not random and has a odd,even,odd,even bias - or at least common implementations do. There are better "pseudo random number generators" than rand(). They are much more computationally expensive and I think outside of the scope of this thread.

The shuffle is "buggy" only in the sense that it isn't fair. (Not every permutation will happen with equal probability). Replacing it with a fair algorithm doesn't make the OPs problem go away.

Well, I may not agree here. "Buggy" if it messes some elements in the deck by either copy them in the swap process. However I`ve tested it, and it`s OK, yes I am missing the srand(seed) but my algorithm is working since it never goes out of range and in worst case it can just swap two similar elements like card2 with card2 but it`s no error at all and it`s quite possible in real life shuffle too.

The awkward moment you shuffle and you get 3 times the same cards to piss everybody out on the table... well it`s no algorithm for a lame shuffler, better never gave him/her to pass cards again!

Re: What I am missing here
by Anonymous Monk on Mar 21, 2012 at 10:02 UTC

Not sure if this has been mentioned yet, but the warning in your screenshot comes from:

```            my \$newcard = pop(@cards[\$i]) . " of " . \$types[\$i];

I'm not sure that is supposed to work (no idea how pop handles array slices), but you meant pop(@{\$cards[\$i]})

Interestingly, if I pass pop a slice, it seems to operate on the last element in the slice, popping the last element from the array to which that element is a reference. If I pass it a slice of ordinary scalars (not array references) it errors. All this is since version 5.14, by the way; prior to that pop simply demands an array as the first argument. Also, perldoc -f pop says this is experimental behavior, so it'd probably be best to keep dereferencing for now and not count on this.

```#!/usr/bin/env perl
use Modern::Perl;
use Data::Dumper;

my @cards = ([qw[0 1 2 3]],
[qw[4 5 6 7]],
[qw[8 9 A B]],
[qw[C D E F]],
);
say pop @cards;       # pop the last array element [C D E F]
say pop @cards[0,1];  # pop from a two-element slice, gets 7 from @\$ca
+rds[1];
say pop @cards[0];    # pop from a one-element slice, gets 3 from @\$ca
+rds[0], throws warning
say pop \$cards[0];    # pop from a scalar array reference, gets 2 from
+ @\$cards[0]
say Dumper @cards;    # see what's left in the array

my @list = (0..15);
say pop @list[3..5];  # error, not an ARRAY reference

Aaron B.
My Woefully Neglected Blog, where I occasionally mention Perl.

My only vision of the things here ( and I am a Perl noob) is that pop @ar0,1 is possible by poping index1 and index0 sequentially, the problem with pop @ar0..3 is that you try to pop a list probably, that`s my assumption tho, it may be completely wrong. The problem with pop is more verbal than system, it`s hard for us humans to read than the compiler to turn to 0s and 1s, when I say:

```pop(\$storage[0]);

I actually want a scalar variable, but I have to know that \$storage[0] is a index to an array from which I`ll pop something. It`s tricky and definitely not a noob-friendly, so noobs like me should read Perl books 2 times more cautiously.

That`s some trick I am not quite good at... Yes the point is good but I want to pop an element from an array it can be written like @cards->\$i but there is still a warning ( not an error which can be omitted for now ). I don`t pop a slice actually, I just pop last element from N row of the 2D array and that was my solution, for good or bad Doing it the way:

```pop(\$cards[\$i]...)

solves the warning but I am not sure why since I want to pop an array variable not a scalar one... Guess I need more Perls to eat :)

Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://960640]
Approved by Old_Gray_Bear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (5)
As of 2023-12-05 21:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
What's your preferred 'use VERSION' for new CPAN modules in 2023?

Results (29 votes). Check out past polls.

Notices?