wip(0.S3): the packages migrated by ludic migrate state packages - every package test green

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 14:02:21 +03:00
parent 1e8b5b0523
commit 07505e7ef2
294 changed files with 14996 additions and 14675 deletions

View file

@ -1,17 +1,17 @@
# near.ludic - the world asks who is nearest, never "the player": an animal notices whoever is
# closest, a camper keeps clear of everyone
export function ply_near_d2(x: float, z: float) -> float {
session__near_i = 0
export function ply_near_d2(session_st: mut SessionState, x: float, z: float) -> float {
session_st.session__near_i = 0
var best = 0.0
var first = true
for p in 0 .. PLY_MAX {
if not ply_on(p) { continue }
let dx = x - ply_x(p)
let dz = z - ply_z(p)
if not ply_on(session_st, p) { continue }
let dx = x - ply_x(session_st, p)
let dz = z - ply_z(session_st, p)
let d2 = dx * dx + dz * dz
if first or d2 < best {
best = d2
session__near_i = p
session_st.session__near_i = p
first = false
}
}
@ -19,12 +19,12 @@ export function ply_near_d2(x: float, z: float) -> float {
}
# the distance to the nearest player, in metres; ply_near_i() says which
export function ply_near(x: float, z: float) -> float { return Math.sqrt(ply_near_d2(x, z)) }
export function ply_near(session_st: mut SessionState, x: float, z: float) -> float { return Math.sqrt(ply_near_d2(session_st, x, z)) }
export function ply_near_i() -> int { return session__near_i }
export function ply_near_i(session_st: SessionState) -> int { return session_st.session__near_i }
export function ply_dist(p: int, x: float, z: float) -> float {
let dx = x - ply_x(p)
let dz = z - ply_z(p)
export function ply_dist(session_st: SessionState, p: int, x: float, z: float) -> float {
let dx = x - ply_x(session_st, p)
let dz = z - ply_z(session_st, p)
return Math.sqrt(dx * dx + dz * dz)
}

View file

@ -1,43 +1,42 @@
# night.ludic - with company the night passes when everyone has turned in. Lying down marks this
# player ready and getting up takes it back; the host counts, and when the last one is in it hears
# SES_NIGHT and passes the night for everyone.
var session__me_sleeping: bool = false
export function ses_sleeping() -> bool { return session__me_sleeping }
export function ses_sleeping(session_st: SessionState) -> bool { return session_st.session__me_sleeping }
# this player turns in (or gets up): false alone, where a bed simply sleeps
export function ses_sleep(on: bool) -> bool {
if not ses_live() { return false }
if on == session__me_sleeping { return on }
session__me_sleeping = on
let f = session__fact(SES_SLEEP, 0, 0)
export function ses_sleep(base_st: mut BaseState, session_st: mut SessionState, on: bool) -> bool {
if not ses_live(session_st) { return false }
if on == session_st.session__me_sleeping { return on }
session_st.session__me_sleeping = on
let f = session__fact(base_st, session_st, SES_SLEEP, 0, 0)
f.yes = on
if on { ses_night_check() }
if on { ses_night_check(base_st, session_st) }
return true
}
# the host: a guest said it turned in, or got up
export function ses_sleep_ready(p: int, on: bool) -> void {
if p <= 0 or not ply_on(p) { return }
session__slot[p].ready = on
ses_night_check()
export function ses_sleep_ready(base_st: mut BaseState, session_st: mut SessionState, p: int, on: bool) -> void {
if p <= 0 or not ply_on(session_st, p) { return }
session_st.session__slot[p].ready = on
ses_night_check(base_st, session_st)
}
# the host: is everyone in? Then the night is due, and everyone's readiness starts again
export function ses_night_check() -> void {
if session__role != SES_HOST or not session__me_sleeping { return }
for p in 1 .. PLY_MAX { if ply_on(p) and not session__slot[p].ready { return } }
for p in 1 .. PLY_MAX { session__slot[p].ready = false }
session__fact(SES_NIGHT, -1, 0)
export function ses_night_check(base_st: mut BaseState, session_st: mut SessionState) -> void {
if session_st.session__role != SES_HOST or not session_st.session__me_sleeping { return }
for p in 1 .. PLY_MAX { if ply_on(session_st, p) and not session_st.session__slot[p].ready { return } }
for p in 1 .. PLY_MAX { session_st.session__slot[p].ready = false }
session__fact(base_st, session_st, SES_NIGHT, -1, 0)
}
# the night went by on this machine: awake again
export function ses_night_passed() -> void { session__me_sleeping = false }
export function ses_night_passed(session_st: mut SessionState) -> void { session_st.session__me_sleeping = false }
# how many are still up, this player included
export function ses_sleep_waiting() -> int {
export function ses_sleep_waiting(session_st: SessionState) -> int {
var n = 0
for p in 1 .. PLY_MAX { if ply_on(p) and not session__slot[p].sleeping { n += 1 } }
if not session__me_sleeping { n += 1 }
for p in 1 .. PLY_MAX { if ply_on(session_st, p) and not session_st.session__slot[p].sleeping { n += 1 } }
if not session_st.session__me_sleeping { n += 1 }
return n
}

View file

@ -1,46 +1,46 @@
# roles.ludic - who owns the world. Alone, this machine does; a host does and has company; a
# guest owns only its own player. The transport says what this machine became.
export function ses_role() -> int { return session__role }
export function ses_role(session_st: SessionState) -> int { return session_st.session__role }
export function ses_set_role(r: int) -> void {
if r == session__role { return }
session__role = r
session__fact(SES_ROLE, -1, 0)
export function ses_set_role(base_st: mut BaseState, session_st: mut SessionState, r: int) -> void {
if r == session_st.session__role { return }
session_st.session__role = r
session__fact(base_st, session_st, SES_ROLE, -1, 0)
}
# this machine runs the world: the clock, the weather, the animals, the people
export function ses_owns_world() -> bool { return session__role != SES_GUEST }
export function ses_owns_world(session_st: SessionState) -> bool { return session_st.session__role != SES_GUEST }
# somebody else is in this world, so nothing this player opens may stop it
export function ses_live() -> bool { return session__role != SES_SOLO }
export function ses_live(session_st: SessionState) -> bool { return session_st.session__role != SES_SOLO }
export function ses_hosting() -> bool { return session__role == SES_HOST }
export function ses_hosting(session_st: SessionState) -> bool { return session_st.session__role == SES_HOST }
export function ses_guest() -> bool { return session__role == SES_GUEST }
export function ses_guest(session_st: SessionState) -> bool { return session_st.session__role == SES_GUEST }
# whether this player lying down ends the night; alone, it always does
export function ses_night_passes() -> bool { return ses_owns_world() and not ses_live() }
export function ses_night_passes(session_st: SessionState) -> bool { return ses_owns_world(session_st) and not ses_live(session_st) }
# the party a host allows (2..PLY_MAX) and whether it takes new players
export function ses_set_party(max: int, open: bool) -> void {
session__max = max
if session__max < 2 { session__max = 2 }
if session__max > PLY_MAX { session__max = PLY_MAX }
session__open = open
export function ses_set_party(session_st: mut SessionState, max: int, open: bool) -> void {
session_st.session__max = max
if session_st.session__max < 2 { session_st.session__max = 2 }
if session_st.session__max > PLY_MAX { session_st.session__max = PLY_MAX }
session_st.session__open = open
}
export function ses_set_open(open: bool) -> void { session__open = open }
export function ses_max() -> int { return session__max }
export function ses_open() -> bool { return session__open }
export function ses_set_open(session_st: mut SessionState, open: bool) -> void { session_st.session__open = open }
export function ses_max(session_st: SessionState) -> int { return session_st.session__max }
export function ses_open(session_st: SessionState) -> bool { return session_st.session__open }
# may one more join: open, and not full
export function ses_room_for_one() -> bool { return session__open and ply_count() < session__max }
export function ses_room_for_one(session_st: SessionState) -> bool { return session_st.session__open and ply_count(session_st) < session_st.session__max }
# the session is over: everyone else gone, alone again, nothing asked and nobody asleep
export function ses_reset() -> void {
ply_leave_all()
ses_set_role(SES_SOLO)
session__me_sleeping = false
session__vote_t = 0.0
session__vote_mine = -1
export function ses_reset(base_st: mut BaseState, session_st: mut SessionState) -> void {
ply_leave_all(base_st, session_st)
ses_set_role(base_st, session_st, SES_SOLO)
session_st.session__me_sleeping = false
session_st.session__vote_t = 0.0
session_st.session__vote_mine = -1
}

View file

@ -1,62 +1,62 @@
# roster.ludic - who is here. Slot 0 is this machine's player and reads the SessionLocal port
# live; slots 1.. are remote players, filled by what their machines report.
export function ply_init() -> void { session__init() }
export function ply_init(session_st: mut SessionState) -> void { session__init(session_st) }
export function ply_on(p: int) -> bool {
export function ply_on(session_st: SessionState, p: int) -> bool {
if p == 0 { return true }
if session__slot == null or p < 0 or p >= PLY_MAX { return false }
return session__slot[p].on
if session_st.session__slot == null or p < 0 or p >= PLY_MAX { return false }
return session_st.session__slot[p].on
}
export function ply_is_local(p: int) -> bool { return p == 0 }
# the roster slot holding network player `pid`, or -1
export function ply_slot_of(pid: int) -> int {
if session__slot == null or pid <= 0 { return -1 }
for p in 1 .. PLY_MAX { if session__slot[p].on and session__slot[p].pid == pid { return p } }
export function ply_slot_of(session_st: SessionState, pid: int) -> int {
if session_st.session__slot == null or pid <= 0 { return -1 }
for p in 1 .. PLY_MAX { if session_st.session__slot[p].on and session_st.session__slot[p].pid == pid { return p } }
return -1
}
export function ply_count() -> int {
export function ply_count(session_st: SessionState) -> int {
var n = 1
for p in 1 .. PLY_MAX { if ply_on(p) { n += 1 } }
for p in 1 .. PLY_MAX { if ply_on(session_st, p) { n += 1 } }
return n
}
# a remote player comes in, standing where this one stands until they say: the first free
# slot, or -1 when the party is full
export function ply_join(pid: int) -> int {
session__init()
export function ply_join(base_st: mut BaseState, session_st: mut SessionState, pid: int) -> int {
session__init(session_st)
if pid <= 0 { return -1 } # 0 is this machine's player
for p in 1 .. PLY_MAX {
if session__slot[p].on { continue }
if session_st.session__slot[p].on { continue }
let r = new PartyMember
r.on = true
r.pid = pid
r.x = SessionLocal.x()
r.y = SessionLocal.y()
r.z = SessionLocal.z()
session__slot[p] = r
session__fact(SES_JOINED, p, pid)
session_st.session__slot[p] = r
session__fact(base_st, session_st, SES_JOINED, p, pid)
return p
}
return -1
}
export function ply_leave(p: int) -> void {
if p <= 0 or not ply_on(p) { return }
let r = session__slot[p]
export function ply_leave(base_st: mut BaseState, session_st: mut SessionState, p: int) -> void {
if p <= 0 or not ply_on(session_st, p) { return }
let r = session_st.session__slot[p]
r.on = false
r.ready = false
session__fact(SES_LEFT, p, r.pid)
session__fact(base_st, session_st, SES_LEFT, p, r.pid)
}
export function ply_leave_all() -> void { for p in 1 .. PLY_MAX { ply_leave(p) } }
export function ply_leave_all(base_st: mut BaseState, session_st: mut SessionState) -> void { for p in 1 .. PLY_MAX { ply_leave(base_st, session_st, p) } }
# what a remote player's machine reported this tick
export function ply_report(p: int, x: float, y: float, z: float, yaw: float, speed: float) -> void {
if p <= 0 or not ply_on(p) { return }
let r = session__slot[p]
export function ply_report(session_st: SessionState, p: int, x: float, y: float, z: float, yaw: float, speed: float) -> void {
if p <= 0 or not ply_on(session_st, p) { return }
let r = session_st.session__slot[p]
r.x = x
r.y = y
r.z = z
@ -64,13 +64,13 @@ export function ply_report(p: int, x: float, y: float, z: float, yaw: float, spe
r.speed = speed
}
export function ply_report_rest(p: int, rest: float, sleeping: bool) -> void {
if p <= 0 or not ply_on(p) { return }
session__slot[p].rest = rest
session__slot[p].sleeping = sleeping
export function ply_report_rest(session_st: mut SessionState, p: int, rest: float, sleeping: bool) -> void {
if p <= 0 or not ply_on(session_st, p) { return }
session_st.session__slot[p].rest = rest
session_st.session__slot[p].sleeping = sleeping
}
export function ply_report_name(p: int, name: string) -> void {
if p <= 0 or not ply_on(p) { return }
session__slot[p].name = name
export function ply_report_name(session_st: mut SessionState, p: int, name: string) -> void {
if p <= 0 or not ply_on(session_st, p) { return }
session_st.session__slot[p].name = name
}

View file

@ -59,31 +59,43 @@ function session__zero() -> float { return 0.0 }
function session__one() -> float { return 1.0 }
function session__nameless() -> string { return "" }
var session__slot: []PartyMember = null
var session__role: int = 0
var session__max: int = 4
var session__open: bool = true
var session__near_i: int = 0
var session__facts: Queue<SessionFact> = null
export function ses_facts() -> Queue<SessionFact> {
if session__facts == null { session__facts = queue_new("session.facts") }
return session__facts
export state SessionState {
session__me_sleeping: bool = false
session__slot: []PartyMember = null
session__role: int = 0
session__max: int = 4
session__open: bool = true
session__near_i: int = 0
session__facts: Queue<SessionFact> = null
session__vote_id: int = 0
session__vote_kind: int = 0
session__vote_param: int = 0
session__vote_text: string = ""
session__vote_t: float = 0.0
session__vote_yes: int = 0
session__vote_no: int = 0
session__vote_mine: int = -1 # -1 not voted, 0 no, 1 yes
session__vote_passed: int = -1 # the last vote: -1 none, 0 failed, 1 passed
}
function session__fact(what: int, slot: int, pid: int) -> SessionFact {
export function ses_facts(base_st: mut BaseState, session_st: mut SessionState) -> Queue<SessionFact> {
if session_st.session__facts == null { session_st.session__facts = queue_new(base_st, "session.facts") }
return session_st.session__facts
}
function session__fact(base_st: mut BaseState, session_st: mut SessionState, what: int, slot: int, pid: int) -> SessionFact {
let f = new SessionFact
f.what = what
f.slot = slot
f.pid = pid
f.role = session__role
q_push(ses_facts(), f)
f.role = session_st.session__role
q_push(base_st, ses_facts(base_st, session_st), f)
return f
}
function session__init() -> void {
if session__slot != null { return }
session__slot = new []PartyMember
for i in 0 .. PLY_MAX { push(session__slot, new PartyMember) }
session__slot[0].on = true
function session__init(session_st: mut SessionState) -> void {
if session_st.session__slot != null { return }
session_st.session__slot = new []PartyMember
for i in 0 .. PLY_MAX { push(session_st.session__slot, new PartyMember) }
session_st.session__slot[0].on = true
}

View file

@ -4,174 +4,176 @@ import "ludic.session"
import "ludic.base"
program SessionTest {
numbers float
var me_x: float = 0.0
var me_z: float = 0.0
function fx() -> float { return me_x }
state SessiontestState {
me_x: float = 0.0
me_z: float = 0.0
}
function fx(sessiontest_st: SessiontestState) -> float { return sessiontest_st.me_x }
function fy() -> float { return 0.0 }
function fz() -> float { return me_z }
function fz(sessiontest_st: SessiontestState) -> float { return sessiontest_st.me_z }
function fname() -> string { return "me" }
bind SessionLocal { x: fn fx, y: fn fy, z: fn fz, name: fn fname }
function whats() -> []int {
let fs = q_drain(ses_facts())
function whats(base_st: mut BaseState, session_st: mut SessionState) -> []int {
let fs = q_drain(base_st, ses_facts(base_st, session_st))
let out = new []int
for i in 0 .. len(fs) { push(out, fs[i].what) }
return out
}
function last_of(what: int) -> SessionFact {
let fs = q_drain(ses_facts())
function last_of(base_st: mut BaseState, session_st: mut SessionState, what: int) -> SessionFact {
let fs = q_drain(base_st, ses_facts(base_st, session_st))
var f: SessionFact = null
for i in 0 .. len(fs) { if fs[i].what == what { f = fs[i] } }
return f
}
# a host with n guests, pids 2.., facts drained
function party(n: int) -> void {
ply_init()
ses_set_role(SES_HOST)
for i in 0 .. n { ply_join(2 + i) }
q_clear(ses_facts())
function party(base_st: mut BaseState, session_st: mut SessionState, n: int) -> void {
ply_init(session_st)
ses_set_role(base_st, session_st, SES_HOST)
for i in 0 .. n { ply_join(base_st, session_st, 2 + i) }
q_clear(base_st, ses_facts(base_st, session_st))
}
test "slot 0 is the local player, read live; others join at its feet and leave" {
ply_init()
me_x = 5.0
expect(ply_on(0))
expect_near(ply_x(0), 5.0, 0.001)
expect(ply_name(0) == "me")
let p = ply_join(7)
test "slot 0 is the local player, read live; others join at its feet and leave" (base_st: mut BaseState, session_st: mut SessionState, sessiontest_st: mut SessiontestState) {
ply_init(session_st)
sessiontest_st.me_x = 5.0
expect(ply_on(session_st, 0))
expect_near(ply_x(session_st, 0), 5.0, 0.001)
expect(ply_name(session_st, 0) == "me")
let p = ply_join(base_st, session_st, 7)
expect_eq(p, 1)
expect_near(ply_x(p), 5.0, 0.001)
expect_eq(ply_slot_of(7), 1)
expect_eq(ply_count(), 2)
expect_eq(ply_join(0), -1)
ply_leave(p)
expect_eq(ply_slot_of(7), -1)
expect_eq(ply_count(), 1)
let w = whats()
expect_near(ply_x(session_st, p), 5.0, 0.001)
expect_eq(ply_slot_of(session_st, 7), 1)
expect_eq(ply_count(session_st), 2)
expect_eq(ply_join(base_st, session_st, 0), -1)
ply_leave(base_st, session_st, p)
expect_eq(ply_slot_of(session_st, 7), -1)
expect_eq(ply_count(session_st), 1)
let w = whats(base_st, session_st)
expect_eq(len(w), 2)
expect_eq(w[0], SES_JOINED)
expect_eq(w[1], SES_LEFT)
}
test "the roster fills at twelve" {
ply_init()
for i in 0 .. PLY_MAX - 1 { expect(ply_join(10 + i) > 0) }
expect_eq(ply_join(99), -1)
expect_eq(ply_count(), PLY_MAX)
test "the roster fills at twelve" (base_st: mut BaseState, session_st: mut SessionState) {
ply_init(session_st)
for i in 0 .. PLY_MAX - 1 { expect(ply_join(base_st, session_st, 10 + i) > 0) }
expect_eq(ply_join(base_st, session_st, 99), -1)
expect_eq(ply_count(session_st), PLY_MAX)
}
test "the nearest player, whoever it is" {
ply_init()
me_x = 100.0
let p = ply_join(2)
ply_report(p, 3.0, 0.0, 4.0, 0.0, 1.0)
expect_near(ply_near(0.0, 0.0), 5.0, 0.001)
expect_eq(ply_near_i(), p)
expect_near(ply_dist(0, 100.0, 0.0), 0.0, 0.001)
test "the nearest player, whoever it is" (base_st: mut BaseState, session_st: mut SessionState, sessiontest_st: mut SessiontestState) {
ply_init(session_st)
sessiontest_st.me_x = 100.0
let p = ply_join(base_st, session_st, 2)
ply_report(session_st, p, 3.0, 0.0, 4.0, 0.0, 1.0)
expect_near(ply_near(session_st, 0.0, 0.0), 5.0, 0.001)
expect_eq(ply_near_i(session_st), p)
expect_near(ply_dist(session_st, 0, 100.0, 0.0), 0.0, 0.001)
}
test "roles: alone owns the world, a guest does not, and a change is a fact" {
ply_init()
expect(ses_owns_world())
expect(not ses_live())
expect(ses_night_passes())
ses_set_role(SES_GUEST)
expect(not ses_owns_world())
expect(ses_live())
expect(not ses_night_passes())
let f = last_of(SES_ROLE)
test "roles: alone owns the world, a guest does not, and a change is a fact" (base_st: mut BaseState, session_st: mut SessionState) {
ply_init(session_st)
expect(ses_owns_world(session_st))
expect(not ses_live(session_st))
expect(ses_night_passes(session_st))
ses_set_role(base_st, session_st, SES_GUEST)
expect(not ses_owns_world(session_st))
expect(ses_live(session_st))
expect(not ses_night_passes(session_st))
let f = last_of(base_st, session_st, SES_ROLE)
expect_eq(f.role, SES_GUEST)
ses_set_role(SES_HOST)
expect(ses_owns_world())
expect(not ses_night_passes())
ses_set_role(base_st, session_st, SES_HOST)
expect(ses_owns_world(session_st))
expect(not ses_night_passes(session_st))
}
test "the party's size and door" {
ses_set_party(20, true)
expect_eq(ses_max(), PLY_MAX)
ses_set_party(3, true)
party(1)
expect(ses_room_for_one())
ply_join(9)
expect(not ses_room_for_one())
ses_set_party(12, false)
expect(not ses_room_for_one())
test "the party's size and door" (base_st: mut BaseState, session_st: mut SessionState) {
ses_set_party(session_st, 20, true)
expect_eq(ses_max(session_st), PLY_MAX)
ses_set_party(session_st, 3, true)
party(base_st, session_st, 1)
expect(ses_room_for_one(session_st))
ply_join(base_st, session_st, 9)
expect(not ses_room_for_one(session_st))
ses_set_party(session_st, 12, false)
expect(not ses_room_for_one(session_st))
}
test "the night passes when the host and every guest have turned in" {
party(2)
expect(ses_sleep(true))
expect_eq(ses_sleep_waiting(), 2)
ses_sleep_ready(1, true)
expect(last_of(SES_NIGHT) == null)
ses_sleep_ready(2, true)
expect(last_of(SES_NIGHT) != null)
ses_night_passed()
expect(not ses_sleeping())
test "the night passes when the host and every guest have turned in" (base_st: mut BaseState, session_st: mut SessionState) {
party(base_st, session_st, 2)
expect(ses_sleep(base_st, session_st, true))
expect_eq(ses_sleep_waiting(session_st), 2)
ses_sleep_ready(base_st, session_st, 1, true)
expect(last_of(base_st, session_st, SES_NIGHT) == null)
ses_sleep_ready(base_st, session_st, 2, true)
expect(last_of(base_st, session_st, SES_NIGHT) != null)
ses_night_passed(session_st)
expect(not ses_sleeping(session_st))
}
test "a guest's turning in is a fact for the host to hear; alone a bed just sleeps" {
ply_init()
expect(not ses_sleep(true))
ses_set_role(SES_GUEST)
q_clear(ses_facts())
expect(ses_sleep(true))
let f = last_of(SES_SLEEP)
test "a guest's turning in is a fact for the host to hear; alone a bed just sleeps" (base_st: mut BaseState, session_st: mut SessionState) {
ply_init(session_st)
expect(not ses_sleep(base_st, session_st, true))
ses_set_role(base_st, session_st, SES_GUEST)
q_clear(base_st, ses_facts(base_st, session_st))
expect(ses_sleep(base_st, session_st, true))
let f = last_of(base_st, session_st, SES_SLEEP)
expect(f.yes)
ses_sleep(false)
expect(not ses_sleeping())
ses_sleep(base_st, session_st, false)
expect(not ses_sleeping(session_st))
}
test "a vote passes on a majority, the host's yes counted" {
party(2)
expect_eq(ses_may(4, 9, "set out"), SES_MAY_ASKED)
expect(ses_vote_live())
expect_eq(ses_may(4, 9, "set out"), SES_MAY_BUSY)
ses_vote_heard(ses_vote_id(), true)
let f = last_of(SES_VOTE_RESOLVED)
test "a vote passes on a majority, the host's yes counted" (base_st: mut BaseState, session_st: mut SessionState) {
party(base_st, session_st, 2)
expect_eq(ses_may(base_st, session_st, 4, 9, "set out"), SES_MAY_ASKED)
expect(ses_vote_live(session_st))
expect_eq(ses_may(base_st, session_st, 4, 9, "set out"), SES_MAY_BUSY)
ses_vote_heard(base_st, session_st, ses_vote_id(session_st), true)
let f = last_of(base_st, session_st, SES_VOTE_RESOLVED)
expect(f.yes)
expect_eq(f.param, 9)
expect(f.text == "set out")
expect(not ses_vote_live())
expect(not ses_vote_live(session_st))
}
test "a vote fails on half saying no, or when the time runs out" {
party(1)
ses_vote_open(1, 0, "a")
ses_vote_heard(ses_vote_id(), false)
expect(not last_of(SES_VOTE_RESOLVED).yes)
party(3)
ses_vote_open(1, 0, "b")
ses_vote_tick(31.0)
let f = last_of(SES_VOTE_RESOLVED)
test "a vote fails on half saying no, or when the time runs out" (base_st: mut BaseState, session_st: mut SessionState) {
party(base_st, session_st, 1)
ses_vote_open(base_st, session_st, 1, 0, "a")
ses_vote_heard(base_st, session_st, ses_vote_id(session_st), false)
expect(not last_of(base_st, session_st, SES_VOTE_RESOLVED).yes)
party(base_st, session_st, 3)
ses_vote_open(base_st, session_st, 1, 0, "b")
ses_vote_tick(base_st, session_st, 31.0)
let f = last_of(base_st, session_st, SES_VOTE_RESOLVED)
expect(f != null)
expect(not f.yes)
}
test "alone it may go; a guest may not, and its vote goes to the host" {
ply_init()
expect_eq(ses_may(1, 0, "x"), SES_MAY_GO)
ses_set_role(SES_GUEST)
expect_eq(ses_may(1, 0, "x"), SES_MAY_HOSTS)
ses_vote_told(5, 1, "x")
expect(ses_vote_live())
q_clear(ses_facts())
ses_vote_cast(true)
let f = last_of(SES_VOTE_CAST)
test "alone it may go; a guest may not, and its vote goes to the host" (base_st: mut BaseState, session_st: mut SessionState) {
ply_init(session_st)
expect_eq(ses_may(base_st, session_st, 1, 0, "x"), SES_MAY_GO)
ses_set_role(base_st, session_st, SES_GUEST)
expect_eq(ses_may(base_st, session_st, 1, 0, "x"), SES_MAY_HOSTS)
ses_vote_told(session_st, 5, 1, "x")
expect(ses_vote_live(session_st))
q_clear(base_st, ses_facts(base_st, session_st))
ses_vote_cast(base_st, session_st, true)
let f = last_of(base_st, session_st, SES_VOTE_CAST)
expect_eq(f.id, 5)
expect(f.yes)
ses_vote_ended(5, true)
expect_eq(ses_vote_passed(), 1)
ses_vote_ended(session_st, 5, true)
expect_eq(ses_vote_passed(session_st), 1)
}
test "a reset leaves everyone and stands alone" {
party(2)
ses_sleep(true)
ses_reset()
expect_eq(ply_count(), 1)
expect_eq(ses_role(), SES_SOLO)
expect(not ses_sleeping())
test "a reset leaves everyone and stands alone" (base_st: mut BaseState, session_st: mut SessionState) {
party(base_st, session_st, 2)
ses_sleep(base_st, session_st, true)
ses_reset(base_st, session_st)
expect_eq(ply_count(session_st), 1)
expect_eq(ses_role(session_st), SES_SOLO)
expect(not ses_sleeping(session_st))
}
}

View file

@ -1,10 +1,10 @@
# vote_state.ludic - the vote as it stands
export function ses_vote_live() -> bool { return session__vote_t > 0.0 }
export function ses_vote_id() -> int { return session__vote_id }
export function ses_vote_kind() -> int { return session__vote_kind }
export function ses_vote_text() -> string { return session__vote_text }
export function ses_vote_left() -> float { return session__vote_t }
export function ses_vote_yes() -> int { return session__vote_yes }
export function ses_vote_no() -> int { return session__vote_no }
export function ses_vote_mine() -> int { return session__vote_mine }
export function ses_vote_passed() -> int { return session__vote_passed }
export function ses_vote_live(session_st: SessionState) -> bool { return session_st.session__vote_t > 0.0 }
export function ses_vote_id(session_st: SessionState) -> int { return session_st.session__vote_id }
export function ses_vote_kind(session_st: SessionState) -> int { return session_st.session__vote_kind }
export function ses_vote_text(session_st: SessionState) -> string { return session_st.session__vote_text }
export function ses_vote_left(session_st: SessionState) -> float { return session_st.session__vote_t }
export function ses_vote_yes(session_st: SessionState) -> int { return session_st.session__vote_yes }
export function ses_vote_no(session_st: SessionState) -> int { return session_st.session__vote_no }
export function ses_vote_mine(session_st: SessionState) -> int { return session_st.session__vote_mine }
export function ses_vote_passed(session_st: SessionState) -> int { return session_st.session__vote_passed }

View file

@ -8,104 +8,95 @@ export const SES_MAY_HOSTS: int = 3 # a guest: it is for the host to ask
const SESSION_VOTE_SECS: float = 30.0
var session__vote_id: int = 0
var session__vote_kind: int = 0
var session__vote_param: int = 0
var session__vote_text: string = ""
var session__vote_t: float = 0.0
var session__vote_yes: int = 0
var session__vote_no: int = 0
var session__vote_mine: int = -1 # -1 not voted, 0 no, 1 yes
var session__vote_passed: int = -1 # the last vote: -1 none, 0 failed, 1 passed
# a world-changing act: may it go ahead now, or does the party have to be asked first?
export function ses_may(kind: int, param: int, text: string) -> int {
if not ses_live() { return SES_MAY_GO }
if session__role == SES_GUEST { return SES_MAY_HOSTS }
if ply_count() <= 1 { return SES_MAY_GO }
if not ses_vote_open(kind, param, text) { return SES_MAY_BUSY }
export function ses_may(base_st: mut BaseState, session_st: mut SessionState, kind: int, param: int, text: string) -> int {
if not ses_live(session_st) { return SES_MAY_GO }
if session_st.session__role == SES_GUEST { return SES_MAY_HOSTS }
if ply_count(session_st) <= 1 { return SES_MAY_GO }
if not ses_vote_open(base_st, session_st, kind, param, text) { return SES_MAY_BUSY }
return SES_MAY_ASKED
}
# the host puts it to the party, voting yes itself
export function ses_vote_open(kind: int, param: int, text: string) -> bool {
if session__role != SES_HOST or ses_vote_live() { return false }
session__vote_id += 1
session__vote_kind = kind
session__vote_param = param
session__vote_text = text
session__vote_t = SESSION_VOTE_SECS
session__vote_yes = 1
session__vote_no = 0
session__vote_mine = 1
session__vote_passed = -1
let f = session__vote_fact(SES_VOTE_OPENED)
export function ses_vote_open(base_st: mut BaseState, session_st: mut SessionState, kind: int, param: int, text: string) -> bool {
if session_st.session__role != SES_HOST or ses_vote_live(session_st) { return false }
session_st.session__vote_id += 1
session_st.session__vote_kind = kind
session_st.session__vote_param = param
session_st.session__vote_text = text
session_st.session__vote_t = SESSION_VOTE_SECS
session_st.session__vote_yes = 1
session_st.session__vote_no = 0
session_st.session__vote_mine = 1
session_st.session__vote_passed = -1
let f = session__vote_fact(base_st, session_st, SES_VOTE_OPENED)
f.yes = true
session__vote_check()
session__vote_check(base_st, session_st)
return true
}
# this player's answer; a guest's goes to the host as SES_VOTE_CAST
export function ses_vote_cast(yes: bool) -> void {
if not ses_vote_live() or session__vote_mine >= 0 { return }
if yes { session__vote_mine = 1 } else { session__vote_mine = 0 }
if session__role == SES_GUEST {
let f = session__vote_fact(SES_VOTE_CAST)
export function ses_vote_cast(base_st: mut BaseState, session_st: mut SessionState, yes: bool) -> void {
if not ses_vote_live(session_st) or session_st.session__vote_mine >= 0 { return }
if yes { session_st.session__vote_mine = 1 } else { session_st.session__vote_mine = 0 }
if session_st.session__role == SES_GUEST {
let f = session__vote_fact(base_st, session_st, SES_VOTE_CAST)
f.yes = yes
return
}
ses_vote_heard(session__vote_id, yes)
ses_vote_heard(base_st, session_st, session_st.session__vote_id, yes)
}
# the host: a player's answer to vote `id`
export function ses_vote_heard(id: int, yes: bool) -> void {
if session__role != SES_HOST or id != session__vote_id or not ses_vote_live() { return }
if yes { session__vote_yes += 1 } else { session__vote_no += 1 }
session__vote_check()
export function ses_vote_heard(base_st: mut BaseState, session_st: mut SessionState, id: int, yes: bool) -> void {
if session_st.session__role != SES_HOST or id != session_st.session__vote_id or not ses_vote_live(session_st) { return }
if yes { session_st.session__vote_yes += 1 } else { session_st.session__vote_no += 1 }
session__vote_check(base_st, session_st)
}
# a guest: the host opened vote `id`, or ended it
export function ses_vote_told(id: int, kind: int, text: string) -> void {
session__vote_id = id
session__vote_kind = kind
session__vote_text = text
session__vote_t = SESSION_VOTE_SECS
session__vote_mine = -1
export function ses_vote_told(session_st: mut SessionState, id: int, kind: int, text: string) -> void {
session_st.session__vote_id = id
session_st.session__vote_kind = kind
session_st.session__vote_text = text
session_st.session__vote_t = SESSION_VOTE_SECS
session_st.session__vote_mine = -1
}
export function ses_vote_ended(id: int, passed: bool) -> void {
if id != session__vote_id { return }
session__vote_t = 0.0
if passed { session__vote_passed = 1 } else { session__vote_passed = 0 }
export function ses_vote_ended(session_st: mut SessionState, id: int, passed: bool) -> void {
if id != session_st.session__vote_id { return }
session_st.session__vote_t = 0.0
if passed { session_st.session__vote_passed = 1 } else { session_st.session__vote_passed = 0 }
}
# the vote's thirty seconds; on the host, running out decides it
export function ses_vote_tick(dt: float) -> void {
if not ses_vote_live() { return }
session__vote_t = session__vote_t - dt
if session__role == SES_HOST and not ses_vote_live() { session__vote_finish() }
export function ses_vote_tick(base_st: mut BaseState, session_st: mut SessionState, dt: float) -> void {
if not ses_vote_live(session_st) { return }
session_st.session__vote_t = session_st.session__vote_t - dt
if session_st.session__role == SES_HOST and not ses_vote_live(session_st) { session__vote_finish(base_st, session_st) }
}
function session__vote_check() -> void {
let n = ply_count()
let yes = session__vote_yes
let no = session__vote_no
if yes * 2 > n or no * 2 >= n or yes + no >= n { session__vote_finish() }
function session__vote_check(base_st: mut BaseState, session_st: mut SessionState) -> void {
let n = ply_count(session_st)
let yes = session_st.session__vote_yes
let no = session_st.session__vote_no
if yes * 2 > n or no * 2 >= n or yes + no >= n { session__vote_finish(base_st, session_st) }
}
function session__vote_finish() -> void {
let passed = session__vote_yes * 2 > ply_count()
session__vote_t = 0.0
if passed { session__vote_passed = 1 } else { session__vote_passed = 0 }
let f = session__vote_fact(SES_VOTE_RESOLVED)
function session__vote_finish(base_st: mut BaseState, session_st: mut SessionState) -> void {
let passed = session_st.session__vote_yes * 2 > ply_count(session_st)
session_st.session__vote_t = 0.0
if passed { session_st.session__vote_passed = 1 } else { session_st.session__vote_passed = 0 }
let f = session__vote_fact(base_st, session_st, SES_VOTE_RESOLVED)
f.yes = passed
}
function session__vote_fact(what: int) -> SessionFact {
let f = session__fact(what, -1, 0)
f.id = session__vote_id
f.kind = session__vote_kind
f.param = session__vote_param
f.text = session__vote_text
function session__vote_fact(base_st: mut BaseState, session_st: mut SessionState, what: int) -> SessionFact {
let f = session__fact(base_st, session_st, what, -1, 0)
f.id = session_st.session__vote_id
f.kind = session_st.session__vote_kind
f.param = session_st.session__vote_param
f.text = session_st.session__vote_text
return f
}

View file

@ -1,47 +1,47 @@
# where.ludic - who and where each player is; slot 0 answers through SessionLocal, live
export function ply_pid(p: int) -> int {
if p <= 0 or not ply_on(p) { return 0 }
return session__slot[p].pid
export function ply_pid(session_st: SessionState, p: int) -> int {
if p <= 0 or not ply_on(session_st, p) { return 0 }
return session_st.session__slot[p].pid
}
export function ply_name(p: int) -> string {
export function ply_name(session_st: SessionState, p: int) -> string {
if p == 0 { return SessionLocal.name() }
if not ply_on(p) { return "" }
return session__slot[p].name
if not ply_on(session_st, p) { return "" }
return session_st.session__slot[p].name
}
export function ply_x(p: int) -> float {
export function ply_x(session_st: SessionState, p: int) -> float {
if p == 0 { return SessionLocal.x() }
return session__slot[p].x
return session_st.session__slot[p].x
}
export function ply_y(p: int) -> float {
export function ply_y(session_st: SessionState, p: int) -> float {
if p == 0 { return SessionLocal.y() }
return session__slot[p].y
return session_st.session__slot[p].y
}
export function ply_z(p: int) -> float {
export function ply_z(session_st: SessionState, p: int) -> float {
if p == 0 { return SessionLocal.z() }
return session__slot[p].z
return session_st.session__slot[p].z
}
export function ply_yaw(p: int) -> float {
export function ply_yaw(session_st: SessionState, p: int) -> float {
if p == 0 { return SessionLocal.yaw() }
return session__slot[p].yaw
return session_st.session__slot[p].yaw
}
export function ply_speed(p: int) -> float {
export function ply_speed(session_st: SessionState, p: int) -> float {
if p == 0 { return SessionLocal.speed() }
return session__slot[p].speed
return session_st.session__slot[p].speed
}
# how much a player's rest speeds the clock: their seat's or the ground's, 1 on their feet
export function ply_rest(p: int) -> float {
export function ply_rest(session_st: SessionState, p: int) -> float {
if p == 0 { return SessionLocal.rest() }
return session__slot[p].rest
return session_st.session__slot[p].rest
}
export function ply_sleeping(p: int) -> bool {
if p == 0 { return session__me_sleeping }
return ply_on(p) and session__slot[p].sleeping
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
}