-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsnake.pl
executable file
·190 lines (150 loc) · 5.15 KB
/
snake.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use Term::ANSIColor;
use Time::HiRes qw/usleep tv_interval gettimeofday/;
use Getopt::Long;
use Pod::Usage;
=head1 NAME
snake.pl [OPTIONS]
=head1 USAGE
snake.pl [OPTIONS]
Options:
-p, --pipes=N Number of pipes. (Default: 1 )
-f, --fps=N Frames per second. (Default: 75 )
-a, --ascii ASCII mode. (Default: no )
-l, --length=N Minimum length of pipe. (Default: 2 )
-r, --prob=N Probability of chaning direction. (Default: 0.1)
-h, --help This help message.
=head1 ARGUMENTS
=over
=item B<-p>, B<--pipes>=I<integer>
Number of pipes. A large number of pipes will probably slow things down a bit.
Default 1.
=item B<-f>, B<--fps>=I<integer>
Frames per second. This will be kept to as well as possible. Default 75.
=item B<-a>, B<--ascii>
Use ASCII rather than unicode. This'll probably look pretty bad.
=item B<-l>, B<--length>=I<integer>
Minimum length of a pipe, measured in characters.
=item B<-r>, B<--prob>=I<float>
Probability of a pipe changing direction per time step.
=item B<-h>, B<--help>
Display this help message.
=back
=cut
#The states describe the current "velocity" of a pipe. Notice that the allowed
#transitions are given by a change of +/- 1. So, for example, a pipe heading
#left may go up or down but not right.
# Right Down Left Up
my @states = ([1,0], [0,1], [-1,0], [0,-1]);
#The transition matrices here describe the character that should be output upon
#a transition from] one state (direction) to another. If you wanted, you could
#modify these to include the probability of a transition.
my $trans_unicode = [
# R D L U
[qw/0 ┓ 0 ┛/], #R
[qw/┗ 0 ┛ 0/], #D
[qw/0 ┏ 0 ┗/], #L
[qw/┏ 0 ┓ 0/], #U
];
my $trans_ascii = [
# R D L U
[qw/0 + 0 +/], #R
[qw/+ 0 + 0/], #D
[qw/0 + 0 +/], #L
[qw/+ 0 + 0/], #U
];
#Colors; You may wish to add "black" and remove another colour, depending on
#the background colour of your terminal.
my @colours = qw/red green yellow blue magenta cyan white/;
#The characters to represent a travelling (or extending, I suppose) pipe.
my $unicode_pipe_chars = [qw/━ ┃/];
my $ascii_pipe_chars = [qw/- |/];
#Command line options
my $fps = 75;
my $num_pipes = 1;
my $ascii = 0;
my $prob = 0.1;
my $min_len = 2;
my $help;
GetOptions(
'fps|f=i' => \$fps,
'pipes|p=i' => \$num_pipes,
'ascii|a', => \$ascii,
'prob|r=f', => \$prob,
'length|l=f', => \$min_len,
'help|h' => \$help,
) or pod2usage(2);
pod2usage(1) if $help;
#Initialisation; read properties of the terminal and set up the transition and
#travel characters.
my $width = int(`tput cols`);
my $height = int(`tput lines`);
my $trans = $ascii ? $trans_ascii : $trans_unicode;
my $pipe_chars = $ascii ? $ascii_pipe_chars : $unicode_pipe_chars;
#Delay is calculated from the frames per second. The "$delta" will be set to
#the desired delay minus the time actually taken for rendering. Time is in
#microseconds.
my $delay = 1e6 / $fps;
my $delta = $delay;
#Try and output UTF-8 if we aren't using ASCII.
binmode STDOUT, ':encoding(UTF-8)' if not $ascii;
#Set up the pipes. Just give each of them a random position, state and colour.
my @pipes = ();
for(my $i=0; $i<$num_pipes; $i++){
my @r = (int(rand($width)), int(rand($height)));
push @pipes, {
colour => int(rand(@colours)),
state => int(rand(@states)),
length => 0,
r => \@r
};
}
#Initialise terminal. This says "save state, clear the screen and make the
#cursor invisible."
system 'tput smcup';
system 'tput reset';
system 'tput civis';
#Catch interrupts and stop main loop
my $go = 1;
$SIG{INT} = sub {$go = 0};
while($go && usleep($delta)){
#Record time taken and take it from $delta.
my $t0 = [gettimeofday];
for my $pipe(@pipes){
last if not $go;
#When a pipe crosses a border, change its colour and translate it.
my $r = $pipe->{r};
$r->[0] += $states[$pipe->{state}][0];
$r->[1] += $states[$pipe->{state}][1];
if( $r->[0] < 0 || $r->[0] == $width
|| $r->[1] < 0 || $r->[1] == $height)
{
$pipe->{colour} = int(rand(@colours));
$r->[1] %= $height;
$r->[0] %= $width;
}
$pipe->{length}++;
#"Please place the cursor at y, x."
system "tput cup $r->[1] $r->[0]";
#Send colour escape code
print color $colours[$pipe->{colour}];
if(rand() < $prob && $pipe->{length} > $min_len){
my $new_state = ($pipe->{state} + (-1,1)[int(rand(2))]) % @states;
print $trans->[$pipe->{state}][$new_state];
$pipe->{state} = $new_state;
$pipe->{length} = 0;
}else{
print $pipe_chars->[$pipe->{state} % 2];
}
}
#Try and take the correct amount of time (falling back to no delay; we
#can't have a negative delay).
my $delta = 1e6 * tv_interval($t0) - $delay;
$delta = ($delta < 0) ? 0 : $delta;
}
#"Please reset the screen and make the cursor visible"
system 'tput rmcup';
system 'tput cnorm';