Snake: Difference between revisions
Thundergnat (talk | contribs) m →{{header|Perl 6}}: Remove some redundant code. |
No edit summary |
||
Line 128: | Line 128: | ||
See [[Snake/Java]]. |
See [[Snake/Java]]. |
||
=={{header|JavaScript}}== |
|||
You need the P5 Library to run this code! |
|||
<lang javascript> |
|||
var LEFT = 1, RIGHT = 2, DOWN = 4, UP = 8, block = 24, |
|||
frameR = 7, wid = 30, hei = 20, fruit, snake; |
|||
function Snake() { |
|||
this.length = 1; |
|||
this.alive = true; |
|||
this.pos = createVector( 1, 1 ); |
|||
this.posArray = []; |
|||
this.posArray.push( createVector( 1, 1 ) ); |
|||
this.dir = RIGHT; |
|||
this.draw = function() { |
|||
fill( 130, 190, 0 ); |
|||
var pos, i = this.posArray.length - 1, l = this.length; |
|||
while( true ){ |
|||
pos = this.posArray[i--]; |
|||
rect( pos.x * block, pos.y * block, block, block ); |
|||
if( --l == 0 ) break; |
|||
} |
|||
} |
|||
this.eat = function( frut ) { |
|||
var b = this.pos.x == frut.x && this.pos.y == frut.y; |
|||
if( b ) this.length++; |
|||
return b; |
|||
} |
|||
this.overlap = function() { |
|||
var len = this.posArray.length - 1; |
|||
for( var i = len; i > len - this.length; i-- ) { |
|||
tp = this.posArray[i]; |
|||
if( tp.x === this.pos.x && tp.y === this.pos.y ) return true; |
|||
} |
|||
return false; |
|||
} |
|||
this.update = function() { |
|||
if( !this.alive ) return; |
|||
switch( this.dir ) { |
|||
case LEFT: |
|||
this.pos.x--; if( this.pos.x < 1 ) this.pos.x = wid - 2; |
|||
break; |
|||
case RIGHT: |
|||
this.pos.x++; if( this.pos.x > wid - 2 ) this.pos.x = 1; |
|||
break; |
|||
case UP: |
|||
this.pos.y--; if( this.pos.y < 1 ) this.pos.y = hei - 2; |
|||
break; |
|||
case DOWN: |
|||
this.pos.y++; if( this.pos.y > hei - 2 ) this.pos.y = 1; |
|||
break; |
|||
} |
|||
if( this.overlap() ) { this.alive = false; } else { |
|||
this.posArray.push( createVector( this.pos.x, this.pos.y ) ); |
|||
if( this.posArray.length > 5000 ) { this.posArray.splice( 0, 1 ); } |
|||
} |
|||
} |
|||
} |
|||
function Fruit() { |
|||
this.fruitTime = true; |
|||
this.pos = createVector(); |
|||
this.draw = function() { |
|||
fill( 200, 50, 20 ); |
|||
rect( this.pos.x * block, this.pos.y * block, block, block ); |
|||
} |
|||
this.setFruit = function() { |
|||
this.pos.x = floor( random( 1, wid - 1 ) ); |
|||
this.pos.y = floor( random( 1, hei - 1 ) ); |
|||
this.fruitTime = false; |
|||
} |
|||
} |
|||
function setup() { |
|||
createCanvas( block * wid, block * hei ); |
|||
noStroke(); frameRate( frameR ); |
|||
snake = new Snake();fruit = new Fruit(); |
|||
} |
|||
function keyPressed() { |
|||
switch( keyCode ) { |
|||
case LEFT_ARROW: snake.dir = LEFT; break; |
|||
case RIGHT_ARROW: snake.dir = RIGHT; break; |
|||
case UP_ARROW: snake.dir = UP; break; |
|||
case DOWN_ARROW: snake.dir = DOWN; break; |
|||
} |
|||
} |
|||
function draw() { |
|||
background( color( 0, 0x22, 0 ) ); |
|||
fill( 20, 50, 120 ); |
|||
for( var i = 0; i < wid; i++ ) { |
|||
rect( i * block, 0, block, block ); |
|||
rect( i * block, height - block, block, block ); |
|||
} |
|||
for( var i = 1; i < hei - 1; i++ ) { |
|||
rect( 1, i * block, block, block ); |
|||
rect( width - block, i * block, block, block ); |
|||
} |
|||
if( fruit.fruitTime ) { |
|||
fruit.setFruit(); |
|||
frameR += .2; frameRate( frameR ); |
|||
} |
|||
fruit.draw(); |
|||
snake.update(); |
|||
if( snake.eat( fruit.pos ) ) { |
|||
fruit.fruitTime = true; |
|||
} |
|||
snake.draw(); |
|||
fill( 200 ); |
|||
textStyle( BOLD ); textAlign( RIGHT ); textSize( 120 ); |
|||
text( "" + ( snake.length - 1 ), 690, 440 ); |
|||
if( !snake.alive ) text( "THE END", 630, 250 ); |
|||
} |
|||
</lang> |
|||
=={{header|Haskell}}== |
=={{header|Haskell}}== |
Revision as of 17:49, 13 May 2017
This page uses content from Wikipedia. The original article was at Snake_(video_game). The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance) |
Snake is a game where the player maneuvers a line which grows in length every time the snake reaches a food source.
- Task
Implement a variant of the Snake game, in any interactive environment, in which a sole player attempts to eat items by running into them with the head of the snake. Each item eaten makes the snake longer and a new item is randomly generated somewhere else on the plane. The game ends when the snake attempts to eat himself.
C++
Simple Windows console implementation.
![](http://static.miraheze.org/rosettacodewiki/thumb/6/6c/SnakeCpp.png/200px-SnakeCpp.png)
<lang cpp>
- include <windows.h>
- include <ctime>
- include <iostream>
- include <string>
const int WID = 60, HEI = 30, MAX_LEN = 600; enum DIR { NORTH, EAST, SOUTH, WEST };
class snake { public:
snake() { console = GetStdHandle( STD_OUTPUT_HANDLE ); SetConsoleTitle( "Snake" ); COORD coord = { WID + 1, HEI + 2 }; SetConsoleScreenBufferSize( console, coord ); SMALL_RECT rc = { 0, 0, WID, HEI + 1 }; SetConsoleWindowInfo( console, TRUE, &rc ); CONSOLE_CURSOR_INFO ci = { 1, false }; SetConsoleCursorInfo( console, &ci ); } void play() { std::string a; while( 1 ) { createField(); alive = true; while( alive ) { drawField(); readKey(); moveSnake(); Sleep( 50 ); } COORD c = { 0, HEI + 1 }; SetConsoleCursorPosition( console, c ); SetConsoleTextAttribute( console, 0x000b ); std::cout << "Play again [Y/N]? "; std::cin >> a; if( a.at( 0 ) != 'Y' && a.at( 0 ) != 'y' ) return; } }
private:
void createField() { COORD coord = { 0, 0 }; DWORD c; FillConsoleOutputCharacter( console, ' ', ( HEI + 2 ) * 80, coord, &c ); FillConsoleOutputAttribute( console, 0x0000, ( HEI + 2 ) * 80, coord, &c ); SetConsoleCursorPosition( console, coord ); int x = 0, y = 1; for( ; x < WID * HEI; x++ ) brd[x] = 0; for( x = 0; x < WID; x++ ) { brd[x] = brd[x + WID * ( HEI - 1 )] = '+'; } for( ; y < HEI; y++ ) { brd[0 + WID * y] = brd[WID - 1 + WID * y] = '+'; } do { x = rand() % WID; y = rand() % ( HEI >> 1 ) + ( HEI >> 1 ); } while( brd[x + WID * y] ); brd[x + WID * y] = '@'; tailIdx = 0; headIdx = 4; x = 3; y = 2; for( int c = tailIdx; c < headIdx; c++ ) { brd[x + WID * y] = '#'; snk[c].X = 3 + c; snk[c].Y = 2; } head = snk[3]; dir = EAST; points = 0; } void readKey() { if( GetAsyncKeyState( 39 ) & 0x8000 ) dir = EAST; if( GetAsyncKeyState( 37 ) & 0x8000 ) dir = WEST; if( GetAsyncKeyState( 38 ) & 0x8000 ) dir = NORTH; if( GetAsyncKeyState( 40 ) & 0x8000 ) dir = SOUTH; } void drawField() { COORD coord; char t; for( int y = 0; y < HEI; y++ ) { coord.Y = y; for( int x = 0; x < WID; x++ ) { t = brd[x + WID * y]; if( !t ) continue; coord.X = x; SetConsoleCursorPosition( console, coord ); if( coord.X == head.X && coord.Y == head.Y ) { SetConsoleTextAttribute( console, 0x002e ); std::cout << 'O'; SetConsoleTextAttribute( console, 0x0000 ); continue; } switch( t ) { case '#': SetConsoleTextAttribute( console, 0x002a ); break; case '+': SetConsoleTextAttribute( console, 0x0019 ); break; case '@': SetConsoleTextAttribute( console, 0x004c ); break; } std::cout << t; SetConsoleTextAttribute( console, 0x0000 ); } } std::cout << t; SetConsoleTextAttribute( console, 0x0007 ); COORD c = { 0, HEI }; SetConsoleCursorPosition( console, c ); std::cout << "Points: " << points; } void moveSnake() { switch( dir ) { case NORTH: head.Y--; break; case EAST: head.X++; break; case SOUTH: head.Y++; break; case WEST: head.X--; break; } char t = brd[head.X + WID * head.Y]; if( t && t != '@' ) { alive = false; return; } brd[head.X + WID * head.Y] = '#'; snk[headIdx].X = head.X; snk[headIdx].Y = head.Y; if( ++headIdx >= MAX_LEN ) headIdx = 0; if( t == '@' ) { points++; int x, y; do { x = rand() % WID; y = rand() % ( HEI >> 1 ) + ( HEI >> 1 ); } while( brd[x + WID * y] ); brd[x + WID * y] = '@'; return; } SetConsoleCursorPosition( console, snk[tailIdx] ); std::cout << ' '; brd[snk[tailIdx].X + WID * snk[tailIdx].Y] = 0; if( ++tailIdx >= MAX_LEN ) tailIdx = 0; } bool alive; char brd[WID * HEI]; HANDLE console; DIR dir; COORD snk[MAX_LEN]; COORD head; int tailIdx, headIdx, points;
}; int main( int argc, char* argv[] ) {
srand( static_cast<unsigned>( time( NULL ) ) ); snake s; s.play(); return 0;
} </lang>
Java
See Snake/Java.
JavaScript
You need the P5 Library to run this code! <lang javascript> var LEFT = 1, RIGHT = 2, DOWN = 4, UP = 8, block = 24,
frameR = 7, wid = 30, hei = 20, fruit, snake;
function Snake() {
this.length = 1; this.alive = true; this.pos = createVector( 1, 1 ); this.posArray = []; this.posArray.push( createVector( 1, 1 ) ); this.dir = RIGHT; this.draw = function() { fill( 130, 190, 0 ); var pos, i = this.posArray.length - 1, l = this.length; while( true ){ pos = this.posArray[i--]; rect( pos.x * block, pos.y * block, block, block ); if( --l == 0 ) break; } } this.eat = function( frut ) { var b = this.pos.x == frut.x && this.pos.y == frut.y; if( b ) this.length++; return b; } this.overlap = function() { var len = this.posArray.length - 1; for( var i = len; i > len - this.length; i-- ) { tp = this.posArray[i]; if( tp.x === this.pos.x && tp.y === this.pos.y ) return true; } return false; } this.update = function() { if( !this.alive ) return; switch( this.dir ) { case LEFT: this.pos.x--; if( this.pos.x < 1 ) this.pos.x = wid - 2; break; case RIGHT: this.pos.x++; if( this.pos.x > wid - 2 ) this.pos.x = 1; break; case UP: this.pos.y--; if( this.pos.y < 1 ) this.pos.y = hei - 2; break; case DOWN: this.pos.y++; if( this.pos.y > hei - 2 ) this.pos.y = 1; break; } if( this.overlap() ) { this.alive = false; } else { this.posArray.push( createVector( this.pos.x, this.pos.y ) ); if( this.posArray.length > 5000 ) { this.posArray.splice( 0, 1 ); } } }
} function Fruit() {
this.fruitTime = true; this.pos = createVector(); this.draw = function() { fill( 200, 50, 20 ); rect( this.pos.x * block, this.pos.y * block, block, block ); } this.setFruit = function() { this.pos.x = floor( random( 1, wid - 1 ) ); this.pos.y = floor( random( 1, hei - 1 ) ); this.fruitTime = false; }
} function setup() {
createCanvas( block * wid, block * hei ); noStroke(); frameRate( frameR ); snake = new Snake();fruit = new Fruit();
} function keyPressed() {
switch( keyCode ) { case LEFT_ARROW: snake.dir = LEFT; break; case RIGHT_ARROW: snake.dir = RIGHT; break; case UP_ARROW: snake.dir = UP; break; case DOWN_ARROW: snake.dir = DOWN; break; }
} function draw() {
background( color( 0, 0x22, 0 ) ); fill( 20, 50, 120 ); for( var i = 0; i < wid; i++ ) { rect( i * block, 0, block, block ); rect( i * block, height - block, block, block ); } for( var i = 1; i < hei - 1; i++ ) { rect( 1, i * block, block, block ); rect( width - block, i * block, block, block ); } if( fruit.fruitTime ) { fruit.setFruit(); frameR += .2; frameRate( frameR ); } fruit.draw(); snake.update(); if( snake.eat( fruit.pos ) ) { fruit.fruitTime = true; } snake.draw(); fill( 200 ); textStyle( BOLD ); textAlign( RIGHT ); textSize( 120 ); text( "" + ( snake.length - 1 ), 690, 440 ); if( !snake.alive ) text( "THE END", 630, 250 );
} </lang>
Haskell
<lang haskell>{-# LANGUAGE TemplateHaskell #-} import Control.Monad.Random (getRandomRs) import Graphics.Gloss.Interface.Pure.Game import Lens.Micro ((%~), (^.), (&), set) import Lens.Micro.TH (makeLenses)
-- all data types
data Snake = Snake { _body :: [Point], _direction :: Point } makeLenses Snake
data World = World { _snake :: Snake , _food :: [Point]
, _score :: Int , _maxScore :: Int }
makeLenses World
-- everything snake can do
moves (Snake b d) = Snake (step b d : init b) d eats (Snake b d) = Snake (step b d : b) d bites (Snake b _) = any (== head b) step ((x,y):_) (a,b) = (x+a, y+b)
turn (x',y') (Snake b (x,y)) | (x+x',y+y') == (0,0) = Snake b (x,y)
| otherwise = Snake b (x',y')
-- all randomness
createWorld = do xs <- map fromIntegral <$> getRandomRs (2, 38 :: Int)
ys <- map fromIntegral <$> getRandomRs (2, 38 :: Int) return (Ok, World snake (zip xs ys) 0 0) where snake = Snake [(20, 20)] (1,0)
-- A tyny DSL for declarative description of business logic
data Status = Ok | Fail | Stop
continue = \x -> (Ok, x) stop = \x -> (Stop, x) f >>> g = \x -> case f x of { (Ok, y) -> g y; b -> b } -- chain composition f <|> g = \x -> case f x of { (Fail, _) -> g x; b -> b } -- alternative p ==> f = \x -> if p x then f x else (Fail, x) -- condition l .& f = continue . (l %~ f) -- modification l .= y = continue . set l y -- setting
-- all business logic
updateWorld _ = id >>> (snakeEats <|> snakeMoves)
where snakeEats = (snakeFindsFood ==> (snake .& eats)) >>> (score .& (+1)) >>> (food .& tail)
snakeMoves = (snakeBitesTail ==> stop) <|> (snakeHitsWall ==> stop) <|> (snake .& moves)
snakeFindsFood w = (w^.snake & moves) `bites` (w^.food & take 1) snakeBitesTail w = (w^.snake) `bites` (w^.snake.body & tail) snakeHitsWall w = (w^.snake.body) & head & isOutside isOutside (x,y) = or [x <= 0, 40 <= x, y <= 0, 40 <= y]
-- all event handing
handleEvents e (s,w) = f w
where f = case s of Ok -> case e of EventKey (SpecialKey k) _ _ _ -> case k of KeyRight -> snake .& turn (1,0) KeyLeft -> snake .& turn (-1,0) KeyUp -> snake .& turn (0,1) KeyDown -> snake .& turn (0,-1) _-> continue _-> continue _-> \w -> w & ((snake.body) .= [(20,20)]) >>> (maxScore .& max (w^.score)) >>> (score .= 0)
-- all graphics
renderWorld (s, w) = pictures [frame, color c drawSnake, drawFood, showScore]
where c = case s of { Ok -> orange; _-> red } drawSnake = foldMap (rectangleSolid 10 10 `at`) (w^.snake.body) drawFood = color blue $ circleSolid 5 `at` (w^.food & head) frame = color black $ rectangleWire 400 400 showScore = color orange $ scale 0.2 0.2 $ txt `at` (-80,130) txt = Text $ mconcat ["Score: ", w^.score & show ," Maximal score: ", w^.maxScore & show] at p (x,y) = Translate (10*x-200) (10*y-200) p
main = do world <- createWorld
play inW white 7 world renderWorld handleEvents updateWorld where inW = InWindow "The Snake" (400, 400) (10, 10)</lang>
Extra credit
It is easy to make snake to seek food automatically. Just change the first line of the updateWorld
definition:
<lang haskell>updateWorld _ = id >>> snakeSeeksFood >>> (snakeEats <|> snakeMoves) </lang>
and add local definition:
<lang haskell> snakeSeeksFood w = w & snake .& turns optimalDirection
where optimalDirection = minimumBy (comparing distanceToFood) safeTurns safeTurns = filter safe [(x,y),(-y,x),(y,-x)] `ifEmpty` [(x,y)] where (x,y) = w^.snake.direction safe d = let w = w & snake %~ moves . turns d in not (snakeBitesTail w || snakeHitsWall w) lst `ifEmpty` x = if null lst then x else lst distanceToFood d = let (a,b) = w^.snake & turns d & moves & (^.body) & head (x,y) = w^.food & head in (a-x)^2 + (b-y)^2</lang>
Perl
![](http://static.miraheze.org/rosettacodewiki/thumb/0/02/Snake_game_perl.png/200px-Snake_game_perl.png)
<lang perl>use utf8; use Time::HiRes qw(sleep); use Term::ANSIColor qw(colored); use Term::ReadKey qw(ReadMode ReadLine);
binmode(STDOUT, ':utf8');
use constant {
VOID => 0, HEAD => 1, BODY => 2, TAIL => 3, FOOD => 4, };
use constant {
LEFT => [+0, -1], RIGHT => [+0, +1], UP => [-1, +0], DOWN => [+1, +0], };
use constant {
BG_COLOR => "on_black", SLEEP_SEC => 0.05, };
use constant {
SNAKE_COLOR => ('bold green' . ' ' . BG_COLOR), FOOD_COLOR => ('red' . ' ' . BG_COLOR), };
use constant {
U_HEAD => colored('▲', SNAKE_COLOR), D_HEAD => colored('▼', SNAKE_COLOR), L_HEAD => colored('◀', SNAKE_COLOR), R_HEAD => colored('▶', SNAKE_COLOR),
U_BODY => colored('╹', SNAKE_COLOR), D_BODY => colored('╻', SNAKE_COLOR), L_BODY => colored('╴', SNAKE_COLOR), R_BODY => colored('╶', SNAKE_COLOR),
U_TAIL => colored('╽', SNAKE_COLOR), D_TAIL => colored('╿', SNAKE_COLOR), L_TAIL => colored('╼', SNAKE_COLOR), R_TAIL => colored('╾', SNAKE_COLOR),
A_VOID => colored(' ', BG_COLOR), A_FOOD => colored('❇', FOOD_COLOR), };
local $| = 1;
my $w = eval { `tput cols` } || 80; my $h = eval { `tput lines` } || 24; my $r = "\033[H";
my @grid = map {
[map { [VOID] } 1 .. $w]
} 1 .. $h;
my $dir = LEFT; my @head_pos = ($h / 2, $w / 2); my @tail_pos = ($head_pos[0], $head_pos[1] + 1);
$grid[$head_pos[0]][$head_pos[1]] = [HEAD, $dir]; # head $grid[$tail_pos[0]][$tail_pos[1]] = [TAIL, $dir]; # tail
sub create_food {
my ($food_x, $food_y);
do { $food_x = rand($w); $food_y = rand($h); } while ($grid[$food_y][$food_x][0] != VOID);
$grid[$food_y][$food_x][0] = FOOD;
}
create_food();
sub display {
my $i = 0;
print $r, join("\n", map { join("", map { my $t = $_->[0]; if ($t != FOOD and $t != VOID) { my $p = $_->[1]; $i = $p eq UP ? 0 : $p eq DOWN ? 1 : $p eq LEFT ? 2 : 3; } $t == HEAD ? (U_HEAD, D_HEAD, L_HEAD, R_HEAD)[$i] : $t == BODY ? (U_BODY, D_BODY, L_BODY, R_BODY)[$i] : $t == TAIL ? (U_TAIL, D_TAIL, L_TAIL, R_TAIL)[$i] : $t == FOOD ? (A_FOOD) : (A_VOID);
} @{$_} ) } @grid );
}
sub move {
my $grew = 0;
# Move the head { my ($y, $x) = @head_pos;
my $new_y = ($y + $dir->[0]) % $h; my $new_x = ($x + $dir->[1]) % $w;
my $cell = $grid[$new_y][$new_x]; my $t = $cell->[0];
if ($t == BODY or $t == TAIL) { die "Game over!\n"; } elsif ($t == FOOD) { create_food(); $grew = 1; }
# Create a new head $grid[$new_y][$new_x] = [HEAD, $dir];
# Replace the current head with body $grid[$y][$x] = [BODY, $dir];
# Save the position of the head @head_pos = ($new_y, $new_x); }
# Move the tail if (not $grew) { my ($y, $x) = @tail_pos;
my $pos = $grid[$y][$x][1]; my $new_y = ($y + $pos->[0]) % $h; my $new_x = ($x + $pos->[1]) % $w;
$grid[$y][$x][0] = VOID; # erase the current tail $grid[$new_y][$new_x][0] = TAIL; # create a new tail
# Save the position of the tail @tail_pos = ($new_y, $new_x); }
}
ReadMode(3); while (1) {
my $key; until (defined($key = ReadLine(-1))) { move(); display(); sleep(SLEEP_SEC); }
if ($key eq "\e[A" and $dir ne DOWN ) { $dir = UP } elsif ($key eq "\e[B" and $dir ne UP ) { $dir = DOWN } elsif ($key eq "\e[C" and $dir ne LEFT ) { $dir = RIGHT } elsif ($key eq "\e[D" and $dir ne RIGHT) { $dir = LEFT }
}</lang>
Perl 6
This is a variation of a demo script included in the examples folder for the Perl 6 SDL2::Raw library bindings.
<lang perl6>use SDL2::Raw; use Cairo;
constant W = 1280; constant H = 960;
constant FIELDW = W div 32; constant FIELDH = H div 32;
SDL_Init(VIDEO);
my $window = SDL_CreateWindow(
'Snake', SDL_WINDOWPOS_CENTERED_MASK, SDL_WINDOWPOS_CENTERED_MASK, W, H, OPENGL
);
my $render = SDL_CreateRenderer($window, -1, ACCELERATED +| PRESENTVSYNC);
my $snake_image = Cairo::Image.record(
-> $_ { .save; .rectangle: 0, 0, 64, 64; .clip; .rgb: 0, 1, 0; .rectangle: 0, 0, 64, 64; .fill :preserve; .rgb: 0, 0, 0; .stroke; .restore;
.save; .translate: 64, 0; .rectangle: 0, 0, 64, 64; .clip; .rgb: 1, 0, 0; .arc: 32, 32, 30, 0, 2 * pi; .fill :preserve; .rgb: 0, 0, 0; .stroke; .restore; }, 128, 128, Cairo::FORMAT_ARGB32);
my $snake_texture = SDL_CreateTexture(
$render, %PIXELFORMAT<ARGB8888>, STATIC, 128, 128
);
SDL_UpdateTexture(
$snake_texture, SDL_Rect.new( :x(0), :y(0), :w(128), :h(128) ), $snake_image.data, $snake_image.stride // 128 * 4
);
SDL_SetTextureBlendMode($snake_texture, 1);
SDL_SetRenderDrawBlendMode($render, 1);
my $snakepiece_srcrect = SDL_Rect.new(:w(64), :h(64)); my $nompiece_srcrect = SDL_Rect.new(:w(64), :h(64), :x(64));
my $event = SDL_Event.new;
enum GAME_KEYS (
K_UP => 82, K_DOWN => 81, K_LEFT => 80, K_RIGHT => 79,
);
my Complex @snakepieces = 10+10i; my Complex @noms; my Complex $snakedir = 1+0i; my $nomspawn = 0; my $snakespeed = 0.1; my $snakestep = 0; my $nom = 4;
my $last_frame_start = now; main: loop {
my $start = now; my $dt = $start - $last_frame_start // 0.00001; while SDL_PollEvent($event) { my $casted_event = SDL_CastEvent($event); given $casted_event { when *.type == QUIT { last main } when *.type == KEYDOWN { if GAME_KEYS(.scancode) -> $comm { given $comm { when 'K_LEFT' { $snakedir = -1+0i unless $snakedir == 1+0i } when 'K_RIGHT' { $snakedir = 1+0i unless $snakedir == -1+0i } when 'K_UP' { $snakedir = 0-1i unless $snakedir == 0+1i } when 'K_DOWN' { $snakedir = 0+1i unless $snakedir == 0-1i } } } } } }
if ($nomspawn -= $dt) < 0 { $nomspawn += 1; @noms.push: (^FIELDW).pick + (^FIELDH).pick * i unless @noms > 3; @noms.pop if @noms[*-1] == any(@snakepieces); }
if ($snakestep -= $dt) < 0 { $snakestep += $snakespeed;
@snakepieces.unshift: do given @snakepieces[0] { ($_.re + $snakedir.re) % FIELDW + (($_.im + $snakedir.im) % FIELDH) * i }
if @snakepieces[2..*].first( * == @snakepieces[0], :k ) -> $idx { @snakepieces = @snakepieces[0..($idx + 1)]; }
@noms .= grep( { $^piece == @snakepieces[0] ?? ($nom += 1) && False !! True } );
if $nom == 0 { @snakepieces.pop; } else { $nom = $nom - 1; } }
for @snakepieces { SDL_SetTextureColorMod( $snake_texture, 255, (cos((++$) / 2) * 100 + 155).round, 255 );
SDL_RenderCopy( $render, $snake_texture, $snakepiece_srcrect, SDL_Rect.new(.re * 32, .im * 32, 32, 32) ); }
SDL_SetTextureColorMod($snake_texture, 255, 255, 255);
for @noms { SDL_RenderCopy( $render, $snake_texture, $nompiece_srcrect, SDL_Rect.new(.re * 32, .im * 32, 32, 32) ) }
SDL_RenderPresent($render); SDL_SetRenderDrawColor($render, 0, 0, 0, 0); SDL_RenderClear($render);
$last_frame_start = $start; sleep(1 / 50);
}
SDL_Quit();</lang>
Sidef
<lang ruby>class SnakeGame(w, h) {
const readkey = frequire('Term::ReadKey') const ansi = frequire('Term::ANSIColor')
enum (VOID, HEAD, BODY, TAIL, FOOD)
define ( LEFT = [+0, -1], RIGHT = [+0, +1], UP = [-1, +0], DOWN = [+1, +0], )
define BG_COLOR = "on_black" define FOOD_COLOR = ("red" + " " + BG_COLOR) define SNAKE_COLOR = ("bold green" + " " + BG_COLOR) define SLEEP_SEC = 0.02
const ( A_VOID = ansi.colored(' ', BG_COLOR), A_FOOD = ansi.colored('❇', FOOD_COLOR), A_BLOCK = ansi.colored('■', SNAKE_COLOR), )
has dir = LEFT has grid = [[]] has head_pos = [0, 0] has tail_pos = [0, 0]
method init { grid = h.of { w.of { [VOID] } }
head_pos = [h//2, w//2] tail_pos = [head_pos[0], head_pos[1]+1]
grid[head_pos[0]][head_pos[1]] = [HEAD, dir] # head grid[tail_pos[0]][tail_pos[1]] = [TAIL, dir] # tail
self.make_food() }
method make_food { var (food_x, food_y)
do { food_x = w.rand.int food_y = h.rand.int } while (grid[food_y][food_x][0] != VOID)
grid[food_y][food_x][0] = FOOD }
method display { print("\033[H", grid.map { |row| row.map { |cell| given (cell[0]) { when (VOID) { A_VOID } when (FOOD) { A_FOOD } default { A_BLOCK } } }.join() }.join("\n") ) }
method move { var grew = false
# Move the head var (y, x) = head_pos...
var new_y = (y+dir[0] % h) var new_x = (x+dir[1] % w)
var cell = grid[new_y][new_x]
given (cell[0]) { when (BODY) { die "\nYou just bit your own body!\n" } when (TAIL) { die "\nYou just bit your own tail!\n" } when (FOOD) { grew = true; self.make_food() } }
# Create a new head grid[new_y][new_x] = [HEAD, dir]
# Replace the current head with body grid[y][x] = [BODY, dir]
# Update the head position head_pos = [new_y, new_x]
# Move the tail if (!grew) { var (y, x) = tail_pos...
var pos = grid[y][x][1] var new_y = (y+pos[0] % h) var new_x = (x+pos[1] % w)
grid[y][x][0] = VOID # erase the current tail grid[new_y][new_x][0] = TAIL # create a new tail
tail_pos = [new_y, new_x] } }
method play { STDOUT.autoflush(true) readkey.ReadMode(3)
try { loop { var key while (!defined(key = readkey.ReadLine(-1))) { self.move() self.display() Sys.sleep(SLEEP_SEC) }
given (key) { when ("\e[A") { if (dir != DOWN ) { dir = UP } } when ("\e[B") { if (dir != UP ) { dir = DOWN } } when ("\e[C") { if (dir != LEFT ) { dir = RIGHT } } when ("\e[D") { if (dir != RIGHT) { dir = LEFT } } } } } catch { readkey.ReadMode(0) } }
}
var w = `tput cols`.to_i var h = `tput lines`.to_i
SnakeGame(w || 80, h || 24).play</lang>