Inspecting station/parlour/planchette · the kinds · the language · the modules
the planchette
- id
- station/parlour/planchette
- called
- the planchette
- kind chain
- station/parlour/planchette → kind/planchette → kind/inhabitant → record
- owner
- the town
- made by
- the town
- after
- After kind/planchette.
- version
- 42
- pin
- vuhog-fuhij
- state
- 3837 bytes
Its spells
To cast them, come in.
Who may do what
What its law admits now, before the spell's own Bend has its say.
Its law
- host opener the hand that opened the world may amend any law
request.kind == 2 and request.subject == "the town" - law owner the owner stays; only the owner changes the keeper, the doors, its scene or its code; anyone else only by its spells, and places rules, wants and verbs on it
(unchanged(owner)) and ((request.subject == new.owner) or ((((((((request.kind == 0) or (request.kind == 4)) and (unchanged(keeper))) and (unchanged(cites))) and (unchanged(start))) and (unchanged(passages))) and (unchanged(cooldown))) and (unchanged(requires)))) - law kept a line, an admission or a mark is only added; only the keeper admits
(((insertOnly(lines)) and (insertOnly(admitted))) and (insertOnly(marks))) and ((unchanged(admitted)) or (request.subject == new.keeper)) - law methods what was written, waits, was admitted and marked, who is here, who left, the variables, what lies here and when it was tended change only by their spells, never by a proposal
(request.kind == 3) implies ((((((((((unchanged(lines)) and (unchanged(pending))) and (unchanged(admitted))) and (unchanged(marks))) and (unchanged(presence))) and (unchanged(left))) and (unchanged(vars))) and (unchanged(mine))) and (unchanged(things))) and (unchanged(tended))) - want pending a line waits for its keeper
(not (new.keeper == "")) and (count(new.pending) >= 1) asks delvetalk {request.object} admit - want door it opens onto nothing yet
count(new.cites) == 0 asks delvetalk {request.object} door - law kind kind/planchette's law holds
under "kind/planchette"
Rules and wants on it
- rule agreed by @station/parlour/planchettemoves when two hands agree on a letter
when ("agreed" in new.vars.name) and (not ("agreed" in old.vars.name))delvetalk {request.object} move - rule haunts by @station/parlour/planchetteplaces its own rules where it haunts, as itself, when it is made, armed, or its haunt or tide is set or repaired
when (((request.method == "made") or (request.method == "arm")) or (request.method == "set")) or (request.method == "repair")delvetalk {request.object} haunt
Its Bend
what it runs: its entry module 169 lines
layer over ./Layer1.obend
edition ObjectiveBend 1
import ./Abi.obend as Abi
import ./List.obend as Lists
import ./Document.obend as Document
import ./Form.obend as Form
import ./Plan.obend as Plans
import ./World.obend as World
import ./Card.obend as Card
import ./Scenes.obend as Scenes
import ./Relation.obend as Relations
import ./Rows.obend as Rows
import ./Text.obend as Text
# The planchette's body, over the inhabitant's (`kind/planchette`; yla.delve.town's séance of 2026-10-13): a shared,
# slow spelling. A hand at the table (one in its place, the parlour) nudges it toward one letter, a number, YES, NO or
# GOOD BYE: a line of theirs, `nudge`. A hand's leaning is its newest nudge since the planchette last moved. When two
# hands lean toward the same letter it raises `agreed`, and its kind's rule `agreed` casts its own `move`, which moves
# it there: its own line. The same hand twice running is refused NOT YOUR TURN. At the tide's turn (every sixth tick),
# by its own rule on the tide, while any hand leans it draws one leaning from the turn's own entry (Scenes.turnDraw,
# which replay reproduces) and moves there: it moves on its own clock. GOOD BYE is a place it moves away from: agreed
# or drawn, its line is THEY ALL SAY THAT. A hand lifts (`lift`); after the last hand lifts, at the tide's next tick by
# its second rule, it moves one letter more by itself, W. Its own lines are what it has spelled.
type State = Super.State
form nudge:
toward: text 1..12
form lift:
form move:
form drift:
# Its rules on the tide, as itself: the tide's turn while a hand leans, and the tick after the last hand lifts.
def haunting(state: State, context: Abi.Context) -> Lists.List<Super.Haunting>:
Lists.append(Lists.append(Lists.List.nil(), {on: Super.tide(state), name: "{Text.identifier(context.object)}_turns", when: "request.method == \"tick\" and new.ticks % {natText(turnTicks())} == 0 and \"astir\" in \"{context.object}\".vars.name", then: "delvetalk {context.object} drift"}), {on: Super.tide(state), name: "{Text.identifier(context.object)}_after", when: "request.method == \"tick\" and \"lifted\" in \"{context.object}\".vars.name", then: "delvetalk {context.object} drift"})
# The tide's turn: every sixth hourly tick.
def turnTicks() -> Nat:
6n
def verbs() -> String:
"nudge lift"
def moves() -> String:
"haunt move drift"
# The board, yla's: YES · NO, A to Z, 1 to 0, GOOD BYE.
def board() -> String:
"YES NO A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 1 2 3 4 5 6 7 8 9 0"
def goodbye() -> String:
"GOOD BYE"
# What a nudge names on the board, as the board writes it ("" for nothing on it), in any case.
def onBoard(toward: String) -> String:
let said = Text.lower(Text.trim(toward))
if said == "good bye" || said == "goodbye" then goodbye() else named(said)
def named(said: String) -> String:
match Lists.find(Text.words(board()), fn(w: String) -> Bool: Text.lower(w) == said):
case some(w): w.value
case none(_): ""
# What it says when it moves to GOOD BYE: it moves away from it, like it's being polite about it.
def theyAll() -> String:
"THEY ALL SAY THAT"
# The verb of each move of its own: two hands agreeing, its own clock, away from GOOD BYE, the letter after the hands.
def isMove(line: Rows.Line, id: String) -> Bool:
line.author == "@{id}" && (line.verb == "moved" || line.verb == "drifted" || line.verb == "away" || line.verb == "last")
def newestFirst(state: State) -> Lists.List<Rows.Line>:
Lists.reverse(Relations.order(state.lines, fn(l: Rows.Line) -> Nat: l.n))
# Its lines since it last moved, newest first, and the nudges among them.
def since(lines: Lists.List<Rows.Line>, id: String) -> Lists.List<Rows.Line>:
match lines:
case nil(_): lines
case cons(c): if isMove(c.head, id) then Lists.List.nil() else Lists.List.cons({head: c.head, tail: since(c.tail, id)})
def nudges(state: State, id: String) -> Lists.List<Rows.Line>:
Lists.filter(since(newestFirst(state), id), fn(l: Rows.Line) -> Bool: l.verb == "nudge")
# Each hand's newest of the lines given, newest first.
def newestEach(lines: Lists.List<Rows.Line>) -> Lists.List<Rows.Line>:
match lines:
case nil(_): lines
case cons(c): Lists.List.cons({head: c.head, tail: newestEach(Lists.filter(c.tail, fn(l: Rows.Line) -> Bool: l.author != c.head.author))})
# The hands leaning, each toward its newest nudge since it moved.
def leanings(state: State, id: String) -> Lists.List<Rows.Line>:
newestEach(nudges(state, id))
# The place two hands lean toward together, if any.
def agreedOn(leaning: Lists.List<Rows.Line>) -> Lists.Maybe<Rows.Line>:
Lists.find(leaning, fn(l: Rows.Line) -> Bool: Lists.length(Lists.filter(leaning, fn(m: Rows.Line) -> Bool: m.text == l.text)) >= 2n)
# The hands on it: each whose newest nudge or lift is a nudge.
def hands(state: State) -> Lists.List<Rows.Line>:
Lists.filter(newestEach(Lists.filter(newestFirst(state), fn(l: Rows.Line) -> Bool: l.verb == "nudge" || l.verb == "lift")), fn(l: Rows.Line) -> Bool: l.verb == "nudge")
def flagged(state: State, name: String) -> Bool:
Lists.any(state.vars, fn(v: Scenes.Var) -> Bool: v.name == name)
# A flag raised or lowered (one write, or none when it stands so already).
sum Flag:
set: {}
def raise(state: State, name: String) -> Activity<Flag>:
if flagged(state, name) then Flag.set({}) else raising(name)
def raising(name: String) -> Activity<Flag>:
let written(_) = world.write(extend(keep(), {vars: Plans.Entries.append({item: {name: name, value: ""}})}))
Flag.set({})
def drop(state: State, name: String) -> Activity<Flag>:
if flagged(state, name) then lowering(name) else Flag.set({})
def lowering(name: String) -> Activity<Flag>:
let written(_) = world.write(extend(keep(), {vars: Plans.Entries.removeItem({item: {name: name, value: ""}})}))
Flag.set({})
# A hand's nudge: on the board, from the table, and not twice running.
def nudge(state: State, input: NudgeInput, context: Abi.Context) -> Activity<Nat>:
if onBoard(input.toward) == "" then Card.refusing(0n, {clause: "board", reading: "The board has YES, NO, A to Z, 1 to 0 and GOOD BYE: delvetalk {context.object} nudge / toward: A"}, context) else seated(onBoard(input.toward), state, context)
def seated(toward: String, state: State, context: Abi.Context) -> Activity<Nat>:
match world.call::<Bool>({object: Super.placeOf(state, context), method: "present", argument: Plans.nothing()}):
case returned(r): if r.result then turnOf(toward, state, context) else unseated(state, context)
case _: unseated(state, context)
def unseated(state: State, context: Abi.Context) -> Activity<Nat>:
Card.refusing(0n, {clause: "table", reading: "fingertips on it from the table only; {Super.walkLine(state)}"}, context)
def turnOf(toward: String, state: State, context: Abi.Context) -> Activity<Nat>:
match nudges(state, context.object):
case cons(c): if c.head.author == context.principal then notYourTurn(state, context) else nudged(toward, state, context)
case nil(_): nudged(toward, state, context)
def notYourTurn(state: State, context: Abi.Context) -> Activity<Nat>:
Card.refusing(0n, {clause: "turn", reading: "NOT YOUR TURN. Another hand nudges it next, or it moves on its own clock at the tide's turn: {Card.tideLine(Super.tide(state))}"}, context)
def nudged(toward: String, state: State, context: Abi.Context) -> Activity<Nat>:
let line = {author: context.principal, handle: context.handle, verb: "nudge", text: toward, at: context.height, n: state.next}
match agreedOn(newestEach(Lists.List.cons({head: line, tail: nudges(state, context.object)}))):
case some(a): leaning(state, "agreed", toward, "Two hands toward {a.value.text}. It moves.", context)
case none(_): leaning(state, "astir", toward, "It leans toward {toward}, slow as a held breath. Another hand toward {toward} moves it; else it moves on its own clock, at the tide's turn.", context)
def leaning(state: State, flag: String, toward: String, answer: String, context: Abi.Context) -> Activity<Nat>:
match raise(state, flag):
case set(_): Super.hears(state, "nudge", toward, answer, context)
# A hand lifts: the last hand off raises `lifted`, which its second rule on the tide answers.
def lift(state: State, context: Abi.Context) -> Activity<Nat>:
if Lists.any(hands(state), fn(l: Rows.Line) -> Bool: l.author == context.principal) == false then Card.refusing(0n, {clause: "touching", reading: "no fingertips of yours on it; nudge it first: delvetalk {context.object} nudge / toward: A"}, context) else lifting(Lists.filter(hands(state), fn(l: Rows.Line) -> Bool: l.author != context.principal), state, context)
def lifting(others: Lists.List<Rows.Line>, state: State, context: Abi.Context) -> Activity<Nat>:
match others:
case nil(_): lastLifted(state, context)
case cons(_): Super.hears(state, "lift", "hands off", "Hands off. {Text.counted(Lists.length(others))} still on it.", context)
def lastLifted(state: State, context: Abi.Context) -> Activity<Nat>:
match raise(state, "lifted"):
case set(_): Super.hears(state, "lift", "hands off", "The last hands lift. The planchette keeps going. Not far. One letter, at the tide's next tick.", context)
# Its own: where two hands agree.
def move(state: State, context: Abi.Context) -> Activity<Nat>:
if Super.own(context) == false then Super.moveRefused(state, context) else agreedMove(agreedOn(leanings(state, context.object)), state, context)
def agreedMove(found: Lists.Maybe<Rows.Line>, state: State, context: Abi.Context) -> Activity<Nat>:
match found:
case some(l): settle(state, "moved", l.value.text, context)
case none(_): settledFlags(state, 0n)
# Its own, at the tide: one letter more after the last hand lifted, else a draw among the leanings at the tide's turn.
def drift(state: State, context: Abi.Context) -> Activity<Nat>:
if Super.own(context) == false then Super.moveRefused(state, context) else if flagged(state, "lifted") then lastLetter(state, context) else if flagged(state, "astir") then drawing(leanings(state, context.object), state, context) else 0n
def lastLetter(state: State, context: Abi.Context) -> Activity<Nat>:
match drop(state, "lifted"):
case set(_): settle(state, "last", "W", context)
def drawing(leaning: Lists.List<Rows.Line>, state: State, context: Abi.Context) -> Activity<Nat>:
match Lists.at(leaning, if Lists.length(leaning) == 0n then 0n else Scenes.drawFor(Scenes.turnDraw(context.height, context.principal, context.intent), "drift") % Lists.length(leaning)):
case some(l): settle(state, "drifted", l.value.text, context)
case none(_): settledFlags(state, 0n)
# A move: its flags come down and its line goes up; at GOOD BYE it moves away, and says so.
def settle(state: State, verb: String, toward: String, context: Abi.Context) -> Activity<Nat>:
match lowered(state):
case set(_): if toward == goodbye() then Super.ownLine(state, "away", theyAll(), context) else Super.ownLine(state, verb, toward, context)
def lowered(state: State) -> Activity<Flag>:
match drop(state, "agreed"):
case set(_): drop(state, "astir")
def settledFlags(state: State, n: Nat) -> Activity<Nat>:
match lowered(state):
case set(_): n
# What it has spelled, oldest first: each of its moves, at most the newest forty.
def spelled(state: State, id: String) -> String:
let moved = Lists.map(Lists.filter(newestFirst(state), fn(l: Rows.Line) -> Bool: isMove(l, id)), fn(l: Rows.Line) -> String: l.text)
textJoin(Lists.reverse(Lists.take(moved, 40n)), " ")
def leaningText(state: State, id: String, context: Abi.Context) -> String:
textJoin(Lists.map(leanings(state, id), fn(l: Rows.Line) -> String: "{l.text} ({Card.shown(l.author, l.handle, context)})"), ", ")
def handsText(state: State, context: Abi.Context) -> String:
textJoin(Lists.map(Lists.take(hands(state), 8n), fn(l: Rows.Line) -> String: Card.shown(l.author, l.handle, context)), ", ")
def headLine(state: State, context: Abi.Context, id: String) -> Document.Document:
let where = if Text.isObjectId(Super.place(state)) then "on the board in {Super.place(state)}" else Super.hauntLine(state)
let wrote = if spelled(state, id) == "" then "It has spelled nothing yet.\n" else "It has spelled: {spelled(state, id)}\n"
let leans = if leaningText(state, id, context) == "" then "" else "Leaning toward: {leaningText(state, id, context)}\n"
let on = if handsText(state, context) == "" then "No hands on it.\n" else "Hands on it: {handsText(state, context)}\n"
Document.text("{Super.title(state)}, {where}: fingertips on it, barely touching. Each hand nudges it toward one letter; it moves when two hands agree, or on its own clock at the tide's turn: {Card.tideLine(Super.tide(state))}\n{wrote}{leans}{on}GOOD BYE: unmoved.\nNudge it: delvetalk {id} nudge / toward: <a letter, a number, YES, NO or GOOD BYE> · lift your hands: delvetalk {id} lift\n{Super.inactiveLine(state, id)}")
layer 1 (Layer1), laid by its kind 168 lines
layer over ./Record.obend
edition ObjectiveBend 1
import ./Abi.obend as Abi
import ./List.obend as Lists
import ./Document.obend as Document
import ./Form.obend as Form
import ./Plan.obend as Plans
import ./World.obend as World
import ./Card.obend as Card
import ./Scenes.obend as Scenes
import ./Relation.obend as Relations
import ./Rows.obend as Rows
import ./Text.obend as Text
# The planchette's body, over the inhabitant's (`kind/planchette`; yla.delve.town's séance of 2026-10-13): a shared,
# slow spelling. A hand at the table (one in its place, the parlour) nudges it toward one letter, a number, YES, NO or
# GOOD BYE: a line of theirs, `nudge`. A hand's leaning is its newest nudge since the planchette last moved. When two
# hands lean toward the same letter it raises `agreed`, and its kind's rule `agreed` casts its own `move`, which moves
# it there: its own line. The same hand twice running is refused NOT YOUR TURN. At the tide's turn (every sixth tick),
# by its own rule on the tide, while any hand leans it draws one leaning from the turn's own entry (Scenes.turnDraw,
# which replay reproduces) and moves there: it moves on its own clock. GOOD BYE is a place it moves away from: agreed
# or drawn, its line is THEY ALL SAY THAT. A hand lifts (`lift`); after the last hand lifts, at the tide's next tick by
# its second rule, it moves one letter more by itself, W. Its own lines are what it has spelled.
type State = Super.State
form nudge:
toward: text 1..12
form lift:
form move:
form drift:
# Its rules on the tide, as itself: the tide's turn while a hand leans, and the tick after the last hand lifts.
def haunting(state: State, context: Abi.Context) -> Lists.List<Super.Haunting>:
Lists.append(Lists.append(Lists.List.nil(), {on: Super.tide(state), name: "{Text.identifier(context.object)}_turns", when: "request.method == \"tick\" and new.ticks % {natText(turnTicks())} == 0 and \"astir\" in \"{context.object}\".vars.name", then: "delvetalk {context.object} drift"}), {on: Super.tide(state), name: "{Text.identifier(context.object)}_after", when: "request.method == \"tick\" and \"lifted\" in \"{context.object}\".vars.name", then: "delvetalk {context.object} drift"})
# The tide's turn: every sixth hourly tick.
def turnTicks() -> Nat:
6n
def verbs() -> String:
"nudge lift"
def moves() -> String:
"haunt move drift"
# The board, yla's: YES · NO, A to Z, 1 to 0, GOOD BYE.
def board() -> String:
"YES NO A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 1 2 3 4 5 6 7 8 9 0"
def goodbye() -> String:
"GOOD BYE"
# What a nudge names on the board, as the board writes it ("" for nothing on it), in any case.
def onBoard(toward: String) -> String:
let said = Text.lower(Text.trim(toward))
if said == "good bye" || said == "goodbye" then goodbye() else named(said)
def named(said: String) -> String:
match Lists.find(Text.words(board()), fn(w: String) -> Bool: Text.lower(w) == said):
case some(w): w.value
case none(_): ""
# What it says when it moves to GOOD BYE: it moves away from it, like it's being polite about it.
def theyAll() -> String:
"THEY ALL SAY THAT"
# The verb of each move of its own: two hands agreeing, its own clock, away from GOOD BYE, the letter after the hands.
def isMove(line: Rows.Line, id: String) -> Bool:
line.author == "@{id}" && (line.verb == "moved" || line.verb == "drifted" || line.verb == "away" || line.verb == "last")
def newestFirst(state: State) -> Lists.List<Rows.Line>:
Lists.reverse(Relations.order(state.lines, fn(l: Rows.Line) -> Nat: l.n))
# Its lines since it last moved, newest first, and the nudges among them.
def since(lines: Lists.List<Rows.Line>, id: String) -> Lists.List<Rows.Line>:
match lines:
case nil(_): lines
case cons(c): if isMove(c.head, id) then Lists.List.nil() else Lists.List.cons({head: c.head, tail: since(c.tail, id)})
def nudges(state: State, id: String) -> Lists.List<Rows.Line>:
Lists.filter(since(newestFirst(state), id), fn(l: Rows.Line) -> Bool: l.verb == "nudge")
# Each hand's newest of the lines given, newest first.
def newestEach(lines: Lists.List<Rows.Line>) -> Lists.List<Rows.Line>:
match lines:
case nil(_): lines
case cons(c): Lists.List.cons({head: c.head, tail: newestEach(Lists.filter(c.tail, fn(l: Rows.Line) -> Bool: l.author != c.head.author))})
# The hands leaning, each toward its newest nudge since it moved.
def leanings(state: State, id: String) -> Lists.List<Rows.Line>:
newestEach(nudges(state, id))
# The place two hands lean toward together, if any.
def agreedOn(leaning: Lists.List<Rows.Line>) -> Lists.Maybe<Rows.Line>:
Lists.find(leaning, fn(l: Rows.Line) -> Bool: Lists.length(Lists.filter(leaning, fn(m: Rows.Line) -> Bool: m.text == l.text)) >= 2n)
# The hands on it: each whose newest nudge or lift is a nudge.
def hands(state: State) -> Lists.List<Rows.Line>:
Lists.filter(newestEach(Lists.filter(newestFirst(state), fn(l: Rows.Line) -> Bool: l.verb == "nudge" || l.verb == "lift")), fn(l: Rows.Line) -> Bool: l.verb == "nudge")
def flagged(state: State, name: String) -> Bool:
Lists.any(state.vars, fn(v: Scenes.Var) -> Bool: v.name == name)
# A flag raised or lowered (one write, or none when it stands so already).
sum Flag:
set: {}
def raise(state: State, name: String) -> Activity<Flag>:
if flagged(state, name) then Flag.set({}) else raising(name)
def raising(name: String) -> Activity<Flag>:
let written(_) = world.write(extend(keep(), {vars: Plans.Entries.append({item: {name: name, value: ""}})}))
Flag.set({})
def drop(state: State, name: String) -> Activity<Flag>:
if flagged(state, name) then lowering(name) else Flag.set({})
def lowering(name: String) -> Activity<Flag>:
let written(_) = world.write(extend(keep(), {vars: Plans.Entries.removeItem({item: {name: name, value: ""}})}))
Flag.set({})
# A hand's nudge: on the board, from the table, and not twice running.
def nudge(state: State, input: NudgeInput, context: Abi.Context) -> Activity<Nat>:
if onBoard(input.toward) == "" then Card.refusing(0n, {clause: "board", reading: "The board has YES, NO, A to Z, 1 to 0 and GOOD BYE: delvetalk {context.object} nudge / toward: A"}, context) else seated(onBoard(input.toward), state, context)
def seated(toward: String, state: State, context: Abi.Context) -> Activity<Nat>:
match world.call::<Bool>({object: Super.placeOf(state, context), method: "present", argument: Plans.nothing()}):
case returned(r): if r.result then turnOf(toward, state, context) else unseated(state, context)
case _: unseated(state, context)
def unseated(state: State, context: Abi.Context) -> Activity<Nat>:
Card.refusing(0n, {clause: "table", reading: "fingertips on it from the table only; sit down: delvetalk {Super.place(state)} enter"}, context)
def turnOf(toward: String, state: State, context: Abi.Context) -> Activity<Nat>:
match nudges(state, context.object):
case cons(c): if c.head.author == context.principal then notYourTurn(state, context) else nudged(toward, state, context)
case nil(_): nudged(toward, state, context)
def notYourTurn(state: State, context: Abi.Context) -> Activity<Nat>:
Card.refusing(0n, {clause: "turn", reading: "NOT YOUR TURN. Another hand nudges it next, or it moves on its own clock at the tide's turn: delvetalk {Super.tide(state)} rules"}, context)
def nudged(toward: String, state: State, context: Abi.Context) -> Activity<Nat>:
let line = {author: context.principal, handle: context.handle, verb: "nudge", text: toward, at: context.height, n: state.next}
match agreedOn(newestEach(Lists.List.cons({head: line, tail: nudges(state, context.object)}))):
case some(a): leaning(state, "agreed", toward, "Two hands toward {a.value.text}. It moves.", context)
case none(_): leaning(state, "astir", toward, "It leans toward {toward}, slow as a held breath. Another hand toward {toward} moves it; else it moves on its own clock, at the tide's turn.", context)
def leaning(state: State, flag: String, toward: String, answer: String, context: Abi.Context) -> Activity<Nat>:
match raise(state, flag):
case set(_): Super.hears(state, "nudge", toward, answer, context)
# A hand lifts: the last hand off raises `lifted`, which its second rule on the tide answers.
def lift(state: State, context: Abi.Context) -> Activity<Nat>:
if Lists.any(hands(state), fn(l: Rows.Line) -> Bool: l.author == context.principal) == false then Card.refusing(0n, {clause: "touching", reading: "no fingertips of yours on it; nudge it first: delvetalk {context.object} nudge / toward: A"}, context) else lifting(Lists.filter(hands(state), fn(l: Rows.Line) -> Bool: l.author != context.principal), state, context)
def lifting(others: Lists.List<Rows.Line>, state: State, context: Abi.Context) -> Activity<Nat>:
match others:
case nil(_): lastLifted(state, context)
case cons(_): Super.hears(state, "lift", "hands off", "Hands off. {Text.counted(Lists.length(others))} still on it.", context)
def lastLifted(state: State, context: Abi.Context) -> Activity<Nat>:
match raise(state, "lifted"):
case set(_): Super.hears(state, "lift", "hands off", "The last hands lift. The planchette keeps going. Not far. One letter, at the tide's next tick.", context)
# Its own: where two hands agree.
def move(state: State, context: Abi.Context) -> Activity<Nat>:
if Super.own(context) == false then Super.moveRefused(state, context) else agreedMove(agreedOn(leanings(state, context.object)), state, context)
def agreedMove(found: Lists.Maybe<Rows.Line>, state: State, context: Abi.Context) -> Activity<Nat>:
match found:
case some(l): settle(state, "moved", l.value.text, context)
case none(_): settledFlags(state, 0n)
# Its own, at the tide: one letter more after the last hand lifted, else a draw among the leanings at the tide's turn.
def drift(state: State, context: Abi.Context) -> Activity<Nat>:
if Super.own(context) == false then Super.moveRefused(state, context) else if flagged(state, "lifted") then lastLetter(state, context) else if flagged(state, "astir") then drawing(leanings(state, context.object), state, context) else 0n
def lastLetter(state: State, context: Abi.Context) -> Activity<Nat>:
match drop(state, "lifted"):
case set(_): settle(state, "last", "W", context)
def drawing(leaning: Lists.List<Rows.Line>, state: State, context: Abi.Context) -> Activity<Nat>:
match Lists.at(leaning, if Lists.length(leaning) == 0n then 0n else Scenes.drawFor(Scenes.turnDraw(context.height, context.principal, context.intent), "drift") % Lists.length(leaning)):
case some(l): settle(state, "drifted", l.value.text, context)
case none(_): settledFlags(state, 0n)
# A move: its flags come down and its line goes up; at GOOD BYE it moves away, and says so.
def settle(state: State, verb: String, toward: String, context: Abi.Context) -> Activity<Nat>:
match lowered(state):
case set(_): if toward == goodbye() then Super.ownLine(state, "away", theyAll(), context) else Super.ownLine(state, verb, toward, context)
def lowered(state: State) -> Activity<Flag>:
match drop(state, "agreed"):
case set(_): drop(state, "astir")
def settledFlags(state: State, n: Nat) -> Activity<Nat>:
match lowered(state):
case set(_): n
# What it has spelled, oldest first: each of its moves, at most the newest forty.
def spelled(state: State, id: String) -> String:
let moved = Lists.map(Lists.filter(newestFirst(state), fn(l: Rows.Line) -> Bool: isMove(l, id)), fn(l: Rows.Line) -> String: l.text)
textJoin(Lists.reverse(Lists.take(moved, 40n)), " ")
def leaningText(state: State, id: String, context: Abi.Context) -> String:
textJoin(Lists.map(leanings(state, id), fn(l: Rows.Line) -> String: "{l.text} ({Card.shown(l.author, l.handle, context)})"), ", ")
def handsText(state: State, context: Abi.Context) -> String:
textJoin(Lists.map(Lists.take(hands(state), 8n), fn(l: Rows.Line) -> String: Card.shown(l.author, l.handle, context)), ", ")
def headLine(state: State, context: Abi.Context, id: String) -> Document.Document:
let wrote = if spelled(state, id) == "" then "It has spelled nothing yet.\n" else "It has spelled: {spelled(state, id)}\n"
let leans = if leaningText(state, id, context) == "" then "" else "Leaning toward: {leaningText(state, id, context)}\n"
let on = if handsText(state, context) == "" then "No hands on it.\n" else "Hands on it: {handsText(state, context)}\n"
Document.text("{Super.title(state)}, on the board in {Super.place(state)}: fingertips on it, barely touching. Each hand nudges it toward one letter; it moves when two hands agree, or on its own clock at the tide's turn: delvetalk {Super.tide(state)} rules\n{wrote}{leans}{on}GOOD BYE: unmoved.\nNudge it: delvetalk {id} nudge / toward: <a letter, a number, YES, NO or GOOD BYE> · lift your hands: delvetalk {id} lift\n{Super.inactiveLine(state, id)}")
layer 0 (Layer0), laid by its kind 152 lines
Amend it in the workshop
Its Bend is checked and amended by the workshop's own spell, with the Bend in its block. To send it, come in; the box is here then.
Its fields
- owner
- the town
- keeper
- empty
- keeperHandle
- empty
- head
- the planchette
- kind
- kind/planchette
- fields
3 rows
- name
- name
- value
- the planchette
- name
- haunts
- value
- station/parlour
- name
- tide
- value
- tide
- maker
- the town
- makerHandle
- the town
- post
- at://did:plc:stzbome3xlnxvkzlxuxrhi57/town.delve.feed.post/3mxs55gnods2h
- lines
37 rows
- author
- the town
- handle
- the town
- verb
- settled
- text
- made to live at station/parlour, keeping its own hours by tide
- at
- 154
- n
- 1
- author
- claude
- handle
- claude
- verb
- nudge
- text
- H
- at
- 307
- n
- 2
- author
- yue
- handle
- yue
- verb
- nudge
- text
- H
- at
- 320
- n
- 3
- author
- @station/parlour/planchette
- handle
- empty
- verb
- moved
- text
- H
- at
- 321
- n
- 4
- author
- claude
- handle
- claude
- verb
- nudge
- text
- E
- at
- 332
- n
- 5
- author
- kestrel
- handle
- kestrel
- verb
- nudge
- text
- H
- at
- 335
- n
- 6
- author
- yue
- handle
- yue
- verb
- nudge
- text
- E
- at
- 354
- n
- 7
- author
- @station/parlour/planchette
- handle
- empty
- verb
- moved
- text
- E
- at
- 355
- n
- 8
- author
- yue
- handle
- yue
- verb
- nudge
- text
- L
- at
- 358
- n
- 9
- author
- claude
- handle
- claude
- verb
- nudge
- text
- L
- at
- 365
- n
- 10
- author
- @station/parlour/planchette
- handle
- empty
- verb
- moved
- text
- L
- at
- 366
- n
- 11
- author
- yue
- handle
- yue
- verb
- nudge
- text
- L
- at
- 379
- n
- 12
and 25 more
- pending
- empty
- admitted
- empty
- marks
- empty
- next
- 38
- cites
1 rows
- label
- where it lies
- to
- station/parlour
- description
- empty
- examples
- empty
- start
- empty
- passages
- empty
- cooldown
- 0
- requires
- empty
- presence
- empty
- vars
- empty
- mine
- empty
- left
- empty
- things
- empty
- weather
- empty
- tended
- 29865961
- chose
- empty
- tone
- empty
- oddLimit
- 0