From 78c261c62528049c0b0b8178d9303697785c842d Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Tue, 29 Sep 2026 22:51:01 +0300 Subject: [PATCH] ludic.session: ply_rest_least, the party's rest for the clock (the valley's selftest57 down into the packages): the roster's freed slot reused and ply_leave_all, the nearest player alone, and ludic.shop's week of demand leaving the shared Random stream untouched Co-Authored-By: Claude Opus 5.5 --- changes/session-rest-least.md | 7 +++++ packages/ludic.session/README.md | 1 + .../ludic.session/tests/session_test.ludic | 28 ++++++++++++++++++- packages/ludic.session/where.ludic | 7 +++++ packages/ludic.shop/tests/shop_test.ludic | 4 +++ 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 changes/session-rest-least.md diff --git a/changes/session-rest-least.md b/changes/session-rest-least.md new file mode 100644 index 00000000..2fcf2a9a --- /dev/null +++ b/changes/session-rest-least.md @@ -0,0 +1,7 @@ +bump: minor +type: feature +**`ludic.session`: `ply_rest_least()`, the party's rest for the clock** - the least any player on is +resting, 1 while one is on their feet, so one player sitting never hurries another's day. A game binds it +straight to `ClockWorld.rest_scale`. Its tests now also hold a freed slot handed to the next to join, +`ply_leave_all`, and the nearest player alone being this one at its own distance; `ludic.shop`'s own test +holds that rolling and asking the week's demand leaves the world's shared `Random` stream where it was. diff --git a/packages/ludic.session/README.md b/packages/ludic.session/README.md index e5e4b4d6..f916f248 100644 --- a/packages/ludic.session/README.md +++ b/packages/ludic.session/README.md @@ -37,6 +37,7 @@ down ends the night). The transport says what this machine became (`ses_set_role | `ply_join(pid) -> slot`, `ply_leave(p)`, `ply_leave_all()` | the roster (-1: full, or pid 0) | | `ply_report(p, x, y, z, yaw, speed)`, `ply_report_rest(p, rest, sleeping)`, `ply_report_name(p, name)` | what a remote machine said | | `ply_on`, `ply_is_local`, `ply_slot_of(pid)`, `ply_count`, `ply_pid`, `ply_name`, `ply_x/y/z/yaw/speed/rest(p)`, `ply_sleeping(p)` | who and where | +| `ply_rest_least()` | the party's rest for the clock: the least any player on is resting (1 while one is on their feet) | | `ply_near(x, z)`, `ply_near_d2`, `ply_near_i()`, `ply_dist(p, x, z)` | the nearest player | | `ses_role()`, `ses_set_role(r)`, `ses_owns_world()`, `ses_live()`, `ses_hosting()`, `ses_guest()`, `ses_night_passes()` | roles | | `ses_set_party(max, open)`, `ses_set_open(b)`, `ses_max()`, `ses_open()`, `ses_room_for_one()` | the party a host allows | diff --git a/packages/ludic.session/tests/session_test.ludic b/packages/ludic.session/tests/session_test.ludic index a2429d2f..3e496e55 100644 --- a/packages/ludic.session/tests/session_test.ludic +++ b/packages/ludic.session/tests/session_test.ludic @@ -6,13 +6,15 @@ program SessionTest { numbers float state SessionTestState { me_x: float = 0.0 + me_rest: float = 1.0 } let me_z: float = 0.0 function fx(session_test_st: SessionTestState) -> float { return session_test_st.me_x } function fy() -> float { return 0.0 } function fz() -> float { return me_z } function fname() -> string { return "me" } - bind SessionLocal { x: fn fx, y: fn fy, z: fn fz, name: fn fname } + function frest(session_test_st: SessionTestState) -> float { return session_test_st.me_rest } + bind SessionLocal { x: fn fx, y: fn fy, z: fn fz, rest: fn frest, name: fn fname } function whats(session_st: SessionState) -> []int { let fs = q_drain(ses_facts(session_st)) @@ -59,6 +61,30 @@ program SessionTest { for i in 0 .. PLY_MAX - 1 { expect(ply_join(session_st, 10 + i) > 0) } expect_eq(ply_join(session_st, 99), -1) expect_eq(ply_count(session_st), PLY_MAX) + ply_leave(session_st, 2) + expect(not ply_on(session_st, 2)) + expect_eq(ply_join(session_st, 98), 2) + ply_leave_all(session_st) + expect_eq(ply_count(session_st), 1) + } + + test "alone, the nearest player is this one, at its own distance" (session_st: mut SessionState, session_test_st: mut SessionTestState) { + session_test_st.me_x = 3.0 + expect_near(ply_near(session_st, 40.0, -21.0), Math.sqrt(37.0 * 37.0 + 21.0 * 21.0), 0.001) + expect_eq(ply_near_i(session_st), 0) + } + + test "the party's rest is the least-resting player's: one sitting does not hurry the other" (session_st: mut SessionState, session_test_st: mut SessionTestState) { + session_test_st.me_rest = 4.0 + expect(ply_rest_least(session_st) == 4.0) + let p = ply_join(session_st, 21) + expect(ply_rest_least(session_st) == 1.0) + ply_report_rest(session_st, p, 3.0, false) + expect(ply_rest_least(session_st) == 3.0) + ply_leave(session_st, p) + expect(ply_rest_least(session_st) == 4.0) + session_test_st.me_rest = 1.0 + expect(ply_rest_least(session_st) == 1.0) } test "the nearest player, whoever it is" (session_st: mut SessionState, session_test_st: mut SessionTestState) { diff --git a/packages/ludic.session/where.ludic b/packages/ludic.session/where.ludic index d0d82a4c..add445af 100644 --- a/packages/ludic.session/where.ludic +++ b/packages/ludic.session/where.ludic @@ -41,6 +41,13 @@ export function ply_rest(session_st: SessionState, p: int) -> float { return session_st.session__slot[p].rest } +# how much the party's rest speeds the clock: only as much as the least-resting player on +export function ply_rest_least(session_st: SessionState) -> float { + var m = ply_rest(session_st, 0) + for p in 1 .. PLY_MAX { if ply_on(session_st, p) { m = Math.min(m, ply_rest(session_st, p)) } } + return m +} + export function ply_sleeping(session_st: SessionState, p: int) -> bool { if p == 0 { return session_st.session__me_sleeping } return ply_on(session_st, p) and session_st.session__slot[p].sleeping diff --git a/packages/ludic.shop/tests/shop_test.ludic b/packages/ludic.shop/tests/shop_test.ludic index 132c0893..b25ee11f 100644 --- a/packages/ludic.shop/tests/shop_test.ludic +++ b/packages/ludic.shop/tests/shop_test.ludic @@ -153,6 +153,7 @@ program ShopTest { } test "the week's demand is the shop's own dice, and turns with the week" (shop_st: mut ShopState, shop_test_st: mut ShopTestState) { + Random.seed(77) # the world's shared stream, which a price never moves fresh(shop_st, shop_test_st) shop_roll(shop_st) # the shop's tick rolls the week; asking only reads it let a = shop_wanted(shop_st, 0) @@ -165,6 +166,9 @@ program ShopTest { let plain = shop_price_sell_at(shop_st, -1, it) expect(shop_demand_mult(shop_st, 0, it) == 1.5) expect(shop_price_sell(shop_st, it) >= plain) + let shared = Random.range(0, 999999) + Random.seed(77) + expect_eq(shared, Random.range(0, 999999)) var moved = false for w in 1 .. 12 { shop_test_st.day = 1 + w * 7