Paintball
A turn-based Paintball simulator written in XLISP-PLUS in a strictly functional style — immutable state, no mutation, no loops — where two AI agents fight to destroy the enemy base by painting it in all three colours.
Paintball
Final project for the Programming Languages course at the Universitat de les Illes Balears. A turn-based Paintball simulator written in XLISP-PLUS following a strictly functional design: the game state is an immutable list and every action returns a brand-new state, so nothing is ever mutated in place. Two teams, each driven by its own AI agent, fight to destroy the enemy base by painting it in all three colours (r, g, b).
The game
The board is a grid of land and water cells with obfuscated coordinates, so units cannot deduce the map borders. Each team owns an immovable base that spends paint to spawn bolles (paintball units); a bolla can move and paint the cells around it, and both bases and bolles are destroyed once they end up covered in the colours that beat them. Every unit is controlled purely from the information it receives each turn — its vision, its resources and an optional shared memory — and returns a list of actions that the controller validates and applies.
Turns alternate between the two teams and a match ends when a base is destroyed or a turn limit is reached, with a tie-break on surviving units and remaining paint. While a match is running the controls are [N] next turn, [B] step back, [A] automatic mode and [Q] quit.
About
The design leans entirely on the functional toolbox seen in class — only recursion and higher-order functions (mapcar, reduce, apply, every, some, funcall), with no iterative loops and no destructive mutation:
- Immutable state — the whole match is a single list that is never mutated; every action returns a new state through dedicated
get-/set-/update-accessors. - Controller — runs the main loop, alternates turns, calls each unit’s AI agent and folds the returned actions into the state with
reduce; units created during a turn may also act in that same turn. - AI agents — one per team, each living in its own file and allowed to use only the information passed in for that turn.
- Vision — computes the cells each unit can see (a wider radius for a base than for a bolla) in the exact format the agents consume.
- Graphics — draws the match in the XLISP-PLUS window with differential redraw, repainting only the cells that change, plus visual traces of the paint shots and movement.
- Maps — loads a map from a LISP expression and builds the initial state with random coordinate obfuscation; a converter turns UPC-format maps from the AI Colliseum editor into the UIB format.
- Tail-call optimization — a
defun-tcomacro rewrites tail-recursive functions into an iterative form, avoiding stack overflows on deep recursion.
Executing the project
Requires XLISP-PLUS (developed and tested on Linux; some graphics functions behave differently on Windows).
- Clone the repository and open the
P1/folder so the relative map paths resolve. - Start XLISP-PLUS and load the entry point:
(load "paintball") - Launch a match, optionally passing a map (it defaults to
maps/uib/tiny.map):(inici) ; default map (inici "maps/uib/simple.map") ; a specific map