ludic/packages/ludic.jobs
Orkuncakilkaya 71735b10a2 feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune
ludic.base's Queue<T> carries a QueueTag (its name and pending count): queue_new(name), q_push(q, v),
q_drain(q), q_clear(q) take no BaseState, and core_undrained(tags) names the given queues still holding
facts. A reducer on a package's state can now call that package's verbs (wallet_earn(wallet_st, n)).

ludic migrate state --prune (ludicc --migrate-prune) takes out each state parameter a function no
longer uses, nor anything it calls, and the argument that fills it - including an argument for a
parameter the callee has dropped, which is taken out before the call is checked, so a generic's T is
told by the argument that says it. A reducer keeps its state. --dry-run now counts the edits it would
make. Every package was moved with it: 608 base_st parameters and their arguments, 1889 edits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 04:19:56 +03:00
..
tests feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
boards.ludic feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
count.ludic feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
defs.ludic feat(packages): ludic.jobs - written jobs gated by standing and story, boards of posts rolled by the day on their own dice, progress counted from events and read back from state, handing in, rewards through a port, facts, a save section per scope 2026-09-25 07:35:45 +03:00
index.ludic feat(packages): ludic.jobs - written jobs gated by standing and story, boards of posts rolled by the day on their own dice, progress counted from events and read back from state, handing in, rewards through a port, facts, a save section per scope 2026-09-25 07:35:45 +03:00
jobs.ludic feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
lists.ludic wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
package.ludic feat(packages): ludic.jobs - written jobs gated by standing and story, boards of posts rolled by the day on their own dice, progress counted from events and read back from state, handing in, rewards through a port, facts, a save section per scope 2026-09-25 07:35:45 +03:00
ports.ludic feat(packages): ludic.jobs - written jobs gated by standing and story, boards of posts rolled by the day on their own dice, progress counted from events and read back from state, handing in, rewards through a port, facts, a save section per scope 2026-09-25 07:35:45 +03:00
posts.ludic wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
README.md feat(packages): ludic.jobs - written jobs gated by standing and story, boards of posts rolled by the day on their own dice, progress counted from events and read back from state, handing in, rewards through a port, facts, a save section per scope 2026-09-25 07:35:45 +03:00
save.ludic wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
state.ludic feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
system.ludic feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00

ludic.jobs

Work a player takes on for pay: written jobs offered by standing and story, boards of posts rolled by the day, progress counted from what happens and read back from what the state proves, handing in, and the reward paid through the game. Uses ludic.base and nothing else.

import "ludic.jobs"

A job is (kind, param, need) plus a reward (money, rep), a scope (JOBS_SELF yours, JOBS_PARTY the party's) and a gate (minrep in its scope, minstage of the game's story). What a kind measures is the game's: it counts a deed with jobs_count(kind, param, n), and answers JobsWorld.evidence(kind, param) with what the state already proves (-1: nothing). The game fills the registries - def Jobs from "jobs.lres", def JobBoards from "boards.lres" - and words every line itself.

The rules it keeps

  1. Events count, state proves, progress only goes up. A job under way takes jobs_count deeds and is reconciled every tick against the evidence, taking whichever is further; work done before a written job was taken counts, and a post counts only what came after it went up.
  2. A job with a need is ready when it is met; one with need: 0 (and no kind) is the game's own play, which says so with jobs_ready or pays at once with jobs_complete.
  3. A board rolls from its own dice. jobs_morning() replaces every empty, stale (keep days) or earlier-finished post, seeding the jobs' Rng from the day, the board, the slot and how many posts came before, and asking JobsWorld.roll(board, slot), which draws with jobs_roll and posts with jobs_post. The same day rolls the same board on every machine.
  4. Scopes have keepers. Where JobsWorld.owns(scope) is false (a guest's party jobs), jobs_count, the recheck and the morning leave them alone; the keeper applies another member's deed with jobs_count_in(scope, ...) and a guest takes the keeper's word with jobs_set / jobs_post_set.
  5. Paid once, through a port. jobs_hand_in / jobs_post_hand_in (or the moment an auto board's post is met) calls JobsPay.reward(scope, money, rep).

The ports

export port JobsWorld {
  rep: fn(int) -> int            # standing in a scope (unbound 0)
  stage: fn() -> int             # the story's stage (0)
  open: fn(int) -> bool          # a job the game allows today (all)
  evidence: fn(int, int) -> int  # (kind, param) as the state proves it (-1)
  day: fn() -> int               # (1)
  owns: fn(int) -> bool          # this machine keeps the scope (all)
  roll: fn(int, int) -> void     # (board, slot): post with jobs_post (nothing)
}
export port JobsPay { reward: fn(int, int, int) -> void }   # (scope, money, rep), required

API

JobDef { key, group, vendor, kind, param, need, money, rep, minrep, minstage, scope, title, text, how, icon }, open registry Jobs ... as JOB a written job (group is the game's list, JOBS_GROUPS of them)
JobBoard { key, slots, keep, auto, refill, scope }, open registry JobBoards ... as BOARD a board of posts
JOBS_OFFERED, JOBS_ACTIVE, JOBS_READY, JOBS_DONE; JOBS_SELF, JOBS_PARTY states and scopes
jobs_state(i), jobs_have(i), jobs_offered(i), jobs_under_way(i), jobs_ready_now(i) a job
jobs_take(i), jobs_ready(i), jobs_hand_in(i), jobs_complete(i), jobs_set(i, state, have) its verbs
jobs_set_have(i, v), jobs_add_have(i, n), jobs_mark(i, bit) the game's own play
jobs_count(kind, param, n), jobs_count_in(scope, kind, param, n), jobs_recheck() progress
jobs_at(group, vendor), jobs_taken(group), jobs_shown(group, most), jobs_first_open(group), jobs_done(group), jobs_set_done(group, n) lists and counts
jobs_morning(), jobs_roll(lo, hi), jobs_post(b, s, kind, param, need, money, rep), jobs_config_seed(seed) posting
jobs_slots(b), jobs_posted(b), jobs_post_kind / _param / _need / _have / _money / _rep / _state / _day(b, s), jobs_posts_done(b), jobs_total(b) the boards
jobs_post_hand_in(b, s), jobs_post_set(b, s, ...), jobs_set_total(b, n) a post's verbs
jobs_facts() -> Queue<JobsFact> { what: JOBS_F_OFFERED / _PROGRESSED / _COMPLETED / _HANDED_IN, job, board, slot, kind, param, have, need, money, rep, scope }
jobs_system(), jobs_party_system() "jobs" (yours, the tick) and "jobs.party" (the party's), each saving its scope's jobs and boards by key

Tests

ludic test packages/ludic.jobs