File Coverage

blib/lib/Ordeal/Model/Evaluator.pm
Criterion Covered Total %
statement 116 169 68.6
branch 16 26 61.5
condition 2 6 33.3
subroutine 22 28 78.5
pod 13 13 100.0
total 169 242 69.8


line stmt bran cond sub pod time code
1             package Ordeal::Model::Evaluator;
2              
3             # vim: ts=3 sts=3 sw=3 et ai :
4              
5 5     5   92 use 5.020;
  5         17  
6 5     5   30 use strict; # redundant, but still useful to document
  5         11  
  5         113  
7 5     5   23 use warnings;
  5         9  
  5         217  
8             { our $VERSION = '0.003'; }
9 5     5   26 use Scalar::Util qw< blessed >;
  5         14  
  5         236  
10 5     5   28 use Mo qw< build default >;
  5         16  
  5         25  
11 5     5   1415 use Ouch;
  5         13  
  5         26  
12 5     5   2669 use Ordeal::Model::Deck;
  5         13  
  5         150  
13 5     5   2179 use Ordeal::Model::Shuffle;
  5         21  
  5         166  
14              
15 5     5   34 use Exporter qw< import >;
  5         9  
  5         281  
16             our @EXPORT_OK = qw< EVALUATE >;
17              
18 5     5   32 use experimental qw< signatures postderef >;
  5         9  
  5         20  
19 5     5   660 no warnings qw< experimental::signatures experimental::postderef >;
  5         10  
  5         8290  
