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; |