Skip to content

Instantly share code, notes, and snippets.

@hqppyz
Last active March 11, 2026 23:00
Show Gist options
  • Select an option

  • Save hqppyz/e70b0d62badf7c16b2ca8da63bacaf30 to your computer and use it in GitHub Desktop.

Select an option

Save hqppyz/e70b0d62badf7c16b2ca8da63bacaf30 to your computer and use it in GitHub Desktop.
Vibecoded ncurses snake on adalang
with Ncurses;
with Interfaces.C;
with Interfaces.C.Strings;
with Ada.Numerics.Discrete_Random;
with Ada.Strings;
with Ada.Strings.Fixed;
with Ada.Strings.UTF_Encoding.Wide_Wide_Strings;
function Adasnake return Integer is
use Ncurses;
use Interfaces.C;
use Interfaces.C.Strings;
use Ada.Strings;
use Ada.Strings.Fixed;
subtype Index is Integer range 1 .. 4096;
type Point is record
Y : Integer;
X : Integer;
end record;
type Snake_Array is array (Index) of Point;
type Direction is record
Y : Integer;
X : Integer;
end record;
subtype Queue_Index is Integer range 1 .. 64;
type Direction_Queue is array (Queue_Index) of Direction;
type Text_Align is (Align_Left, Align_Center, Align_Right);
type Game_State is (Playing, Paused, Game_Over);
State : Game_State := Playing;
Previous_State : Game_State := Playing;
Turbo_Enabled : Boolean := False;
Turbo_Flash : Natural := 0;
Snake_Initial_Length : constant Positive := 5;
Snake_Body : Snake_Array;
Snake_Length : Integer := Snake_Initial_Length;
Snake_Y : Integer := 0;
Snake_X : Integer := 1;
Snake_Tail_Y : Integer := 0;
Snake_Tail_X : Integer := 1;
Apple : Point;
Apple_Width : constant Positive := 2;
Frame_Ms : constant C_Int := 80;
Turbo_Frame_Ms : constant C_Int := 27;
Input_Queue : Direction_Queue;
Queue_Head : Queue_Index := Queue_Index'First;
Queue_Tail : Queue_Index := Queue_Index'First;
Queue_Count : Natural := 0;
Last_Lines : Integer := 0;
Last_Columns : Integer := 0;
package Rand_Int is new Ada.Numerics.Discrete_Random (Integer);
Random : Rand_Int.Generator;
Margin_Block : constant Integer := 4;
Margin_Inline : constant Integer := 0;
Border_Size : constant Integer := 1;
Color_Canvas : constant C_Int := 0;
Color_Primary : constant C_Int := 5;
Color_Secondary : constant C_Int := 6;
Pair_Primary_Id : constant C_Int := 1;
Pair_Secondary_Id : constant C_Int := 2;
Pair_Primary : constant C_Int := Pair_Primary_Id * 256;
Pair_Secondary : constant C_Int := Pair_Secondary_Id * 256;
Key_Up : constant C_Int := 259;
Key_Down : constant C_Int := 258;
Key_Left : constant C_Int := 260;
Key_Right : constant C_Int := 261;
Key_Resize : constant C_Int := 410;
Key_None : constant C_Int := -1;
Locale_All : constant C_Int := 6;
function To_UTF8 (Glyph : Wide_Wide_String) return String is
begin
return UTF_Encoding.Wide_Wide_Strings.Encode (Glyph);
end To_UTF8;
Utf8_Up : constant String := To_UTF8 ("↑");
Utf8_Down : constant String := To_UTF8 ("↓");
Utf8_Left : constant String := To_UTF8 ("←");
Utf8_Right : constant String := To_UTF8 ("→");
Utf8_Box_TL : constant String := To_UTF8 ("┏");
Utf8_Box_TR : constant String := To_UTF8 ("┓");
Utf8_Box_BL : constant String := To_UTF8 ("┗");
Utf8_Box_BR : constant String := To_UTF8 ("┛");
Utf8_Box_H : constant String := To_UTF8 ("━");
Utf8_Box_V : constant Stwith Interfaces.C;
with Interfaces.C.Strings;
with System;
package Ncurses is
pragma Linker_Options ("-lncursesw");
pragma Linker_Options ("-ltinfo");
subtype C_Int is Interfaces.C.int;
subtype C_Chars_Ptr is Interfaces.C.Strings.chars_ptr;
procedure initscr
with Import, Convention => C, External_Name => "initscr";
procedure endwin
with Import, Convention => C, External_Name => "endwin";
procedure refresh
with Import, Convention => C, External_Name => "refresh";
procedure clear
with Import, Convention => C, External_Name => "clear";
procedure noecho
with Import, Convention => C, External_Name => "noecho";
procedure start_color
with Import, Convention => C, External_Name => "start_color";
procedure init_pair (Pair, Foreground, Background : C_Int)
with Import, Convention => C, External_Name => "init_pair";
procedure attrset (Attrs : C_Int)
with Import, Convention => C, External_Name => "attrset";
procedure bkgd (Ch : C_Int)
with Import, Convention => C, External_Name => "bkgd";
procedure curs_set (Visibility : C_Int)
with Import, Convention => C, External_Name => "curs_set";
procedure timeout (Millis : C_Int)
with Import, Convention => C, External_Name => "timeout";
function setlocale (Category : C_Int; Locale : C_Chars_Ptr) return C_Chars_Ptr
with Import, Convention => C, External_Name => "setlocale";
function keypad (Win : System.Address; Bf : C_Int) return C_Int
with Import, Convention => C, External_Name => "keypad";
function getch return C_Int
with Import, Convention => C, External_Name => "getch";
procedure napms (Ms : C_Int)
with Import, Convention => C, External_Name => "napms";
procedure move (Y : C_Int; X : C_Int)
with Import, Convention => C, External_Name => "move";
procedure addch (Ch : C_Int)
with Import, Convention => C, External_Name => "addch";
function addstr (Str : C_Chars_Ptr) return C_Int
with Import, Convention => C, External_Name => "addstr";
C_Lines : C_Int
with Import, Convention => C, External_Name => "LINES";
function Lines return Integer is (Integer (C_Lines));
C_Columns : C_Int
with Import, Convention => C, External_Name => "COLS";
function Columns return Integer is (Integer (C_Columns));
Stdscr : System.Address
with Import, Convention => C, External_Name => "stdscr";
end Ncurses;
ring := To_UTF8 ("┃");
Utf8_Snake_H : constant String := To_UTF8 ("─");
Utf8_Snake_V : constant String := To_UTF8 ("│");
Utf8_Snake_UL : constant String := To_UTF8 ("╭");
Utf8_Snake_UR : constant String := To_UTF8 ("╮");
Utf8_Snake_DL : constant String := To_UTF8 ("╰");
Utf8_Snake_DR : constant String := To_UTF8 ("╯");
Utf8_Head_Left : constant String := To_UTF8 ("◀");
Utf8_Head_Right : constant String := To_UTF8 ("▶");
Utf8_Head_Up : constant String := To_UTF8 ("▲");
Utf8_Head_Down : constant String := To_UTF8 ("▼");
Utf8_Apple : constant String := To_UTF8 ("🍎");
function Random_Pos_Y return Integer is
Playfield_Top : constant Integer := Margin_Block + Border_Size;
Playfield_Height : constant Integer :=
Lines - 2 * Margin_Block - 2 * Border_Size;
begin
return Rand_Int.Random (Random) mod Playfield_Height + Playfield_Top;
end;
function Random_Pos_X return Integer is
Playfield_Left : constant Integer := Margin_Inline + Border_Size;
Playfield_Width : constant Integer :=
Columns - 2 * Margin_Inline - 2 * Border_Size;
Spawn_Width : constant Integer :=
Integer'Max (1, Playfield_Width - Apple_Width + 1);
begin
return Rand_Int.Random (Random) mod Spawn_Width + Playfield_Left;
end;
function Wrapped_Delta (Other, Current, Span : Integer) return Integer is
Raw_Diff : constant Integer := Other - Current;
begin
if Span <= 1 or else abs Raw_Diff /= Span - 1 then
return Raw_Diff;
end if;
return -Raw_Diff / (Span - 1);
end;
procedure Draw_Char (Y, X : Integer; Ch : Character) is
begin
move (C_Int (Y), C_Int (X));
addch (C_Int (Character'Pos (Ch)));
end;
procedure Draw_Text
(Y, X : Integer; Text : String; Align : Text_Align := Align_Left)
is
Draw_X : Integer := X;
C_Text : chars_ptr;
Ignore : C_Int;
begin
case Align is
when Align_Left =>
null;
when Align_Center =>
Draw_X := X - Text'Length / 2;
when Align_Right =>
Draw_X := X - Text'Length + 1;
end case;
if Y < 0 or else Y >= Lines or else Draw_X < 0 or else Draw_X >= Columns
then
return;
end if;
move (C_Int (Y), C_Int (Draw_X));
C_Text := New_String (Text);
Ignore := addstr (C_Text);
Free (C_Text);
end;
procedure Clear_Row (Y : Integer) is
begin
if Y < 0 or else Y >= Lines then
return;
end if;
attrset (Pair_Primary);
for X in 0 .. Columns - 1 loop
Draw_Char (Y, X, ' ');
end loop;
end;
procedure Draw_HUD is
Score_Value_Int : constant Integer :=
Snake_Length - Snake_Initial_Length;
Score_Title_Text : constant String :=
"*" & (if State = Game_Over then "GAME OVER" else "SCORE") & "*";
Score_Value_Text : constant String :=
Trim (Integer'Image (Score_Value_Int), Both);
Controls_Line_1_Text : constant String :=
"Use <WASD> <" & Utf8_Up & Utf8_Down & Utf8_Left & Utf8_Right & ">";
Controls_Line_2_Text : constant String := "or <HJKL> to move";
Pause_Status_Text : constant String :=
"Press <SPACE> to " & (if State = Paused then "PLAY" else "PAUSE");
Turbo_Hint_Text : constant String := "Press <X> to TURBO";
Quit_Text : constant String := "Press <Q> to quit";
Horizontal_Margin : constant Integer := 2;
Hud_Center_X : constant Integer := Columns / 2;
Turbo_Hint_Right_X : constant Integer := Columns - 1 - Horizontal_Margin;
Score_Title_Row : constant Integer := 1;
Score_Value_Row : constant Integer := 2;
Controls_Line_1_Row : constant Integer := Lines - 3;
Controls_Line_2_Row : constant Integer := Lines - 2;
Turbo_Hint_Row : constant Integer := Lines - 2;
Pause_Status_Row : constant Integer := Lines - 2;
begin
attrset (Pair_Primary);
Clear_Row (0);
Clear_Row (1);
Clear_Row (2);
Clear_Row (Lines - 3);
Clear_Row (Lines - 2);
Clear_Row (Lines - 1);
Draw_Text
(Score_Title_Row,
Hud_Center_X,
Score_Title_Text,
Align => Align_Center);
Draw_Text
(Score_Value_Row,
Hud_Center_X,
Score_Value_Text,
Align => Align_Center);
if State = Game_Over then
Draw_Text
(Pause_Status_Row, Hud_Center_X, Quit_Text, Align => Align_Center);
else
Draw_Text
(Controls_Line_1_Row, Horizontal_Margin, Controls_Line_1_Text);
Draw_Text
(Controls_Line_2_Row, Horizontal_Margin, Controls_Line_2_Text);
Draw_Text
(Turbo_Hint_Row,
Turbo_Hint_Right_X,
Turbo_Hint_Text,
Align => Align_Right);
Draw_Text
(Pause_Status_Row,
Hud_Center_X,
Pause_Status_Text,
Align => Align_Center);
end if;
end;
procedure Draw_Apple is
begin
attrset (Pair_Primary);
Draw_Text (Apple.Y, Apple.X, Utf8_Apple);
end;
procedure Spawn_Apple is
begin
Apple.Y := Random_Pos_Y;
Apple.X := Random_Pos_X;
end;
procedure Draw_Border;
procedure Clear_Playfield;
procedure Redraw is
begin
clear;
Draw_Border;
Clear_Playfield;
Draw_HUD;
refresh;
end Redraw;
procedure Clamp_To_Playfield (P : in out Point) is
Playfield_Top : constant Integer := Margin_Block + Border_Size;
Playfield_Left : constant Integer := Margin_Inline + Border_Size;
Playfield_Bottom : constant Integer :=
Lines - 1 - Margin_Block - Border_Size;
Playfield_Right : constant Integer :=
Columns - 1 - Margin_Inline - Border_Size;
Playfield_Height : constant Integer :=
Playfield_Bottom - Playfield_Top + 1;
Playfield_Width : constant Integer :=
Playfield_Right - Playfield_Left + 1;
begin
if Playfield_Height <= 0 or else Playfield_Width <= 0 then
return;
end if;
if P.Y < Playfield_Top then
P.Y := Playfield_Top;
elsif P.Y > Playfield_Bottom then
P.Y := Playfield_Bottom;
end if;
if P.X < Playfield_Left then
P.X := Playfield_Left;
elsif P.X > Playfield_Right then
P.X := Playfield_Right;
end if;
end;
procedure Resize is
Playfield_Height : constant Integer :=
Lines - 2 * Margin_Block - 2 * Border_Size;
Playfield_Width : constant Integer :=
Columns - 2 * Margin_Inline - 2 * Border_Size;
begin
if Lines = Last_Lines and then Columns = Last_Columns then
return;
end if;
Last_Lines := Lines;
Last_Columns := Columns;
if Playfield_Height <= 0 or else Playfield_Width <= 0 then
return;
end if;
for I in 1 .. Snake_Length loop
Clamp_To_Playfield (Snake_Body (I));
end loop;
Clamp_To_Playfield (Apple);
Redraw;
end;
function Apple_Contains (P : Point) return Boolean is
begin
return
P.Y = Apple.Y
and then P.X >= Apple.X
and then P.X < Apple.X + Apple_Width;
end;
procedure Draw_Border is
Top_Row : constant Integer := Margin_Block;
Bottom_Row : constant Integer := Lines - 1 - Margin_Block;
Left_Col : constant Integer := Margin_Inline;
Right_Col : constant Integer := Columns - 1 - Margin_Inline;
begin
attrset (Pair_Secondary);
Draw_Text (Top_Row, Left_Col, Utf8_Box_TL);
Draw_Text (Top_Row, Right_Col, Utf8_Box_TR);
Draw_Text (Bottom_Row, Left_Col, Utf8_Box_BL);
Draw_Text (Bottom_Row, Right_Col, Utf8_Box_BR);
for X in Left_Col + 1 .. Right_Col - 1 loop
Draw_Text (Top_Row, X, Utf8_Box_H);
Draw_Text (Bottom_Row, X, Utf8_Box_H);
end loop;
for Y in Top_Row + 1 .. Bottom_Row - 1 loop
Draw_Text (Y, Left_Col, Utf8_Box_V);
Draw_Text (Y, Right_Col, Utf8_Box_V);
end loop;
end;
procedure Clear_Playfield is
Playfield_Top : constant Integer := Margin_Block + Border_Size;
Playfield_Left : constant Integer := Margin_Inline + Border_Size;
Playfield_Bottom : constant Integer :=
Lines - 1 - Margin_Block - Border_Size;
Playfield_Right : constant Integer :=
Columns - 1 - Margin_Inline - Border_Size;
begin
attrset (Pair_Primary);
for Y in Playfield_Top .. Playfield_Bottom loop
for X in Playfield_Left .. Playfield_Right loop
Draw_Char (Y, X, ' ');
end loop;
end loop;
end;
procedure Init_Snake is
Playfield_Top : constant Integer := Margin_Block + Border_Size;
Playfield_Left : constant Integer := Margin_Inline + Border_Size;
Playfield_Bottom : constant Integer :=
Lines - 1 - Margin_Block - Border_Size;
Playfield_Right : constant Integer :=
Columns - 1 - Margin_Inline - Border_Size;
Mid_Y : constant Integer :=
(Playfield_Top + Playfield_Bottom) / 2;
Mid_X : constant Integer :=
(Playfield_Left + Playfield_Right) / 2;
begin
for I in 1 .. Snake_Length loop
Snake_Body (I).Y := Mid_Y;
Snake_Body (I).X := Mid_X - I;
end loop;
end;
function Is_Opposite (A, B : Direction) return Boolean is
begin
return A.Y = -B.Y and then A.X = -B.X;
end;
procedure Enqueue_Direction (New_Dir : Direction) is
Last_Dir : Direction;
Next_Tail : Queue_Index;
begin
if Queue_Count > 0 then
if Queue_Tail = Queue_Index'First then
Last_Dir := Input_Queue (Queue_Index'Last);
else
Last_Dir := Input_Queue (Queue_Tail - 1);
end if;
else
Last_Dir := (Y => Snake_Y, X => Snake_X);
end if;
if New_Dir = Last_Dir then
return;
end if;
if Snake_Length > 1 and then Is_Opposite (New_Dir, Last_Dir) then
return;
end if;
if Queue_Count >= Input_Queue'Length then
return;
end if;
Input_Queue (Queue_Tail) := New_Dir;
if Queue_Tail = Queue_Index'Last then
Next_Tail := Queue_Index'First;
else
Next_Tail := Queue_Tail + 1;
end if;
Queue_Tail := Next_Tail;
Queue_Count := Queue_Count + 1;
end;
procedure Apply_Queued_Direction is
Next_Head : Queue_Index;
Next_Dir : Direction;
begin
if Queue_Count = 0 then
return;
end if;
Next_Dir := Input_Queue (Queue_Head);
if Queue_Head = Queue_Index'Last then
Next_Head := Queue_Index'First;
else
Next_Head := Queue_Head + 1;
end if;
Queue_Head := Next_Head;
Queue_Count := Queue_Count - 1;
Snake_Y := Next_Dir.Y;
Snake_X := Next_Dir.X;
end;
procedure Draw_Snake is
Playfield_Height : constant Integer :=
Lines - 2 * Margin_Block - 2 * Border_Size;
Playfield_Width : constant Integer :=
Columns - 2 * Margin_Inline - 2 * Border_Size;
function Segment_Glyph (I : Integer) return String is
Cur : constant Point := Snake_Body (I);
Dy_Prev : Integer;
Dx_Prev : Integer;
Dy_Next : Integer;
Dx_Next : Integer;
Dy_Tip : Integer;
Dx_Tip : Integer;
Has_Up : Boolean;
Has_Down : Boolean;
Has_Left : Boolean;
Has_Right : Boolean;
begin
if Snake_Length = 1 or else I = 1 then
if Snake_X > 0 then
return Utf8_Head_Right;
elsif Snake_X < 0 then
return Utf8_Head_Left;
elsif Snake_Y > 0 then
return Utf8_Head_Down;
else
return Utf8_Head_Up;
end if;
end if;
if I = Snake_Length then
Dy_Prev :=
Wrapped_Delta (Snake_Body (I - 1).Y, Cur.Y, Playfield_Height);
Dx_Prev :=
Wrapped_Delta (Snake_Body (I - 1).X, Cur.X, Playfield_Width);
Dy_Tip := -Snake_Tail_Y;
Dx_Tip := -Snake_Tail_X;
if Dy_Prev = 0 and then Dy_Tip = 0 then
return Utf8_Snake_H;
elsif Dx_Prev = 0 and then Dx_Tip = 0 then
return Utf8_Snake_V;
end if;
Has_Up := Dy_Prev = -1 or else Dy_Tip = -1;
Has_Down := Dy_Prev = 1 or else Dy_Tip = 1;
Has_Left := Dx_Prev = -1 or else Dx_Tip = -1;
Has_Right := Dx_Prev = 1 or else Dx_Tip = 1;
if Has_Up and then Has_Right then
return Utf8_Snake_DL;
elsif Has_Up and then Has_Left then
return Utf8_Snake_DR;
elsif Has_Down and then Has_Right then
return Utf8_Snake_UL;
else
return Utf8_Snake_UR;
end if;
end if;
Dy_Prev :=
Wrapped_Delta (Snake_Body (I - 1).Y, Cur.Y, Playfield_Height);
Dx_Prev :=
Wrapped_Delta (Snake_Body (I - 1).X, Cur.X, Playfield_Width);
Dy_Next :=
Wrapped_Delta (Snake_Body (I + 1).Y, Cur.Y, Playfield_Height);
Dx_Next :=
Wrapped_Delta (Snake_Body (I + 1).X, Cur.X, Playfield_Width);
if Dy_Prev = 0 and then Dy_Next = 0 then
return Utf8_Snake_H;
elsif Dx_Prev = 0 and then Dx_Next = 0 then
return Utf8_Snake_V;
end if;
Has_Up := Dy_Prev = -1 or else Dy_Next = -1;
Has_Down := Dy_Prev = 1 or else Dy_Next = 1;
Has_Left := Dx_Prev = -1 or else Dx_Next = -1;
Has_Right := Dx_Prev = 1 or else Dx_Next = 1;
if Has_Up and then Has_Right then
return Utf8_Snake_DL;
elsif Has_Up and then Has_Left then
return Utf8_Snake_DR;
elsif Has_Down and then Has_Right then
return Utf8_Snake_UL;
else
return Utf8_Snake_UR;
end if;
end;
begin
if State = Game_Over then
if Snake_Length >= 2 then
attrset (Pair_Secondary);
for I in 2 .. Snake_Length loop
Draw_Text
(Snake_Body (I).Y, Snake_Body (I).X, Segment_Glyph (I));
end loop;
end if;
if Snake_Length >= 1 then
attrset (Pair_Primary);
Draw_Text (Snake_Body (1).Y, Snake_Body (1).X, Segment_Glyph (1));
end if;
else
for I in 1 .. Snake_Length loop
if Turbo_Enabled then
if (I + Integer (Turbo_Flash)) mod 2 = 0 then
attrset (Pair_Secondary);
else
attrset (Pair_Primary);
end if;
else
attrset (Pair_Secondary);
end if;
Draw_Text (Snake_Body (I).Y, Snake_Body (I).X, Segment_Glyph (I));
end loop;
end if;
end;
procedure Update_Snake is
Playfield_Top : constant Integer := Margin_Block + Border_Size;
Playfield_Left : constant Integer := Margin_Inline + Border_Size;
Playfield_Bottom : constant Integer :=
Lines - 1 - Margin_Block - Border_Size;
Playfield_Right : constant Integer :=
Columns - 1 - Margin_Inline - Border_Size;
Playfield_Height : constant Integer :=
Playfield_Bottom - Playfield_Top + 1;
Playfield_Width : constant Integer :=
Playfield_Right - Playfield_Left + 1;
Old_Tail : constant Point := Snake_Body (Snake_Length);
begin
for I in reverse 2 .. Snake_Length loop
Snake_Body (I) := Snake_Body (I - 1);
end loop;
Snake_Body (1).Y := Snake_Body (1).Y + Snake_Y;
Snake_Body (1).X := Snake_Body (1).X + Snake_X;
if Snake_Body (1).Y < Playfield_Top then
Snake_Body (1).Y := Playfield_Bottom;
elsif Snake_Body (1).Y > Playfield_Bottom then
Snake_Body (1).Y := Playfield_Top;
end if;
if Snake_Body (1).X < Playfield_Left then
Snake_Body (1).X := Playfield_Right;
elsif Snake_Body (1).X > Playfield_Right then
Snake_Body (1).X := Playfield_Left;
end if;
Snake_Tail_Y :=
Wrapped_Delta
(Snake_Body (Snake_Length).Y, Old_Tail.Y, Playfield_Height);
Snake_Tail_X :=
Wrapped_Delta
(Snake_Body (Snake_Length).X, Old_Tail.X, Playfield_Width);
end;
procedure Handle_Input (Ch : C_Int) is
New_Dir : Direction := (Y => Snake_Y, X => Snake_X);
begin
case Ch is
when C_Int (Character'Pos ('h'))
| C_Int (Character'Pos ('a'))
| Key_Left =>
New_Dir := (Y => 0, X => -1);
when C_Int (Character'Pos ('l'))
| C_Int (Character'Pos ('d'))
| Key_Right =>
New_Dir := (Y => 0, X => 1);
when C_Int (Character'Pos ('k'))
| C_Int (Character'Pos ('w'))
| Key_Up =>
New_Dir := (Y => -1, X => 0);
when C_Int (Character'Pos ('j'))
| C_Int (Character'Pos ('s'))
| Key_Down =>
New_Dir := (Y => 1, X => 0);
when others =>
null;
end case;
if New_Dir /= (Y => Snake_Y, X => Snake_X) then
Enqueue_Direction (New_Dir);
end if;
end;
function Process_Input return Boolean is
Ch : C_Int := getch;
begin
while Ch /= Key_None loop
if Ch = C_Int (Character'Pos ('q'))
or else Ch = C_Int (Character'Pos ('Q'))
then
return True;
elsif Ch = Key_Resize then
Resize;
else
case State is
when Game_Over =>
null;
when Paused =>
if Ch = C_Int (Character'Pos (' ')) then
State := Playing;
end if;
when Playing =>
if Ch = C_Int (Character'Pos (' ')) then
State := Paused;
elsif Ch = C_Int (Character'Pos ('x'))
or else Ch = C_Int (Character'Pos ('X'))
then
Turbo_Enabled := not Turbo_Enabled;
else
Handle_Input (Ch);
end if;
end case;
end if;
Ch := getch;
end loop;
return False;
end;
function Collided return Boolean is
Head : Point := Snake_Body (1);
begin
for I in 2 .. Snake_Length loop
if Snake_Body (I) = Head then
return True;
end if;
end loop;
return False;
end;
function Run_Game return Integer is
begin
Rand_Int.Reset (Random);
declare
Locale_Empty : chars_ptr := New_String ("");
Ignore_Locale : chars_ptr := setlocale (Locale_All, Locale_Empty);
begin
pragma Unreferenced (Ignore_Locale);
Free (Locale_Empty);
end;
initscr;
noecho;
start_color;
init_pair (Pair_Primary_Id, Color_Primary, Color_Canvas);
init_pair (Pair_Secondary_Id, Color_Secondary, Color_Canvas);
attrset (Pair_Primary);
clear;
curs_set (0);
timeout (0);
declare
Ignore : constant C_Int := keypad (Stdscr, 1);
begin
null;
end;
bkgd (C_Int (Character'Pos (' ')) + Pair_Primary);
attrset (Pair_Primary);
if Lines - 2 * Margin_Block - 2 * Border_Size < 3
or else Columns - 2 * Margin_Inline - 2 * Border_Size < 3
then
endwin;
return 0;
end if;
Last_Lines := Lines;
Last_Columns := Columns;
Redraw;
Init_Snake;
Spawn_Apple;
loop
Resize;
exit when Process_Input;
if State = Playing then
declare
Tail_Before_Move : constant Point := Snake_Body (Snake_Length);
begin
Apply_Queued_Direction;
Update_Snake;
if Apple_Contains (Snake_Body (1)) then
if Snake_Length < Index'Last then
Snake_Length := Snake_Length + 1;
Snake_Body (Snake_Length) := Tail_Before_Move;
end if;
Spawn_Apple;
end if;
if Collided then
State := Game_Over;
end if;
end;
end if;
Clear_Playfield;
Draw_Snake;
Draw_Apple;
Draw_HUD;
refresh;
if State = Playing and then Turbo_Enabled then
Turbo_Flash := (Turbo_Flash + 1) mod 2;
elsif not Turbo_Enabled then
Turbo_Flash := 0;
end if;
if Turbo_Enabled then
napms (Turbo_Frame_Ms);
else
napms (Frame_Ms);
end if;
end loop;
endwin;
return Snake_Length - Snake_Initial_Length;
end Run_Game;
begin
return Run_Game;
end Adasnake;
with Interfaces.C;
with Interfaces.C.Strings;
with System;
package Ncurses is
pragma Linker_Options ("-lncursesw");
pragma Linker_Options ("-ltinfo");
subtype C_Int is Interfaces.C.int;
subtype C_Chars_Ptr is Interfaces.C.Strings.chars_ptr;
procedure initscr
with Import, Convention => C, External_Name => "initscr";
procedure endwin
with Import, Convention => C, External_Name => "endwin";
procedure refresh
with Import, Convention => C, External_Name => "refresh";
procedure clear
with Import, Convention => C, External_Name => "clear";
procedure noecho
with Import, Convention => C, External_Name => "noecho";
procedure start_color
with Import, Convention => C, External_Name => "start_color";
procedure init_pair (Pair, Foreground, Background : C_Int)
with Import, Convention => C, External_Name => "init_pair";
procedure attrset (Attrs : C_Int)
with Import, Convention => C, External_Name => "attrset";
procedure bkgd (Ch : C_Int)
with Import, Convention => C, External_Name => "bkgd";
procedure curs_set (Visibility : C_Int)
with Import, Convention => C, External_Name => "curs_set";
procedure timeout (Millis : C_Int)
with Import, Convention => C, External_Name => "timeout";
function setlocale
(Category : C_Int; Locale : C_Chars_Ptr) return C_Chars_Ptr
with Import, Convention => C, External_Name => "setlocale";
function keypad (Win : System.Address; Bf : C_Int) return C_Int
with Import, Convention => C, External_Name => "keypad";
function getch return C_Int
with Import, Convention => C, External_Name => "getch";
procedure napms (Ms : C_Int)
with Import, Convention => C, External_Name => "napms";
procedure move (Y : C_Int; X : C_Int)
with Import, Convention => C, External_Name => "move";
procedure addch (Ch : C_Int)
with Import, Convention => C, External_Name => "addch";
function addstr (Str : C_Chars_Ptr) return C_Int
with Import, Convention => C, External_Name => "addstr";
C_Lines : C_Int
with Import, Convention => C, External_Name => "LINES";
function Lines return Integer
is (Integer (C_Lines));
C_Columns : C_Int
with Import, Convention => C, External_Name => "COLS";
function Columns return Integer
is (Integer (C_Columns));
Stdscr : System.Address
with Import, Convention => C, External_Name => "stdscr";
end Ncurses;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment