Skip to content
joolibarPublic

About

A 2D game built with the MiniLibX graphics library — parses a custom map file, validates it (rectangular, enclosed, solvable), and renders a player who collects items and reaches the exit, with sprite rendering, event handling, and a live move counter.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

so_long - A 2D Game Built on MiniLibX

​

Language Norminette Bonus

Most so_long builds stay flat. Mine doesn't.
Stone platforms adrift in a starless dark, a lamplighter chasing light between shrines — and the isometric engine built from scratch to render it all.

📜 Context  •  🕯️ The World  •  🎮 How to Play  •  🧩 Modules
📺 Visuals  •  🏗️ Architecture  •  🧪 Testing  •  💻 Usage


📜 The Context

"A subject can name the pieces. It can't tell you what to build with them."

so_long is 42's first graphics project: a 2D game built on MiniLibX, reading a map from a .ber file, moving a character across it with the keyboard, and rendering everything through textures instead of raw pixels. The subject hands you a working example and then tells you, explicitly, to replace every part of it.

Unlike libft or get_next_line, this project doesn't restrict what you can use. open, close, read, write, the entire math library, every MLX42 function, and your own ft_printf are all fair game. The constraint here isn't the toolbox — it's whether the result is worth looking at.

This version takes that freedom further — not just a different perspective, but a different reference entirely. Monument Valley didn't just lend the isometric angle. It lent the instinct to tell a story without a single line of dialogue, to let floating architecture imply a world nobody has to explain, and to trust a muted palette over a busy one. so_long borrows the discipline, not just the aesthetic.

Every tile, every character facing, every flicker of animation in this build was drawn specifically for it — not pulled from a tileset, not adapted from someone else's asset pack. The visual language is as much a deliverable here as the code: floor and wall textures that read clearly at an isometric angle, a lamplighter and a set of shadows with four-directional sprites each, and a light that animates frame by frame instead of sitting as a static icon.

🕯️ The World

These platforms don't rest on any ground — they float, pale stone adrift in a sky that never sees daylight. Scattered across them are shrines, each one holding a single light.

You play the lamplighter: no name, no dialogue, just a lantern for company and a map full of platforms to cross. Every shrine you reach adds its light to yours. The shadows drifting between the stones want the same lights — and they don't share.


Animated light collectible

The Shrines of Light — Each collectible flickers through handcrafted animation frames.


The exit doesn't open on its own. It opens once every shrine stands empty and every light is exactly where the lamplighter decided it should be.

🎮 How to Play

Every map plays out the same simple loop: spawn at P, explore the grid, and track down every point of light (C) scattered across it. The exit stays sealed the whole time — it only switches from closed to open once the last light is collected, so there's no accidentally rushing past the objective in the dark. From there, it's a straight race to E in as few moves as possible.

Objective: collect every light, then get out — efficiently.

🗺️ Map Elements

Symbol Element Behavior & Rules
0 Floor Walkable open stone platform.
1 Wall Impassable floating architecture barrier.
C Light Collectibles scattered across shrines — all must be gathered.
E Exit Sealed portal; unlocks only once every light is collected.
P Player Lamplighter starting spawn point.

🕹️ Controls & Mechanics

Key Action
W A S D Move character up / left / down / right
↑ ↓ ← → Alternative directional movement
ESC / Close Quit game cleanly without memory leaks

Every move counts — literally. The mandatory build prints the running total to the terminal after each step; the bonus build puts that same counter directly on-screen, so "efficiently" stops being an abstract goal and becomes a live metric staring back at you.

The grid isn't limited to the maps already in assets/maps/ — any .ber file that passes the six checks in Map Validation is fair game, so building your own layout is just a text file away.

🧩 The Modules

Two phases, two different disciplines:

  1. Map Validation & Gameplay (Mandatory): Parsing a .ber file into a provably valid map, then rendering it and letting a player move through it cleanly.
  2. Enemy Patrols, Animation & HUD (Bonus): Turning a static, silent map into something with movement, risk, and feedback on screen instead of in the terminal.

