| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Games::Cards::Undo; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# part of Games::Cards by Amir Karger (See Cards.pm for details) |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=pod |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 NAME |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
Games::Cards::Undo -- undoing/redoing moves in Games::Cards games |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use Games::Cards::Undo; |
|
14
|
|
|
|
|
|
|
$Undo = new Games::Cards::Undo(100); # Make undo engine to save 100 moves |
|
15
|
|
|
|
|
|
|
$Undo->undo; # undo last move |
|
16
|
|
|
|
|
|
|
$Undo->redo; # redo last undone move |
|
17
|
|
|
|
|
|
|
$Undo->end_move; # tell undo engine we're done with a move |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
This is the package for methods to undo & redo moves. The GC::Undo object has |
|
22
|
|
|
|
|
|
|
no publicly accessible fields. But it stores an array of the |
|
23
|
|
|
|
|
|
|
preceding moves. Note that a "move" is made up of several "atoms" (objects of |
|
24
|
|
|
|
|
|
|
the private class GC::Undo::Atom and its subclassess). For example, moving a |
|
25
|
|
|
|
|
|
|
card from one column to another in solitaire involves one or more Splice atoms |
|
26
|
|
|
|
|
|
|
(removing or adding card(s) to a CardSet) and possibly a Face atom (turning a |
|
27
|
|
|
|
|
|
|
card over). |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Many of the GC::Undo methods (and all of the GC::Undo::Atom methods) will be |
|
30
|
|
|
|
|
|
|
called by other Games::Cards methods, but not by the actual games. Here are |
|
31
|
|
|
|
|
|
|
the publicly accesssible methods: |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=over 4 |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=cut |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# TODO write Undo::Sort? |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# sub-packages |
|
41
|
|
|
|
|
|
|
{ |
|
42
|
|
|
|
|
|
|
package Games::Cards::Undo; |
|
43
|
|
|
|
|
|
|
package Games::Cards::Undo::Atom; |
|
44
|
|
|
|
|
|
|
package Games::Cards::Undo::Splice; |
|
45
|
|
|
|
|
|
|
package Games::Cards::Undo::Face; |
|
46
|
|
|
|
|
|
|
package Games::Cards::Undo::End_Move; |
|
47
|
|
|
|
|
|
|
} |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# How does Games::Cards handle undo? |
|
50
|
|
|
|
|
|
|
# |
|
51
|
|
|
|
|
|
|
# Undo_List is just an array of (objects from derived classes of) Undo::Atoms. |
|
52
|
|
|
|
|
|
|
# E.g. in solitaire one "move" might include moving cards from one column to |
|
53
|
|
|
|
|
|
|
# another (two Undo::Splice objects) and turning a card over (a Undo::Face |
|
54
|
|
|
|
|
|
|
# object) The undo list will store those Atoms as well as an End_Move object, |
|
55
|
|
|
|
|
|
|
# which is just a placeholder saying that move is over. |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# Global private variables |
|
58
|
|
|
|
|
|
|
# Can't keep this info in an object, because private GC subroutines |
|
59
|
|
|
|
|
|
|
# (like CardSet::splice) need access to the Undo list, and I shouldn't have |
|
60
|
|
|
|
|
|
|
# to pass the undo object around to every sub. |
|
61
|
|
|
|
|
|
|
# GC::Undo::Undo_List holds all previous moves in GC::Undo::Atom objects |
|
62
|
|
|
|
|
|
|
# GC::Undo::Current_Atom is the index of the current Atom in @Undo_List |
|
63
|
|
|
|
|
|
|
# GC::Undo::Max_Size is the maximum size (moves, not Atoms!) of the undo list |
|
64
|
|
|
|
|
|
|
# GC::Undo::In_Undo says that we're currently doing (or undoing) an Undo, so we |
|
65
|
|
|
|
|
|
|
# shouldn't store undo information when we move cards around |
|
66
|
|
|
|
|
|
|
my (@Undo_List, $Current_Atom, $Max_Size, $In_Undo); |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=item new(MOVES) |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
Initialize the Undo engine. MOVES is the number of atoms to save. |
|
71
|
|
|
|
|
|
|
0 (or no argument) allows infinite undo. |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
This method must be called before any undo-able moves are made (i.e., it can be |
|
74
|
|
|
|
|
|
|
called after the hands are dealt). This method will also re-initialize the |
|
75
|
|
|
|
|
|
|
engine for a new game. |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=cut |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
sub new { |
|
80
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
|
81
|
|
|
|
|
|
|
# (re)set global private variables |
|
82
|
0
|
|
0
|
|
|
0
|
$Max_Size = shift || 0; |
|
83
|
0
|
|
|
|
|
0
|
$Current_Atom = -1; |
|
84
|
0
|
|
|
|
|
0
|
@Undo_List = (); |
|
85
|
0
|
|
|
|
|
0
|
$In_Undo = 0; |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# Make the (dummy) object to give a "handle" for methods |
|
88
|
0
|
|
|
|
|
0
|
my $thing = {}; |
|
89
|
0
|
|
|
|
|
0
|
bless $thing, $class; |
|
90
|
0
|
|
|
|
|
0
|
return $thing; |
|
91
|
|
|
|
|
|
|
} |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item end_move |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
End the current move. Everything between the last call to end_move and now |
|
96
|
|
|
|
|
|
|
is considered one move. This tells undo how much to undo. |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=cut |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub end_move { |
|
101
|
|
|
|
|
|
|
# Don't store anything if no atoms have been stored since the |
|
102
|
|
|
|
|
|
|
# last End_Move atom. This could happen e.g. if someone does |
|
103
|
|
|
|
|
|
|
# an illegal move & then wants to undo it. |
|
104
|
0
|
0
|
0
|
0
|
1
|
0
|
if (! defined $Current_Atom || |
|
|
|
|
0
|
|
|
|
|
|
105
|
|
|
|
|
|
|
$Current_Atom == -1 || |
|
106
|
|
|
|
|
|
|
ref($Undo_List[$Current_Atom]) eq "Games::Cards::Undo::End_Move") { |
|
107
|
0
|
|
|
|
|
0
|
return; |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# calling with just "store(foo)" there aren't enough args! |
|
111
|
0
|
|
|
|
|
0
|
my $atom = new Games::Cards::Undo::End_Move; |
|
112
|
0
|
|
|
|
|
0
|
$atom->store; |
|
113
|
|
|
|
|
|
|
} # end sub Games::Cards::Undo::end_move |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
sub store { |
|
116
|
|
|
|
|
|
|
# Stores a move in the undo list, which can later be undone or redone. The |
|
117
|
|
|
|
|
|
|
# first argument is the type of move to store, other args give details about |
|
118
|
|
|
|
|
|
|
# the move depending on the move type. |
|
119
|
|
|
|
|
|
|
# |
|
120
|
|
|
|
|
|
|
# arg1 is a subclass of Undo::Atom |
|
121
|
|
|
|
|
|
|
# Don't store moves if the undo engine hasn't been initialized |
|
122
|
1288
|
50
|
|
1288
|
0
|
9441
|
return unless defined $Current_Atom; |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
# don't store undo moves when we're currently implementing an undo/redo |
|
125
|
0
|
0
|
|
|
|
0
|
return if $In_Undo; |
|
126
|
|
|
|
|
|
|
|
|
127
|
0
|
|
|
|
|
0
|
shift; # ignore class |
|
128
|
0
|
|
|
|
|
0
|
my $atom = shift; # the Undo::Atom to store |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
# If we undid some moves & then do a new move instead of redoing, |
|
131
|
|
|
|
|
|
|
# then erase the moves we undid |
|
132
|
0
|
|
|
|
|
0
|
$#Undo_List = $Current_Atom; |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# Now add the move to the undo list |
|
135
|
0
|
|
|
|
|
0
|
push @Undo_List, $atom; |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# If the list is too big, remove a whole move (not just an Atom) |
|
138
|
|
|
|
|
|
|
# from the beginning of the list (oldest undos) |
|
139
|
0
|
|
|
|
|
0
|
my $end_class = "Games::Cards::Undo::End_Move"; |
|
140
|
0
|
0
|
0
|
|
|
0
|
if ($Max_Size && grep {ref eq $end_class} @Undo_List > $Max_Size) { |
|
|
0
|
|
|
|
|
0
|
|
|
141
|
0
|
|
|
|
|
0
|
$atom = shift @Undo_List until ref($atom) eq $end_class; |
|
142
|
|
|
|
|
|
|
} |
|
143
|
|
|
|
|
|
|
|
|
144
|
0
|
|
|
|
|
0
|
$Current_Atom = $#Undo_List; |
|
145
|
|
|
|
|
|
|
|
|
146
|
0
|
|
|
|
|
0
|
return 1; |
|
147
|
|
|
|
|
|
|
} # end sub Games::Cards::Undo::store |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=item undo |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Undo a move. |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=cut |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub undo { |
|
156
|
|
|
|
|
|
|
# undoing a move means undoing all the Atoms since the last |
|
157
|
|
|
|
|
|
|
# End_Move Atom |
|
158
|
|
|
|
|
|
|
# Note that this sub can (?) also undo from the middle of a move |
|
159
|
|
|
|
|
|
|
# If called w/ class instead of object, and we never called new(), |
|
160
|
|
|
|
|
|
|
# then return. This shouldn't happen. |
|
161
|
0
|
0
|
|
0
|
1
|
0
|
return unless defined $Current_Atom; # never called new |
|
162
|
0
|
0
|
|
|
|
0
|
return if $Current_Atom == -1; |
|
163
|
0
|
|
|
|
|
0
|
$In_Undo = 1; # Don't store info when moving cards around |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# Loop until the next End_Move Atom or until we exhaust the undo list |
|
166
|
0
|
|
|
|
|
0
|
my $end_class= "Games::Cards::Undo::End_Move"; |
|
167
|
0
|
0
|
|
|
|
0
|
$Current_Atom-- if ref($Undo_List[$Current_Atom]) eq $end_class; |
|
168
|
0
|
|
|
|
|
0
|
for (;$Current_Atom > -1; $Current_Atom--) { |
|
169
|
0
|
|
|
|
|
0
|
my $atom = $Undo_List[$Current_Atom]; |
|
170
|
0
|
0
|
|
|
|
0
|
last if ref($atom) eq $end_class; |
|
171
|
0
|
|
|
|
|
0
|
$atom->undo; |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
# now $Current_Atom is on the End_Move at the end of the last move |
|
174
|
|
|
|
|
|
|
|
|
175
|
0
|
|
|
|
|
0
|
$In_Undo = 0; # done undoing. Allowed to store again. |
|
176
|
0
|
|
|
|
|
0
|
return 1; |
|
177
|
|
|
|
|
|
|
} # end sub Games::Cards::Undo::undo |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=item redo |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
Redo a move that had been undone with undo. |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=cut |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub redo { |
|
187
|
|
|
|
|
|
|
# redoing a move means redoing every Atom from the current atom |
|
188
|
|
|
|
|
|
|
# (which should be an End_Move) until the next End_Move atom |
|
189
|
|
|
|
|
|
|
# If called w/ class instead of object, and we never called new(), |
|
190
|
|
|
|
|
|
|
# then return. This shouldn't happen. |
|
191
|
0
|
0
|
|
0
|
1
|
0
|
return unless defined $Current_Atom; |
|
192
|
0
|
0
|
|
|
|
0
|
return if $Current_Atom == $#Undo_List; |
|
193
|
0
|
|
|
|
|
0
|
$In_Undo = 1; # Don't store info when moving cards around |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# Loop until the next End_Move Atom or until we exhaust the undo list |
|
196
|
0
|
|
|
|
|
0
|
my $atom; |
|
197
|
0
|
|
|
|
|
0
|
my $end_class = "Games::Cards::Undo::End_Move"; |
|
198
|
0
|
0
|
|
|
|
0
|
$Current_Atom++ if ref($Undo_List[$Current_Atom]) eq $end_class; |
|
199
|
0
|
|
|
|
|
0
|
for (;$Current_Atom <= $#Undo_List; $Current_Atom++) { |
|
200
|
0
|
|
|
|
|
0
|
my $atom = $Undo_List[$Current_Atom]; |
|
201
|
0
|
0
|
|
|
|
0
|
last if ref($atom) eq $end_class; |
|
202
|
0
|
|
|
|
|
0
|
$atom->redo; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
# now $Current_Atom is on the End_Move at the end of this move |
|
205
|
|
|
|
|
|
|
|
|
206
|
0
|
|
|
|
|
0
|
$In_Undo = 0; # done redoing. Allowed to store again. |
|
207
|
0
|
|
|
|
|
0
|
return 1; |
|
208
|
|
|
|
|
|
|
} # end sub Games::Cards::Undo::redo |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=back |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=cut |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
{ |
|
215
|
|
|
|
|
|
|
package Games::Cards::Undo::Atom; |
|
216
|
|
|
|
|
|
|
# A CG::Undo::Atom object stores the smallest indivisible amount of undo |
|
217
|
|
|
|
|
|
|
# information. The subclasses of this class implement different kinds of atoms, |
|
218
|
|
|
|
|
|
|
# as well as the way to undo and redo them. |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
sub new { |
|
221
|
|
|
|
|
|
|
# This new will be used by subclasses |
|
222
|
|
|
|
|
|
|
# arg0 is the class. arg1 is a hashref containing various fields. Just |
|
223
|
|
|
|
|
|
|
# store 'em. |
|
224
|
1288
|
|
|
1288
|
|
1696
|
my $class = shift; |
|
225
|
1288
|
|
50
|
|
|
2590
|
my $atom = shift || {}; |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# turn it into an undo move |
|
228
|
1288
|
|
|
|
|
4440
|
bless $atom, $class; |
|
229
|
|
|
|
|
|
|
} # end sub Games::Cards::Undo::Atom::new |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub store { |
|
232
|
|
|
|
|
|
|
# Store this Atom in the Undo List |
|
233
|
1288
|
|
|
1288
|
|
2649
|
Games::Cards::Undo->store(shift); |
|
234
|
|
|
|
|
|
|
} # end sub Games::Cards::Undo::Atom::store |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
} # end package Games::Cards::Undo::Atom |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
{ |
|
239
|
|
|
|
|
|
|
package Games::Cards::Undo::End_Move; |
|
240
|
|
|
|
|
|
|
# An Undo::End_Move is just a marker. Everything in the Undo_List from just |
|
241
|
|
|
|
|
|
|
# after the last End_Move until this one is one "move". |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
@Games::Cards::Undo::End_Move::ISA = qw(Games::Cards::Undo::Atom); |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# inherit SUPER::new |
|
246
|
|
|
|
|
|
|
# No other methods necessary! |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
} # end package Games::Cards::Undo::End_Move |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
{ |
|
251
|
|
|
|
|
|
|
package Games::Cards::Undo::Face; |
|
252
|
|
|
|
|
|
|
# This object stores the act of turning a card over |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
@Games::Cards::Undo::Face::ISA = qw(Games::Cards::Undo::Atom); |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# inherit SUPER::new |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub undo { |
|
259
|
0
|
|
|
0
|
|
|
my $face = shift; |
|
260
|
0
|
|
|
|
|
|
my ($card, $direction) = ($face->{"card"}, $face->{"direction"}); |
|
261
|
0
|
0
|
|
|
|
|
if ($direction eq "up") { |
|
|
|
0
|
|
|
|
|
|
|
262
|
0
|
|
|
|
|
|
$card->face_down; |
|
263
|
|
|
|
|
|
|
} elsif ($direction eq "down") { |
|
264
|
0
|
|
|
|
|
|
$card->face_up; |
|
265
|
|
|
|
|
|
|
} else { |
|
266
|
0
|
|
|
|
|
|
my $func = (caller(0))[3]; |
|
267
|
0
|
|
|
|
|
|
die ("$func called with unknown direction $direction\n"); |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
} # end sub Games::Cards::Undo::Face::undo |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub redo { |
|
272
|
0
|
|
|
0
|
|
|
my $face = shift; |
|
273
|
0
|
|
|
|
|
|
my ($card, $direction) = ($face->{"card"}, $face->{"direction"}); |
|
274
|
0
|
0
|
|
|
|
|
if ($direction eq "up") { |
|
|
|
0
|
|
|
|
|
|
|
275
|
0
|
|
|
|
|
|
$card->face_up; |
|
276
|
|
|
|
|
|
|
} elsif ($direction eq "down") { |
|
277
|
0
|
|
|
|
|
|
$card->face_down; |
|
278
|
|
|
|
|
|
|
} else { |
|
279
|
0
|
|
|
|
|
|
my $func = (caller(0))[3]; |
|
280
|
0
|
|
|
|
|
|
die ("$func called with unknown direction $direction\n"); |
|
281
|
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
} # end sub Games::Cards::Undo::Face::redo |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
} # end package Games::Cards::Undo::Face |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
{ |
|
287
|
|
|
|
|
|
|
package Games::Cards::Undo::Splice; |
|
288
|
|
|
|
|
|
|
# This object stores the act of adding or removing cards from a CardSet, i.e. |
|
289
|
|
|
|
|
|
|
# one of these objects gets created each time GC::CardSet::splice is called. |
|
290
|
|
|
|
|
|
|
# This stores most of the actions in a card game. |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
@Games::Cards::Undo::Splice::ISA = qw(Games::Cards::Undo::Atom); |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# inherit SUPER::new |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub undo { |
|
297
|
|
|
|
|
|
|
# If we changed ARRAY by doing: |
|
298
|
|
|
|
|
|
|
# RESULT = splice(ARRAY, OFFSET, LENGTH, LIST); |
|
299
|
|
|
|
|
|
|
# then we can return ARRAY to its original form by |
|
300
|
|
|
|
|
|
|
# splice(ARRAY, OFFSET, scalar(LIST), RESULT); |
|
301
|
|
|
|
|
|
|
# |
|
302
|
|
|
|
|
|
|
# (sub splice also made sure that for calls to splice without |
|
303
|
|
|
|
|
|
|
# all the arguments, the missing arguments were added, and that OFFSET |
|
304
|
|
|
|
|
|
|
# would be >= 0) |
|
305
|
|
|
|
|
|
|
|
|
306
|
0
|
|
|
0
|
|
|
my $splice = shift; |
|
307
|
|
|
|
|
|
|
# Could do this quicket with no strict refs :) |
|
308
|
0
|
|
|
|
|
|
my ($set, $offset, $in_cards, $out_cards) = |
|
309
|
0
|
|
|
|
|
|
map {$splice->{$_}} qw(set offset in_cards out_cards); |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# Do the anti-splice and return its return value |
|
312
|
|
|
|
|
|
|
# (Return will actually be in_cards!) |
|
313
|
0
|
|
|
|
|
|
$set->splice ($offset, scalar(@$in_cards), $out_cards); |
|
314
|
|
|
|
|
|
|
} # end sub Cards::Games::Undo::Splice::undo |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
sub redo { |
|
317
|
|
|
|
|
|
|
# we changed ARRAY by doing: |
|
318
|
|
|
|
|
|
|
# RESULT = splice(ARRAY, OFFSET, LENGTH, LIST); |
|
319
|
|
|
|
|
|
|
# Just redo the splice. |
|
320
|
|
|
|
|
|
|
# (sub splice also made sure that for calls to splice without |
|
321
|
|
|
|
|
|
|
# all the arguments, the missing arguments were added, and that OFFSET |
|
322
|
|
|
|
|
|
|
# would be >= 0) |
|
323
|
|
|
|
|
|
|
|
|
324
|
0
|
|
|
0
|
|
|
my $splice = shift; |
|
325
|
|
|
|
|
|
|
# Could do this quicket with no strict refs :) |
|
326
|
0
|
|
|
|
|
|
my ($set, $offset, $in_cards, $length) = |
|
327
|
0
|
|
|
|
|
|
map {$splice->{$_}} qw(set offset in_cards length); |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# Do the splice and return its return value |
|
330
|
|
|
|
|
|
|
# (Return will actually be out_cards!) |
|
331
|
0
|
|
|
|
|
|
$set->splice ($offset, $length, $in_cards); |
|
332
|
|
|
|
|
|
|
} # end sub Cards::Games::Undo::Splice::redo |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
} # end package Games::Cards::Undo::Splice |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
1; # end package Games::Cards::Undo |