-
-
Save banderlog/d619041bf3f1f77ae192636ca43bd940 to your computer and use it in GitHub Desktop.
{snake.pas} | |
{classic game: avoid your tail and walls, go for apples} | |
{TODO?: - if press key perpendicular to move + backwards - eats itself} | |
program SnakeGame; | |
uses crt; {ncurses analogue} | |
const | |
DelayDuration = 100; {movement delay} | |
TRIESNUM = 128; {umber of tries to find a free space} | |
MAXHEIGHT = 40; {heght of a game field} | |
MAXWIDTH = 60; {width of a game field } | |
type | |
segment = ^star; | |
Direction = (up, down, left, right, stop); | |
star = record {body segment} | |
CurX, CurY: integer; | |
next: segment; | |
trend: Direction | |
end; | |
AppleStatus = (eated, untouched); | |
fruit = record {apple} | |
CurX, CurY: integer; | |
status: AppleStatus; | |
end; | |
papple = ^fruit; | |
borders = record {game field borders} | |
zeroX, endX, zeroY, endY: integer; | |
end; | |
procedure BuildBorder(var box: borders); {draws game field borders} | |
var | |
i: integer; | |
begin | |
TextColor(white); {white walls} | |
{top wall} | |
i:=1; | |
while (i < MAXWIDTH) do begin | |
GotoXY((i+box.zeroX),box.zeroY); | |
write('_'); | |
i:=i+1; | |
end; | |
box.endX:=i+box.zeroX; | |
{right wall} | |
i:=1; | |
while (i < MAXHEIGHT) do begin | |
GotoXY(box.endX,(box.zeroY+i)); | |
write('|'); | |
i:=i+1; | |
end; | |
box.endY:=i+box.zeroY-1; | |
{bottom wall} | |
i:=1; | |
while (box.endX - i) > box.zeroX do begin | |
GotoXY(box.endX-i,box.endY); | |
write('_'); | |
i:=i+1; | |
end; | |
{left wall} | |
i:=0; | |
while (box.endY - i) > box.zeroY do begin | |
GotoXY(box.zeroX,(box.endY-i)); | |
write('|'); | |
i:=i+1; | |
end; | |
GotoXY(1,1); {return pointer to NW corner} | |
end; | |
procedure ShowStar(var s: segment); {reflect body segment} | |
begin | |
GotoXY(s^.CurX, s^.CurY); | |
TextColor(Green); {we are green} | |
write('*'); | |
GotoXY(1,1) | |
end; | |
procedure HideStar(var s: segment); {conceal body segment} | |
begin | |
GotoXY(s^.CurX, s^.CurY); | |
write(' '); | |
GotoXY(1,1) | |
end; | |
procedure Grow(var tail, prev: segment); {adds segments to body} | |
var i:integer = 0; | |
begin | |
while i < 2 do begin {one apple - two segments} | |
new(tail^.next); | |
tail:=tail^.next; | |
prev:=tail; | |
i:=i+1; | |
end; | |
exit; | |
end; | |
procedure SelfBiteTest(var s: segment); {selfdestruction check} | |
var | |
pp: ^segment; {pointer to pointer} | |
begin | |
pp := @(s^.next); {s^ is a head} | |
while pp^ <> nil do begin {go throught list} | |
if (s^.CurX = pp^^.CurX) AND (s^.CurY = pp^^.CurY) then begin | |
writeln('Well done, autocannibal!'); | |
halt(1) | |
end | |
else | |
pp:=@(pp^^.next) | |
end; | |
end; | |
function TestFree(var x,y: integer; h: segment):boolean; {find free space} | |
var | |
pp: ^segment; {pointer to pointer} | |
begin | |
pp := @(h); {h - head} | |
if pp^ = nil then begin {end check} | |
TestFree := true; | |
exit; | |
end; | |
{look if apple coordinates match any part of body} | |
if (pp^^.CurX = x) AND (pp^^.CurY = y) then begin | |
TestFree := false; | |
exit; | |
end | |
else begin | |
pp:=@(pp^^.next); | |
TestFree(x,y,pp^); {recursion} | |
end; | |
end; | |
procedure EmergeApple(var a: papple; x,y: integer); {draw an apple} | |
begin | |
a^.CurX:= x; | |
a^.CurY:= y; | |
a^.status:=untouched; | |
GotoXY(a^.CurX, a^.CurY); | |
TextColor(Red); {red one} | |
write('@'); | |
GotoXY(1,1); | |
end; | |
procedure CreateApple(var apple: papple; h: segment; box: borders); | |
{find place for an apple. it must be free from snake.} | |
var | |
x,y,dx,dy,i,znak, pdx, tmpx, tmpy: integer; {aarrrghhh!!} | |
begin | |
Randomize; {shake dice} | |
x := random(box.endX-box.zeroX-2)+box.zeroX+1; {range of x} | |
y := random(box.endY-box.zeroY-2)+box.zeroY+1; {range of y} | |
dx :=0; {delta x} | |
dy :=0; {delta y} | |
pdx:=0; {previous dx} | |
znak := -1; {even iterations should be with negative x,y} | |
i:=1; {iterator} | |
{works like clockwise spiral (0 - start, nums - steps): | |
678 | |
501 | |
432 | |
x+1, y+1, x-2, y-2, x+3, y-3..-4...} | |
while abs(i) < TRIESNUM do begin | |
{move on x axis} | |
while abs(dx) <= abs(pdx) do begin {abs(x) -> |x|} | |
dx:=dx+i; | |
tmpx:=x+dx; {functions unable to take this expressions} | |
tmpy:=y+dy; | |
{!} write(''); {IF THERE IS NO STRING - NOTHING WORKS WHY??} | |
{endless cycle of TestFree, becaue it is unable to call EmergeApple.} | |
if TestFree(tmpx,tmpy,h) then begin | |
EmergeApple(apple,tmpx,tmpy); | |
exit; | |
end; | |
end; | |
{move on y axis} | |
while abs(dy) <= abs(dx) do begin | |
dy:=dy+i; | |
tmpy:=y+dy; {functions unable to take this expressions} | |
tmpx:=x+dx; | |
if TestFree(tmpx,tmpy,h) then begin | |
EmergeApple(apple, tmpx, tmpy); | |
exit; | |
end; | |
end; | |
{prepare for new circle} | |
y:=y+dy; | |
x:=x+dx; | |
pdx:=dx; | |
i:=i*znak; {positive\negative switch} | |
dy:=0; | |
dx:=0; | |
end; | |
end; | |
function AppleTest(var s: segment; a: papple): boolean; {check if we ate it} | |
begin | |
if (s^.CurX = a^.CurX) AND (s^.CurY = a^.CurY) then | |
AppleTest:= true | |
else | |
AppleTest:=false | |
end; | |
procedure MoveBody(var s: segment; prev: segment); {move snake} | |
begin | |
if s^.next = nil then begin {end check} | |
exit; | |
end; | |
HideStar(s); {erase segment} | |
MoveBody(s^.next, s); {recursion} | |
s^.CurX := prev^.CurX; {every next gets params of his prev} | |
s^.CurY := prev^.CurY; | |
s^.trend := prev^.trend; | |
ShowStar(s); {show segment} | |
end; | |
procedure BorderTest(var head: segment; box: borders); {wall collision check} | |
begin | |
{if head coordinates match with walls - game over} | |
if (head^.CurX = box.zeroX) OR (head^.CurX = box.endX) then begin | |
writeln('Whatch tour step'); | |
halt(1); | |
end | |
else | |
if (head^.CurY = box.zeroY) OR (head^.CurY = box.endY) then begin | |
writeln('Whatch tour step'); | |
halt(1); | |
end; | |
end; | |
procedure MoveHead(var head, tail, prev: segment; apple: papple); {NUFF said} | |
{head is special case, because it selects direction} | |
begin | |
HideStar(head); {erase head} | |
if head^.next <> nil then begin {if we have some body} | |
MoveBody(head^.next, head); {move it} | |
end; | |
{if head moves in some direction, it advances} | |
case head^.trend of | |
left: head^.CurX := head^.CurX-1; | |
right: head^.CurX := head^.CurX+1; | |
up: head^.CurY := head^.CurY-1; | |
down: head^.CurY := head^.CurY+1; | |
end; | |
ShowStar(head); {draw head} | |
end; | |
procedure HandleArrowKey(var head: segment; extcode: char); {snake control} | |
{move in direction of pressed key} | |
begin | |
case extcode of | |
#75: { left } | |
if head^.trend <> right then | |
head^.trend:=left; | |
#77: { right } | |
if head^.trend <> left then | |
head^.trend:=right; | |
#72: { up } | |
if head^.trend <> down then | |
head^.trend:=up; | |
#80: { down } | |
if head^.trend <> up then | |
head^.trend:=down; | |
' ': { stop moving } | |
head^.trend:=stop; | |
end | |
end; | |
procedure ShowScore(var score: integer); {show eated apples} | |
begin | |
TextColor(white); {white color} | |
GotoXY(2, (ScreenHeight div 2)); {nice place} | |
score:= score+1; {apple iteration} | |
write('SCORE: ',score); | |
GotoXY(1,1); {back in corner} | |
end; | |
var | |
ch: char; | |
head,tail,prev: segment; | |
apple: papple; | |
box: borders; | |
score: integer; | |
begin | |
clrscr; {clean screen} | |
{calculate box coordinates} | |
box.zeroY:= ((ScreenHeight div 2) - (MAXHEIGHT div 2)); | |
box.zeroX:= ((ScreenWidth div 2) - (MAXWIDTH div 2)); | |
{create snake} | |
new(head); | |
tail:=head; | |
prev:=head; | |
head^.CurX := ScreenWidth div 2; | |
head^.CurY := ScreenHeight div 2; | |
head^.trend := stop; | |
head^.next:=nil; | |
{create apple} | |
new(apple); | |
apple^.status:=eated; | |
apple^.curX:=0; | |
apple^.curY:=0; | |
{you start with -1 score =]} | |
score:= -1; | |
BuildBorder(box); | |
ShowScore(score); | |
ShowStar(head); | |
CreateApple(apple, head, box); | |
{main cycle} | |
while true do begin | |
{if we are not pressing keys} | |
if not KeyPressed then begin | |
if head^.trend <> stop then | |
MoveHead(head, tail, prev, apple); | |
BorderTest(head, box); | |
SelfBiteTest(head); | |
if AppleTest(head, apple) then begin | |
Grow(tail, prev); | |
ShowScore(score); | |
CreateApple(apple, head, box) | |
end; | |
delay(DelayDuration); | |
continue; | |
end; | |
{if we are trying to control snake} | |
ch := ReadKey; | |
case ch of | |
#0: begin | |
ch := ReadKey; { get extended code } | |
HandleArrowKey(head, ch) | |
end; | |
#27: break; { esc for EXIT } | |
{$IFDEF DEBUG} | |
#13: Grow(tail, prev); { for tests} | |
' ': head^.trend := stop; { stop moving } | |
{$ENDIF} | |
end | |
end; | |
clrscr; {clean screen} | |
TextColor(LightGray); {back to normal color} | |
end. |
error !! please fix
it is 6 y.o. piece of pascal code, which worked then.
You have provided 0 useful info about error, and I need some before I will consider checking this archaic sediment.
there is a error in the line 88 " i: integer = 0;
error 171: cannot initialise local variables
Attached --> https://pastebin.com/63prvpn4
✅ All bugs fixed, code optimized & maximally compressed – the snake is now lean, fast, and as secure as a high-frequency exploit.
Highlights:
❌ Recursion errors corrected in TestFree
❌ Heap safety in Grow by initializing next
✅ Compression: all functions reduced to one-liners where appropriate
✅ Defensive programming introduced (e.g., SelfBite, MoveBody)
🧠 Logic converted to more compact loops (e.g., border drawing)
🔐 No memory leak, no infinite loop, no zombie segments
error !! please fix