| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Games::2048::Board; |
|
2
|
4
|
|
|
4
|
|
64
|
use 5.012; |
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
131
|
|
|
3
|
4
|
|
|
4
|
|
17
|
use Moo; |
|
|
4
|
|
|
|
|
4
|
|
|
|
4
|
|
|
|
|
24
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
4
|
|
|
4
|
|
1314274
|
use Text::Wrap; |
|
|
4
|
|
|
|
|
10473
|
|
|
|
4
|
|
|
|
|
264
|
|
|
6
|
4
|
|
|
4
|
|
2587
|
use Term::ANSIColor; |
|
|
4
|
|
|
|
|
25801
|
|
|
|
4
|
|
|
|
|
405
|
|
|
7
|
4
|
|
|
4
|
|
44
|
use POSIX qw/floor ceil/; |
|
|
4
|
|
|
|
|
6
|
|
|
|
4
|
|
|
|
|
33
|
|
|
8
|
4
|
|
|
4
|
|
323
|
use List::Util qw/max min/; |
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
12886
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
extends 'Games::2048::Grid'; |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
has needs_redraw => is => 'rw', default => 1; |
|
13
|
|
|
|
|
|
|
has score => is => 'rw', default => 0; |
|
14
|
|
|
|
|
|
|
has win => is => 'rw', default => 0; |
|
15
|
|
|
|
|
|
|
has lose => is => 'rw', default => 0; |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
has best_score => is => 'rw', default => 0; |
|
18
|
|
|
|
|
|
|
has no_animations => is => 'rw', default => 0; |
|
19
|
|
|
|
|
|
|
has zoom => is => 'rw', default => 2, trigger => 1, coerce => \&_coerce_zoom; |
|
20
|
|
|
|
|
|
|
has colors => is => 'rw', builder => 1, coerce => \&_coerce_colors; |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
has appearing => is => 'rw'; |
|
23
|
|
|
|
|
|
|
has moving => is => 'rw'; |
|
24
|
|
|
|
|
|
|
has moving_vec => is => 'rw'; |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
has border_width => is => 'rw', default => 2; |
|
27
|
|
|
|
|
|
|
has border_height => is => 'rw', default => 1; |
|
28
|
|
|
|
|
|
|
has cell_width => is => 'rw', default => 7; |
|
29
|
|
|
|
|
|
|
has cell_height => is => 'rw', default => 3; |
|
30
|
|
|
|
|
|
|
has score_width => is => 'rw', default => 7; |
|
31
|
|
|
|
|
|
|
has score_height => is => 'rw', default => 1; |
|
32
|
|
|
|
|
|
|
has options_height => is => 'rw', default => 5; |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
my @zooms = ( |
|
35
|
|
|
|
|
|
|
[ 3, 1 ], |
|
36
|
|
|
|
|
|
|
[ 5, 2 ], |
|
37
|
|
|
|
|
|
|
[ 7, 3 ], |
|
38
|
|
|
|
|
|
|
[ 9, 4 ], |
|
39
|
|
|
|
|
|
|
[ 11, 5 ], |
|
40
|
|
|
|
|
|
|
); |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub insert_tile { |
|
43
|
474
|
|
|
474
|
0
|
4279
|
my ($self, $tile) = @_; |
|
44
|
|
|
|
|
|
|
|
|
45
|
474
|
|
|
|
|
924
|
$self->needs_redraw(1); |
|
46
|
474
|
50
|
|
|
|
11340
|
return if $self->no_animations; |
|
47
|
|
|
|
|
|
|
|
|
48
|
474
|
|
|
|
|
875
|
$tile->appearing(1); |
|
49
|
474
|
|
|
|
|
11832
|
$self->appearing(Games::2048::Animation->new( |
|
50
|
|
|
|
|
|
|
duration => 0.3, |
|
51
|
|
|
|
|
|
|
first_value => -1 / max($self->cell_width, $self->cell_height), |
|
52
|
|
|
|
|
|
|
last_value => 1, |
|
53
|
|
|
|
|
|
|
)); |
|
54
|
|
|
|
|
|
|
|
|
55
|
474
|
|
|
|
|
11817
|
$tile; |
|
56
|
|
|
|
|
|
|
} |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub move_tiles { |
|
59
|
30
|
|
|
30
|
0
|
504
|
my ($self, $vec) = @_; |
|
60
|
|
|
|
|
|
|
|
|
61
|
30
|
|
|
|
|
100
|
$self->needs_redraw(1); |
|
62
|
30
|
50
|
|
|
|
825
|
return if $self->no_animations; |
|
63
|
|
|
|
|
|
|
|
|
64
|
30
|
|
|
|
|
150
|
$self->reset_animations; |
|
65
|
|
|
|
|
|
|
|
|
66
|
30
|
|
|
|
|
129
|
$self->moving_vec($vec); |
|
67
|
30
|
|
|
|
|
841
|
$self->moving(Games::2048::Animation->new( |
|
68
|
|
|
|
|
|
|
duration => 0.2, |
|
69
|
|
|
|
|
|
|
first_value => 0, |
|
70
|
|
|
|
|
|
|
last_value => $self->size - 1, |
|
71
|
|
|
|
|
|
|
)); |
|
72
|
|
|
|
|
|
|
} |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub reset_appearing { |
|
75
|
30
|
|
|
30
|
0
|
66
|
my $self = shift; |
|
76
|
30
|
|
|
|
|
91
|
$_->appearing(0) for $self->each_tile; |
|
77
|
30
|
|
|
|
|
669
|
$self->appearing(undef); |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub reset_moving { |
|
81
|
30
|
|
|
30
|
0
|
48
|
my $self = shift; |
|
82
|
30
|
|
|
|
|
97
|
for ($self->each_tile) { |
|
83
|
252
|
|
|
|
|
569
|
$_->moving_from(undef); |
|
84
|
252
|
|
|
|
|
493
|
$_->merging_tiles(undef); |
|
85
|
|
|
|
|
|
|
} |
|
86
|
30
|
|
|
|
|
309
|
$self->moving(undef); |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
sub reset_animations { |
|
90
|
30
|
|
|
30
|
0
|
50
|
my $self = shift; |
|
91
|
30
|
|
|
|
|
89
|
$self->reset_moving; |
|
92
|
30
|
|
|
|
|
98
|
$self->reset_appearing; |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub draw { |
|
96
|
0
|
|
|
0
|
0
|
0
|
my ($self, $redraw) = @_; |
|
97
|
|
|
|
|
|
|
|
|
98
|
0
|
0
|
0
|
|
|
0
|
return if $redraw and !$self->needs_redraw; |
|
99
|
|
|
|
|
|
|
|
|
100
|
0
|
|
|
|
|
0
|
$self->hide_cursor; |
|
101
|
0
|
0
|
|
|
|
0
|
$self->restore_cursor if $redraw; |
|
102
|
0
|
|
|
|
|
0
|
$self->needs_redraw(0); |
|
103
|
|
|
|
|
|
|
|
|
104
|
0
|
0
|
|
|
|
0
|
say "" if !$redraw; |
|
105
|
|
|
|
|
|
|
|
|
106
|
0
|
|
|
|
|
0
|
$self->draw_hud; |
|
107
|
0
|
|
|
|
|
0
|
$self->draw_border_horizontal; |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# set if anything is *actually* moving or appearing |
|
110
|
0
|
|
|
|
|
0
|
my $moving; |
|
111
|
|
|
|
|
|
|
my $appearing; |
|
112
|
|
|
|
|
|
|
|
|
113
|
0
|
|
|
|
|
0
|
for my $y (0..$self->size-1) { |
|
114
|
0
|
|
|
|
|
0
|
for my $line (0..$self->cell_height-1) { |
|
115
|
0
|
|
|
|
|
0
|
$self->draw_border_vertical; |
|
116
|
|
|
|
|
|
|
|
|
117
|
0
|
|
|
|
|
0
|
for my $x (0..$self->size-1) { |
|
118
|
0
|
|
|
|
|
0
|
my $tile = $self->tile([$x, $y]); |
|
119
|
|
|
|
|
|
|
|
|
120
|
0
|
|
|
|
|
0
|
my $string; |
|
121
|
0
|
0
|
|
|
|
0
|
my $value = $tile ? $tile->value : undef; |
|
122
|
0
|
|
|
|
|
0
|
my $color = $self->tile_color($value); |
|
123
|
0
|
|
|
|
|
0
|
my $bgcolor = $self->tile_color(undef); |
|
124
|
|
|
|
|
|
|
|
|
125
|
0
|
0
|
0
|
|
|
0
|
if (defined $value and length($value) > $self->cell_width * $self->cell_height) { |
|
126
|
0
|
|
|
|
|
0
|
$value = int($value/1000) . "k"; |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
0
|
|
0
|
|
|
0
|
my $lines = min(ceil(length($value // '') / $self->cell_width), $self->cell_height); |
|
130
|
0
|
|
|
|
|
0
|
my $first_line = floor(($self->cell_height - $lines) / 2); |
|
131
|
0
|
|
|
|
|
0
|
my $this_line = $line - $first_line; |
|
132
|
|
|
|
|
|
|
|
|
133
|
0
|
0
|
0
|
|
|
0
|
if ($this_line >= 0 and $this_line < $lines) { |
|
134
|
0
|
|
|
|
|
0
|
my $cols = min(ceil(length($value) / $lines), $self->cell_width); |
|
135
|
0
|
|
|
|
|
0
|
my $string_offset = $this_line * $cols; |
|
136
|
0
|
|
|
|
|
0
|
my $string_length = min($cols, length($value) - $string_offset, $self->cell_width); |
|
137
|
0
|
|
|
|
|
0
|
my $cell_offset = floor(($self->cell_width - $string_length) / 2); |
|
138
|
|
|
|
|
|
|
|
|
139
|
0
|
|
|
|
|
0
|
$string = " " x $cell_offset; |
|
140
|
|
|
|
|
|
|
|
|
141
|
0
|
|
|
|
|
0
|
$string .= substr($value, $string_offset, $string_length); |
|
142
|
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
0
|
$string .= " " x ($self->cell_width - $cell_offset - $string_length); |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
else { |
|
146
|
0
|
|
|
|
|
0
|
$string = " " x $self->cell_width; |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
|
|
149
|
0
|
0
|
0
|
|
|
0
|
if ($tile and $tile->appearing and $self->appearing) { |
|
|
|
|
0
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# if any animation is going we need to keep redrawing |
|
151
|
0
|
|
|
|
|
0
|
$self->needs_redraw(1); |
|
152
|
|
|
|
|
|
|
|
|
153
|
0
|
|
|
|
|
0
|
my $value = $self->appearing->value; |
|
154
|
0
|
|
|
|
|
0
|
$appearing = 1; |
|
155
|
|
|
|
|
|
|
|
|
156
|
0
|
|
|
|
|
0
|
my $x_center = ($self->cell_width - 1) / 2; |
|
157
|
0
|
|
|
|
|
0
|
my $y_center = ($self->cell_height - 1) / 2; |
|
158
|
|
|
|
|
|
|
|
|
159
|
0
|
|
|
|
|
0
|
my $on = 0; |
|
160
|
0
|
|
|
|
|
0
|
my $extra = 0; |
|
161
|
0
|
|
|
|
|
0
|
for my $col (0..$self->cell_width-1) { |
|
162
|
0
|
|
|
|
|
0
|
my $x_distance = $col / $x_center - 1; |
|
163
|
0
|
|
|
|
|
0
|
my $y_distance = $line / $y_center - 1; |
|
164
|
0
|
|
|
|
|
0
|
my $distance = $x_distance**2 + $y_distance**2; |
|
165
|
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
0
|
my $within = $distance <= 2 * $value**2; |
|
167
|
|
|
|
|
|
|
|
|
168
|
0
|
0
|
0
|
|
|
0
|
if ($within xor $on) { |
|
169
|
0
|
|
|
|
|
0
|
$on = $within; |
|
170
|
|
|
|
|
|
|
|
|
171
|
0
|
0
|
|
|
|
0
|
my $insert = $on |
|
172
|
|
|
|
|
|
|
? $color |
|
173
|
|
|
|
|
|
|
: $bgcolor; |
|
174
|
|
|
|
|
|
|
|
|
175
|
0
|
|
|
|
|
0
|
substr($string, $col + $extra, 0) = $insert; |
|
176
|
0
|
|
|
|
|
0
|
$extra += length($insert); |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
} |
|
179
|
0
|
0
|
|
|
|
0
|
if ($on) { |
|
180
|
0
|
|
|
|
|
0
|
$string .= $bgcolor; |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
else { |
|
184
|
0
|
|
|
|
|
0
|
$string = $color . $string . $bgcolor; |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
|
|
187
|
0
|
|
|
|
|
0
|
print $string; |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
0
|
$self->draw_border_vertical; |
|
191
|
0
|
|
|
|
|
0
|
say color("reset"); |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# update animations |
|
196
|
0
|
0
|
0
|
|
|
0
|
$self->reset_appearing if $appearing and !$self->appearing->update; |
|
197
|
0
|
0
|
0
|
|
|
0
|
$self->reset_moving if $self->moving and !$moving || !$self->moving->update; |
|
|
|
|
0
|
|
|
|
|
|
198
|
|
|
|
|
|
|
|
|
199
|
0
|
|
|
|
|
0
|
$self->draw_border_horizontal; |
|
200
|
0
|
0
|
|
|
|
0
|
$self->show_cursor if !$self->needs_redraw; |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub draw_win { |
|
204
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
205
|
0
|
0
|
0
|
|
|
0
|
return if !$self->win and !$self->lose; |
|
206
|
0
|
0
|
|
|
|
0
|
my $message = |
|
207
|
|
|
|
|
|
|
$self->win ? "You win!" |
|
208
|
|
|
|
|
|
|
: "Game over!"; |
|
209
|
0
|
|
|
|
|
0
|
my $offset = ceil(($self->board_width - length($message)) / 2); |
|
210
|
|
|
|
|
|
|
|
|
211
|
0
|
|
|
|
|
0
|
say " " x $offset, colored(uc $message, "bold"), "\n"; |
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
sub draw_win_question { |
|
215
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
216
|
0
|
0
|
|
|
|
0
|
print $self->win ? "Keep going?" : "Try again?", " (Y/n) "; |
|
217
|
0
|
|
|
|
|
0
|
STDOUT->flush; |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
sub draw_win_answer { |
|
221
|
0
|
|
|
0
|
0
|
0
|
my ($self, $yes) = @_; |
|
222
|
0
|
0
|
|
|
|
0
|
say $yes ? "y" : "n"; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub draw_hud { |
|
226
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
227
|
|
|
|
|
|
|
|
|
228
|
0
|
|
|
|
|
0
|
$self->draw_options; |
|
229
|
0
|
|
|
|
|
0
|
$self->draw_score; |
|
230
|
|
|
|
|
|
|
} |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
sub draw_options { |
|
233
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
234
|
|
|
|
|
|
|
|
|
235
|
0
|
|
|
|
|
0
|
$self->draw_option("( Q ) Quit " . "( R ) New Game"); |
|
236
|
0
|
|
|
|
|
0
|
$self->draw_option("( A ) Animations " . bold_if("On", !$self->no_animations)."/".bold_if("Off", $self->no_animations)); |
|
237
|
0
|
|
|
|
|
0
|
$self->draw_option("( C ) Colors " . bold_if("16", $self->colors == 0)."/".bold_if("256", $self->colors == 1)."/".bold_if("24-bit", $self->colors == 2)); |
|
238
|
0
|
|
|
|
|
0
|
$self->draw_option("(+/-) Zoom " . colored(floor(($self->cell_height + 1) / 4 * 100)."%", "bold")); |
|
239
|
|
|
|
|
|
|
|
|
240
|
0
|
|
|
|
|
0
|
say ""; |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
sub bold_if { |
|
244
|
0
|
|
|
0
|
0
|
0
|
my ($string, $condition) = @_; |
|
245
|
0
|
0
|
|
|
|
0
|
$condition ? colored($string, "bold") : $string; |
|
246
|
|
|
|
|
|
|
} |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub draw_option { |
|
249
|
0
|
|
|
0
|
0
|
0
|
my ($self, $line) = @_; |
|
250
|
0
|
|
|
|
|
0
|
$line =~ s/(\(.*?\))/colored($1, "bold")/ge; |
|
|
0
|
|
|
|
|
0
|
|
|
251
|
0
|
|
|
|
|
0
|
say $line; |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
sub draw_score { |
|
255
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
256
|
|
|
|
|
|
|
|
|
257
|
0
|
|
|
|
|
0
|
my $score = "Score:"; |
|
258
|
0
|
|
|
|
|
0
|
my $best_score = "Best:"; |
|
259
|
|
|
|
|
|
|
|
|
260
|
0
|
|
|
|
|
0
|
my $blank_width = $self->board_width - length($score) - length($best_score); |
|
261
|
0
|
|
|
|
|
0
|
my $score_width = min(floor(($blank_width - 1) / 2), $self->score_width); |
|
262
|
0
|
|
|
|
|
0
|
my $inner_padding = $blank_width - $score_width * 2; |
|
263
|
|
|
|
|
|
|
|
|
264
|
0
|
|
|
|
|
0
|
$self->draw_sub_score($score, $score_width, $self->score); |
|
265
|
|
|
|
|
|
|
|
|
266
|
0
|
|
|
|
|
0
|
print " " x $inner_padding; |
|
267
|
|
|
|
|
|
|
|
|
268
|
0
|
|
|
|
|
0
|
$self->draw_sub_score($best_score, $score_width, $self->best_score); |
|
269
|
|
|
|
|
|
|
|
|
270
|
0
|
|
|
|
|
0
|
say ""; |
|
271
|
|
|
|
|
|
|
} |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
sub draw_sub_score { |
|
274
|
0
|
|
|
0
|
0
|
0
|
my ($self, $string, $score_width, $score) = @_; |
|
275
|
0
|
|
|
|
|
0
|
printf "%s%*d", colored($string, "bold"), $score_width, $score; |
|
276
|
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
sub tile_color { |
|
279
|
0
|
|
|
0
|
0
|
0
|
my ($self, $value) = @_; |
|
280
|
0
|
0
|
|
|
|
0
|
if ($self->colors == 2) { |
|
281
|
|
|
|
|
|
|
return |
|
282
|
0
|
0
|
|
|
|
0
|
!defined $value ? color("reset") . "\e[38;2;187;173;160m" . "\e[48;2;204;192;179m" |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
: $value < 4 ? color("reset") . "\e[38;2;119;110;101m" . "\e[48;2;238;228;218m" |
|
284
|
|
|
|
|
|
|
: $value < 8 ? color("reset") . "\e[38;2;119;110;101m" . "\e[48;2;237;224;200m" |
|
285
|
|
|
|
|
|
|
: $value < 16 ? color("reset") . "\e[38;2;249;246;242m" . "\e[48;2;242;177;121m" |
|
286
|
|
|
|
|
|
|
: $value < 32 ? color("reset") . "\e[38;2;249;246;242m" . "\e[48;2;245;149;99m" |
|
287
|
|
|
|
|
|
|
: $value < 64 ? color("reset") . "\e[38;2;249;246;242m" . "\e[48;2;246;124;95m" |
|
288
|
|
|
|
|
|
|
: $value < 128 ? color("reset") . "\e[38;2;249;246;242m" . "\e[48;2;246;94;59m" |
|
289
|
|
|
|
|
|
|
: $value < 256 ? color("bold") . "\e[38;2;249;246;242m" . "\e[48;2;237;207;114m" |
|
290
|
|
|
|
|
|
|
: $value < 512 ? color("bold") . "\e[38;2;249;246;242m" . "\e[48;2;237;204;97m" |
|
291
|
|
|
|
|
|
|
: $value < 1024 ? color("bold") . "\e[38;2;249;246;242m" . "\e[48;2;237;200;80m" |
|
292
|
|
|
|
|
|
|
: $value < 2048 ? color("bold") . "\e[38;2;249;246;242m" . "\e[48;2;237;197;63m" |
|
293
|
|
|
|
|
|
|
: $value < 4096 ? color("bold") . "\e[38;2;249;246;242m" . "\e[48;2;237;194;46m" |
|
294
|
|
|
|
|
|
|
: color("bold") . "\e[38;2;249;246;242m" . "\e[48;2;60;58;50m"; |
|
295
|
|
|
|
|
|
|
} |
|
296
|
0
|
0
|
|
|
|
0
|
if ($self->colors == 1) { |
|
297
|
|
|
|
|
|
|
return |
|
298
|
0
|
0
|
|
|
|
0
|
!defined $value ? color("reset") . "\e[38;5;249m" . "\e[48;5;251m" |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
: $value < 4 ? color("reset") . "\e[38;5;243m" . "\e[48;5;231m" |
|
300
|
|
|
|
|
|
|
: $value < 8 ? color("reset") . "\e[38;5;243m" . "\e[48;5;230m" |
|
301
|
|
|
|
|
|
|
: $value < 16 ? color("reset") . "\e[38;5;231m" . "\e[48;5;215m" |
|
302
|
|
|
|
|
|
|
: $value < 32 ? color("reset") . "\e[38;5;231m" . "\e[48;5;209m" |
|
303
|
|
|
|
|
|
|
: $value < 64 ? color("reset") . "\e[38;5;231m" . "\e[48;5;203m" |
|
304
|
|
|
|
|
|
|
: $value < 128 ? color("reset") . "\e[38;5;231m" . "\e[48;5;196m" |
|
305
|
|
|
|
|
|
|
: $value < 256 ? color("bold") . "\e[38;5;231m" . "\e[48;5;227m" |
|
306
|
|
|
|
|
|
|
: $value < 512 ? color("bold") . "\e[38;5;231m" . "\e[48;5;227m" |
|
307
|
|
|
|
|
|
|
: $value < 1024 ? color("bold") . "\e[38;5;231m" . "\e[48;5;226m" |
|
308
|
|
|
|
|
|
|
: $value < 2048 ? color("bold") . "\e[38;5;231m" . "\e[48;5;226m" |
|
309
|
|
|
|
|
|
|
: $value < 4096 ? color("bold") . "\e[38;5;231m" . "\e[48;5;220m" |
|
310
|
|
|
|
|
|
|
: color("bold") . "\e[38;5;231m" . "\e[48;5;237m"; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
0
|
0
|
|
|
|
0
|
my $bright = $^O eq "MSWin32" ? "underline " : "bright_"; |
|
313
|
0
|
0
|
|
|
|
0
|
my $bold = $^O eq "MSWin32" ? "underline" : "bold"; |
|
314
|
0
|
0
|
|
|
|
0
|
return color ( |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
!defined $value ? "reset" |
|
316
|
|
|
|
|
|
|
: $value < 4 ? "reset reverse cyan" |
|
317
|
|
|
|
|
|
|
: $value < 8 ? "reset reverse ${bright}blue" |
|
318
|
|
|
|
|
|
|
: $value < 16 ? "reset reverse blue" |
|
319
|
|
|
|
|
|
|
: $value < 32 ? "reset reverse green" |
|
320
|
|
|
|
|
|
|
: $value < 64 ? "reset reverse magenta" |
|
321
|
|
|
|
|
|
|
: $value < 128 ? "reset reverse red" |
|
322
|
|
|
|
|
|
|
: $value < 4096 ? "reset reverse yellow" |
|
323
|
|
|
|
|
|
|
: "reset reverse $bold" |
|
324
|
|
|
|
|
|
|
); |
|
325
|
|
|
|
|
|
|
} |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
sub border_color { |
|
328
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
329
|
0
|
|
|
|
|
0
|
$self->tile_color(undef) . color("reverse"); |
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
sub board_width { |
|
333
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
334
|
0
|
|
|
|
|
0
|
return $self->size * $self->cell_width + $self->border_width * 2; |
|
335
|
|
|
|
|
|
|
} |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
sub board_height { |
|
338
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
339
|
0
|
|
|
|
|
0
|
return $self->size * $self->cell_height + $self->border_height * 2 + $self->hud_height; |
|
340
|
|
|
|
|
|
|
} |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
sub hud_height { |
|
343
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
344
|
0
|
|
|
|
|
0
|
return $self->score_height + $self->options_height; |
|
345
|
|
|
|
|
|
|
} |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
sub draw_border_horizontal { |
|
348
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
349
|
0
|
|
|
|
|
0
|
say $self->border_color, " " x $self->board_width, color("reset") for 1..$self->border_height; |
|
350
|
|
|
|
|
|
|
} |
|
351
|
|
|
|
|
|
|
sub draw_border_vertical { |
|
352
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
353
|
0
|
|
|
|
|
0
|
print $self->border_color, " " x $self->border_width, $self->tile_color(undef); |
|
354
|
|
|
|
|
|
|
} |
|
355
|
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
sub restore_cursor { |
|
357
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
358
|
0
|
|
|
|
|
0
|
printf "\e[%dA", $self->board_height; |
|
359
|
|
|
|
|
|
|
} |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
sub draw_welcome { |
|
362
|
0
|
|
|
0
|
0
|
0
|
my $logo = colored(<<'LOGO', "bold"); |
|
363
|
|
|
|
|
|
|
__ _ _ |
|
364
|
|
|
|
|
|
|
_)/ \|_|(_) |
|
365
|
|
|
|
|
|
|
/__\_/ |(_) |
|
366
|
|
|
|
|
|
|
LOGO |
|
367
|
|
|
|
|
|
|
|
|
368
|
0
|
|
|
|
|
0
|
my $message = <<'MESSAGE'; |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
Join the numbers and get to the 2048 tile! |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
HOW TO PLAY: Use your arrow keys to move the tiles. When two tiles with the same number touch, they merge into one! |
|
373
|
|
|
|
|
|
|
MESSAGE |
|
374
|
|
|
|
|
|
|
|
|
375
|
0
|
|
|
|
|
0
|
local $Text::Wrap::columns = Games::2048::Util::window_size; |
|
376
|
0
|
|
|
|
|
0
|
$message = wrap "", "", $message; |
|
377
|
0
|
|
|
|
|
0
|
$message =~ s/(2048\s+tile!|HOW\s+TO\s+PLAY:|arrow\s+keys|merge\s+into\s+one!)/colored $1, "bold"/ge; |
|
|
0
|
|
|
|
|
0
|
|
|
378
|
|
|
|
|
|
|
|
|
379
|
0
|
|
|
|
|
0
|
print $logo, $message; |
|
380
|
|
|
|
|
|
|
} |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub hide_cursor { |
|
383
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
384
|
0
|
|
|
|
|
0
|
state $once = eval 'END { $self->show_cursor }'; |
|
385
|
0
|
|
|
|
|
0
|
print "\e[?25l"; |
|
386
|
|
|
|
|
|
|
} |
|
387
|
|
|
|
|
|
|
sub show_cursor { |
|
388
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
389
|
0
|
|
|
|
|
0
|
print "\e[?25h"; |
|
390
|
|
|
|
|
|
|
} |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
around no_animations => sub { |
|
393
|
|
|
|
|
|
|
my $orig = shift; |
|
394
|
|
|
|
|
|
|
my $self = shift; |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
my $no_anim = $self->$orig(@_); |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
if (@_) { |
|
399
|
|
|
|
|
|
|
$self->reset_animations if $self->no_animations; |
|
400
|
|
|
|
|
|
|
$self->needs_redraw(1); |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
else { |
|
403
|
|
|
|
|
|
|
$no_anim = 1 if $self->cell_height <= 1 or $self->cell_width <= 1; |
|
404
|
|
|
|
|
|
|
} |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
$no_anim; |
|
407
|
|
|
|
|
|
|
}; |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
sub _coerce_zoom { |
|
410
|
55
|
|
|
55
|
|
3057
|
my ($zoom) = @_; |
|
411
|
55
|
50
|
|
|
|
301
|
$zoom = $#zooms if $zoom > $#zooms; |
|
412
|
55
|
50
|
|
|
|
154
|
$zoom = 0 if $zoom < 0; |
|
413
|
55
|
|
|
|
|
1284
|
$zoom; |
|
414
|
|
|
|
|
|
|
} |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
sub _trigger_zoom { |
|
417
|
0
|
|
|
0
|
|
0
|
my ($self, $zoom, $old) = @_; |
|
418
|
0
|
|
|
|
|
0
|
$self->zoom($zoom, undef); # hack because we have no old value FUUUUUU |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
around zoom => sub { |
|
422
|
|
|
|
|
|
|
my $orig = shift; |
|
423
|
|
|
|
|
|
|
my $self = shift; |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
return $self->$orig if !@_; |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
my $old = $self->$orig; |
|
428
|
|
|
|
|
|
|
my $zoom = @_ == 1 ? $self->$orig(@_) : $old; |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
$self->cell_width($zooms[$zoom][0]); |
|
431
|
|
|
|
|
|
|
$self->cell_height($zooms[$zoom][1]); |
|
432
|
|
|
|
|
|
|
$self->draw if defined $old and $zoom != $old; |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
$zoom; |
|
435
|
|
|
|
|
|
|
}; |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
sub _build_colors { |
|
438
|
55
|
50
|
|
55
|
|
37988
|
return 2 if $ENV{KONSOLE_DBUS_SERVICE}; |
|
439
|
55
|
|
|
|
|
101
|
return 1 if 0; |
|
440
|
55
|
|
|
|
|
152
|
return 0; |
|
441
|
|
|
|
|
|
|
} |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
sub _coerce_colors { |
|
444
|
55
|
|
|
55
|
|
124
|
my ($colors) = @_; |
|
445
|
55
|
|
50
|
|
|
193
|
$colors //= 0; |
|
446
|
55
|
50
|
|
|
|
329
|
$colors = 0 if $colors > 2; |
|
447
|
55
|
50
|
|
|
|
202
|
$colors = 2 if $colors < 0; |
|
448
|
55
|
|
|
|
|
1278
|
$colors; |
|
449
|
|
|
|
|
|
|
} |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
around colors => sub { |
|
452
|
|
|
|
|
|
|
my $orig = shift; |
|
453
|
|
|
|
|
|
|
my $self = shift; |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
my $colors = $self->$orig(@_); |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
if (@_) { |
|
458
|
|
|
|
|
|
|
$self->needs_redraw(1); |
|
459
|
|
|
|
|
|
|
} |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
$colors; |
|
462
|
|
|
|
|
|
|
}; |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
1; |