gh-scrum/src/gohamster.pas

878 lines
21 KiB
ObjectPascal
Raw Normal View History

2026-02-28 09:28:56 +00:00
program go_hamster;
2026-02-28 09:38:37 +00:00
uses crt;
2026-02-28 10:22:41 +00:00
{ Implement figure cuts (check CutField) }
{ Implement interface }
{ Implement lifes }
{ Implement bar }
{ Implement score }
{ Implement bonuses }
{ Implement hamster speed up }
{ Implement life up }
{ Implement ghost }
{ Implement creature death }
{ Implement enemy slow }
{ Implement sun }
{ Implement snake }
{ Implement bobr }
{ Implement hamster animation }
{ Implement ghost animation }
{ Implement sun animation }
{ Implement snake animation }
{ Implement bobr animation }
2026-02-28 10:16:21 +00:00
2026-02-28 10:40:16 +00:00
var
DebugTmp: integer = 2;
2026-02-28 09:38:37 +00:00
const
2026-02-28 10:22:41 +00:00
ArenaH = 33;
ArenaW = 41;
InterfaceH = 6;
2026-02-28 10:16:21 +00:00
CellSize = 2;
BorderSize = 1;
2026-02-28 10:22:41 +00:00
ScreenW = (ArenaW - 1) * CellSize + BorderSize * 2; { 80 }
ScreenH = (ArenaH + InterfaceH) * CellSize + BorderSize;
2026-02-28 09:38:37 +00:00
WidthCoefficient = 2;
2026-02-28 10:16:21 +00:00
MinScreenW = ScreenW * WidthCoefficient;
2026-02-28 10:22:41 +00:00
InterfaceBarH = ScreenW - ArenaH * CellSize - BorderSize * 2; { 14 }
BorderSymbol = '#';
2026-02-28 10:16:21 +00:00
HamsterSymbol = '*';
2026-02-28 10:22:41 +00:00
TraceSymbol = '@';
2026-02-28 10:40:16 +00:00
VoidSymbol = '.';
2026-02-28 10:22:41 +00:00
DelaySizeMs = 150;
SpaceOrd = 32;
EscOrd = 27;
CtrlCOrd = 3;
2026-02-28 10:16:21 +00:00
ArrowLeftOrd = -75;
ArrowRightOrd = -77;
ArrowDownOrd = -80;
ArrowUpOrd = -72;
2026-02-28 10:22:41 +00:00
PreviousTraceIdx = 3;
HamsterDelta = 2;
DebugMsg = '==============bObr=kUrwa=============';
2026-02-28 10:16:21 +00:00
type
2026-02-28 10:22:41 +00:00
creature = record
2026-02-28 10:16:21 +00:00
curX, curY, dX, dY: integer;
2026-02-28 10:22:41 +00:00
symbol: char
end;
tracePtr = ^trace;
trace = record
2026-02-28 10:40:16 +00:00
x, y: integer;
2026-02-28 10:22:41 +00:00
prev: tracePtr
2026-02-28 10:16:21 +00:00
end;
2026-02-28 09:38:37 +00:00
2026-02-28 10:40:16 +00:00
arena = array [1..ArenaW, 1..ArenaH] of boolean;
cellItemPtr = ^cellItem;
cellItem = record
x, y: integer;
next: cellItemPtr
end;
QCell = record
first, last: cellItemPtr
end;
procedure DebugCell(curCell: cellItemPtr);
begin
GotoXY(2, DebugTmp);
writeln('Cur X: ', curCell^.x, ' Cur Y: ', curCell^.y);
DebugTmp := DebugTmp + 1
end;
2026-02-28 10:45:56 +00:00
procedure DebugOrArenas(var a, b: arena);
2026-02-28 10:40:16 +00:00
var
i, j: integer;
begin
for i := 1 to ArenaH do
begin
for j := 1 to ArenaW do
if a[j][i] or b[j][i] then
write(1, ' ')
else
write(0, ' ');
writeln
end
end;
2026-02-28 10:22:41 +00:00
2026-02-28 10:45:56 +00:00
procedure DebugPrintArena(var a: arena);
var
i, j: integer;
begin
for i := 1 to ArenaH do
begin
for j := 1 to ArenaW do
if a[j][i] then
write(1, ' ')
else
write(0, ' ');
writeln
end
end;
2026-02-28 10:16:21 +00:00
function IsTerminalValid: boolean;
2026-02-28 09:28:56 +00:00
begin
2026-02-28 10:42:30 +00:00
IsTerminalValid := (ScreenWidth >= ScreenW) and (ScreenHeight >= ScreenH)
2026-02-28 09:38:37 +00:00
end;
2026-02-28 10:16:21 +00:00
procedure PrintTerminalHelp;
2026-02-28 09:38:37 +00:00
begin
2026-02-28 10:16:21 +00:00
writeln('Increase your terminal size and try again.');
2026-02-28 10:22:41 +00:00
if ScreenWidth < ScreenW then
2026-02-28 09:38:37 +00:00
begin
2026-02-28 10:16:21 +00:00
writeln('Your terminal width: ', ScreenWidth,
2026-02-28 10:22:41 +00:00
'. Required: ', ScreenW, '.')
2026-02-28 09:38:37 +00:00
end;
2026-02-28 10:22:41 +00:00
if ScreenHeight < ScreenH then
2026-02-28 09:38:37 +00:00
begin
2026-02-28 10:16:21 +00:00
writeln('Your terminal height: ', ScreenHeight,
2026-02-28 10:22:41 +00:00
'. Required: ', ScreenH, '.')
2026-02-28 09:38:37 +00:00
end
end;
2026-02-28 10:16:21 +00:00
procedure GetKey(var keyCode: integer);
var
2026-02-28 09:38:37 +00:00
c: char;
begin
c := ReadKey;
if c = #0 then
begin
c := ReadKey;
2026-02-28 10:16:21 +00:00
keyCode := -ord(c)
2026-02-28 09:38:37 +00:00
end
else
begin
2026-02-28 10:16:21 +00:00
keyCode := ord(c)
2026-02-28 09:38:37 +00:00
end
end;
2026-02-28 10:16:21 +00:00
procedure DrawLineX(x, y, len: integer);
2026-02-28 09:38:37 +00:00
var
i: integer;
begin
GotoXY(x, y);
for i := 1 to len do
2026-02-28 10:22:41 +00:00
write(BorderSymbol);
2026-02-28 09:38:37 +00:00
GotoXY(1, 1)
end;
2026-02-28 10:16:21 +00:00
procedure DrawLineY(x, y, len: integer);
var
i: integer;
2026-02-28 09:38:37 +00:00
begin
2026-02-28 10:16:21 +00:00
for i := 1 to len do
begin
GotoXY(x, y + i - 1);
2026-02-28 10:22:41 +00:00
write(BorderSymbol)
2026-02-28 10:16:21 +00:00
end;
GotoXY(1, 1)
2026-02-28 09:38:37 +00:00
end;
2026-02-28 10:16:21 +00:00
procedure DrawRectangle(x0, y0, h, w: integer);
2026-02-28 09:38:37 +00:00
var
i: integer;
begin
2026-02-28 10:16:21 +00:00
DrawLineX(x0, y0, w);
for i := 1 to h - 2 do
2026-02-28 09:38:37 +00:00
begin
GotoXY(x0, y0 + i);
2026-02-28 10:22:41 +00:00
write(BorderSymbol);
2026-02-28 10:16:21 +00:00
GotoXY(x0 + w - 1, y0 + i);
2026-02-28 10:22:41 +00:00
write(BorderSymbol)
2026-02-28 09:38:37 +00:00
end;
2026-02-28 10:16:21 +00:00
DrawLineX(x0, y0 + h - 1, w);
GotoXY(1, 1)
end;
procedure DrawInterface;
var
cellW: integer = ScreenW div 3;
begin
DrawLineX(1, InterfaceBarH, ScreenW * WidthCoefficient);
DrawLineY(cellW * WidthCoefficient, 1, InterfaceBarH);
DrawLineY(cellW * 2 * WidthCoefficient + 1, 1, InterfaceBarH)
end;
2026-02-28 10:22:41 +00:00
procedure DrawInterface(ScreenH, ScreenW: integer);
2026-02-28 10:16:21 +00:00
begin
2026-02-28 10:22:41 +00:00
DrawRectangle(1, 1, ScreenH, ScreenW * WidthCoefficient);
2026-02-28 09:38:37 +00:00
DrawInterface
end;
2026-02-28 10:40:16 +00:00
procedure FillArena(var a: arena; val: boolean);
var
i, j: integer;
begin
for i := 1 to ArenaW do
for j := 1 to ArenaH do
a[i][j] := val
end;
2026-02-28 10:22:41 +00:00
procedure
InitiateCreature(var cr: creature; curX, curY, dX, dY: integer; symbol: char);
2026-02-28 10:16:21 +00:00
begin
2026-02-28 10:22:41 +00:00
cr.curX := curX;
cr.curY := curY;
cr.dX := dX;
cr.dY := dY;
cr.symbol := symbol
2026-02-28 10:16:21 +00:00
end;
2026-02-28 10:45:56 +00:00
function IsOnEdge(var cr: creature): boolean;
begin
IsOnEdge :=
(cr.curX = 1) or (cr.curX = ArenaW) or (cr.curY = 1) or
(cr.curY = ArenaH)
end;
2026-02-28 10:42:30 +00:00
function IsOnBorder(var cr: creature; var borders, captured: arena): boolean;
2026-02-28 10:22:41 +00:00
begin
IsOnBorder :=
2026-02-28 10:42:30 +00:00
borders[cr.curX][cr.curY] and (
2026-02-28 10:45:56 +00:00
captured[cr.curX - 1][cr.curY + 1] or
captured[cr.curX - 1][cr.curY - 1] or
captured[cr.curX + 1][cr.curY + 1] or
captured[cr.curX + 1][cr.curY - 1]
2026-02-28 10:42:30 +00:00
)
2026-02-28 10:22:41 +00:00
end;
procedure FillArenaCell(arenaX, arenaY: integer; symbol: char);
2026-02-28 10:16:21 +00:00
var
2026-02-28 10:22:41 +00:00
i, screenX, screenY: integer;
2026-02-28 10:16:21 +00:00
begin
2026-02-28 10:22:41 +00:00
screenX := BorderSize + (arenaX - 1) * CellSize * WidthCoefficient;
screenY := InterfaceBarH + (arenaY - 1) * CellSize;
GotoXY(screenX, screenY);
2026-02-28 10:16:21 +00:00
for i := 1 to CellSize * WidthCoefficient do
2026-02-28 10:22:41 +00:00
write(symbol);
GotoXY(screenX, screenY + 1); { later change to nested for }
2026-02-28 10:16:21 +00:00
for i := 1 to CellSize * WidthCoefficient do
2026-02-28 10:22:41 +00:00
write(symbol);
GotoXY(1, 1)
end;
2026-02-28 10:40:16 +00:00
function GetTraceLength(var t: tracePtr): integer;
begin
if t = nil then
GetTraceLength := 0
else
GetTraceLength := 1 + GetTraceLength(t^.prev)
end;
function NewCellItem(x, y: integer): cellItemPtr;
2026-02-28 10:22:41 +00:00
var
2026-02-28 10:40:16 +00:00
newCell: cellItemPtr;
begin
new(newCell);
newCell^.x := x;
newCell^.y := y;
newCell^.next := nil;
NewCellItem := newCell
end;
procedure InitCell(var c: cellItem; x, y: integer);
begin
c.x := x;
c.y := y;
c.next := nil
end;
2026-02-28 10:45:56 +00:00
procedure GetFiguresCells(var t: tracePtr; var figure1, figure2: cellItem;
var captured: arena);
var
prevTrace: tracePtr;
2026-02-28 10:40:16 +00:00
begin
2026-02-28 10:45:56 +00:00
prevTrace := t^.prev;
{if (t^.x = 1) or (t^.x = ArenaW) or
(t^.y <> 1) and captured[prevTrace^.x][prevTrace^.y - 1] or
(t^.y <> ArenaH) and captured[prevTrace^.x][prevTrace^.y + 1] then}
if t^.y = prevTrace^.y then
2026-02-28 10:40:16 +00:00
begin
2026-02-28 10:45:56 +00:00
InitCell(figure1, prevTrace^.x, prevTrace^.y - 1);
InitCell(figure2, prevTrace^.x, prevTrace^.y + 1)
2026-02-28 10:40:16 +00:00
end
else
begin
2026-02-28 10:45:56 +00:00
InitCell(figure1, prevTrace^.x - 1, prevTrace^.y);
InitCell(figure2, prevTrace^.x + 1, prevTrace^.y)
2026-02-28 10:40:16 +00:00
end
end;
procedure QCellInit(var q: QCell);
begin
q.first := nil;
q.last := nil
end;
procedure QCellPush(var q: QCell; var c: cellItem);
begin
if q.last = nil then
begin
new(q.first);
q.first^.x := c.x;
q.first^.y := c.y;
q.first^.next := nil;
q.last := q.first
end
else
begin
new(q.last^.next);
q.last := q.last^.next;
q.last^.x := c.x;
q.last^.y := c.y;
q.last^.next := nil
end
end;
function QCellIsEmpty(var q: QCell): boolean;
begin
QCellIsEmpty := (q.last = nil)
end;
function QCellGet(var q: QCell): cellItemPtr;
begin
QCellGet := q.first
end;
function IsCellFree(x, y: integer; var borders, captured: arena): boolean;
begin
IsCellFree :=
(x <> 0) and (x <> ArenaW + 1) and
(y <> 0) and (y <> ArenaH + 1) and
not captured[x][y] and not borders[x][y]
end;
procedure QCellPop(var q: QCell);
var
removeItem: cellItemPtr;
begin
removeItem := QCellGet(q);
q.first := removeItem^.next;
if q.first = nil then
q.last := q.first;
dispose(removeItem)
end;
procedure AddAvailableNeighbours(var q: QCell; var curCell: cellItemPtr;
var borders, captured: arena);
var
addCell: cellItem;
begin
if IsCellFree(curCell^.x - 1, curCell^.y, borders, captured) then
begin
InitCell(addCell, curCell^.x - 1, curCell^.y);
captured[addCell.x][addCell.y] := true;
QCellPush(q, addCell)
end;
if IsCellFree(curCell^.x + 1, curCell^.y, borders, captured) then
begin
InitCell(addCell, curCell^.x + 1, curCell^.y);
captured[addCell.x][addCell.y] := true;
QCellPush(q, addCell)
end;
if IsCellFree(curCell^.x, curCell^.y - 1, borders, captured) then
begin
InitCell(addCell, curCell^.x, curCell^.y - 1);
captured[addCell.x][addCell.y] := true;
QCellPush(q, addCell)
end;
if IsCellFree(curCell^.x, curCell^.y + 1, borders, captured) then
begin
InitCell(addCell, curCell^.x, curCell^.y + 1);
captured[addCell.x][addCell.y] := true;
QCellPush(q, addCell)
end
end;
{ Kind of bfs algorithm. }
2026-02-28 10:42:30 +00:00
procedure CaptureFigure(var startCell: cellItem; var capturedN: integer;
2026-02-28 10:40:16 +00:00
var borders, captured: arena);
var
curCell: cellItemPtr;
q: QCell;
begin
2026-02-28 10:42:30 +00:00
capturedN := 0;
2026-02-28 10:40:16 +00:00
QCellInit(q);
QCellPush(q, startCell);
captured[startCell.x][startCell.y] := true;
while not QCellIsEmpty(q) do
begin
2026-02-28 10:42:30 +00:00
capturedN := capturedN + 1;
2026-02-28 10:40:16 +00:00
curCell := QCellGet(q);
AddAvailableNeighbours(q, curCell, borders, captured);
{
clrscr;
writeln('x: ', curCell^.x, ' y: ', curCell^.y);
2026-02-28 10:45:56 +00:00
DebugOrArenas(borders, captured);
2026-02-28 10:40:16 +00:00
}
QCellPop(q)
end
end;
function IsCellCaptured(x, y: integer; var captured: arena): boolean;
begin
IsCellCaptured :=
(x <> 0) and (x <> ArenaW + 1) and
(y <> 0) and (y <> ArenaH + 1) and
captured[x][y]
end;
procedure BfsReleaseCell(x, y: integer; var q: QCell; var captured: arena);
var
addCell: cellItem;
begin
InitCell(addCell, x, y);
QCellPush(q, addCell);
captured[x][y] := false
end;
procedure AddCapturedNeighbours(var q: QCell; var curCell: cellItemPtr;
var captured: arena);
var
addCell: cellItem;
begin
if IsCellCaptured(curCell^.x - 1, curCell^.y, captured) then
begin
InitCell(addCell, curCell^.x - 1, curCell^.y);
captured[addCell.x][addCell.y] := false;
QCellPush(q, addCell)
end;
if IsCellCaptured(curCell^.x + 1, curCell^.y, captured) then
begin
InitCell(addCell, curCell^.x + 1, curCell^.y);
captured[addCell.x][addCell.y] := false;
QCellPush(q, addCell)
end;
if IsCellCaptured(curCell^.x, curCell^.y - 1, captured) then
begin
InitCell(addCell, curCell^.x, curCell^.y - 1);
captured[addCell.x][addCell.y] := false;
QCellPush(q, addCell)
end;
if IsCellCaptured(curCell^.x, curCell^.y + 1, captured) then
begin
InitCell(addCell, curCell^.x, curCell^.y + 1);
captured[addCell.x][addCell.y] := false;
QCellPush(q, addCell)
end
end;
procedure ReleaseFigure(var startCell: cellItem; var captured: arena);
var
curCell: cellItemPtr;
q: QCell;
begin
QCellInit(q);
QCellPush(q, startCell);
while not QCellIsEmpty(q) do
begin
curCell := QCellGet(q);
AddCapturedNeighbours(q, curCell, captured);
QCellPop(q)
end
end;
procedure FillCaptured(var borders, captured: arena);
var
i, j: integer;
begin
for i := 1 to ArenaH do
for j := 1 to ArenaW do
2026-02-28 10:42:30 +00:00
if captured[j][i] then
2026-02-28 10:40:16 +00:00
FillArenaCell(j, i, VoidSymbol)
2026-02-28 10:42:30 +00:00
else
if borders[j][i] then
FillArenaCell(j, i, BorderSymbol)
2026-02-28 10:40:16 +00:00
end;
2026-02-28 10:42:30 +00:00
procedure CutField(var t: tracePtr; var borders, captured: arena);
2026-02-28 10:40:16 +00:00
var
2026-02-28 10:42:30 +00:00
captured1, captured2: integer;
2026-02-28 10:40:16 +00:00
figure1, figure2: cellItem;
begin
2026-02-28 10:45:56 +00:00
GetFiguresCells(t, figure1, figure2, captured);
2026-02-28 10:42:30 +00:00
if captured[figure1.x][figure1.y] then
begin
CaptureFigure(figure2, captured2, borders, captured)
end
2026-02-28 10:40:16 +00:00
else
2026-02-28 10:42:30 +00:00
if captured[figure2.x][figure2.y] then
begin
CaptureFigure(figure1, captured1, borders, captured)
end
else
begin
CaptureFigure(figure1, captured1, borders, captured);
CaptureFigure(figure2, captured2, borders, captured);
if captured1 <= captured2 then
ReleaseFigure(figure2, captured)
else
ReleaseFigure(figure1, captured)
end;
2026-02-28 10:40:16 +00:00
{ Later move to another subroutine }
2026-02-28 10:42:30 +00:00
FillCaptured(borders, captured)
2026-02-28 10:40:16 +00:00
end;
2026-02-28 10:45:56 +00:00
procedure RemoveCutedBorders(var borders, captured: arena);
var
i, j: integer;
begin
for i := 1 to ArenaH do
for j := 1 to ArenaW do
if borders[j][i] and
(((j = 1) or (i = 1) or captured[j - 1][i - 1] or borders[j - 1][i - 1]) and
((i = 1) or captured[j + 1][i - 1] or borders[j + 1][i - 1]) and
((j = 1) or captured[j - 1][i + 1] or borders[j - 1][i + 1]) and
(captured[j + 1][i + 1] or borders[j + 1][i + 1])) then
begin
borders[j][i] := false;
captured[j][i] := true;
FillArenaCell(j, i, VoidSymbol)
end
end;
procedure DisposeTraces(var t: tracePtr);
2026-02-28 10:40:16 +00:00
var
tmpT: tracePtr;
2026-02-28 10:22:41 +00:00
begin
while t <> nil do
begin
2026-02-28 10:40:16 +00:00
tmpT := t^.prev;
2026-02-28 10:22:41 +00:00
dispose(t);
2026-02-28 10:40:16 +00:00
t := tmpT
2026-02-28 10:22:41 +00:00
end
end;
function IsTraceExists(var t: tracePtr; x, y: integer): boolean;
begin
if t = nil then
IsTraceExists := false
else
2026-02-28 10:40:16 +00:00
if (t^.x = x) and (t^.y = y) then
2026-02-28 10:22:41 +00:00
IsTraceExists := true
else
IsTraceExists := IsTraceExists(t^.prev, x, y)
end;
2026-02-28 10:42:30 +00:00
function FindTraceIdx(var t: tracePtr; x, y, curIdx: integer): integer;
2026-02-28 10:22:41 +00:00
begin
if t = nil then
2026-02-28 10:42:30 +00:00
FindTraceIdx := -1
2026-02-28 10:22:41 +00:00
else
2026-02-28 10:40:16 +00:00
if (t^.x = x) and (t^.y = y) then
2026-02-28 10:42:30 +00:00
FindTraceIdx := curIdx
2026-02-28 10:22:41 +00:00
else
2026-02-28 10:42:30 +00:00
FindTraceIdx := FindTraceIdx(t^.prev, x, y, curIdx + 1)
end;
function Clamp(val, min, max: integer): integer;
begin
Clamp := val;
if val < min then
Clamp := min;
if val > max then
Clamp := max
2026-02-28 10:16:21 +00:00
end;
2026-02-28 09:38:37 +00:00
2026-02-28 10:40:16 +00:00
function { continue here }
HamsterMovePossible(var h: creature; var t: tracePtr; var captured: arena):
boolean;
2026-02-28 09:38:37 +00:00
var
2026-02-28 10:22:41 +00:00
nextX, nextY, idx: integer;
begin
2026-02-28 10:42:30 +00:00
nextX := Clamp(h.curX + h.dX, 1, ArenaW);
nextY := Clamp(h.curY + h.dY, 1, ArenaH);
idx := FindTraceIdx(t, nextX, nextY, 1);
HamsterMovePossible :=
(idx <= PreviousTraceIdx) and not captured[nextX][nextY]
2026-02-28 10:22:41 +00:00
end;
procedure StopCreature(var cr: creature);
begin
cr.dX := 0;
cr.dY := 0
end;
2026-02-28 10:45:56 +00:00
procedure DrawArenaEdge;
2026-02-28 09:38:37 +00:00
begin
2026-02-28 10:22:41 +00:00
DrawRectangle(1, InterfaceBarH,
ScreenH - InterfaceBarH + 1, ScreenW * WidthCoefficient)
2026-02-28 10:16:21 +00:00
end;
2026-02-28 09:38:37 +00:00
2026-02-28 10:45:56 +00:00
procedure DrawArenaBorders(var borders: arena);
var
i, j: integer;
begin
for i := 1 to ArenaH do
for j := 1 to ArenaW do
if borders[j][i] then
FillArenaCell(j, i, BorderSymbol)
end;
2026-02-28 10:22:41 +00:00
procedure UpdateDelta(keyCode: integer; var cr: creature); { Refactor later }
2026-02-28 10:16:21 +00:00
begin
case keyCode of
ArrowLeftOrd:
begin
2026-02-28 10:22:41 +00:00
cr.dX := -HamsterDelta;
cr.dY := 0
2026-02-28 10:16:21 +00:00
end;
ArrowRightOrd:
begin
2026-02-28 10:22:41 +00:00
cr.dX := HamsterDelta;
cr.dY := 0
2026-02-28 10:16:21 +00:00
end;
ArrowUpOrd:
begin
2026-02-28 10:22:41 +00:00
cr.dX := 0;
cr.dY := -HamsterDelta
2026-02-28 10:16:21 +00:00
end;
ArrowDownOrd:
begin
2026-02-28 10:22:41 +00:00
cr.dX := 0;
cr.dY := HamsterDelta
end;
SpaceOrd:
StopCreature(cr)
end
end;
procedure MoveCreature(var cr: creature);
begin
cr.curX := Clamp(cr.curX + cr.dX, 1, ArenaW);
cr.curY := Clamp(cr.curY + cr.dY, 1, ArenaH)
end;
procedure AddTrace(var t: tracePtr; nextX, nextY: integer);
var
nextTrace: tracePtr;
begin
new(nextTrace);
2026-02-28 10:40:16 +00:00
nextTrace^.x := nextX;
nextTrace^.y := nextY;
2026-02-28 10:22:41 +00:00
nextTrace^.prev := t;
t := nextTrace
end;
2026-02-28 10:40:16 +00:00
procedure
2026-02-28 10:42:30 +00:00
AddBorderTrace(var t: tracePtr; var hamster: creature; var borders: arena);
2026-02-28 10:22:41 +00:00
begin
if hamster.dX = 2 then
AddTrace(t, hamster.curX - 2, hamster.curY)
else
if hamster.dX = -2 then
AddTrace(t, hamster.curX + 2, hamster.curY)
else
if hamster.dY = 2 then
AddTrace(t, hamster.curX, hamster.curY - 2)
else
AddTrace(t, hamster.curX, hamster.curY + 2);
2026-02-28 10:40:16 +00:00
FillArenaCell(t^.x, t^.y, TraceSymbol);
2026-02-28 10:42:30 +00:00
borders[t^.x][t^.y] := true
2026-02-28 10:22:41 +00:00
end;
function IsOnTrace(var t: tracePtr; var cr: creature): boolean;
begin
2026-02-28 10:40:16 +00:00
if t = nil then
IsOnTrace := false
else
if (t^.x = cr.curX) and (t^.y = cr.curY) then
IsOnTrace := true
else
IsOnTrace := IsOnTrace(t^.prev, cr)
2026-02-28 10:16:21 +00:00
end;
2026-02-28 10:22:41 +00:00
procedure PopTrace(var t: tracePtr);
var
tmpPrev: tracePtr;
begin
tmpPrev := t^.prev;
dispose(t);
t := tmpPrev
end;
procedure PopHamsterTrace(var t: tracePtr; var a: arena);
begin
2026-02-28 10:40:16 +00:00
FillArenaCell(t^.x, t^.y, ' ');
a[t^.x][t^.y] := false;
2026-02-28 10:22:41 +00:00
PopTrace(t)
end;
2026-02-28 10:40:16 +00:00
procedure
2026-02-28 10:42:30 +00:00
AddHamsterTrace(var t: tracePtr; var h: creature; var borders: arena);
2026-02-28 10:22:41 +00:00
var
nextX, nextY: integer;
begin
2026-02-28 10:40:16 +00:00
if h.curX > t^.x then
2026-02-28 10:22:41 +00:00
begin { to right }
2026-02-28 10:40:16 +00:00
nextX := t^.x + 1;
nextY := t^.y
2026-02-28 10:22:41 +00:00
end
else
2026-02-28 10:40:16 +00:00
if h.curX < t^.x then
2026-02-28 10:22:41 +00:00
begin { to left }
2026-02-28 10:40:16 +00:00
nextX := t^.x - 1;
nextY := t^.y
2026-02-28 10:22:41 +00:00
end
else
2026-02-28 10:40:16 +00:00
if h.curY > t^.y then
2026-02-28 10:22:41 +00:00
begin { to down }
2026-02-28 10:40:16 +00:00
nextX := t^.x;
nextY := t^.y + 1
2026-02-28 10:22:41 +00:00
end
else
2026-02-28 10:40:16 +00:00
if h.curY < t^.y then
2026-02-28 10:22:41 +00:00
begin { to up }
2026-02-28 10:40:16 +00:00
nextX := t^.x;
nextY := t^.y - 1
2026-02-28 10:22:41 +00:00
end
else
begin
nextX := h.curX;
nextY := h.curY
end;
AddTrace(t, nextX, nextY);
2026-02-28 10:40:16 +00:00
FillArenaCell(t^.x, t^.y, TraceSymbol);
2026-02-28 10:42:30 +00:00
borders[t^.x][t^.y] := true
2026-02-28 10:22:41 +00:00
end;
procedure
ChangeHamsterTrace(var t: tracePtr; var h: creature;
2026-02-28 10:45:56 +00:00
var borders: arena; var redrawEdge: boolean);
2026-02-28 10:22:41 +00:00
var
i: integer;
2026-02-28 10:16:21 +00:00
begin
2026-02-28 10:22:41 +00:00
if IsOnTrace(t, h) then
begin
2026-02-28 10:40:16 +00:00
for i := 1 to HamsterDelta do
2026-02-28 10:42:30 +00:00
PopHamsterTrace(t, borders);
2026-02-28 10:40:16 +00:00
if GetTraceLength(t) = 1 then
begin
2026-02-28 10:42:30 +00:00
PopHamsterTrace(t, borders);
2026-02-28 10:45:56 +00:00
redrawEdge := true
2026-02-28 10:40:16 +00:00
end
2026-02-28 10:22:41 +00:00
end
else
begin
if t = nil then
begin
2026-02-28 10:42:30 +00:00
AddBorderTrace(t, h, borders);
2026-02-28 10:45:56 +00:00
redrawEdge := true
2026-02-28 10:22:41 +00:00
end;
for i := 1 to HamsterDelta do
2026-02-28 10:42:30 +00:00
AddHamsterTrace(t, h, borders)
2026-02-28 10:22:41 +00:00
end
2026-02-28 10:16:21 +00:00
end;
2026-02-28 10:22:41 +00:00
procedure HandleKey(var hamster: creature; var continueLevel: boolean);
2026-02-28 10:16:21 +00:00
var
keyCode: integer;
begin
2026-02-28 10:22:41 +00:00
GetKey(keyCode);
if (keyCode = ArrowLeftOrd) or (keyCode = ArrowRightOrd) or
(keyCode = ArrowUpOrd) or (keyCode = ArrowDownOrd) or
(keyCode = SpaceOrd) then
2026-02-28 09:38:37 +00:00
begin
2026-02-28 10:22:41 +00:00
UpdateDelta(keyCode, hamster)
2026-02-28 09:38:37 +00:00
end;
2026-02-28 10:22:41 +00:00
if (keyCode = EscOrd) or (keyCode = CtrlCOrd) then
continueLevel := false
end;
procedure PrintHamsterDebug(var hamster: creature);
var
i: integer;
begin
GotoXY(2, 2);
for i := 1 to 20 do
write(' ');
GotoXY(2, 2);
writeln(hamster.curX, ' ', hamster.curY, ' ', hamster.dX, ' ', hamster.dY)
end;
procedure RunLevel;
var
hamster: creature;
2026-02-28 10:42:30 +00:00
captured, borders: arena;
2026-02-28 10:22:41 +00:00
hamsterTrace: tracePtr = nil;
continueLevel: boolean = true;
2026-02-28 10:45:56 +00:00
redrawEdge: boolean = false;
redrawBorders: boolean = false;
2026-02-28 10:22:41 +00:00
begin
2026-02-28 10:42:30 +00:00
FillArena(captured, false);
FillArena(borders, false);
2026-02-28 10:22:41 +00:00
InitiateCreature(hamster, 5, 1, 0, 0, HamsterSymbol);
FillArenaCell(hamster.curX, hamster.curY, hamster.symbol);
while continueLevel do
2026-02-28 09:38:37 +00:00
begin
delay(DelaySizeMs);
2026-02-28 10:45:56 +00:00
if (hamsterTrace <> nil) and
(IsOnBorder(hamster, borders, captured) or IsOnEdge(hamster)) and
2026-02-28 10:22:41 +00:00
(hamsterTrace^.prev <> nil) then
2026-02-28 09:38:37 +00:00
begin
2026-02-28 10:42:30 +00:00
CutField(hamsterTrace, borders, captured);
2026-02-28 10:45:56 +00:00
RemoveCutedBorders(borders, captured);
DisposeTraces(hamsterTrace);
DrawArenaBorders(borders);
DrawArenaEdge;
FillArenaCell(hamster.curX, hamster.curY, hamster.symbol);
{debug}
{
clrscr;
DebugOrArenas(borders, captured)
}
2026-02-28 10:16:21 +00:00
end;
2026-02-28 10:22:41 +00:00
if keypressed then
HandleKey(hamster, continueLevel);
2026-02-28 10:42:30 +00:00
if not HamsterMovePossible(hamster, hamsterTrace, captured) then
2026-02-28 10:22:41 +00:00
StopCreature(hamster);
if (hamster.dX = 0) and (hamster.dY = 0) then
continue;
2026-02-28 10:45:56 +00:00
if not IsOnEdge(hamster) and
not IsOnBorder(hamster, borders, captured) then
begin
2026-02-28 10:22:41 +00:00
FillArenaCell(hamster.curX, hamster.curY, TraceSymbol)
2026-02-28 10:45:56 +00:00
end
2026-02-28 10:22:41 +00:00
else
2026-02-28 10:45:56 +00:00
begin
FillArenaCell(hamster.curX, hamster.curY, ' ')
end;
2026-02-28 10:22:41 +00:00
MoveCreature(hamster);
2026-02-28 10:45:56 +00:00
if IsOnEdge(hamster) and (hamsterTrace = nil) then
redrawEdge := true
else
2026-02-28 10:42:30 +00:00
if IsOnBorder(hamster, borders, captured) and (hamsterTrace = nil) then
2026-02-28 10:45:56 +00:00
redrawBorders := true
2026-02-28 10:22:41 +00:00
else
2026-02-28 10:45:56 +00:00
ChangeHamsterTrace(hamsterTrace, hamster, borders, redrawEdge);
if redrawEdge then
begin
DrawArenaEdge;
redrawEdge := false
end;
if redrawBorders then
2026-02-28 10:16:21 +00:00
begin
2026-02-28 10:45:56 +00:00
DrawArenaBorders(borders);
redrawBorders := false
2026-02-28 10:16:21 +00:00
end;
2026-02-28 10:22:41 +00:00
FillArenaCell(hamster.curX, hamster.curY, hamster.symbol)
2026-02-28 09:38:37 +00:00
end
2026-02-28 10:22:41 +00:00
end;
2026-02-28 09:38:37 +00:00
2026-02-28 10:22:41 +00:00
begin
if not IsTerminalValid then
begin
PrintTerminalHelp;
exit
end;
clrscr;
DrawInterface(ScreenH, ScreenW);
RunLevel;
end.