Click on each module below to view technical specifications and rendering pipelines.


🔹 Module I — Map Validation & Gameplay (Mandatory)

A map with a hole in one wall isn't "mostly valid." It's a program that hasn't crashed yet.

🗺️ Map Validation

Six ways a .ber file can be wrong — and the six categories the entire map corpus is built around:

Category What it catches
file Wrong extension, missing file, empty file
format Not rectangular, dimensions too small, malformed lines
grid Forbidden characters, hidden whitespace
elements Duplicate or missing player, exit, or collectible
wall Map not fully enclosed
path Player can't reach the exit, or can't reach every collectible — checked with a flood fill before the window opens

No MLX call happens until a map clears all six. A broken map never reaches the window.

🎨 2.5D Isometric Rendering Pipeline

While standard so_long implementations use orthogonal 2D top-down grids, this engine features a true 2.5D isometric projection pipeline, transforming 2D cartesian coordinates into 3D isometric screen space:

iso_x = (x - y) * cos(30°)
iso_y = (x + y) * sin(30°)

Isometric perspective introduces complex depth ordering challenges: diagonal screen overlaps require dynamic Z-sorting rather than simple sequential array loops. The engine solves this via an optimized Painter's Algorithm, ordering draw calls back-to-front by depth key (x + y) on every frame. This guarantees proper occlusion for terrain elevation, static obstacles, and animated dynamic entities.

🎮 Game Loop & Controls

  • Grid-Aligned Locomotion: Discrete tile-based movement bounded by collision masks, responsive to both WASD and Arrow key vectors.
  • Telemetry Logging: Real-time step counter emitted to standard output following every verified position update.
  • Graceful Termination: Binds ESC and window close events to a centralized destructor, guaranteeing clean teardown of MLX contexts, textures, and map buffers without memory leaks.
  • Hardware-Accelerated Image Rendering: All scene visuals are rendered using MLX42's texture and image pipeline, strictly avoiding software pixel-by-pixel bottlenecks.

🔸 Module II — Enemy Patrols, Animation & HUD (Bonus)

The subject calls these "extra points." They're also the difference between a program that passes and a game someone wants to keep playing.

  • 🌑 AI Patrol & Tracking Subsystem: Shadows operate on a calibrated 0.5-second clock tick rather than frame rate. Within a 5-tile radius, enemies transition into a greedy chase state targeting the player along the dominant axis. Beyond 5 tiles, entities revert to bounded pseudo-random exploration while honoring map collision barriers.
  • 💀 Narrative Failure Sequence: Collision with a shadow triggers an atmospheric terminal notification ([ The path has faded... ]) followed by an immediate clean shutdown.
  • 🎨 Depth-Integrated Entity Occlusion: Enemy Z-indexes are dynamically interpolated within the isometric depth buffer, ensuring entities correctly pass behind foreground structures without visual artifacts.
  • 🎞️ Frame-Based Sprite Animation: Animated multi-frame sprites for the player, collectible light shrines, and roaming shadows drive fluid visual feedback.
  • 🔢 Direct Framebuffer HUD: Custom monospace bitmap font renderer directly draws real-time step counters and collectible progress onto the screen buffer without external font libraries.

📺 Visual Evidence

Custom texture and sprite set

The full sprite set, hand-drawn — floor, walls, the exit, the lamplighter, the shadows, and the lights they chase.


so_long isometric gameplay

The isometric build in motion — depth, collision, and movement, start to finish.



Enemy patrol AI

Enemy patrols (Bonus)
The shadows chase within 5 tiles, patrol otherwise — no pathfinding.


Step counter HUD

Steps (Bonus)
Move count, rendered pixel by pixel.





Lights collected HUD

Lights (Bonus)
Collection progress, tracked the same way.


🏗️ System Architecture

so_long keeps parsing, rendering, and game logic in separate folders on purpose — each layer only knows its own job, nothing about how the others work.

📂 Project Structure

