- βΒ Previous
- Graphics and Ada's Type System
- NextΒ β
- Creating Transform Components
Get Latest Code
π Beginning of Lesson 6 β Two Options
Each lesson starts with a new machine, you need to pull the latest code first, then either resume your own progress or start from the official snapshot.
Clone your fork (replace <YOUR_GITHUB_USERNAME>):
git clone https://github.com/<YOUR_GITHUB_USERNAME>/snake-tutorial.git
cd ~/sandbox/snake-tutorial
π °οΈ Option A β Resume from Your Fork (Recommended)
Check out your saved progress to a branch:
git checkout -b lesson-06-work origin/lesson-05-work
β You're now ready to start Lesson 6 from your own progress.
π ±οΈ Option B β Start from Official Course Snapshot
Check out and push to your origin the starting point for Lesson 6 :
git remote add upstream https://github.com/GNAT-Academic-Program/snake-tutorial.git
git fetch upstream
git checkout -b lesson-06-work upstream/lesson-06-start
git push -u origin lesson-06-work
β You're now starting Lesson 6 from the clean official reference code.
Building Texture Types with Ada Vectors
Let's build a proper texture system! Instead of manually calling Draw (Torso_Pix) four times, we'll create 2D textures using Ada's generic vector containers.
Understanding Ada Generics and Vectors
Ada Generics are like C++ templates but require explicit instantiation. You can't just use Vector<Pixel> - you must create a specific package instance first.
Why Vectors? Ada's Ada.Containers.Vectors provides dynamic arrays that can grow and shrink. Perfect for storing rows and columns of pixels.
- Import the Vector package - add to the top of
noki.ads:with Ada.Containers.Vectors;
Building a 1D Pixel Vector
- Create our first vector package - add after your existing types in
noki.ads:package Pixels_N_Vecs is new Ada.Containers.Vectors (Index_Type => Natural, Element_Type => Pixel_T);
Why Natural? Natural is defined as 0 .. Integer'Last - perfect for array indexing starting from 0.
- Add the use clause to bring operations into scope:
use Pixels_N_Vecs;
What does use do? Without it, you'd need to write Pixels_N_Vecs.Append, Pixels_N_Vecs.Length, etc. The use clause lets you write just Append, Length directly.
- Create a convenient subtype to reduce verbosity:
subtype Pixels_N_T is Pixels_N_Vecs.Vector;
Package vs Type: Pixels_N_Vecs is the package containing operations. Vector is the actual type inside that package. Our subtype Pixels_N_T is just a shorter name for Pixels_N_Vecs.Vector.
Building a 2D Texture (Vector of Vectors)
- Create the 2D vector package - each element is now a row of pixels:
package Pixels_NxM_Vecs is new Ada.Containers.Vectors (Index_Type => Natural, Element_Type => Pixels_N_T); use Pixels_NxM_Vecs; subtype Pixels_NxM_T is Pixels_NxM_Vecs.Vector; - Create our final texture type:
subtype Texture_T is Pixels_NxM_T;
Why another subtype? Texture_T gives semantic meaning - when you see it in code, you know it's a 2D graphics texture, not just any vector.
- Add an empty pixel for transparent areas:
Black : Color_T := (0, 0, 0); Empty_Pix : Pixel_T := (Black, Black, ' ', False);
Creating the Render System
- Add render procedure spec to
noki.ads:procedure Render (T : Texture_T); - Implement the renderer in
noki.adbafter yourDrawprocedure:procedure Render (T : Texture_T) is function Move_Cursor (Row : Positive; Col : Positive) return String is (CSI & Trim (Row'Image) & ";" & Trim (Col'Image) & "H"); begin for Y in T.First_Index .. T.Last_Index loop Ada.Text_IO.Put (Move_Cursor (Y + 1, Positive'First)); for X in T (Y).First_Index .. T (Y).Last_Index loop Draw (T (Y) (X)); end loop; end loop; end Render;
Understanding Local Functions
Why is Move_Cursor local? We declare it inside Render because:
- Scope: Only
Renderneeds cursor movement - no other procedure uses it - Encapsulation: Keeps the function close to where it's used
- Namespace: Doesn't pollute the global namespace with implementation details
Local Declaration Syntax: In Ada, you declare local functions between is and begin. They can access the outer procedure's parameters (like T in our case).
Terminal Coordinates: Terminals use 1-based indexing, not 0-based. That's why we use Positive type (1 .. Integer'Last) - it prevents accidental zero coordinates that would crash the program.
Testing Our Texture System
- Create a player texture in
snake_game.adbdeclarative section:Player : Texture_T := [[Tongue_Pix, Head_Pix, Empty_Pix], [Empty_Pix, Torso_Pix, Empty_Pix], [Empty_Pix, Torso_Pix, Torso_Pix]];
π€― Heads Up! The double bracket syntax [[...], [...]] creates a 2D array literal. Each inner bracket is one row of pixels.
- Update the Play state to use our new renderer:
when Play => Render (Player); Game_State := Game_Over;
π Code Review - Complete Addition to noki.ads
Add this after your existing types:
package Pixels_N_Vecs is new
Ada.Containers.Vectors
(Index_Type => Natural,
Element_Type => Pixel_T);
use Pixels_N_Vecs;
subtype Pixels_N_T is Pixels_N_Vecs.Vector;
package Pixels_NxM_Vecs is new
Ada.Containers.Vectors
(Index_Type => Natural,
Element_Type => Pixels_N_T);
use Pixels_NxM_Vecs;
subtype Pixels_NxM_T is Pixels_NxM_Vecs.Vector;
subtype Texture_T is Pixels_NxM_T;
Black : Color_T := (0, 0, 0);
Empty_Pix : Pixel_T := (Black, Black, ' ', False);
procedure Render (T : Texture_T);
Build and run:
alr build
bin/snake_game
β Expected result: Your snake now renders as a proper 2D texture with clean, organized code!
Save Your Progress
π― End of Lesson 6 β Save Your Progress
To keep your work safe and accessible, push it to your fork.
Make sure you're at the root of the repo:
cd ~/sandbox/snake-tutorial
Make sure you're on your work branch:
git status
- π¨ If you're NOT on branch
lesson-06-work, run:git checkout -b lesson-06-work
Stage and commit your progress:
git add .
git commit -m "Lesson 6 progress"
Push to your fork:
git push origin lesson-06-work
β Expected result: Your fork now has a lesson-6-work branch with your code so far.
Level up your Server Side game β Join 15,000 engineers who receive insightful learning materials straight to their inbox
- βΒ Previous
- Graphics and Ada's Type System
- NextΒ β
- Creating Transform Components