Inspecting kind/planchette · the kinds · the language · the modules
{name}
- id
- kind/planchette
- called
- {name}
- kind chain
- kind/planchette → kind/inhabitant → record
- owner
- the town
- made by
- the town
- version
- 6
- pin
- hagap-fopan
- state
- 13706 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.
| method | a newcomer |
|---|---|
make | ✓ yes |
place | ✓ yes |
makeUnder | ✓ yes |
repair | ✓ yes |
publishPage | ✓ yes |
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 form, the law, the head, the voice, what it is or the body; anyone makes, 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(form))) and (unchanged(law))) and (unchanged(head))) and (unchanged(voice))) and (unchanged(about))) and (unchanged(body)))) - law kind kinds's law holds
under "kinds"
Its Bend
what it runs: its entry module 20 lines
layer over ./Kind.obend
edition ObjectiveBend 1
import ./Abi.obend as Abi
import ./List.obend as Lists
import ./Form.obend as Form
import ./Rows.obend as Rows
type State = Super.State
form make:
name: text 1..64
haunts: text 1..128
tide: text 1..128
def make(state: State, input: MakeInput, context: Abi.Context) -> Activity<Super.Done>:
Super.making(state, Lists.List.cons({head: {name: "name", value: input.name}, tail: Lists.List.cons({head: {name: "haunts", value: input.haunts}, tail: Lists.List.cons({head: {name: "tide", value: input.tide}, tail: Lists.List.nil()})})}), "", context)
form place:
name: text 1..64
haunts: text 1..128
tide: text 1..128
id: text 1..64
def place(state: State, input: PlaceInput, context: Abi.Context) -> Activity<Super.Done>:
Super.making(state, Lists.List.cons({head: {name: "name", value: input.name}, tail: Lists.List.cons({head: {name: "haunts", value: input.haunts}, tail: Lists.List.cons({head: {name: "tide", value: input.tide}, tail: Lists.List.cons({head: {name: "id", value: input.id}, tail: Lists.List.nil()})})})}), "", context)
the body this kind lays on what it makes 169 lines
Each thing of this kind runs this layer over what the kind is of.
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; {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)}")
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
- ownerHandle
- the town
- name
- planchette
- of
- kind/inhabitant
- form
name: text 1..64 (54 characters)
name: text 1..64 haunts: text 1..128 tide: text 1..128
- law
law below "kind/inhabitant's law holds": under "kind/inhabitant" (217 characters)
law below "kind/inhabitant's law holds": under "kind/inhabitant" rule agreed "moves when two hands agree on a letter": "agreed" in new.vars.name and not ("agreed" in old.vars.name) then delvetalk {request.object} move- head
- {name}
- voice
- slow as a held breath; it circles the alphabet like a dog deciding where to lie down; it moves on its own clock
- about
- a planchette on a ouija board: each hand nudges it toward a letter; it moves when two hands agree, or on its own clock at the tide's turn; after yla.delve.town
- body
layer over ./Record.obend (12915 characters)
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; {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)}")- made
2 rows
- object
- planchette/1
- n
- 2
- about
- empty
- object
- station/parlour/planchette
- n
- 1
- about
- empty
- count
- 2