so_long/
├── inc/
│   ├── mandatory/        # so_long.h, types.h, settings.h, errors.h
│   └── bonus/             # Same headers, _bonus suffix
├── src/
│   ├── mandatory/
│   │   ├── core/           # main, game_loop, game_start
│   │   ├── parsing/        # map validation — zero MLX calls
│   │   ├── init/           # MLX + struct setup
│   │   ├── game/           # input, movement, collection
│   │   ├── render/         # window, map, player, UI
│   │   └── utils/          # cleanup, error handling
│   └── bonus/               # mirrors mandatory file-for-file, plus enemy AI
├── assets/
│   ├── maps/                # 50 .ber maps, categorized by what they test
│   └── textures/            # Custom sprite set, isometric style
├── lib/                       # Unified core library (libft, ft_printf, get_next_line) & MLX42
├── config/                    # Terminal banners & ANSI color palettes
└── Makefile                   # Multi-target build orchestrator (all, bonus, fclean)

🔩 Design Decisions

  • Strict File-for-File Bonus Parity: The bonus architecture mirrors the mandatory codebase file-for-file (e.g. render_map.c vs render_map_bonus.c), ensuring clean diffability and isolated extension of gameplay mechanics.
  • Unified Vendored Toolchain: Integrates libft (primitives), ft_printf (terminal logging), and get_next_line (stream parsing) as pre-validated internal libraries without redundant code.
  • Autonomous In-Tree Graphics Pipeline: MLX42 is vendored directly within lib/MLX42 and compiled from source, eliminating external system package manager dependencies across host environments.

🧪 Testing & Validation

A game doesn't have a libc to diff against. The only ground truth is: does it behave exactly like the spec says it should?

so_long has no deterministic reference to compare output against — there's no real printf here to check byte-for-byte. What it has instead is 50 hand-built .ber maps, split evenly across mandatory and bonus, organized into the same six failure categories the parser is designed to catch:

assets/maps/
├── mandatory/
│   ├── valid/      # 4 maps — verified playable, start to finish
│   └── invalid/    # 21 maps across the 6 failure categories
└── bonus/
    ├── valid/      # 6 maps — verified playable, start to finish
    └── invalid/    # 19 maps across the same 6 categories

Each map gets run against its matching binary — ./so_long for mandatory, ./so_long_bonus for bonus — checking that the result, a clean load or a rejection with the right error, matches what its category promises. There's no automated runner yet; every one of these 50 maps has been verified manually, one at a time. The categorization itself is the test design — the runner is the next thing to build.

  • Static Analysis: Validated by Norminette (42's internal linter).
  • Compiler Flags: Compiled strictly with -Wall -Wextra -Werror.
  • Memory: Verified leak-free across both the mandatory and bonus builds, MLX resources included.

💻 Compilation & Usage

⚡ Installation

Make sure you have a C compiler (gcc or clang), make, and MLX42's system dependencies installed, then clone the repository:

git clone https://github.com/joolibar/so_long.git
cd so_long

Compile the game:

make          # Compiles so_long — the mandatory build
make bonus    # Compiles so_long_bonus — enemies, animation, on-screen HUD

🛠️ Usage

Each build has its own binary and its own map set — mandatory maps are built for so_long, bonus maps for so_long_bonus. Running a map against the wrong binary isn't the tested path.

./so_long assets/maps/mandatory/valid/01_cartesian_init.ber
./so_long_bonus assets/maps/bonus/valid/01_sentinel_incursion.ber

See How to Play for controls and the map legend.



"A lamplighter doesn't build the path — he just makes sure you can see it." 🏮🗺️


Capybara
Crafted by joolibar
Creative Developer building digital experiences at 42

joolibar GitHub LinkedIn Return

© 2026 joolibar  •  Validated by Moulinette 🤖, crafted by Humans 🧠.


42 so_long Footer

About

A 2D game built with the MiniLibX graphics library — parses a custom map file, validates it (rectangular, enclosed, solvable), and renders a player who collects items and reaches the exit, with sprite rendering, event handling, and a live move counter.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages