A local bound straight from another module's global (let t =
thing_cur), or from such a local, is followed within its function, and
a write through its field or element is listed as a warning after the
counted writes. A reference from a function's result is not followed.
Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The parse, the types and the module rules (export, uses, layers, ports,
registries) run and nothing is emitted or linked: about three seconds
on Maroon Lake. In this mode the checker asks vis_check at each
reference it resolves, with a global's initializer and a registry's
entries seen from the files the emitter would use. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bind Purse { money: g_money } writes the getter bind_Purse_money in the
bind's own file, so a one-line wrapper per member is not needed; a
member that takes something is refused a variable. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The modules of one layer use each other freely and may go round;
anything outside the layer is held to the module's uses, and a layered
module with no uses reaches nothing outside it. The cycle check walks
the graph with each layer as one node, so a cycle leaving a layer is
refused. ludic deps shows the layers and counts the largest cycle
without their own edges. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every file is compiled, linked and every test block run as up to N jobs
at once (xargs -P; default the CPU count), each test with a TMPDIR of
its own, and the report is read back in file order. Windows keeps the
sequential path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With LUDIC_DEPS=<file> the compiler records every reference the
visibility pass resolves (from module, to module) and every assignment
to another module's global. ludic deps prints the modules,
dependencies, largest cycle, cross writes and globals written from
outside, with --graph, --dot, --writes, --uses MOD, --check FILE and
--baseline FILE. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A property or event a module does not export no longer collides with
another module's of the same spelling: each private one is renamed for
its module, with the types, new, emit and @On written in that module.
Exported ones stay one namespace; two events of one spelling are now
refused. Generic records, entity components and component or view
records stay global. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A template literal nested in a {...} hole crashed the parser: the outer
literal ended at the inner backtick. The lexer (and ludic-fmt's) now
reads a hole as code, taking strings, chars and templates in it whole.
A decimal literal past 2147483647, or a hex one of more than eight
digits, was wrapped into a negative int; it is a long with its value
now, and giving one to an int is refused. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A scene is an entry in the open registry LabScenes, shown on a plate: a
flat lit disc, the package's own small sky with the sun at one hour, and
a frame clock. Headless each camera (lab_shot, lab_shot_at) settles and
is written as build/lab/<scene>/<shot>.png (a stored PNG, written here);
in a window N and P step through them. tools/lab/run.sh and sheet.py
make the contact sheet; tools/lab/plate_build.py makes plate/.
ludic.render3d gains r3d_plate_mode (no terrain, grass or water is made)
and r3d_sky_path. quit() in a program with no frame loop links. The
example takes ~120 MB resident, 417 MiB peak footprint. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The entries are read and checked against the registry's record as a
registry ... from file is, errors at the resource file's line, and they
are defs of the module that wrote the line: the registry must be open to
it, and they take that module's place in the stable order. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A private function, var or const no longer collides with the same
spelling in another module, in no module or in the runtime: where two
meet, each private one is renamed for its module (seed$shop), with every
reference its module writes that no local shadows. Exported names stay
one namespace. Nothing is renamed without a clash. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A line is cast at a point the FishingWorld port says is water (depth, or afloat), bait (asked,
then said as FISHING_BAIT_USED for the game to take) halves the wait, the bite picks a species -
a legend only on a lure, likelier in deep water and with skill, else an everyday one by weight -
and the fight is a marker, a green band that moves above Relaxed and closes on Hard or for a
fish whose data says so, a Gentle ring of one press, and a legend's runs and the line it takes
back. Length and weight come from FishSpecies data. The package never draws, speaks or touches
a pack; every step is a FishingFact (CAUGHT, BAIT_USED, LINE_SNAPPED, SLACK, MISSED, ...). Its
dice are its own Rng. A legend on Gentle fights as a legend (the old ring lost him at once).
Uses ludic.base only; thirteen tests over a fake lake.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Items (base price, sell fraction) and vendors (stock, buys, refuses) are the game's data handed
over at boot; standing, the day, a shortage and "buys beyond its list" are the ShopWorld port;
money is ShopPurse and the pack ShopPack. The week's wanted and glutted items come from an Rng
seeded by the week, never the world's. The balance invariants - a shop is not a fountain,
friction falls and never inverts, raw keeps its order - are the package's tests (nine).
ludic.crafting: its private names carry the package's prefix too (a private name is still one
global namespace with the game's - ludic.shop's `sp_seed` met the game's).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A recipe is `def CraftRecipes key { out, n, ins: [...], ns: [...], station, minutes, hold,
group, learned }`. The pack is the CraftPack port (count, take, add, room, and `leaves`: what
an item leaves once used, so water gives back its bottle); stations are CraftStations (ready,
start), unbound meaning hands only. craft_make hands a click recipe over or starts a timed one
at its station, refunding when the station refuses; the hold runs from the screen that shows it.
Uses ludic.base only; ten tests with a fake pack and fake stations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Minutes of fuel that burn down in game time, twice as fast in the rain with
nothing over the ring, and the hours it held under cover. Lighting and feeding
are decided (fire_decide), costed in wood (fire_cost) and applied
(fire_apply) apart, so a machine that does not own the world can ask the one
that does. Warmth and cooking by distance are queries. FIRE_LIT,
FIRE_WENT_OUT and FIRE_LOW_FUEL are facts; the rain, the tarp, the ban and
who owns the world come through the FireWorld port. Its own save section.
Uses ludic_base only; 8 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Warmth, food, water and energy as bars of 0..100; what an hour of doing something
costs them (the package's rates: a full bar is a day at a trip's median); the
countdown when one runs out, reported as NEEDS_CRITICAL / NEEDS_RESTORED /
NEEDS_COLLAPSED facts. The body's work, the air, clothing, the sun, the
difficulty and the effects come through the NeedsWorld port. Its own save
section. Uses ludic_base only; 13 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A step is (kind, param, need) and a question about state: a kind has a
shape (yes, a count, a mask counted, every bit of a mask) and a pool
(world, any, sum, or, max, each), handed in by the game as StepsKind
records. An arc is chapters of up to STEPS_PER_CHAPTER steps; steps_check
sticks each met step of the current chapter (STEPS_MET) and opens the
next when all are (STEPS_CHAPTER), so a step met before its chapter
ticks at once. With company each seat's shares are held per chapter,
pooled as the kind says (steps_met, steps_party_got), counted for an
EACH step (steps_each, steps_lacking), and a leaver takes theirs along.
Port: StepsWorld { value(kind, param, player) (required), party,
present }. The package knows no kind's meaning and no chapter's words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
latest.json asked once and polled (the newest version and its notes, in
a language or English); then Velopack's releases.<os>.json, the plan (the
delta chain when the installed full package is on disk, no delta is
missing and the chain weighs less than the full one; the newest full
package otherwise, named as the feed names it), each package streamed to
the packages folder with its bytes on a bar and an eased pace, its
SHA-256 by Get-FileHash or shasum in a child, Update patch per delta and
Update apply --waitPid. macOS keeps its packages outside the bundle and
names --rootDir and --packageDir; a translocated app is not installed.
Ports with the runtime as every default: UpdateWorld (platform, exe, pid,
now_ms), UpdateNet (get, download, poll, text, received, free),
UpdateProc (spawn, poll, free). Config: feed, app_id, version, root,
packages, mac_packages, scratch. It words nothing: states, UPDATE_ERR_*
codes and numbers, and UPDATE_STARTED / _STOPPED / _APPLYING facts - the
game exits on the last.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ludic.clock, .effects, .inventory, .wallet, .weather and .tracks say
'uses ludic_base' (ludic.base uses nothing). ClockWorld, PackRules,
WeatherWorld and TracksWorld are export ports whose defaults are the old
fallbacks; clock_bind, inv_bind, weather_bind and tracks_bind are gone,
and the tests bind their fakes. The base README's toy does the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A module that says uses must name every package module it reaches; a
package with no module line is named for its directory (ludic_render3d)
for this rule, so a mechanic reaching into the renderer is caught. Only
the engine's runtime needs no naming. 'module x uses' with nothing after
it reaches no other module. A port whose every member has a default
answers with its defaults when unbound, and 'new' of a port says to bind
it. ludic-dev test runs 'ludic test packages'. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Events are encoded once into lines held in memory; a batch is the first
lines joined, with the player id put in where a mark stood, POSTed on
Http's own thread every flush_ms or at flush_at events; a failure keeps
its lines and doubles the wait up to backoff_max; the queue is on disk
every save_ms and read back at the next start; off (the enabled port)
drops the request in flight, empties the queue and deletes the file; and
a run that may not send (can_send) keeps nothing. The player id is the
machine's own id (MachineGuid, IOPlatformUUID, /etc/machine-id) hashed
with the game's salt, else random; it lives in the game's id file beside
whatever else the game keeps there.
Ports: TelemetryWorld (can_send, enabled, now_ms, stamp) and
TelemetryTransport (send, poll, drop; unbound: Http). Config is a record
(host, key, path, lib, queue_file, id_file, id_salt, and the timings).
Facts: TELEMETRY_SENT, _FAILED, _ID. Not a System: it runs from a
launcher's first frame, before any world exists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The runner's list starts from the declared systems, in the order the
compiler gives an open registry, and core_add appends after them.
registry_test covers it; the README says ludic test runs the package.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Random.range is rng_range, so a package's own rng_range(a, b, c) took
every Random.range call silently. Where the program calls such a method
and the target resolves to a function of its own, the function is
refused, naming the namespace method and the call. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a float or double they emitted an i32 compare and clang refused the
IR; they compare as the wider float kind now and a failure prints the
numbers with %g. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The seeded random numbers lived in the ECS runtime, so a tool or a
program of test blocks got "unknown function rng_range". They are
runtime/native/rng.ludic now, spliced on Random.* or a bare rng_*/seed
call nobody defines, and imported by core.ludic for a game. A bare
value_*/json_* call nothing in the program defines splices the value
tree the way Value.* does. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A ring of prints per kind; a port for the trip's time, the reader's tier, a
bearing in words and a hook the game draws each print through. A read is a
TrackRead fact on tracks_reads() every time and a count the first time; other
sign is told with tracks_note. The count and last bearing are its save section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The kinds and their odds are data the game hands in; a port asks whether this
machine owns the world, the hour, the night, the odds from a kind, what the
story wants of the sky (a front, a hold, a clear night), the front's kind and
length, and a seed. Its own dice; a new spell, a clearing, lightning and a
wanted front are facts on weather_facts(); its own save section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Test blocks were never type-checked, and the checker is what makes a
generic call real; they are checked like entry now. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>