20              
21             has _dc => (); # deck cache
22             has _model => ();
23             has _rs => (
24             default => sub {
25             require Ordeal::Model::ChaCha20;
26             return Ordeal::Model::ChaCha20->new;
27             }
28             );
29             has _stack => ();
30              
31 2     2 1 73 sub BUILD ($self) {
  2         3  
  2         3  
32 2 50       11 my $m = delete($self->{model}) or ouch 400, 'no model provided';
33 2         10 $self->_model($m);
34             $self->_rs(delete $self->{random_source})
35 2 50       17 if exists $self->{random_source};
36 2         17 $self->_dc({});
37 2         12 $self->_stack([]);
38 2         8 return $self;
39             }
40              
41 2     2 1 30 sub EVALUATE (%args) {
  2         6  
  2         2  
42 2 50       10 my $ast = delete($args{ast}) or ouch 400, 'no ast provided';
43 2         27 return __PACKAGE__->new(%args)->_eval($ast);
44             }
45              
46 10     10   22 sub _eval ($self, $ast) {
  10         26  
  10         14  
  10         12  
47 10         24 my ($op, @params) = $ast->@*;
48 10 50       14 my $method = eval {
49 10 50       25 die '' if substr($op, 0, 1) eq '_'; # no "private" stuff
50 10 50       29 die '' if lc($op) ne $op; # no "uppercase" stuff
51 10         42 $self->can($op);
52             } or ouch 400, 'unknown op', $op;
53 10         34 return $self->$method(@params);
54             }
55              
56 4     4   5 sub _get_integer ($self, $n) {
  4         5  
  4         7  
  4         5  
57 4         9 push $self->_stack->@*, 0;
58 4         25 ($n) = $self->_unroll($n);
59 4         9 pop $self->_stack->@*;
60 4         22 return $n;
61             }
62              
63 4     4   6 sub _shuffle ($self, $deck) {
  4         7  
  4         6  
  4         5  
64 4 100       30 $deck = Ordeal::Model::Deck->new(cards => $deck) unless blessed $deck;
65 4         29 return Ordeal::Model::Shuffle->new(
66             auto_reshuffle => 0,
67             deck => $deck,
68             default_n_draw => $deck->n_cards,
69             random_source => $self->_rs,
70             )->sort;
71             } ## end sub _shuffle ($self, $deck)
72              
73 8     8   10 sub _unroll ($self, @potentials) {
  8         12  
  8         20  
  8         12  
74 8         15 my $N = $self->_stack->[-1];
75 18 100       43 return map { $N ? ($_ % $N) : $_ } map {
76 8 100       32 ref($_) ? $self->_eval($_) : $_;
  10         27  
77             } @potentials;
78             }
79              
80 2     2 1 13 sub math_subtract ($self, $t1, $t2) {
  2         4  
  2         4  
  2         3  
  2         3  
81 2         6 return $self->_get_integer($t1) - $self->_get_integer($t2);
82             }
83              
84 0     0 1 0 sub random ($self, @potentials) {
  0         0  
  0         0  
  0         0  
85 0         0 my @candidates = $self->_unroll(@potentials);
86 0         0 return $candidates[$self->_rs->int_rand(0, $#candidates)];
87             }
88              
89 2     2 1 4 sub range ($self, $lo, $hi) {
  2         4  
  2         3  
  2         2  
  2         4  
90 2         6 ($lo, $hi) = $self->_unroll($lo, $hi);
91 2         7 return $lo .. $hi;
92             }
93              
94 0     0 1 0 sub repeat ($self, $s_ast, $n) {
  0         0  
  0         0  
  0         0  
  0         0  
95 0         0 $n = $self->_get_integer($n);
96 0         0 my @cards;
97 0         0 while ($n-- > 0) {
98 0         0 my $s = $self->_eval($s_ast);
99 0         0 push @cards, $s->draw;
100             }
101 0         0 return $self->_shuffle(\@cards);
102             }
103              
104 0     0 1 0 sub replicate ($self, $s_ast, $n) {
  0         0  
  0         0  
  0         0  
  0         0  
105 0         0 $n = $self->_get_integer($n);
106 0         0 my $s = $self->_eval($s_ast);
107 0         0 my @cards = $s->draw;
108 0         0 return $self->_shuffle([(@cards) x $n]);
109             }
110              
111 2     2 1 19 sub resolve ($self, $shuffle) {
  2         3  
  2         3  
  2         4  
112 2 50 33     8 return $shuffle
113             if blessed($shuffle) && $shuffle->isa('Ordeal::Model::Shuffle');
114 2   33     14 my $deck = $self->_dc->{$shuffle} //= $self->_model->get_deck($shuffle);
115 2         30 return $self->_shuffle($deck);
116             }
117              
118 2     2 1 3 sub shuffle ($self, $s_ast) { return $self->_eval($s_ast)->shuffle }
  2         4  
  2         2  
  2         3  
  2         7  
119              
120 2     2 1 4 sub slice ($self, $s_ast, @slices) {
  2         3  
  2         3  
  2         3  
  2         3  
121 2 50       8 my $s = $self->_eval($s_ast) # 400's upon error
122             or ouch 500, 'slice: invalid AST', $s_ast; # "my" error => 500
123              
124 2         7 push $self->_stack->@*, $s->deck->n_cards;
125 2         23 my @indexes = $self->_unroll(@slices);
126 2         10 pop $self->_stack->@*;
127              
128 2         21 my $max = 0;
129 2 100       12 $max = ($max < $_ ? $_ : $max) for @indexes;
130 2         9 my @cards = $s->draw($max + 1);
131 2         9 return $self->_shuffle([@cards[@indexes]]);
132             }
133              
134 0     0 1   sub sort ($self, $s_ast) { return $self->_eval($s_ast)->sort }
  0            
  0            
  0            
  0            
135              
136 0     0 1   sub subtract ($self, $s1_ast, $s2_ast) {
  0            
  0            
  0            
  0            
137 0           my $s1 = $self->_eval($s1_ast);
138 0           my $s2 = $self->_eval($s2_ast);
139 0           my @cards = $s1->draw;
140 0           for my $deleted ($s2->draw) {
141 0           for my $i (0 .. $#cards) {
142 0 0         next if $cards[$i] ne $deleted;
143 0           splice @cards, $i, 1;
144 0           last;
145             }
146             }
147 0           return $self->_shuffle(\@cards);
148             }
149              
150 0     0 1   sub sum ($self, $s1_ast, $s2_ast) {
  0            
  0            
  0            
  0            
151 0           my $s1 = $self->_eval($s1_ast);
152 0           my $s2 = $self->_eval($s2_ast);
153 0           return $self->_shuffle([$s1->draw, $s2->draw]);
154             }
155              
156             1;