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>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-26 04:19:56 +03:00
parent 8cf4b3fed6
commit 71735b10a2
151 changed files with 58668 additions and 54719 deletions

View file

@ -38,19 +38,19 @@ function jobs__clear(jobs_st: mut JobsState, k: int) -> void {
}
# take a slot down and ask the game for what replaces it
function jobs__repost(base_st: mut BaseState, jobs_st: mut JobsState, b: int, s: int) -> void {
function jobs__repost(jobs_st: mut JobsState, b: int, s: int) -> void {
let k = jobs__at(jobs_st, b, s)
let day = JobsWorld.day()
rng_seed(jobs_st.jobs__dice, jobs_st.jobs__seed + day * 7 + b * 7919 + s * 104729 + jobs_st.jobs__serial[b] * 31)
jobs_st.jobs__serial[b] += 1
jobs__clear(jobs_st, k)
JobsWorld.roll(b, s)
if jobs_st.jobs__pk[k] >= 0 { jobs__fact(base_st, jobs_st, JOBS_F_OFFERED, -1, b, s, 0, jobs_st.jobs__pn[k]) }
if jobs_st.jobs__pk[k] >= 0 { jobs__fact(jobs_st, JOBS_F_OFFERED, -1, b, s, 0, jobs_st.jobs__pn[k]) }
}
# a new day, on the boards this machine keeps: an empty or stale post is replaced, and one
# finished on an earlier day
export function jobs_morning(base_st: mut BaseState, jobs_st: mut JobsState) -> void {
export function jobs_morning(jobs_st: mut JobsState) -> void {
jobs__ensure(jobs_st)
let day = JobsWorld.day()
for b in 0 .. BOARD_COUNT {
@ -59,31 +59,31 @@ export function jobs_morning(base_st: mut BaseState, jobs_st: mut JobsState) ->
for s in 0 .. bd.slots {
let k = jobs_st.jobs__off[b] + s
let finished = jobs_st.jobs__ps[k] == JOBS_DONE and day > jobs_st.jobs__pd[k]
if jobs_st.jobs__pk[k] < 0 or finished or day - jobs_st.jobs__pd[k] >= bd.keep { jobs__repost(base_st, jobs_st, b, s) }
if jobs_st.jobs__pk[k] < 0 or finished or day - jobs_st.jobs__pd[k] >= bd.keep { jobs__repost(jobs_st, b, s) }
}
}
}
function jobs__post_moved(base_st: mut BaseState, jobs_st: mut JobsState, b: int, s: int, have: int) -> void {
function jobs__post_moved(jobs_st: mut JobsState, b: int, s: int, have: int) -> void {
let k = jobs_st.jobs__off[b] + s
let was = jobs_st.jobs__ph[k]
jobs_st.jobs__ph[k] = have
if jobs_st.jobs__ph[k] > jobs_st.jobs__pn[k] { jobs_st.jobs__ph[k] = jobs_st.jobs__pn[k] }
if jobs_st.jobs__ph[k] > was { jobs__fact(base_st, jobs_st, JOBS_F_PROGRESSED, -1, b, s, jobs_st.jobs__ph[k], jobs_st.jobs__pn[k]) }
if jobs_st.jobs__ph[k] > was { jobs__fact(jobs_st, JOBS_F_PROGRESSED, -1, b, s, jobs_st.jobs__ph[k], jobs_st.jobs__pn[k]) }
if jobs_st.jobs__ps[k] != JOBS_ACTIVE or jobs_st.jobs__ph[k] < jobs_st.jobs__pn[k] { return }
jobs_st.jobs__ps[k] = JOBS_READY
jobs__fact(base_st, jobs_st, JOBS_F_COMPLETED, -1, b, s, jobs_st.jobs__ph[k], jobs_st.jobs__pn[k])
if JobBoards[b].auto { jobs_post_hand_in(base_st, jobs_st, b, s) }
jobs__fact(jobs_st, JOBS_F_COMPLETED, -1, b, s, jobs_st.jobs__ph[k], jobs_st.jobs__pn[k])
if JobBoards[b].auto { jobs_post_hand_in(jobs_st, b, s) }
}
export function jobs_post_hand_in(base_st: mut BaseState, jobs_st: mut JobsState, b: int, s: int) -> bool {
export function jobs_post_hand_in(jobs_st: mut JobsState, b: int, s: int) -> bool {
if not jobs_board_ok(b) or s < 0 or s >= JobBoards[b].slots { return false }
let k = jobs__at(jobs_st, b, s)
if jobs_st.jobs__ps[k] != JOBS_READY { return false }
jobs_st.jobs__ps[k] = JOBS_DONE
jobs_st.jobs__total[b] += 1
JobsPay.reward(JobBoards[b].scope, jobs_st.jobs__pm[k], jobs_st.jobs__pr[k])
jobs__fact(base_st, jobs_st, JOBS_F_HANDED_IN, -1, b, s, jobs_st.jobs__ph[k], jobs_st.jobs__pn[k])
if JobBoards[b].refill { jobs__repost(base_st, jobs_st, b, s) }
jobs__fact(jobs_st, JOBS_F_HANDED_IN, -1, b, s, jobs_st.jobs__ph[k], jobs_st.jobs__pn[k])
if JobBoards[b].refill { jobs__repost(jobs_st, b, s) }
return true
}

View file

@ -6,17 +6,17 @@ function jobs__matches(want_kind: int, want_param: int, kind: int, param: int) -
}
# `n` of (kind, param) happened here: every job and post of a scope this machine keeps
export function jobs_count(base_st: mut BaseState, jobs_st: mut JobsState, kind: int, param: int, n: int) -> void { jobs__count(base_st, jobs_st, -1, kind, param, n) }
export function jobs_count(jobs_st: mut JobsState, kind: int, param: int, n: int) -> void { jobs__count(jobs_st, -1, kind, param, n) }
# the same for one scope alone, kept or not: another member's deed arriving at the host
export function jobs_count_in(base_st: mut BaseState, jobs_st: mut JobsState, scope: int, kind: int, param: int, n: int) -> void { jobs__count(base_st, jobs_st, scope, kind, param, n) }
export function jobs_count_in(jobs_st: mut JobsState, scope: int, kind: int, param: int, n: int) -> void { jobs__count(jobs_st, scope, kind, param, n) }
function jobs__takes(only: int, scope: int) -> bool {
if only >= 0 { return scope == only }
return JobsWorld.owns(scope)
}
function jobs__count(base_st: mut BaseState, jobs_st: mut JobsState, only: int, kind: int, param: int, n: int) -> void {
function jobs__count(jobs_st: mut JobsState, only: int, kind: int, param: int, n: int) -> void {
jobs__ensure(jobs_st)
for i in 0 .. JOB_COUNT {
let d = Jobs[i]
@ -24,50 +24,50 @@ function jobs__count(base_st: mut BaseState, jobs_st: mut JobsState, only: int,
if not jobs__matches(d.kind, d.param, kind, param) { continue }
let was = jobs_st.jobs__hv[i]
jobs_st.jobs__hv[i] = was + n
jobs__moved(base_st, jobs_st, i, was)
jobs__moved(jobs_st, i, was)
}
for b in 0 .. BOARD_COUNT {
if not jobs__takes(only, JobBoards[b].scope) { continue }
for s in 0 .. JobBoards[b].slots {
let k = jobs_st.jobs__off[b] + s
if jobs_st.jobs__ps[k] != JOBS_ACTIVE or not jobs__matches(jobs_st.jobs__pk[k], jobs_st.jobs__pp[k], kind, param) { continue }
jobs__post_moved(base_st, jobs_st, b, s, jobs_st.jobs__ph[k] + n)
jobs__post_moved(jobs_st, b, s, jobs_st.jobs__ph[k] + n)
}
}
}
function jobs__recheck_one(base_st: mut BaseState, jobs_st: mut JobsState, i: int) -> void {
function jobs__recheck_one(jobs_st: mut JobsState, i: int) -> void {
let d = Jobs[i]
if jobs_st.jobs__st[i] != JOBS_ACTIVE or d.kind < 0 or not JobsWorld.owns(d.scope) { return }
let e = JobsWorld.evidence(d.kind, d.param)
if e <= jobs_st.jobs__hv[i] { return }
let was = jobs_st.jobs__hv[i]
jobs_st.jobs__hv[i] = e
jobs__moved(base_st, jobs_st, i, was)
jobs__moved(jobs_st, i, was)
}
# every job under way against what the state already proves: work done before it was taken counts
# for a written job; a post counts only what came after it went up
export function jobs_recheck(base_st: mut BaseState, jobs_st: mut JobsState) -> void {
export function jobs_recheck(jobs_st: mut JobsState) -> void {
jobs__ensure(jobs_st)
for i in 0 .. JOB_COUNT { jobs__recheck_one(base_st, jobs_st, i) }
for i in 0 .. JOB_COUNT { jobs__recheck_one(jobs_st, i) }
for b in 0 .. BOARD_COUNT {
if not JobsWorld.owns(JobBoards[b].scope) { continue }
for s in 0 .. JobBoards[b].slots {
let k = jobs_st.jobs__off[b] + s
if jobs_st.jobs__ps[k] != JOBS_ACTIVE or jobs_st.jobs__pk[k] < 0 { continue }
let e = JobsWorld.evidence(jobs_st.jobs__pk[k], jobs_st.jobs__pp[k])
if e >= 0 and e - jobs_st.jobs__pb[k] > jobs_st.jobs__ph[k] { jobs__post_moved(base_st, jobs_st, b, s, e - jobs_st.jobs__pb[k]) }
if e >= 0 and e - jobs_st.jobs__pb[k] > jobs_st.jobs__ph[k] { jobs__post_moved(jobs_st, b, s, e - jobs_st.jobs__pb[k]) }
}
}
}
# offers newly open are said once; the first look after a reset says none of them
function jobs__offers(base_st: mut BaseState, jobs_st: mut JobsState) -> void {
function jobs__offers(jobs_st: mut JobsState) -> void {
jobs__ensure(jobs_st)
for i in 0 .. JOB_COUNT {
let now = jobs_offered(jobs_st, i)
if now and not jobs_st.jobs__seen[i] and jobs_st.jobs__primed { jobs__fact(base_st, jobs_st, JOBS_F_OFFERED, i, -1, -1, 0, Jobs[i].need) }
if now and not jobs_st.jobs__seen[i] and jobs_st.jobs__primed { jobs__fact(jobs_st, JOBS_F_OFFERED, i, -1, -1, 0, Jobs[i].need) }
jobs_st.jobs__seen[i] = now
}
jobs_st.jobs__primed = true

View file

@ -26,55 +26,55 @@ export function jobs_offered(jobs_st: mut JobsState, i: int) -> bool {
return JobsWorld.rep(d.scope) >= d.minrep and JobsWorld.stage() >= d.minstage and JobsWorld.open(i)
}
export function jobs_take(base_st: mut BaseState, jobs_st: mut JobsState, i: int) -> bool {
export function jobs_take(jobs_st: mut JobsState, i: int) -> bool {
if not jobs_ok(i) or jobs_state(jobs_st, i) != JOBS_OFFERED { return false }
jobs_st.jobs__st[i] = JOBS_ACTIVE
jobs_st.jobs__hv[i] = 0
jobs__recheck_one(base_st, jobs_st, i)
jobs__recheck_one(jobs_st, i)
return true
}
# a count has moved: a job with a need is ready when it is met
function jobs__moved(base_st: mut BaseState, jobs_st: mut JobsState, i: int, was: int) -> void {
function jobs__moved(jobs_st: mut JobsState, i: int, was: int) -> void {
let d = Jobs[i]
if d.need > 0 and jobs_st.jobs__hv[i] > d.need { jobs_st.jobs__hv[i] = d.need }
if jobs_st.jobs__hv[i] > was { jobs__fact(base_st, jobs_st, JOBS_F_PROGRESSED, i, -1, -1, jobs_st.jobs__hv[i], d.need) }
if jobs_st.jobs__st[i] == JOBS_ACTIVE and d.need > 0 and jobs_st.jobs__hv[i] >= d.need { jobs_ready(base_st, jobs_st, i) }
if jobs_st.jobs__hv[i] > was { jobs__fact(jobs_st, JOBS_F_PROGRESSED, i, -1, -1, jobs_st.jobs__hv[i], d.need) }
if jobs_st.jobs__st[i] == JOBS_ACTIVE and d.need > 0 and jobs_st.jobs__hv[i] >= d.need { jobs_ready(jobs_st, i) }
}
# the game's own play: a step counter, a mask of targets met
export function jobs_set_have(base_st: mut BaseState, jobs_st: mut JobsState, i: int, v: int) -> void {
export function jobs_set_have(jobs_st: mut JobsState, i: int, v: int) -> void {
let was = jobs_have(jobs_st, i)
jobs_st.jobs__hv[i] = v
jobs__moved(base_st, jobs_st, i, was)
jobs__moved(jobs_st, i, was)
}
export function jobs_add_have(base_st: mut BaseState, jobs_st: mut JobsState, i: int, n: int) -> void { jobs_set_have(base_st, jobs_st, i, jobs_have(jobs_st, i) + n) }
export function jobs_add_have(jobs_st: mut JobsState, i: int, n: int) -> void { jobs_set_have(jobs_st, i, jobs_have(jobs_st, i) + n) }
export function jobs_mark(base_st: mut BaseState, jobs_st: mut JobsState, i: int, bit: int) -> void { jobs_set_have(base_st, jobs_st, i, jobs_have(jobs_st, i) | (1 << bit)) }
export function jobs_mark(jobs_st: mut JobsState, i: int, bit: int) -> void { jobs_set_have(jobs_st, i, jobs_have(jobs_st, i) | (1 << bit)) }
# everything in hand
export function jobs_ready(base_st: mut BaseState, jobs_st: mut JobsState, i: int) -> void {
export function jobs_ready(jobs_st: mut JobsState, i: int) -> void {
if jobs_state(jobs_st, i) != JOBS_ACTIVE { return }
jobs_st.jobs__st[i] = JOBS_READY
jobs__fact(base_st, jobs_st, JOBS_F_COMPLETED, i, -1, -1, jobs_st.jobs__hv[i], Jobs[i].need)
jobs__fact(jobs_st, JOBS_F_COMPLETED, i, -1, -1, jobs_st.jobs__hv[i], Jobs[i].need)
}
export function jobs_hand_in(base_st: mut BaseState, jobs_st: mut JobsState, i: int) -> bool {
export function jobs_hand_in(jobs_st: mut JobsState, i: int) -> bool {
if not jobs_ok(i) or jobs_state(jobs_st, i) != JOBS_READY { return false }
let d = Jobs[i]
jobs_st.jobs__st[i] = JOBS_DONE
if d.group >= 0 and d.group < JOBS_GROUPS { jobs_st.jobs__done_n[d.group] += 1 }
JobsPay.reward(d.scope, d.money, d.rep)
jobs__fact(base_st, jobs_st, JOBS_F_HANDED_IN, i, -1, -1, jobs_st.jobs__hv[i], d.need)
jobs__fact(jobs_st, JOBS_F_HANDED_IN, i, -1, -1, jobs_st.jobs__hv[i], d.need)
return true
}
# finished and paid in one go, whatever the count says
export function jobs_complete(base_st: mut BaseState, jobs_st: mut JobsState, i: int) -> bool {
export function jobs_complete(jobs_st: mut JobsState, i: int) -> bool {
if not jobs_ok(i) or not jobs_under_way(jobs_st, i) { return false }
jobs_ready(base_st, jobs_st, i)
return jobs_hand_in(base_st, jobs_st, i)
jobs_ready(jobs_st, i)
return jobs_hand_in(jobs_st, i)
}
# the state outright: the host's word for a party job, a test

View file

@ -59,12 +59,12 @@ function jobs__ensure(jobs_st: mut JobsState) -> void {
jobs_st.jobs__dice = rng_new(jobs_st.jobs__seed)
}
export function jobs_facts(base_st: mut BaseState, jobs_st: mut JobsState) -> Queue<JobsFact> {
if jobs_st.jobs__q == null { jobs_st.jobs__q = queue_new(base_st, "jobs.facts") }
export function jobs_facts(jobs_st: mut JobsState) -> Queue<JobsFact> {
if jobs_st.jobs__q == null { jobs_st.jobs__q = queue_new("jobs.facts") }
return jobs_st.jobs__q
}
function jobs__fact(base_st: mut BaseState, jobs_st: mut JobsState, what: int, job: int, b: int, s: int, have: int, need: int) -> void {
function jobs__fact(jobs_st: mut JobsState, what: int, job: int, b: int, s: int, have: int, need: int) -> void {
let f = new JobsFact
f.what = what
f.job = job
@ -85,7 +85,7 @@ function jobs__fact(base_st: mut BaseState, jobs_st: mut JobsState, what: int, j
f.rep = jobs_st.jobs__pr[jobs_st.jobs__off[b] + s]
f.scope = JobBoards[b].scope
}
q_push(base_st, jobs_facts(base_st, jobs_st), f)
q_push(jobs_facts(jobs_st), f)
}
# the base of every roll: a post's seed is this, the day, the board, the slot and how many before

View file

@ -1,9 +1,9 @@
# system.ludic - two systems, one per owner: "jobs" is this player's (personal jobs and boards, the
# done counts, the tick), "jobs.party" the party's, saved with the world
export function jobs_reset(base_st: mut BaseState, jobs_st: mut JobsState) -> void {
export function jobs_reset(jobs_st: mut JobsState) -> void {
jobs__reset_scope(jobs_st, JOBS_SELF)
for g in 0 .. JOBS_GROUPS { jobs_st.jobs__done_n[g] = 0 }
q_clear(base_st, jobs_facts(base_st, jobs_st))
q_clear(jobs_facts(jobs_st))
}
export function jobs_party_reset(jobs_st: mut JobsState) -> void { jobs__reset_scope(jobs_st, JOBS_PARTY) }
@ -31,9 +31,9 @@ export function jobs_party_save(jobs_st: mut JobsState) -> Val {
export function jobs_party_load(jobs_st: mut JobsState, v: Val, version: int) -> void { jobs__load_scope(jobs_st, JOBS_PARTY, v) }
function jobs__tick(base_st: mut BaseState, jobs_st: mut JobsState, t: Tick) -> void {
jobs__offers(base_st, jobs_st)
jobs_recheck(base_st, jobs_st)
function jobs__tick(jobs_st: mut JobsState, t: Tick) -> void {
jobs__offers(jobs_st)
jobs_recheck(jobs_st)
}
export function jobs_system() -> System {

View file

@ -47,7 +47,7 @@ program JobsTest {
bind JobsWorld { rep: fn world_rep, stage: fn world_stage, day: fn world_day, owns: fn world_owns, evidence: fn world_evidence, roll: fn world_roll }
bind JobsPay { reward: fn pay }
function fresh(base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) -> void {
function fresh(jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) -> void {
jobs_test_st.rep = 0
jobs_test_st.stage = 0
jobs_test_st.day = 1
@ -55,12 +55,12 @@ program JobsTest {
jobs_test_st.fish = 0
jobs_test_st.paid = 0
jobs_test_st.paid_rep = 0
jobs_reset(base_st, jobs_st)
jobs_reset(jobs_st)
jobs_party_reset(jobs_st)
}
function count_of(base_st: mut BaseState, jobs_st: mut JobsState, what: int) -> int {
let fs = q_drain(base_st, jobs_facts(base_st, jobs_st))
function count_of(jobs_st: mut JobsState, what: int) -> int {
let fs = q_drain(jobs_facts(jobs_st))
var n = 0
for i in 0 .. len(fs) { if fs[i].what == what { n += 1 } }
return n
@ -72,8 +72,8 @@ program JobsTest {
expect_eq(BOARD_COUNT, 2)
}
test "a gate of standing and story" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
test "a gate of standing and story" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
expect(jobs_offered(jobs_st, JOB_CATCH))
expect(not jobs_offered(jobs_st, JOB_DEER))
jobs_test_st.rep = 10
@ -84,148 +84,148 @@ program JobsTest {
expect_eq(len(jobs_at(jobs_st, 0, -1)), 3)
}
test "counted by events, readied, handed in and paid" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
jobs_count(base_st, jobs_st, K_FISH, 0, 1)
test "counted by events, readied, handed in and paid" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
jobs_count(jobs_st, K_FISH, 0, 1)
expect_eq(jobs_have(jobs_st, JOB_CATCH), 0)
expect(jobs_take(base_st, jobs_st, JOB_CATCH))
jobs_count(base_st, jobs_st, K_FISH, 0, 2)
expect(jobs_take(jobs_st, JOB_CATCH))
jobs_count(jobs_st, K_FISH, 0, 2)
expect_eq(jobs_state(jobs_st, JOB_CATCH), JOBS_ACTIVE)
jobs_count(base_st, jobs_st, K_FISH, 0, 5)
jobs_count(jobs_st, K_FISH, 0, 5)
expect_eq(jobs_have(jobs_st, JOB_CATCH), 3)
expect(jobs_ready_now(jobs_st, JOB_CATCH))
expect_eq(count_of(base_st, jobs_st, JOBS_F_COMPLETED), 1)
expect(jobs_hand_in(base_st, jobs_st, JOB_CATCH))
expect(not jobs_hand_in(base_st, jobs_st, JOB_CATCH))
expect_eq(count_of(jobs_st, JOBS_F_COMPLETED), 1)
expect(jobs_hand_in(jobs_st, JOB_CATCH))
expect(not jobs_hand_in(jobs_st, JOB_CATCH))
expect_eq(jobs_test_st.paid, 30)
expect_eq(jobs_test_st.paid_rep, 5)
expect_eq(jobs_done(jobs_st, 0), 1)
expect_eq(count_of(base_st, jobs_st, JOBS_F_HANDED_IN), 1)
expect_eq(count_of(jobs_st, JOBS_F_HANDED_IN), 1)
}
test "the state already proves the work" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
test "the state already proves the work" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
jobs_test_st.fish = 2
jobs_take(base_st, jobs_st, JOB_CATCH)
jobs_take(jobs_st, JOB_CATCH)
expect_eq(jobs_have(jobs_st, JOB_CATCH), 2)
jobs_test_st.fish = 3
jobs_recheck(base_st, jobs_st)
jobs_recheck(jobs_st)
expect(jobs_ready_now(jobs_st, JOB_CATCH))
}
test "a param narrows the count" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
test "a param narrows the count" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
jobs_test_st.rep = 10
jobs_take(base_st, jobs_st, JOB_DEER)
jobs_count(base_st, jobs_st, K_PHOTO, 3, 1)
jobs_take(jobs_st, JOB_DEER)
jobs_count(jobs_st, K_PHOTO, 3, 1)
expect_eq(jobs_have(jobs_st, JOB_DEER), 0)
jobs_count(base_st, jobs_st, K_PHOTO, 4, 1)
jobs_count(jobs_st, K_PHOTO, 4, 1)
expect(jobs_ready_now(jobs_st, JOB_DEER))
}
test "the game's own play readies a job with no need" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
jobs_take(base_st, jobs_st, JOB_STILL)
jobs_mark(base_st, jobs_st, JOB_STILL, 2)
test "the game's own play readies a job with no need" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
jobs_take(jobs_st, JOB_STILL)
jobs_mark(jobs_st, JOB_STILL, 2)
expect_eq(jobs_have(jobs_st, JOB_STILL), 4)
expect_eq(jobs_state(jobs_st, JOB_STILL), JOBS_ACTIVE)
expect(jobs_complete(base_st, jobs_st, JOB_STILL))
expect(jobs_complete(jobs_st, JOB_STILL))
expect_eq(jobs_test_st.paid, 60)
expect_eq(jobs_done(jobs_st, 1), 1)
}
test "a guest counts its own and leaves the party's to the host" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
test "a guest counts its own and leaves the party's to the host" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
jobs_test_st.guest = true
jobs_take(base_st, jobs_st, JOB_TEN)
jobs_take(base_st, jobs_st, JOB_CATCH)
jobs_count(base_st, jobs_st, K_FISH, 0, 2)
jobs_take(jobs_st, JOB_TEN)
jobs_take(jobs_st, JOB_CATCH)
jobs_count(jobs_st, K_FISH, 0, 2)
expect_eq(jobs_have(jobs_st, JOB_TEN), 0)
expect_eq(jobs_have(jobs_st, JOB_CATCH), 2)
jobs_count_in(base_st, jobs_st, JOBS_PARTY, K_FISH, 0, 4)
jobs_count_in(jobs_st, JOBS_PARTY, K_FISH, 0, 4)
expect_eq(jobs_have(jobs_st, JOB_TEN), 4)
expect_eq(jobs_have(jobs_st, JOB_CATCH), 2)
}
test "an offer is said once it opens, never for what was open at the reset" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
test "an offer is said once it opens, never for what was open at the reset" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
jobs_system().tick(tick_new(0.1, 1, 0.0))
expect_eq(count_of(base_st, jobs_st, JOBS_F_OFFERED), 0)
expect_eq(count_of(jobs_st, JOBS_F_OFFERED), 0)
jobs_test_st.rep = 10
jobs_system().tick(tick_new(0.1, 2, 0.0))
expect_eq(count_of(base_st, jobs_st, JOBS_F_OFFERED), 1)
expect_eq(count_of(jobs_st, JOBS_F_OFFERED), 1)
}
test "a board rolled by the day, the same every time" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
test "a board rolled by the day, the same every time" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
jobs_test_st.day = 3
jobs_morning(base_st, jobs_st)
jobs_morning(jobs_st)
expect_eq(len(jobs_posted(jobs_st, BOARD_DAILY)), 3)
expect_eq(len(jobs_posted(jobs_st, BOARD_ADA)), 2)
let a = jobs_post_need(jobs_st, BOARD_DAILY, 0) * 100 + jobs_post_need(jobs_st, BOARD_DAILY, 1) * 10 + jobs_post_need(jobs_st, BOARD_DAILY, 2)
fresh(base_st, jobs_st, jobs_test_st)
fresh(jobs_st, jobs_test_st)
jobs_test_st.day = 3
jobs_morning(base_st, jobs_st)
jobs_morning(jobs_st)
let b = jobs_post_need(jobs_st, BOARD_DAILY, 0) * 100 + jobs_post_need(jobs_st, BOARD_DAILY, 1) * 10 + jobs_post_need(jobs_st, BOARD_DAILY, 2)
expect_eq(a, b)
expect(jobs_post_need(jobs_st, BOARD_DAILY, 0) >= 2 and jobs_post_need(jobs_st, BOARD_DAILY, 0) <= 4)
}
test "a daily pays the moment it is met and waits for the morning" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
test "a daily pays the moment it is met and waits for the morning" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
jobs_test_st.day = 2
jobs_morning(base_st, jobs_st)
jobs_morning(jobs_st)
let need = jobs_post_need(jobs_st, BOARD_DAILY, 1)
jobs_count(base_st, jobs_st, K_WOOD, -1, need)
jobs_count(jobs_st, K_WOOD, -1, need)
expect_eq(jobs_post_state(jobs_st, BOARD_DAILY, 1), JOBS_DONE)
expect(jobs_posts_done(jobs_st, BOARD_DAILY) >= 1)
expect_eq(jobs_test_st.paid, 7 * jobs_posts_done(jobs_st, BOARD_DAILY))
jobs_morning(base_st, jobs_st)
jobs_morning(jobs_st)
expect_eq(jobs_post_state(jobs_st, BOARD_DAILY, 1), JOBS_DONE)
jobs_test_st.day = 3
jobs_morning(base_st, jobs_st)
jobs_morning(jobs_st)
expect_eq(jobs_post_state(jobs_st, BOARD_DAILY, 1), JOBS_ACTIVE)
}
test "a post counts only what came after it went up, and is replaced when handed in" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
test "a post counts only what came after it went up, and is replaced when handed in" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
jobs_test_st.fish = 5
jobs_morning(base_st, jobs_st)
jobs_morning(jobs_st)
let need = jobs_post_need(jobs_st, BOARD_ADA, 0)
jobs_recheck(base_st, jobs_st)
jobs_recheck(jobs_st)
expect_eq(jobs_post_have(jobs_st, BOARD_ADA, 0), 0)
jobs_test_st.fish = 5 + need
jobs_recheck(base_st, jobs_st)
jobs_recheck(jobs_st)
expect_eq(jobs_post_state(jobs_st, BOARD_ADA, 0), JOBS_READY)
expect(jobs_post_hand_in(base_st, jobs_st, BOARD_ADA, 0))
expect(jobs_post_hand_in(jobs_st, BOARD_ADA, 0))
expect_eq(jobs_test_st.paid_scope, JOBS_PARTY)
expect_eq(jobs_total(jobs_st, BOARD_ADA), 1)
expect_eq(jobs_post_state(jobs_st, BOARD_ADA, 0), JOBS_ACTIVE)
}
test "a stale post comes down after its days" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
jobs_morning(base_st, jobs_st)
test "a stale post comes down after its days" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
jobs_morning(jobs_st)
jobs_test_st.day = 7
jobs_morning(base_st, jobs_st)
jobs_morning(jobs_st)
expect_eq(jobs_post_day(jobs_st, BOARD_ADA, 0), 1)
jobs_test_st.day = 8
jobs_morning(base_st, jobs_st)
jobs_morning(jobs_st)
expect_eq(jobs_post_day(jobs_st, BOARD_ADA, 0), 8)
}
test "each scope saves its own, by key" (base_st: mut BaseState, jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(base_st, jobs_st, jobs_test_st)
jobs_take(base_st, jobs_st, JOB_CATCH)
jobs_count(base_st, jobs_st, K_FISH, 0, 2)
jobs_take(base_st, jobs_st, JOB_TEN)
jobs_count(base_st, jobs_st, K_FISH, 0, 1)
test "each scope saves its own, by key" (jobs_st: mut JobsState, jobs_test_st: mut JobsTestState) {
fresh(jobs_st, jobs_test_st)
jobs_take(jobs_st, JOB_CATCH)
jobs_count(jobs_st, K_FISH, 0, 2)
jobs_take(jobs_st, JOB_TEN)
jobs_count(jobs_st, K_FISH, 0, 1)
jobs_test_st.day = 2
jobs_morning(base_st, jobs_st)
jobs_morning(jobs_st)
let body = jobs_save(jobs_st)
let world = jobs_party_save(jobs_st)
let need = jobs_post_need(jobs_st, BOARD_ADA, 1)
fresh(base_st, jobs_st, jobs_test_st)
fresh(jobs_st, jobs_test_st)
jobs_load(jobs_st, body, 1)
expect_eq(jobs_have(jobs_st, JOB_CATCH), 3)
expect_eq(jobs_state(jobs_st, JOB_TEN), JOBS_OFFERED)