sourcedemos/rosetta/Comparison.xtl
1⍝# Comparison: the Rosetta stone's state machine (docs/rosetta.md), a
2⍝# library beside the demo ("cm:" u_se< "Comparison"). The state is one
3⍝# 4 x 5 matrix of numbers. The first three rows are the axes (the
4⍝# idiom drum, the top ring, the bottom ring), four columns each: how
5⍝# many items the axis has, how many steps it has taken (an integer; the
6⍝# item facing is 1 + steps, wrapped, and this is the truth), its angle
7⍝# in degrees (90 per step; the presentation, easing toward 90 * steps)
8⍝# and whether it plays on its own. The fourth row is the pointer: the
9⍝# axis being dragged (0 none, 0.5 pressed and not yet moved, else the
10⍝# axis), where it last was (x, y), how far it has moved in all, and the
11⍝# last turn it gave in degrees (for a flick). The fifth row is the
12⍝# attract mode's clock: seconds since the last interaction, seconds
13⍝# since its last step, and its two counters (how far the bottom ring
14⍝# is through its sweep of the other languages, how far the drum is
15⍝# through its sweep of the idioms). Nothing
16⍝# is ever inferred from an angle except what to draw. Every function
17⍝# gives a new state; none has an effect. Events (RS1) come in as
18⍝# []E_VENT gives them; cm:u_pdate takes one.
19
20⍝# The attract mode's pace, in seconds: how long a comparison holds
21⍝# still before the next (long enough to read two faces of code), and
22⍝# how long after an interaction the stone waits before moving on its
23⍝# own again (long enough to look round a comparison by hand). The
24⍝# traversal is the nested loop for top, for idiom, for bottom: the
25⍝# bottom ring steps at each dwell; when it has swept every other
26⍝# language the drum rolls to the next idiom in the same instant, the
27⍝# bottom going on round; and only when every idiom has been shown
28⍝# does the top ring advance to the next language. So the language
29⍝# compared from stays put through the whole table, and no comparison
30⍝# comes round again within minutes (the user, 2026-10-06; before,
31⍝# the top stepped after each sweep of the bottom and the drum after
32⍝# each sweep of the top: two minutes per idiom and the same pairs
33⍝# again and again).
34ˡdwell ← 5.0
35ˡidleBefore ← 20.0
36
37⍝# The state for i idioms and l languages, i ᶜᵐs̲tart l: nothing
38⍝# turned, all playing, no pointer, the attract mode ready (idle long
39⍝# enough already).
40⍝# >> "cm:" u_se< "Comparison"
41⍝# >> 31 cm:s_tart 7
42⍝# 31.0 0.0 0.0 1.0 0.0
43⍝# 7.0 0.0 0.0 1.0 0.0
44⍝# 7.0 0.0 0.0 1.0 0.0
45⍝# 0.0 0.0 0.0 0.0 0.0
46⍝# 20.0 0.0 0.0 0.0 0.0
47ˡs̲tart ← { i l → 5 5 r̲eshape (f̲loat i) c̲at 0.0 0.0 1.0 0.0 c̲at (f̲loat l) c̲at 0.0 0.0 1.0 0.0 c̲at (f̲loat l) c̲at 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 c̲at ˡidleBefore c̲at 0.0 0.0 0.0 0.0 }
48
49⍝# The rows and columns, by name.
50ˡIDIOM ← 1.0
51ˡTOP ← 2.0
52ˡBOTTOM ← 3.0
53ˡPOINTER ← 4.0
54ˡATTRACT ← 5.0
55ˡIDLE ← 1.0
56ˡSINCE ← 2.0
57ˡBOTTOMS ← 3.0
58ˡIDIOMS ← 4.0
59ˡCOUNT ← 1.0
60ˡSTEPS ← 2.0
61ˡANGLE ← 3.0
62ˡPLAYING ← 4.0
63ˡDRAGGING ← 1.0
64ˡX ← 2.0
65ˡY ← 3.0
66ˡMOVED ← 4.0
67ˡLAST ← 5.0
68
69⍝# The picture's middle, in its own pixels (Stone's size is 420): the
70⍝# top half above it, the bottom half below; and how many degrees a
71⍝# pixel of drag turns an axis (a quarter turn across half the picture).
72ˡmiddle ← 210.0
73ˡperPixel ← 90.0 ÷ 210.0
74
75⍝# A cell of the state: (row c̲at col) ᶜᵐa̲t state.
76ˡa̲t ← { rc state → (f̲loor 2 s̲elect rc) s̲elect (f̲loor 1 s̲elect rc) s̲elect state }
77
78⍝# The state with one cell replaced: (row c̲at col c̲at v) ᶜᵐp̲ut state (a
79⍝# mask over the matrix, so no cell is ever addressed by hand).
80ˡp̲ut ← { rcv state →
81 mask ← f̲loat (rows = 1 s̲elect rcv) × cols = 2 s̲elect rcv
82 (state × 1.0 − mask) + mask × 3 s̲elect rcv
83}
84rows ← 5 5 r̲eshape 5 r̲eplicate 1.0 2.0 3.0 4.0 5.0
85cols ← 5 5 r̲eshape 1.0 2.0 3.0 4.0 5.0
86
87⍝# An item of an axis of n, wrapped around: item k of n for any integer k.
88ˡw̲rap ← { n k → 1 + (k − 1) m̲od n }
89
90⍝# The item facing on an axis: 1 + its steps, wrapped.
91⍝# >> "cm:" u_se< "Comparison"
92⍝# >> cm:TOP cm:c_urrent (cm:TOP c_at 1.0) cm:s_tep 31 cm:s_tart 7
93⍝# 2
94ˡc̲urrent ← { axis state → (f̲loor 0.5 + (axis c̲at ˡCOUNT) ˡa̲t state) ˡw̲rap f̲loor 1.5 + (axis c̲at ˡSTEPS) ˡa̲t state }
95
96⍝# An axis stepped by d items (1 forward, -1 back): its steps change,
97⍝# its angle will follow at each tick. A ring never stops on the other
98⍝# ring's language (the two halves always compare two languages): it
99⍝# steps once more the same way. (axis c̲at d) ᶜᵐs̲tep state.
100⍝# >> "cm:" u_se< "Comparison"
101⍝# >> cm:IDIOM cm:c_urrent (cm:IDIOM c_at -1.0) cm:s_tep 31 cm:s_tart 7
102⍝# 31
103⍝# >> cm:BOTTOM cm:c_urrent (cm:BOTTOM c_at 1.0) cm:s_tep (cm:TOP c_at 2.0) cm:c_hoose 31 cm:s_tart 7
104⍝# 3
105ˡs̲tep ← { ad state →
106 axis ← 1 s̲elect ad
107 d ← 2 s̲elect ad
108 moved ← (axis c̲at ˡSTEPS c̲at d + (axis c̲at ˡSTEPS) ˡa̲t state) ˡp̲ut state
109 axis = ˡIDIOM ? moved
110 other ← 5.0 − axis
111 clash ← (axis ˡc̲urrent moved) = other ˡc̲urrent moved
112 "clash" i̲f< "(axis c_at d) l:s_tep moved; moved"
113}
114
115⍝# An axis turned to item k the short way round, and paused: choosing
116⍝# is navigating, turning is exploring. A ring is not turned to the
117⍝# other ring's language (the choice is offered disabled); the state is
118⍝# unchanged. (axis c̲at k) ᶜᵐc̲hoose state.
119⍝# >> "cm:" u_se< "Comparison"
120⍝# >> cm:TOP cm:c_urrent (cm:TOP c_at 7.0) cm:c_hoose 31 cm:s_tart 7
121⍝# 7
122⍝# >> (cm:TOP c_at cm:STEPS) cm:a_t (cm:TOP c_at 7.0) cm:c_hoose 31 cm:s_tart 7
123⍝# -1.0
124ˡc̲hoose ← { ak state →
125 axis ← 1 s̲elect ak
126 n ← f̲loor 0.5 + (axis c̲at ˡCOUNT) ˡa̲t state
127 d ← ((f̲loor 2 s̲elect ak) − axis ˡc̲urrent state) m̲od n
128 short ← "(2 * d) > n" i̲f< "d - n; d"
129 chosen ← (axis c̲at ˡPLAYING c̲at 0.0) ˡp̲ut (axis c̲at f̲loat short) ˡs̲tep state
130 axis = ˡIDIOM ? chosen
131 (f̲loor 2 s̲elect ak) = (5.0 − axis) ˡc̲urrent state ? state
132 chosen
133}
134
135⍝# An axis's angle eased toward 90 times its steps over dt seconds: it
136⍝# covers the gap in about half a second, and lands exactly when near.
137⍝# (axis c̲at dt) ᶜᵐe̲ase state.
138⍝# >> "cm:" u_se< "Comparison"
139⍝# >> (cm:TOP c_at cm:ANGLE) cm:a_t (cm:TOP c_at 0.25) cm:e_ase (cm:TOP c_at 1.0) cm:s_tep 31 cm:s_tart 7
140⍝# 45.0
141ˡe̲ase ← { adt state →
142 axis ← 1 s̲elect adt
143 angle ← (axis c̲at ˡANGLE) ˡa̲t state
144 target ← 90.0 × (axis c̲at ˡSTEPS) ˡa̲t state
145 gap ← target − angle
146 moved ← angle + gap × 1.0 m̲in 2.0 × 2 s̲elect adt
147 near ← "0.5 > a_bs target - moved" i̲f< "target; moved"
148 (axis c̲at ˡANGLE c̲at near) ˡp̲ut state
149}
150
151⍝# Every axis at rest where its steps put it (angles 90 times steps),
152⍝# for a state built by choosing before the first tick.
153⍝# >> "cm:" u_se< "Comparison"
154⍝# >> cm:s_ettle (cm:IDIOM c_at 3.0) cm:c_hoose 31 cm:s_tart 7
155⍝# 31.0 2.0 180.0 0.0 0.0
156⍝# 7.0 0.0 0.0 1.0 0.0
157⍝# 7.0 0.0 0.0 1.0 0.0
158⍝# 0.0 0.0 0.0 0.0 0.0
159⍝# 20.0 0.0 0.0 0.0 0.0
160ˡs̲ettle ← { state → (ˡBOTTOM c̲at 9.0) ˡe̲ase (ˡTOP c̲at 9.0) ˡe̲ase (ˡIDIOM c̲at 9.0) ˡe̲ase state }
161
162⍝# Every axis eased over dt seconds (a tick), and the attract mode's
163⍝# clock advanced: when the stone has been left alone long enough and
164⍝# the last comparison has held still for a dwell, the attract mode
165⍝# steps (cm:a_ttract).
166⍝# >> "cm:" u_se< "Comparison"
167⍝# >> cm:BOTTOM cm:c_urrent cm:dwell cm:t_ick 31 cm:s_tart 7
168⍝# 2
169⍝# >> cm:BOTTOM cm:c_urrent (cm:dwell - 0.1) cm:t_ick 31 cm:s_tart 7
170⍝# 1
171ˡt̲ick ← { dt state →
172 eased ← (ˡBOTTOM c̲at dt) ˡe̲ase (ˡTOP c̲at dt) ˡe̲ase (ˡIDIOM c̲at dt) ˡe̲ase state
173 idle ← dt + (ˡATTRACT c̲at ˡIDLE) ˡa̲t state
174 since ← dt + (ˡATTRACT c̲at ˡSINCE) ˡa̲t state
175 timed ← (ˡATTRACT c̲at ˡSINCE c̲at since) ˡp̲ut (ˡATTRACT c̲at ˡIDLE c̲at idle) ˡp̲ut eased
176 (idle < ˡidleBefore) ∨ since < ˡdwell ? timed
177 ˡa̲ttract (ˡATTRACT c̲at ˡSINCE c̲at 0.0) ˡp̲ut timed
178}
179
180⍝# One step of the attract mode: the bottom ring steps (if it plays);
181⍝# when it has swept every other language (its count reaches one less
182⍝# than the languages), the drum rolls to the next idiom too and the
183⍝# bottom's count starts over; when the drum has rolled through every
184⍝# idiom, the top ring steps too and the drum's count starts over. A
185⍝# paused axis is counted but not turned, so the sweeps keep their
186⍝# rhythm. With 3 idioms and 4 languages: the drum rolls at every
187⍝# third step, the top steps at the ninth.
188⍝# >> "cm:" u_se< "Comparison"
189⍝# >> s := 3 cm:s_tart 4
190⍝# >> (cm:IDIOM cm:c_urrent cm:a_ttract^2 s) c_at cm:IDIOM cm:c_urrent cm:a_ttract^3 s
191⍝# 1 2
192⍝# >> (cm:TOP cm:c_urrent cm:a_ttract^8 s) c_at cm:TOP cm:c_urrent cm:a_ttract^9 s
193⍝# 1 2
194⍝# >> cm:IDIOM cm:c_urrent cm:a_ttract^9 s
195⍝# 1
196ˡa̲ttract ← { state →
197 n ← (f̲loor 0.5 + (ˡTOP c̲at ˡCOUNT) ˡa̲t state) − 1
198 m ← f̲loor 0.5 + (ˡIDIOM c̲at ˡCOUNT) ˡa̲t state
199 bottoms ← 1.0 + (ˡATTRACT c̲at ˡBOTTOMS) ˡa̲t state
200 idioms ← 1.0 + (ˡATTRACT c̲at ˡIDIOMS) ˡa̲t state
201 moved ← ˡBOTTOM ˡp̲lay state
202 bottoms < f̲loat n ? (ˡATTRACT c̲at ˡBOTTOMS c̲at bottoms) ˡp̲ut moved
203 rolled ← ˡIDIOM ˡp̲lay (ˡATTRACT c̲at ˡBOTTOMS c̲at 0.0) ˡp̲ut moved
204 idioms < f̲loat m ? (ˡATTRACT c̲at ˡIDIOMS c̲at idioms) ˡp̲ut rolled
205 ˡTOP ˡp̲lay (ˡATTRACT c̲at ˡIDIOMS c̲at 0.0) ˡp̲ut rolled
206}
207
208⍝# An axis stepped forward if it plays, else left as it is.
209ˡp̲lay ← { axis state → "1.0 = (axis c_at l:PLAYING) l:a_t state" i̲f< "(axis c_at 1.0) l:s_tep state; state" }
210
211⍝# The stone touched: the attract mode waits again from now.
212ˡt̲ouched ← { state → (ˡATTRACT c̲at ˡSINCE c̲at 0.0) ˡp̲ut (ˡATTRACT c̲at ˡIDLE c̲at 0.0) ˡp̲ut state }
213
214⍝# Every axis playing again (a start chosen with cm:c_hoose is paused).
215ˡr̲esume ← { state → (ˡBOTTOM c̲at ˡPLAYING c̲at 1.0) ˡp̲ut (ˡTOP c̲at ˡPLAYING c̲at 1.0) ˡp̲ut (ˡIDIOM c̲at ˡPLAYING c̲at 1.0) ˡp̲ut state }
216
217⍝# The tour paused when any axis plays, else every axis resumed (the
218⍝# space bar: one key stops everything, and starts it again).
219⍝# >> "cm:" u_se< "Comparison"
220⍝# >> (cm:TOP c_at cm:PLAYING) cm:a_t cm:t_oggleTour 31 cm:s_tart 7
221⍝# 0.0
222⍝# >> (cm:BOTTOM c_at cm:PLAYING) cm:a_t cm:t_oggleTour cm:TOP cm:t_oggle 31 cm:s_tart 7
223⍝# 0.0
224⍝# >> (cm:TOP c_at cm:PLAYING) cm:a_t cm:t_oggleTour cm:t_oggleTour 31 cm:s_tart 7
225⍝# 1.0
226ˡt̲oggleTour ← { state →
227 playing ← ((ˡIDIOM c̲at ˡPLAYING) ˡa̲t state) + ((ˡTOP c̲at ˡPLAYING) ˡa̲t state) + (ˡBOTTOM c̲at ˡPLAYING) ˡa̲t state
228 0.0 < playing ? (ˡBOTTOM c̲at ˡPLAYING c̲at 0.0) ˡp̲ut (ˡTOP c̲at ˡPLAYING c̲at 0.0) ˡp̲ut (ˡIDIOM c̲at ˡPLAYING c̲at 0.0) ˡp̲ut state
229 ˡr̲esume state
230}
231
232⍝# The tour's mode, for the host to show: "paused" when every axis is
233⍝# paused, "paused: top, bottom" naming the axes paused when some are,
234⍝# "holding" while the stone waits after a touch, else "touring".
235⍝# >> "cm:" u_se< "Comparison"
236⍝# >> cm:m_ode 31 cm:s_tart 7
237⍝# touring
238⍝# >> cm:m_ode cm:t_ouched 31 cm:s_tart 7
239⍝# holding
240⍝# >> cm:m_ode cm:TOP cm:t_oggle 31 cm:s_tart 7
241⍝# paused: top
242⍝# >> cm:m_ode cm:t_oggleTour 31 cm:s_tart 7
243⍝# paused
244ˡm̲ode ← { state →
245 p̲aused ← { axis → 0.0 = (axis c̲at ˡPLAYING) ˡa̲t state }
246 paused ← (p̲aused ˡIDIOM) c̲at (p̲aused ˡTOP) c̲at p̲aused ˡBOTTOM
247 3 = '+ r̲/ paused ? "paused"
248 names ← paused r̲eplicate (e̲nclose "roll") c̲at (e̲nclose "top") c̲at e̲nclose "bottom"
249 0 < '+ r̲/ paused ? "paused: " c̲at d̲isclose '{ x y → e̲nclose (d̲isclose x) c̲at ", " c̲at d̲isclose y } r̲/ names
250 ˡidleBefore > (ˡATTRACT c̲at ˡIDLE) ˡa̲t state ? "holding"
251 "touring"
252}
253
254⍝# An axis paused or resumed.
255ˡt̲oggle ← { axis state → (axis c̲at ˡPLAYING c̲at 1.0 − (axis c̲at ˡPLAYING) ˡa̲t state) ˡp̲ut state }
256
257⍝# The state after an event: a tick eases every axis; the arrow keys
258⍝# step the drum (Up, Down) and the top ring (Left, Right), a and d the
259⍝# bottom ring, space pauses or resumes the whole tour (cm:t_oggleTour),
260⍝# r resumes it at once, the idle wait over (cm:r_esume; the host
261⍝# sends it after its opening choices, since a choice pauses its
262⍝# axis), s the bottom ring and w
263⍝# the drum on their own; the pointer drags and
264⍝# clicks (cm:d_own, cm:m_ove, cm:u_p,
265⍝# cm:c_lick); a choice from the host's controls, choose AXIS ITEM,
266⍝# turns that axis to the item (cm:c_hoose).
267ˡu̲pdate ← { state e →
268 kind ← ⎕E̲KIND e
269 kind m̲atch "tick" ? (f̲irst ⎕E̲AT e) ˡt̲ick state
270 touched ← ˡt̲ouched state
271 kind m̲atch "key" ? (⎕E̲KEY e) ˡk̲ey touched
272 kind m̲atch "down" ? (⎕E̲AT e) ˡd̲own touched
273 kind m̲atch "move" ? (⎕E̲AT e) ˡm̲ove touched
274 kind m̲atch "up" ? (⎕E̲AT e) ˡu̲p touched
275 kind m̲atch "click" ? (⎕E̲AT e) ˡc̲lick touched
276 kind m̲atch "choose" ? (⎕E̲AT e) ˡc̲hoose touched
277 state
278}
279
280⍝# The half under a point: the top ring above the middle, the bottom
281⍝# ring below.
282ˡh̲alf ← { xy → "(2 s_elect xy) < l:middle" i̲f< "l:TOP; l:BOTTOM" }
283
284⍝# The pointer pressed at a point: remembered, nothing turned yet.
285⍝# xy ᶜᵐd̲own state.
286⍝# >> "cm:" u_se< "Comparison"
287⍝# >> 4 s_elect 100.0 50.0 cm:d_own 31 cm:s_tart 7
288⍝# 0.5 100.0 50.0 0.0 0.0
289ˡd̲own ← { xy state → (ˡPOINTER c̲at ˡLAST c̲at 0.0) ˡp̲ut (ˡPOINTER c̲at ˡMOVED c̲at 0.0) ˡp̲ut (ˡPOINTER c̲at ˡY c̲at 2 s̲elect xy) ˡp̲ut (ˡPOINTER c̲at ˡX c̲at 1 s̲elect xy) ˡp̲ut (ˡPOINTER c̲at ˡDRAGGING c̲at 0.5) ˡp̲ut state }
290
291⍝# The pointer moved to a point: nothing unless it is pressed; the
292⍝# first move of more than a few pixels decides which axis it drags (a
293⍝# sideways move the half it is over, an up or down move the drum);
294⍝# then each move turns that axis by its distance, a drag right or down
295⍝# turning toward the previous item. A drag does not pause the axis for
296⍝# good: the stone waits cm:idleBefore seconds after any touch before
297⍝# moving on its own again. xy ᶜᵐm̲ove state.
298⍝# >> "cm:" u_se< "Comparison"
299⍝# >> (cm:TOP c_at cm:ANGLE) cm:a_t 142.0 50.0 cm:m_ove 100.0 50.0 cm:d_own 31 cm:s_tart 7
300⍝# -18.0
301⍝# >> (cm:BOTTOM c_at cm:ANGLE) cm:a_t 142.0 50.0 cm:m_ove 100.0 50.0 cm:d_own 31 cm:s_tart 7
302⍝# 0.0
303ˡm̲ove ← { xy state →
304 held ← (ˡPOINTER c̲at ˡDRAGGING) ˡa̲t state
305 held = 0.0 ? state
306 dx ← (1 s̲elect xy) − (ˡPOINTER c̲at ˡX) ˡa̲t state
307 dy ← (2 s̲elect xy) − (ˡPOINTER c̲at ˡY) ˡa̲t state
308 moved ← (a̲bs dx) + (a̲bs dy) + (ˡPOINTER c̲at ˡMOVED) ˡa̲t state
309 placed ← (ˡPOINTER c̲at ˡMOVED c̲at moved) ˡp̲ut (ˡPOINTER c̲at ˡY c̲at 2 s̲elect xy) ˡp̲ut (ˡPOINTER c̲at ˡX c̲at 1 s̲elect xy) ˡp̲ut state
310 (held = 0.5) ∧ moved ≤ 4.0 ? placed
311 axis ← "held = 0.5" i̲f< "\"(a_bs dx) >= a_bs dy\" i_f< \"l:h_alf xy; l:IDIOM\"; held"
312 along ← "axis = l:IDIOM" i̲f< "dy; dx"
313 turn ← n̲eg along × ˡperPixel
314 angle ← turn + (axis c̲at ˡANGLE) ˡa̲t placed
315 (ˡPOINTER c̲at ˡLAST c̲at turn) ˡp̲ut (ˡPOINTER c̲at ˡDRAGGING c̲at axis) ˡp̲ut (axis c̲at ˡANGLE c̲at angle) ˡp̲ut placed
316}
317
318⍝# The pointer released: a drag settles on the nearest item, one
319⍝# further along when the last move was a flick (more than 6 degrees),
320⍝# its steps becoming that item so the angle eases the rest of the way;
321⍝# a press that never moved is a click. xy ᶜᵐu̲p state.
322⍝# >> "cm:" u_se< "Comparison"
323⍝# >> (cm:TOP c_at cm:STEPS) cm:a_t 142.0 50.0 cm:u_p 142.0 50.0 cm:m_ove 100.0 50.0 cm:d_own 31 cm:s_tart 7
324⍝# -1.0
325⍝# >> (cm:TOP c_at cm:STEPS) cm:a_t 125.0 50.0 cm:u_p 125.0 50.0 cm:m_ove 120.0 50.0 cm:m_ove 100.0 50.0 cm:d_own 31 cm:s_tart 7
326⍝# 0.0
327ˡu̲p ← { xy state →
328 held ← (ˡPOINTER c̲at ˡDRAGGING) ˡa̲t state
329 held = 0.0 ? state
330 released ← (ˡPOINTER c̲at ˡDRAGGING c̲at 0.0) ˡp̲ut state
331 held = 0.5 ? xy ˡc̲lick released
332 last ← (ˡPOINTER c̲at ˡLAST) ˡa̲t state
333 flick ← "6.0 < a_bs last" i̲f< "45.0 * last / a_bs last; 0.0"
334 angle ← (held c̲at ˡANGLE) ˡa̲t state
335 (held c̲at ˡSTEPS c̲at f̲loat f̲loor 0.5 + (angle + flick) ÷ 90.0) ˡp̲ut released
336}
337
338⍝# A click on a half pauses or resumes it. xy ᶜᵐc̲lick state.
339⍝# >> "cm:" u_se< "Comparison"
340⍝# >> (cm:BOTTOM c_at cm:PLAYING) cm:a_t 100.0 300.0 cm:c_lick 31 cm:s_tart 7
341⍝# 0.0
342ˡc̲lick ← { xy state → (ˡh̲alf xy) ˡt̲oggle state }
343
344⍝# A key's effect: see cm:u_pdate. Named keys compare with []K_NAMED
345⍝# (1 Up, 2 Down, 3 Left, 4 Right); printing keys by their character.
346ˡk̲ey ← { k state →
347 k = ⎕K̲NAMED 1 ? (ˡIDIOM c̲at -1.0) ˡs̲tep state
348 k = ⎕K̲NAMED 2 ? (ˡIDIOM c̲at 1.0) ˡs̲tep state
349 k = ⎕K̲NAMED 3 ? (ˡTOP c̲at -1.0) ˡs̲tep state
350 k = ⎕K̲NAMED 4 ? (ˡTOP c̲at 1.0) ˡs̲tep state
351 c ← ⎕K̲CHAR k
352 c m̲atch "a" ? (ˡBOTTOM c̲at -1.0) ˡs̲tep state
353 c m̲atch "d" ? (ˡBOTTOM c̲at 1.0) ˡs̲tep state
354 c m̲atch " " ? ˡt̲oggleTour state
355 c m̲atch "r" ? (ˡATTRACT c̲at ˡIDLE c̲at ˡidleBefore) ˡp̲ut ˡr̲esume state
356 c m̲atch "s" ? ˡBOTTOM ˡt̲oggle state
357 c m̲atch "w" ? ˡIDIOM ˡt̲oggle state
358 state
359}
i̲f< expands to
{ @ → (1.0 = (axis c̲at ˡPLAYING) ˡa̲t state) ? (axis c̲at 1.0) ˡs̲tep state◆ state } @