Skip to content

Instantly share code, notes, and snippets.

@0racle
Created February 1, 2023 02:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0racle/9852f5903879addbcaa7666375c04840 to your computer and use it in GitHub Desktop.
Save 0racle/9852f5903879addbcaa7666375c04840 to your computer and use it in GitHub Desktop.
Terminal Snake game in Raku
#!/usr/bin/env raku
use Terminal::Print;
use Terminal::Print::RawInput;
use Terminal::ANSIColor;
use Point;
my constant COLS = 22;
my constant ROWS = 22;
class BlockyTerm {
has $.scr;
submethod BUILD {
$!scr = Terminal::Print.new;
$!scr.initialize-screen;
}
submethod DESTROY {
$!scr.shutdown-screen;
}
method beep($x, $y) {
$!scr.print-string($x × 2, $y, '██')
}
method boop($x, $y) {
$!scr.print-string($x × 2, $y, ' ')
}
}
my \T = BlockyTerm.new;
END { T.DESTROY }
print color('blue');
for (0 .. COLS + 1) -> \i {
T.beep(i, 0);
T.beep(i, ROWS + 1);
}
for (1 .. ROWS) -> \i {
T.beep(0, i);
T.beep(COLS + 1, i);
}
print color('magenta');
T.scr.print-string(4, 5, title-text());
class Snake {
has Point $.head = point(2, 2);
has Point $.dir = point(1, 0);
has Point @.tail;
has Int $.length is rw = 5;
my $U = point( 0, -1);
my $L = point(-1, 0);
my $D = point( 0, 1);
my $R = point( 1, 0);
my %dirmap = (
w => $U, a => $L, s => $D, d => $R,
h => $L, j => $D, k => $U, l => $R,
"\e[A" => $U, "\e[B" => $D, "\e[C" => $R, "\e[D" => $L
);
method process-input($k) {
with %dirmap{$k} -> $d {
$!dir = $d
if $d.x == $!dir.y
|| $d.y == $!dir.x
}
}
method move() {
@!tail.unshift: $!head;
$!head += $!dir;
}
method draw-head() {
print color('yellow');
T.beep(.x, .y) with $!head;
print color('green');
T.beep(.x, .y) with @!tail.head;
print color('reset');
}
method chop-tail() {
T.boop(.x, .y) with @!tail.pop
}
method display() {
self.draw-head;
if @!tail ≥ $!length {
self.chop-tail
}
}
method hit-wall() {
!(0 < $!head.x < ROWS + 1 &&
0 < $!head.y < COLS + 1 )
}
method hit-tail() {
@!tail.first($!head)
}
method hide() {
for $!head, |@!tail {
T.boop(.x, .y);
}
}
method show() {
print color('yellow');
T.beep(.x, .y) with $!head;
print color('green');
for @!tail {
T.beep(.x, .y)
}
}
method show-death() {
sleep 0.5;
for @!tail {
T.boop(.x, .y);
sleep 0.025;
}
}
}
my $snake = Snake.new;
my $delay = 0.125;
sub random-nibble() {
state @points = ((1 ..^ COLS - 1) X (1 ..^ ROWS - 1)).map: -> ($x, $y) {
point($x, $y)
}
my $nibble = @points.pick(*).first: -> $p {
$p ∉ ($snake.head, |$snake.tail)
}
print color('red');
T.beep($nibble.x, $nibble.y);
return $nibble;
}
my $score = 0;
sub draw-score() {
print color('black on_cyan');
T.scr.print-string(18, ROWS + 1, " SCORE: $score.fmt('%3d') ")
}
draw-score();
my @input;
my $key-pressed = raw-input-supply();
my $tap = $key-pressed.tap: -> $k {
@input.push: $k;
}
sub flush-and-await-key() {
@input = ();
loop {
if @input.shift -> $k {
if $k.ord == 3 { die }
return $k
}
sleep 0.02
}
}
sub process-input() {
loop {
if @input.shift -> $k is copy {
if $k.ord == 3 { die }
if $k.ord == 27 {
$k ~= @input.splice(0, 2).join
}
if $k eq 'p' {
pause()
}
if $k eq '=' { $delay += 0.01 }
if $k eq '-' { $delay -= 0.01 }
$snake.process-input($k) or next
}
last
}
}
flush-and-await-key;
print color('reset');
T.scr.print-string(4, 5, title-text(:clear));
my $nibble = random-nibble();
sub pause() {
$snake.hide();
T.boop(.x, .y) with $nibble;
print color('magenta');
T.scr.print-string(4, 5, pause-text());
flush-and-await-key();
T.scr.print-string(4, 5, pause-text(:clear));
print color('red');
T.beep(.x, .y) with $nibble;
$snake.show();
}
sub game-over() {
T.boop(.x, .y) with $nibble;
$snake.show-death;
print color('magenta');
T.scr.print-string(6, 5, end-text());
print color('reset');
flush-and-await-key();
T.scr.print-string(6, 5, end-text(:clear));
}
sub play-again() {
print color('magenta');
T.scr.print-string(6, 3, play-again-text());
loop {
given flush-and-await-key().lc {
when 'n' { return False }
when 'y' {
T.scr.print-string(6, 3, play-again-text(:clear));
$score = 0;
draw-score();
$snake = Snake.new;
$nibble = random-nibble();
return True
}
}
}
}
sub eat-nibble() {
$score += 1;
$delay -= 0.0001;
$snake.length += 1;
$nibble = random-nibble();
draw-score();
}
sub update-state() {
$snake.move;
if T.scr.grid[$snake.head.y][$snake.head.x × 2] eq ' ' {
return True;
}
if $snake.head ~~ $nibble {
eat-nibble();
return True;
}
game-over();
return False;
}
sub draw-state() {
$snake.display()
}
print color('reset');
loop {
loop {
process-input();
update-state() or last;
draw-state();
sleep $delay;
}
play-again() or last
}
CATCH {
default {
exit
}
}
$tap.close;
multi sub trait_mod:<is>(Routine $func, :$clearable) {
$func.wrap: -> :$clear {
$clear ?? callwith().trans(/\S/ => ' ') !! callwith()
}
}
sub title-text() is clearable {
q:to<END>
██████╗███╗ ██╗ █████╗ ██╗ ██╗███████╗
██╔════╝████╗ ██║██╔══██╗██║ ██╔╝██╔════╝
╚█████╗ ██╔██╗██║███████║█████═╝ █████╗
╚═══██╗██║╚████║██╔══██║██╔═██╗ ██╔══╝
██████╔╝██║ ╚███║██║ ██║██║ ╚██╗███████╗
╚═════╝ ╚═╝ ╚══╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝
END
}
sub pause-text() is clearable {
q:to<END>
██████╗ █████╗ ██╗ ██╗ ██████╗███████╗
██╔══██╗██╔══██╗██║ ██║██╔════╝██╔════╝
██████╔╝███████║██║ ██║╚█████╗ █████╗
██╔═══╝ ██╔══██║██║ ██║ ╚═══██╗██╔══╝
██║ ██║ ██║╚██████╔╝██████╔╝███████╗
╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝
END
}
sub end-text() is clearable {
q:to<END>
██████╗ █████╗ ███╗ ███╗███████╗
██╔════╝ ██╔══██╗████╗ ████║██╔════╝
██║ ██╗ ███████║██╔████╔██║█████╗
██║ ╚██╗██╔══██║██║╚██╔╝██║██╔══╝
╚██████╔╝██║ ██║██║ ╚═╝ ██║███████╗
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝
█████╗ ██╗ ██╗███████╗██████╗
██╔══██╗██║ ██║██╔════╝██╔══██╗
██║ ██║╚██╗ ██╔╝█████╗ ██████╔╝
██║ ██║ ╚████╔╝ ██╔══╝ ██╔══██╗
╚█████╔╝ ╚██╔╝ ███████╗██║ ██║
╚════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
END
}
sub play-again-text() is clearable {
q:to<END>
██████╗ ██╗ █████╗ ██╗ ██╗
██╔══██╗██║ ██╔══██╗╚██╗ ██╔╝
██████╔╝██║ ███████║ ╚████╔╝
██╔═══╝ ██║ ██╔══██║ ╚██╔╝
██║ ███████╗██║ ██║ ██║
╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝
█████╗ ██████╗ █████╗ ██╗███╗ ██╗
██╔══██╗██╔════╝ ██╔══██╗██║████╗ ██║
███████║██║ ██╗ ███████║██║██╔██╗██║
██╔══██║██║ ╚██╗██╔══██║██║██║╚████║
██║ ██║╚██████╔╝██║ ██║██║██║ ╚███║
╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚══╝
██╗ ██╗ ██╗███╗ ██╗
╚██╗ ██╔╝ ██╔╝████╗ ██║
╚████╔╝ ██╔╝ ██╔██╗██║
╚██╔╝ ██╔╝ ██║╚████║
██║ ██╔╝ ██║ ╚███║
╚═╝ ╚═╝ ╚═╝ ╚══╝
END
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment