An engine for describing games built around Xerces, exprtk, and SFML.
Working notes for any AI coding agent (or new contributor) picking this project up. Everything the project itself relies on is in ../readme.md and ../designs/00-designs.md; this folder is for agent-facing bookkeeping.
The first version of these docs (the design write-ups, sources.md and the scan script) was written by Claude (Sonnet 5.5) on 2026-09-29, from the author’s exported chat history plus a read of the source on the claude branch. Frogger (games/frogger.xml), the verbs it needed and design write-up 21 were added by Claude (Sonnet 5.5) on 2026-09-30, as was Space Race (games/spacerace.xml, first to two points, no new verbs). Kaboom (games/kaboom.xml, design write-up 30, no new verbs) was added by Claude (Sonnet 5.5) on 2026-09-30 as a game to have fun with, and to try <random> inside a <group>; it was first a looser game named Gem Catcher, and was renamed and rewritten to follow the arcade rules (bombs only, waves, a miss sets off the wave and costs a bucket) on the same day. Freeway, Depth Charge and Astrosmash (design write-ups 31 to 33, no new verbs) were added the same day, chosen because they needed nothing the vocabulary lacked. Lunar Lander (games/lunarlander.xml, design write-up 34) was added by Claude (Sonnet 5.5) on 2026-09-30 at the author’s request, with the <line> sprite shape, <type>pixel</type> collisions, <acceleration>, <accelerate>, <stop /> and the slower/faster filters; the window backends for lines were only compile-checked. A second export (ChatGPT) was mined on 2026-09-30: it added design docs 22 to 28 and a section headed second batch in most earlier docs; only paraphrased ideas were kept, and titles of chats that did not start as game discussions were made generic. On 2026-10-01 Claude (Opus 5.5) checked every doc against the code, fixed the crash when XGEGUI switched to raylib or OpenGL (OpenGL contexts shared with Qt, 39), paused the game while the Options dialog is open, and fixed a handful of engine bugs found on the way (a <push> naming no state read past the end of the list of states, <pop /> could empty the state stack, a condition’s <pop /> freed the commands it was running, a bad file or image ended the program instead of throwing); it built every library on Linux to do it, including raylib 6.0 configured as vcpkg does. On 2026-10-02 Claude (Sonnet 5.5) built design 40 at the author’s request: XGEGUI shows the game in one window (the Qt renderer) or two (a video library’s own window), the embedding code of design 39 was removed from lib/ and gui/, and the question before moving to two windows is kept in xgegui.ini. Only the Qt renderer, SDL2 and OpenGL (GLFW) could be built and run for that (under a virtual X server); SFML 3 and raylib were not built. On 2026-10-02 Claude (Sonnet 5.5) also built design 42 at the author’s request: the <bitmap> sprite shape (ASCII rows), named <sprite>s with an <animation> of <frame>s on a seconds interval, a Space Invaders redrawn from them (three kinds of alien as a <group> of <grid>s, a bitmap cannon, a thin bullet), and <fire> now leaves from the middle of the shooter. Built and run on Linux with SFML 3, SDL2, raylib and OpenGL; the Qt renderer was not built.
docs/readme.md: the engine as it works now. Update it whenever a verb, attribute or file changes.docs/designs/00-designs.md: index of design topics with options and status. One file per topic, numbered NN-name.md. Keep the table and the files in step.docs/agents/sources.md: which conversations fed which design entry.docs/agents/scan_export.py: ranks and dumps conversations from a Claude chat export.docs/agents/scan_chatgpt_export.py: the same for a ChatGPT export (many conversations-NNN.json files; keys are file:position).Key conventions for games/: Space starts, pauses and plays again (never Enter); player one is W/A/S/D. Where Space fires, pause is P or Escape. Keep new games to this.
readme.md is a short overview (games, format, build, status); the detailed description is docs/readme.md. Update both when a verb, a game or a dependency changes.master, rewrite, claude. The active engine described in these docs is on claude.Value variant exist yet, despite being decided or discussed (07, 08). Swept collision is built (11).CollisionDetector::circleRectangle was rewritten (nearest point on the rectangle, and the closest side for a centre inside it); tests/test_collision_geometry.cpp pins it. Add a test there before changing it.xsd_lite (used by the three non-Xerces backends) covers only the XSD subset the schema uses, and tests/test_xml_format.cpp pins what both validators must reject..gitattributes stores LF and checks out CRLF); keep them when editing. A few newer files (design 22 to 28, scan_chatgpt_export.py, sources.md) are LF on disk; git normalizes them on commit.<grid> has its own name (aliens.3.2, column then row from 1), so a single cell can be addressed; the grid’s name still means the whole grid.basic (collisionData.basic), a leftover of the old basic="basic" spelling; the XML no longer has it.-w/--window and -x/--xml (37), and in XGEGUI by its Options dialog, which can also change the video library of a running game (39).EndDrawing() reads the keyboard and the close button itself, once a frame. RaylibWindow::pollEvents() must not call PollInputEvents() too (that reset what EndDrawing() had just read, so every key press and close request was lost) and reports key changes by comparing IsKeyDown with the last call. Escape is not raylib’s exit key here (SetExitKey(KEY_NULL)). Some raylib builds (the author’s Windows build, raylib 6.0) are compiled with SUPPORT_CUSTOM_FRAME_CONTROL, which makes EndDrawing() skip the buffer swap, the polling and the frame wait (a blank, “Not Responding” window); RaylibWindow::display() notices this once (GetFrameTime() is still 0 after EndDrawing()) and does those three steps itself. Anything drawn into a render texture with DrawTexture must stay alive until EndTextureMode(), which is when raylib draws its batch (unloading it first drew nothing, so no sprite of lines showed).XGELIB, in lib/); cli/ is XGECLI (the command line program) and gui/ is XGEGUI (the Qt application, 38); each has its own source/ and include/. Targets: XGELIB, XGECLI, XGEGUI, XGETEST (tests) and XGEDATA (copies games and assets). New engine source files go in ENGINE_SOURCES in the top-level CMakeLists.txt; tests link the library, so they no longer need that list. Static by default, XGE_BUILD_SHARED for shared (35).Window backend opens a window of its own (WindowFactory::create(desc, backend)), and throws std::runtime_error if the library cannot start. Never destroy a library’s window after creating the next one: raylib and GLFW can only have one (Engine::replaceWindow destroys first). A front end that pauses the game but keeps a library’s window calls Engine::pump() so the window stays responsive; it does not run the simulation, and keeps the keys it reads for the next step().XGEGUI uses no OpenGL through Qt: GameView is an ordinary widget. A QOpenGLWidget and a library’s OpenGL context on the one thread draw into each other (Qt trusts its own record of the current context), and a window that once held a QOpenGLWidget seemed to stay broken after a library had run; see 40. Do not bring one back without a guard around every call into a library.XGEGUI that can run while GameSession swaps the game or its window (a Qt signal from a focus change, a timer) must check GameSession’s changing guard, as step, reset and redraw do: for part of a swap the engine has no window.exit(): XGEGUI loads one game after another and has to survive a bad one. XGECLI catches and prints.lib/source/builtin_font.cpp, 8x8, public domain) when assets/tuffy.ttf cannot be found; the assets are looked for relative to the working directory, so XGECLI and XGEGUI first make the folder holding games/ and assets/ the working directory (lib/source/data_folder.cpp: working directory, program folder, the one above); a program that skips that falls back to the built-in font (36).data_xml in scripts/cmake/assets.cmake, or the build does not copy it into the build directory; a new test file has to be added to the list in scripts/cmake/tests.cmake.<group>s, read as one object per member (logrow3.2, pads.1), and Space Race’s debris lanes the same way. The tests address members by those names. See 21 and 29.ShapeKind::Line now means any picture the engine draws itself: <line> and <bitmap> sprites both become a Bitmap, with spriteParams {"line", w, h}, so no window backend knows the difference. The XGEGUI inspector calls such a sprite “drawn” / “pixels”; that edit was not compiled (no Qt on the agent).tests/invaders_fixture.h holds a frozen copy of the old Space Invaders XML; the win, swept-collision and lockstep tests use it, so redrawing games/spaceinvaders.xml does not break them. Space Invaders itself is tested in tests/test_bitmap_sprites.cpp.<fire> centres the projectile on the shooter’s top edge (a thin bullet leaves the middle of the gun); Depth Charge and Astrosmash tests expect that.seconds * framerate) by Object::advanceAnimation, called for every shown object at the top of Game::updateObjects. A <bitmap> on an object with a <heading> is drawn once and kept; an object that turns keeps a shared Turnable (the lines or the original picture, one per animation frame in Object::turnables) and its one current bitmap, redrawn by Object::showHeading only when the heading rounded to a whole degree changes (360 headings; turnBitmap, rasterizeTurned). Letting each backend rotate a texture was rejected: the drawn and the collision-tested pixels would differ between libraries..gitattributes converts line endings on commit; git prints LF-to-CRLF warnings, which are harmless.<acceleration>, see design 34; what is missing is grounded-versus-airborne and a jump impulse) (design 13, and the air-control refinement in design 23) as the next test of whether the vocabulary approach extends. hop (Frogger) was the first: one instant step per press.inc/dec is built, see design 41), and per-row velocity in <grid> (the built <group> tag, design 29, covers lanes; its open points are nested groups, a bare <x> in a member and evenly spaced members).collisionData.basic (and the basic= label in printGame()) to something that says what it is.lib/source/ and lib/include/ are flat and growing (25 and 26 files). Move them into folders by responsibility (parse and evaluate, engine loop, collision, window backends, XML backends); the Visual Studio filters are meant to be built from the directories, so this is only about the layout on disk. Not started; design 16 has the history of the earlier, over-layered attempt on the rewrite branch and why it was flattened.stb_image: the link fails with duplicate stbi_* symbols on Linux. vcpkg’s DLLs do not have the problem. Building one of the two as a shared library would avoid it; not done.<turn>, <thrust>, <drag>, <hidden>, <release> and an amount on <inc>/<dec>. Its open points: shots that wrap and expire, a safe wait before the ship comes back, runtime spawning instead of pools, a flying saucer and more than one wave. Not yet watched in a window backend on Windows.This repository is public. Nothing personal from a chat export goes in it: no health, identity, contact or business details, and no names beyond the beefviper author handle already in the code headers. Conversation titles are listed only for design-relevant chats.