Ordeal::Model::Shuffle - Manage shuffling of Decks


   use Ordeal::Model::Shuffle;
   use Ordeal::Model::Deck;

   my $deck = Ordeal::Model::Deck->new(...);
   my $shuffler = Ordeal::Model::Shuffle->new(
      deck => Ordeal::Model::Deck->new(...),

   my @three_cards = $shuffler->draw(3);
   my $remaining = $shuffler->n_remaining;
   my @rest = $shuffler->draw($remaining);

   # start over, no reset on the random generator though
   my $one_card = $shuffler->draw;

   # shortcut to take the rest
   my @rest2 = $shuffler->draw(0); # 0 => whatever remains

   # set a different default number of cards for draw
   $shuffler->default_n_draw(3); # take 3 cards by default
   my @three = $shuffler->draw;

   # restart from initial condition, with same seed

   # Accessors
   my $original_deck = $shuffler->deck;
   my $n_default = $shuffler->default_n_draw;
   my $chacha20_randomizer = $shuffler->random_source;
   my $seed = $shuffler->seed;


This class acts as a wrapper around an Ordeal::Model::Deck to provide a shuffled version of its cards. You can access this via method "deck".

Getting cards out of the shuffle is done via method "draw". You cannot ask for more cards than are left on the shuffled deck (they are virtually removed as long as you "draw" them), you can check how many are left via method "n_remaining".

Shuffling is performed using the Fisher-Yates algorithm, using a random_source that defaults to an Ordeal::Model::ChaCha20 object, set at initialization. You can pass your own "random_source" to the constructor if you want, the only requirement is that it supports an int_rand($min, $max) method for generating integers between $min and $max, both included.

You can reshuffle the deck via method "shuffle". You can also reset the whole thing to its initial state via method "reset"; in this case, the "random_source" MUST support a reset() method as well.

You can also set a sorted shuffle by calling "sort". This will make so that "draw" returns cards in the same order as they appear in "deck". Calling "shuffle" reshuffles the cards again; this also happens if "auto_reshuffle" is set (because it's called at the end of each "draw"). By default, the constructor will give you a shuffled shuffle though, so "sort" must be called explicitly.



   my $boolean = $shfl->auto_reshuffle;

accessor for a flag determining whether after each "draw" a "shuffle" should be called automatically. Setting this basically transforms the shuffle into a die.


   my $other = $shfl->clone(%args);

clone the shuffle, including the current shuffling state (which is arguably overkill).

You can pass the following overriding arguments:


whether "shuffle" should be called upon each "draw" or not.


the number of cards to draw by default.


the random source object to use (or undef if the default one is OK). If you don't pass this parameter, the "clone" method will try to call a clone method on whatever is set as random source in the generating object, to make sure it does support this operation.


   my $deck = $shfl->deck;

accessor to the Ordeal::Model::Deck object (or whatever supports its interface). You shouldn't really set it after the object has been created; if you really want to, remember to at least "shuffle".


   my $default_n = $shfl->default_n_draw;

accessor for setting/getting the default number of cards to be taken per draw (i.e. when you don't pass an explicit number to "draw").


   my $card  = $shfl->draw;     # get 1 card
   my @cards = $shfl->draw($n); # get n cards (n can be 1 of course)
   my @all   = $shfl->draw(0);  # get all cards

draw cards from the (possibly shuffled) deck.

By default it returns "default_n_draw", honoring scalar context. You can optionally pass the number of cards you want, with the convention that 0 means all the remaining ones.

Depending on whether "sort" or "shuffle" was called last, the returned cards will be sorted or shuffled.

If "auto_reshuffle" is set, after drawing the cards, but before they are returned, "shuffle" is called. Otherwise, the status is kept for the following call.

The method will complain if the provided input is invalid, e.g. because it's not a number or it's out of range. You can check how many cards you can still draw via "n_remaining".


   say 'sorted!' if $shfl->is_sorted;

boolean flag to check whether a shuffle is sorted (i.e. "sort" was called last) or not (i.e. "shuffle" was called last).


   my $n = $shfl->n_remaining;

get the number of cards that still haven't been "draw"n.


   my $rs = $shfl->random_source;

The source of randomness for applying the Fisher-Yates algorithm. It MUST support a method with the following signature:

   sub int_rand ($min, $max);

for generating an integer between values $min and $max, both included. If you want to call "reset", the random source object MUST also support the following method:

   sub reset;

It defaults to an instance of Ordeal::Model::ChaCha20, set at construction time.

You can change this object at runtime, although your shuffling process might suffer from it because of the possible reduced randomness.



completely reset the object. This means calling the reset method over the "random_source" object, as well as "shuffle" the deck.



(re)shuffle the shuffled deck, re-integrating all drawn cards (if any).

As an example, if you have a deck with 10 cards and you already called "draw" to take 7 of them, "n_remaining" will tell you that there are 3 left. Calling this method is the same as re-integrating the other 7 into the deck, and shuffling it again.



set a special shuffle mode where cards from "draw" will appear in the same order as in "deck". Calling "shuffle" (either directly, or implicitly via "auto_shuffle") resets the shuffle to a more random attitude. Calling this method always makes "draw" restart from the first card in "deck".


The code leverages some experimental Perl features like signatures and postderef; for this reason, at least perl 5.20 will be needed.

Report bugs through GitHub (patches welcome) at


Flavio Poletti <>


Copyright (C) 2018 by Flavio Poletti <>

This module is free software. You can redistribute it and/or modify it under the terms of the Artistic License 2.0.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.