ludic/packages/ludic.jobs
Orkuncakilkaya 317d63d822 fix(ludic.jobs): fact records pooled (the ludic.wildlife idiom) - a record per deed that moved a job was never given back
tests: 200 jobs taken and given up make 2 records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:17:55 +03:00
..
tests fix(ludic.jobs): fact records pooled (the ludic.wildlife idiom) - a record per deed that moved a job was never given back 2026-09-28 13:17:55 +03:00
boards.ludic refactor(packages): 0.R5 - jobs, session, settings, hints and net make their tables when the state is made, not on the first question (jobs__ensure, session__init and ply_init, sg_init, hn_mute_ensure, net__init gone); their questions read 2026-09-27 01:29:32 +03:00
count.ludic refactor(packages): 0.R5 - jobs, session, settings, hints and net make their tables when the state is made, not on the first question (jobs__ensure, session__init and ply_init, sg_init, hn_mute_ensure, net__init gone); their questions read 2026-09-27 01:29:32 +03:00
defs.ludic feat(jobs): phase 3 - a job can be given up: jobs_give_up puts a written job back on offer from the next day (the day back is saved with it, so giving up is never a reroll), jobs_post_give_up takes a post down for the morning to replace; an auto board's posts are not taken and not given up; JOBS_F_GIVEN_UP tells the game to take down what it placed 2026-09-27 15:28:06 +03:00
facts.ludic fix(ludic.jobs): fact records pooled (the ludic.wildlife idiom) - a record per deed that moved a job was never given back 2026-09-28 13:17:55 +03:00
giveup.ludic feat(jobs): phase 3 - a job can be given up: jobs_give_up puts a written job back on offer from the next day (the day back is saved with it, so giving up is never a reroll), jobs_post_give_up takes a post down for the morning to replace; an auto board's posts are not taken and not given up; JOBS_F_GIVEN_UP tells the game to take down what it placed 2026-09-27 15:28:06 +03:00
index.ludic fix(ludic.jobs): fact records pooled (the ludic.wildlife idiom) - a record per deed that moved a job was never given back 2026-09-28 13:17:55 +03:00
jobs.ludic feat(jobs): phase 3 - a job can be given up: jobs_give_up puts a written job back on offer from the next day (the day back is saved with it, so giving up is never a reroll), jobs_post_give_up takes a post down for the morning to replace; an auto board's posts are not taken and not given up; JOBS_F_GIVEN_UP tells the game to take down what it placed 2026-09-27 15:28:06 +03:00
lists.ludic refactor(packages): 0.R5 - jobs, session, settings, hints and net make their tables when the state is made, not on the first question (jobs__ensure, session__init and ply_init, sg_init, hn_mute_ensure, net__init gone); their questions read 2026-09-27 01:29:32 +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 refactor(packages): 0.R5 - jobs, session, settings, hints and net make their tables when the state is made, not on the first question (jobs__ensure, session__init and ply_init, sg_init, hn_mute_ensure, net__init gone); their questions read 2026-09-27 01:29:32 +03:00
README.md feat(jobs): phase 3 - a job can be given up: jobs_give_up puts a written job back on offer from the next day (the day back is saved with it, so giving up is never a reroll), jobs_post_give_up takes a post down for the morning to replace; an auto board's posts are not taken and not given up; JOBS_F_GIVEN_UP tells the game to take down what it placed 2026-09-27 15:28:06 +03:00
save.ludic feat(jobs): phase 3 - a job can be given up: jobs_give_up puts a written job back on offer from the next day (the day back is saved with it, so giving up is never a reroll), jobs_post_give_up takes a post down for the morning to replace; an auto board's posts are not taken and not given up; JOBS_F_GIVEN_UP tells the game to take down what it placed 2026-09-27 15:28:06 +03:00
state.ludic fix(ludic.jobs): fact records pooled (the ludic.wildlife idiom) - a record per deed that moved a job was never given back 2026-09-28 13:17:55 +03:00
system.ludic refactor(packages): 0.R5 - jobs, session, settings, hints and net make their tables when the state is made, not on the first question (jobs__ensure, session__init and ply_init, sg_init, hn_mute_ensure, net__init gone); their questions read 2026-09-27 01:29:32 +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_give_up(i), jobs_back_on(i), jobs_post_give_up(b, s) letting one go: a written job is offered again from the next day (the day back is saved with it), a post comes down and the morning replaces it; an auto board's posts are never taken, so never given up
jobs_facts() -> Queue<JobsFact> { what: JOBS_F_OFFERED / _PROGRESSED / _COMPLETED / _HANDED_IN / _GIVEN_UP, 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