Byzantium-Game 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- byzantium_game-0.1.0/Byzantium.py +16 -0
- byzantium_game-0.1.0/Byzantium_Game.egg-info/PKG-INFO +5 -0
- byzantium_game-0.1.0/Byzantium_Game.egg-info/SOURCES.txt +19 -0
- byzantium_game-0.1.0/Byzantium_Game.egg-info/dependency_links.txt +1 -0
- byzantium_game-0.1.0/Byzantium_Game.egg-info/entry_points.txt +2 -0
- byzantium_game-0.1.0/Byzantium_Game.egg-info/requires.txt +1 -0
- byzantium_game-0.1.0/Byzantium_Game.egg-info/top_level.txt +2 -0
- byzantium_game-0.1.0/Game/Citadel.py +555 -0
- byzantium_game-0.1.0/Game/Crypt.py +523 -0
- byzantium_game-0.1.0/Game/Dream.py +545 -0
- byzantium_game-0.1.0/Game/Field.py +591 -0
- byzantium_game-0.1.0/Game/Forge.py +501 -0
- byzantium_game-0.1.0/Game/Gateway.py +488 -0
- byzantium_game-0.1.0/Game/Sanctum.py +302 -0
- byzantium_game-0.1.0/Game/Spire.py +515 -0
- byzantium_game-0.1.0/Game/Vault.py +321 -0
- byzantium_game-0.1.0/Game/__init__.py +1 -0
- byzantium_game-0.1.0/PKG-INFO +5 -0
- byzantium_game-0.1.0/README.md +180 -0
- byzantium_game-0.1.0/pyproject.toml +22 -0
- byzantium_game-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
base = os.path.dirname(os.path.abspath(__file__))
|
|
5
|
+
game = os.path.join(base, "Game")
|
|
6
|
+
|
|
7
|
+
if game not in sys.path:
|
|
8
|
+
sys.path.insert(0, game)
|
|
9
|
+
|
|
10
|
+
import Gateway
|
|
11
|
+
|
|
12
|
+
def main():
|
|
13
|
+
Gateway.main()
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
main()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Byzantium.py
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
Byzantium_Game.egg-info/PKG-INFO
|
|
5
|
+
Byzantium_Game.egg-info/SOURCES.txt
|
|
6
|
+
Byzantium_Game.egg-info/dependency_links.txt
|
|
7
|
+
Byzantium_Game.egg-info/entry_points.txt
|
|
8
|
+
Byzantium_Game.egg-info/requires.txt
|
|
9
|
+
Byzantium_Game.egg-info/top_level.txt
|
|
10
|
+
Game/Citadel.py
|
|
11
|
+
Game/Crypt.py
|
|
12
|
+
Game/Dream.py
|
|
13
|
+
Game/Field.py
|
|
14
|
+
Game/Forge.py
|
|
15
|
+
Game/Gateway.py
|
|
16
|
+
Game/Sanctum.py
|
|
17
|
+
Game/Spire.py
|
|
18
|
+
Game/Vault.py
|
|
19
|
+
Game/__init__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cryptography
|
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any, Optional, Tuple, Callable
|
|
4
|
+
import time
|
|
5
|
+
import Forge
|
|
6
|
+
from Forge import Action, Focus, MENU, UiCache, geometry
|
|
7
|
+
core = None
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Relay:
|
|
11
|
+
cache: Any = None
|
|
12
|
+
_state: Any = None
|
|
13
|
+
ash: list = None
|
|
14
|
+
|
|
15
|
+
def bindcache(self, cache: Any) -> Any:
|
|
16
|
+
self.cache = cache
|
|
17
|
+
if self.ash is None:
|
|
18
|
+
self.ash = []
|
|
19
|
+
if cache is not None:
|
|
20
|
+
cache.ash = list(self.ash)
|
|
21
|
+
cache.feed = list(self.ash)
|
|
22
|
+
cache.visible_feed_count = len(cache.feed)
|
|
23
|
+
if cache is not None and self._state is not None:
|
|
24
|
+
try:
|
|
25
|
+
cache.state = self._state
|
|
26
|
+
cache.monuments = list(getattr(self._state, 'monument', ()) or ())
|
|
27
|
+
cache.pending_request = ''
|
|
28
|
+
cache.intent.Q.self = Forge.Qof(self._state)
|
|
29
|
+
if getattr(self._state, 'cells', ()):
|
|
30
|
+
cache.focus = Focus.MENU
|
|
31
|
+
cache.syncIntent()
|
|
32
|
+
except Exception:
|
|
33
|
+
pass
|
|
34
|
+
return cache
|
|
35
|
+
|
|
36
|
+
def surfacestate(self, value: Any) -> Any:
|
|
37
|
+
value = Forge.makeState(value)
|
|
38
|
+
self._state = value
|
|
39
|
+
try:
|
|
40
|
+
pass
|
|
41
|
+
except Exception:
|
|
42
|
+
pass
|
|
43
|
+
if self.cache is not None:
|
|
44
|
+
try:
|
|
45
|
+
self.cache.state = value
|
|
46
|
+
self.cache.pending_request = ''
|
|
47
|
+
self.cache.monuments = list(getattr(value, 'monument', ()) or ())
|
|
48
|
+
self.cache.intent.Q.self = Forge.Qof(value)
|
|
49
|
+
if getattr(value, 'cells', ()):
|
|
50
|
+
self.cache.focus = Focus.MENU
|
|
51
|
+
self.cache.syncIntent()
|
|
52
|
+
if self.ash is None:
|
|
53
|
+
self.ash = []
|
|
54
|
+
self.cache.ash = list(self.ash)
|
|
55
|
+
self.cache.feed = list(self.ash)
|
|
56
|
+
self.cache.visible_feed_count = len(self.cache.feed)
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
return self._state
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def state(self) -> Any:
|
|
63
|
+
return self._state
|
|
64
|
+
|
|
65
|
+
@state.setter
|
|
66
|
+
def state(self, value: Any) -> None:
|
|
67
|
+
self.surfacestate(value)
|
|
68
|
+
|
|
69
|
+
def intent(self, value: Any) -> Any:
|
|
70
|
+
return send(value)
|
|
71
|
+
|
|
72
|
+
def ashfall(self, value: Any) -> Any:
|
|
73
|
+
if self.ash is None:
|
|
74
|
+
self.ash = []
|
|
75
|
+
if not isinstance(value, dict):
|
|
76
|
+
return None
|
|
77
|
+
sender = str(value.get('sender', '') or '').strip()
|
|
78
|
+
kind = str(value.get('kind', '') or '').strip().upper()
|
|
79
|
+
text = str(value.get('text', '') or '')
|
|
80
|
+
total = int(value.get('total', 0) or 0)
|
|
81
|
+
name = sender.ljust(Forge.NAME_W)[:Forge.NAME_W]
|
|
82
|
+
left = 'DEFECTED' if kind == 'DEFECT' else Forge.fmtSpineCost(total, width=Forge.COST_W, signed=True)
|
|
83
|
+
line = f'{name} {left}:{text}'.rstrip()
|
|
84
|
+
entry = (kind or 'ASH', line)
|
|
85
|
+
self.ash.append(entry)
|
|
86
|
+
self.ash = self.ash[-7:]
|
|
87
|
+
if self.cache is not None:
|
|
88
|
+
self.cache.ash = list(self.ash)
|
|
89
|
+
self.cache.feed = list(self.ash)
|
|
90
|
+
self.cache.visible_feed_count = len(self.cache.feed)
|
|
91
|
+
return entry
|
|
92
|
+
|
|
93
|
+
def peerdisplay(self, key: str) -> str:
|
|
94
|
+
key = str(key or '').strip()
|
|
95
|
+
if not key:
|
|
96
|
+
return ''
|
|
97
|
+
cells = list(getattr(self._state, 'cells', []) or [])
|
|
98
|
+
for cell in cells:
|
|
99
|
+
if _cell_key(cell) == key:
|
|
100
|
+
soul = str(getattr(cell, 'soul', '') or '').strip()
|
|
101
|
+
if soul:
|
|
102
|
+
return soul
|
|
103
|
+
else:
|
|
104
|
+
pass
|
|
105
|
+
short = Forge.id6(key)
|
|
106
|
+
return short or key[:6].upper()
|
|
107
|
+
citadel = Relay()
|
|
108
|
+
_CITADEL = citadel
|
|
109
|
+
|
|
110
|
+
def _cell_key(cell: Any) -> str:
|
|
111
|
+
if hasattr(Forge, 'key'):
|
|
112
|
+
return str(Forge.key(cell) or '').strip()
|
|
113
|
+
value = getattr(cell, 'key', getattr(cell, 'pubkey', ''))
|
|
114
|
+
if isinstance(value, (bytes, bytearray)):
|
|
115
|
+
return bytes(value).hex()
|
|
116
|
+
return str(value or '').strip()
|
|
117
|
+
|
|
118
|
+
def _floor(current: Action, state: Any) -> int:
|
|
119
|
+
rank = Forge.resolveRank(state) if hasattr(Forge, 'resolveRank') else None
|
|
120
|
+
q = Forge.Qof(state) if hasattr(Forge, 'Qof') else None
|
|
121
|
+
if hasattr(Forge, 'actionFloor'):
|
|
122
|
+
try:
|
|
123
|
+
return int(Forge.actionFloor(current, rank=rank, q=q) or 0)
|
|
124
|
+
except TypeError:
|
|
125
|
+
return int(Forge.actionFloor(current, rank=rank, Q=q) or 0)
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
def _first_defect(state: Any, mine: int, start: int):
|
|
129
|
+
cells = list(getattr(state, 'cells', []) or [])
|
|
130
|
+
n = len(cells)
|
|
131
|
+
if n <= 0:
|
|
132
|
+
return None
|
|
133
|
+
start = int(start) % n
|
|
134
|
+
if hasattr(Forge, 'defectViable'):
|
|
135
|
+
for hop in range(n):
|
|
136
|
+
q = (start + hop) % n
|
|
137
|
+
if Forge.defectViable(state, int(mine), q):
|
|
138
|
+
return q
|
|
139
|
+
else:
|
|
140
|
+
pass
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
def _pubkey_at(state: Any, q: int) -> str:
|
|
144
|
+
if hasattr(Forge, 'keyAt'):
|
|
145
|
+
return str(Forge.keyAt(state, int(q)) or '').strip()
|
|
146
|
+
cells = list(getattr(state, 'cells', []) or [])
|
|
147
|
+
if 0 <= int(q) < len(cells):
|
|
148
|
+
return _cell_key(cells[int(q)])
|
|
149
|
+
return ''
|
|
150
|
+
|
|
151
|
+
def _defect_viable(state: Any, mine: int, target: int) -> bool:
|
|
152
|
+
if hasattr(Forge, 'defectViable'):
|
|
153
|
+
return bool(Forge.defectViable(state, int(mine), int(target)))
|
|
154
|
+
return int(target) != int(mine)
|
|
155
|
+
|
|
156
|
+
def say(cache: UiCache, chan: str, line: str) -> None:
|
|
157
|
+
feed = list(getattr(cache, 'feed', []) or [])
|
|
158
|
+
feed.append((chan, line))
|
|
159
|
+
cache.feed = feed[-7:]
|
|
160
|
+
cache.visible_feed_count = len(cache.feed)
|
|
161
|
+
|
|
162
|
+
def action(cache: UiCache) -> Action:
|
|
163
|
+
return MENU[int(cache.menuQ) % len(MENU)]
|
|
164
|
+
|
|
165
|
+
def sync(cache: UiCache, state: Any) -> Any:
|
|
166
|
+
state = Forge.makeState(state)
|
|
167
|
+
cache.state = state
|
|
168
|
+
cache.focus = cache.focus or Focus.MENU
|
|
169
|
+
cache.intent.focus = cache.focus
|
|
170
|
+
cache.intent.action = action(cache)
|
|
171
|
+
cache.intent.Q.self = Forge.Qof(state)
|
|
172
|
+
cache.intent.Q.city = int(getattr(cache, 'stateQ', 0) or 0)
|
|
173
|
+
cache.intent.Q.target = getattr(cache, 'targetQ', None)
|
|
174
|
+
cache.intent.amount = max(0, int(getattr(cache, 'salt', 0) or 0))
|
|
175
|
+
cache.intent.text = Forge.cleanDraft(getattr(cache, 'text', ''))
|
|
176
|
+
cache.intent.kind = str(getattr(cache.intent.action, 'value', cache.intent.action) or '').upper()
|
|
177
|
+
return state
|
|
178
|
+
|
|
179
|
+
def menu(cache: UiCache, delta: int) -> None:
|
|
180
|
+
cache.menuQ = (int(cache.menuQ) + int(delta)) % len(MENU)
|
|
181
|
+
cache.intent.action = action(cache)
|
|
182
|
+
|
|
183
|
+
def reset(cache: UiCache) -> None:
|
|
184
|
+
cache.focus = Focus.MENU
|
|
185
|
+
cache.menuQ = 0
|
|
186
|
+
cache.stateQ = int(cache.intent.Q.self or 0)
|
|
187
|
+
cache.targetQ = None
|
|
188
|
+
cache.salt = 1
|
|
189
|
+
cache.text = ''
|
|
190
|
+
cache.syncIntent()
|
|
191
|
+
|
|
192
|
+
def selfQorzero(state: Any) -> int:
|
|
193
|
+
q = Forge.Qof(state)
|
|
194
|
+
return 0 if q is None else q
|
|
195
|
+
|
|
196
|
+
def moveboard(cache: UiCache, arrow: str, current: Action, state: Any) -> None:
|
|
197
|
+
cache.stateQ = Forge.moveBoard(state, getattr(cache, 'stateQ', 0), arrow, current)
|
|
198
|
+
cache.syncIntent()
|
|
199
|
+
|
|
200
|
+
def floor(current: Action, state: Any) -> int:
|
|
201
|
+
return _floor(current, state)
|
|
202
|
+
|
|
203
|
+
def _resolve_sender() -> Optional[Callable[[Any], Any]]:
|
|
204
|
+
global core
|
|
205
|
+
live = core
|
|
206
|
+
if live is None:
|
|
207
|
+
return None
|
|
208
|
+
for name in ('intent', 'submit', 'transact'):
|
|
209
|
+
fn = getattr(live, name, None)
|
|
210
|
+
if callable(fn):
|
|
211
|
+
return fn
|
|
212
|
+
else:
|
|
213
|
+
pass
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
def send(value: Any) -> Any:
|
|
217
|
+
if isinstance(value, dict):
|
|
218
|
+
payload = dict(value)
|
|
219
|
+
else:
|
|
220
|
+
payload = {'kind': str(getattr(value, 'kind', '') or getattr(getattr(value, 'action', ''), 'value', getattr(value, 'action', '')) or '').upper(), 'pairs': list(getattr(value, 'pairs', []) or []), 'text': Forge.msgNorm(getattr(value, 'text', '')), 'lock': getattr(value, 'lock', None), 'key': str(getattr(value, 'key', '') or '').strip()}
|
|
221
|
+
try:
|
|
222
|
+
pass
|
|
223
|
+
except Exception:
|
|
224
|
+
pass
|
|
225
|
+
fn = _resolve_sender()
|
|
226
|
+
if fn is None:
|
|
227
|
+
return None
|
|
228
|
+
try:
|
|
229
|
+
out = fn(payload)
|
|
230
|
+
return out
|
|
231
|
+
except Exception as exc:
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
def _target_cells(state: Any, mine: int, current: Action, target: Optional[int]):
|
|
235
|
+
cells = list(getattr(state, 'cells', []) or [])
|
|
236
|
+
qs = Forge.targets(state, int(mine), current, target) if hasattr(Forge, 'targets') else ()
|
|
237
|
+
return [cells[int(q)] for q in qs if 0 <= int(q) < len(cells)]
|
|
238
|
+
|
|
239
|
+
def _intent_pairs(state: Any, mine: int, current: Action, target: Optional[int], total: int):
|
|
240
|
+
cells = _target_cells(state, mine, current, target)
|
|
241
|
+
if current == Action.PURGE:
|
|
242
|
+
return ()
|
|
243
|
+
if current == Action.WHISPER:
|
|
244
|
+
return Forge.geometry.split(int(total), cells) if len(cells) > 1 else tuple(((Forge.key(cell), int(total)) for cell in cells if Forge.key(cell)))
|
|
245
|
+
if current == Action.DEFECT:
|
|
246
|
+
victimkey = Forge.defecttargetkey(state, target) if hasattr(Forge, 'defecttargetkey') else _pubkey_at(state, int(target))
|
|
247
|
+
legs = list(Forge.geometry.split(int(total), cells))
|
|
248
|
+
if victimkey:
|
|
249
|
+
legs.append((victimkey, 0))
|
|
250
|
+
return tuple(legs)
|
|
251
|
+
return Forge.geometry.split(int(total), cells)
|
|
252
|
+
|
|
253
|
+
def _intent_lock(state: Any, mine: int):
|
|
254
|
+
cells = list(getattr(state, 'cells', []) or [])
|
|
255
|
+
if 0 <= int(mine) < len(cells):
|
|
256
|
+
out = getattr(cells[int(mine)], 'lock', None)
|
|
257
|
+
try:
|
|
258
|
+
if out is not None:
|
|
259
|
+
pass
|
|
260
|
+
except Exception:
|
|
261
|
+
pass
|
|
262
|
+
return out
|
|
263
|
+
return None
|
|
264
|
+
|
|
265
|
+
def submit(cache: UiCache, state: Any, current: Action) -> None:
|
|
266
|
+
mine = selfQorzero(state)
|
|
267
|
+
total = int(getattr(cache, 'salt', 0) or 0)
|
|
268
|
+
text = str(getattr(cache, 'text', '') or '')
|
|
269
|
+
target = getattr(cache, 'targetQ', None)
|
|
270
|
+
try:
|
|
271
|
+
intent = cache.syncIntent()
|
|
272
|
+
intent.kind = str(getattr(current, 'value', current) or '').upper()
|
|
273
|
+
intent.pairs = _intent_pairs(state, mine, current, target, total)
|
|
274
|
+
intent.text = Forge.msgNorm(text)
|
|
275
|
+
intent.lock = _intent_lock(state, mine)
|
|
276
|
+
if current == Action.PURGE:
|
|
277
|
+
intent.key = Forge.purgeTarget(state, target)
|
|
278
|
+
intent.text = Forge.purgeFlavor() if hasattr(Forge, 'purgeFlavor') else ''
|
|
279
|
+
intent.pairs = ()
|
|
280
|
+
intent.lock = None
|
|
281
|
+
send(intent)
|
|
282
|
+
except Exception as exc:
|
|
283
|
+
say(cache, 'ASH', str(exc) or 'intent failed')
|
|
284
|
+
finally:
|
|
285
|
+
reset(cache)
|
|
286
|
+
|
|
287
|
+
def handlemenu(cache: UiCache, state: Any, kind: str, value: Optional[str]):
|
|
288
|
+
current = action(cache)
|
|
289
|
+
if kind == 'ARROW':
|
|
290
|
+
if value == 'C':
|
|
291
|
+
menu(cache, +1)
|
|
292
|
+
elif value == 'D':
|
|
293
|
+
menu(cache, -1)
|
|
294
|
+
return (cache, state, False)
|
|
295
|
+
if kind != 'ENTER':
|
|
296
|
+
return (cache, state, False)
|
|
297
|
+
if current == Action.EXIT:
|
|
298
|
+
return (cache, state, True)
|
|
299
|
+
if current == Action.PURGE:
|
|
300
|
+
cache.focus = Focus.TABLE_MOVE
|
|
301
|
+
mine = selfQorzero(state) % geometry.cells
|
|
302
|
+
start = (mine + 1) % geometry.cells
|
|
303
|
+
cache.stateQ = start
|
|
304
|
+
cache.targetQ = None
|
|
305
|
+
cache.text = ''
|
|
306
|
+
cache.syncIntent()
|
|
307
|
+
return (cache, state, False)
|
|
308
|
+
if current == Action.MONUMENT:
|
|
309
|
+
cache.show_banner = not bool(getattr(cache, 'show_banner', True))
|
|
310
|
+
cache.syncIntent()
|
|
311
|
+
return (cache, state, False)
|
|
312
|
+
if current == Action.LORE:
|
|
313
|
+
showing = bool(getattr(cache, 'show_lore', False))
|
|
314
|
+
cache.show_lore = not showing
|
|
315
|
+
if cache.show_lore:
|
|
316
|
+
cache.lore_offset = 0
|
|
317
|
+
cache.syncIntent()
|
|
318
|
+
return (cache, state, False)
|
|
319
|
+
if current in (Action.RALLY, Action.WRATH):
|
|
320
|
+
cache.focus = Focus.TABLE_LOCK
|
|
321
|
+
cache.salt = floor(current, state)
|
|
322
|
+
cache.text = ''
|
|
323
|
+
cache.syncIntent()
|
|
324
|
+
return (cache, state, False)
|
|
325
|
+
if current == Action.WHISPER:
|
|
326
|
+
cache.focus = Focus.TABLE_MOVE
|
|
327
|
+
cache.salt = 1
|
|
328
|
+
cache.text = ''
|
|
329
|
+
cache.stateQ = (selfQorzero(state) + 1) % geometry.cells
|
|
330
|
+
cache.syncIntent()
|
|
331
|
+
return (cache, state, False)
|
|
332
|
+
if current == Action.DEFECT:
|
|
333
|
+
cache.focus = Focus.TABLE_MOVE
|
|
334
|
+
cache.salt = floor(current, state)
|
|
335
|
+
mine = selfQorzero(state) % geometry.cells
|
|
336
|
+
start = int(getattr(cache, 'stateQ', mine) or mine) % geometry.cells
|
|
337
|
+
if start == mine:
|
|
338
|
+
start = (mine + 1) % geometry.cells
|
|
339
|
+
target = _first_defect(state, mine, start)
|
|
340
|
+
cache.stateQ = mine if target is None else target
|
|
341
|
+
cache.syncIntent()
|
|
342
|
+
return (cache, state, False)
|
|
343
|
+
return (cache, state, False)
|
|
344
|
+
|
|
345
|
+
def handletablemove(cache: UiCache, state: Any, kind: str, value: Optional[str]):
|
|
346
|
+
current = action(cache)
|
|
347
|
+
if kind == 'ARROW' and value in ('A', 'B', 'C', 'D'):
|
|
348
|
+
moveboard(cache, value, current, state)
|
|
349
|
+
return (cache, state, False)
|
|
350
|
+
if kind != 'ENTER':
|
|
351
|
+
return (cache, state, False)
|
|
352
|
+
if current == Action.WHISPER:
|
|
353
|
+
mine = selfQorzero(state) % geometry.cells
|
|
354
|
+
if int(cache.stateQ) == mine:
|
|
355
|
+
cache.stateQ = (mine + 1) % geometry.cells
|
|
356
|
+
cache.targetQ = int(cache.stateQ)
|
|
357
|
+
cache.focus = Focus.SPINE
|
|
358
|
+
cache.syncIntent()
|
|
359
|
+
return (cache, state, False)
|
|
360
|
+
if current == Action.PURGE:
|
|
361
|
+
mine = selfQorzero(state) % geometry.cells
|
|
362
|
+
target = int(cache.stateQ) % geometry.cells
|
|
363
|
+
if mine == target or (hasattr(Forge, 'purgeViable') and (not Forge.purgeViable(state, mine, target))):
|
|
364
|
+
say(cache, 'ASH', 'invalid target')
|
|
365
|
+
reset(cache)
|
|
366
|
+
return (cache, state, False)
|
|
367
|
+
cache.targetQ = target
|
|
368
|
+
submit(cache, state, current)
|
|
369
|
+
return (cache, state, False)
|
|
370
|
+
if current == Action.DEFECT:
|
|
371
|
+
mine = selfQorzero(state) % geometry.cells
|
|
372
|
+
target = int(cache.stateQ) % geometry.cells
|
|
373
|
+
if not _defect_viable(state, mine, target):
|
|
374
|
+
say(cache, 'ASH', 'invalid target')
|
|
375
|
+
reset(cache)
|
|
376
|
+
return (cache, state, False)
|
|
377
|
+
cache.targetQ = target
|
|
378
|
+
cache.focus = Focus.SPINE
|
|
379
|
+
cache.text = ''
|
|
380
|
+
cache.syncIntent()
|
|
381
|
+
return (cache, state, False)
|
|
382
|
+
return (cache, state, False)
|
|
383
|
+
|
|
384
|
+
def handletablelock(cache: UiCache, state: Any, kind: str, value: Optional[str]):
|
|
385
|
+
if kind == 'ENTER':
|
|
386
|
+
cache.focus = Focus.SPINE
|
|
387
|
+
cache.text = ''
|
|
388
|
+
cache.syncIntent()
|
|
389
|
+
return (cache, state, False)
|
|
390
|
+
|
|
391
|
+
def handlespine(cache: UiCache, state: Any, kind: str, value: Optional[str]):
|
|
392
|
+
current = action(cache)
|
|
393
|
+
if kind == 'ARROW':
|
|
394
|
+
if current == Action.DEFECT:
|
|
395
|
+
return (cache, state, False)
|
|
396
|
+
step = floor(current, state)
|
|
397
|
+
cells = list(getattr(state, 'cells', []) or [])
|
|
398
|
+
mine = selfQorzero(state) % geometry.cells
|
|
399
|
+
have = Forge.amount(cells[mine]) if 0 <= mine < len(cells) else 0
|
|
400
|
+
cap = have if have >= step else step
|
|
401
|
+
if value == 'A':
|
|
402
|
+
cache.salt = min(cap, int(cache.salt) + step)
|
|
403
|
+
elif value == 'B':
|
|
404
|
+
cache.salt = max(step, int(cache.salt) - step)
|
|
405
|
+
cache.syncIntent()
|
|
406
|
+
return (cache, state, False)
|
|
407
|
+
if kind == 'BS':
|
|
408
|
+
if current in (Action.WHISPER, Action.RALLY, Action.WRATH, Action.DEFECT):
|
|
409
|
+
cache.text = (cache.text or '')[:-1]
|
|
410
|
+
cache.syncIntent()
|
|
411
|
+
return (cache, state, False)
|
|
412
|
+
if kind == 'CH' and value and value.isprintable() and (current in (Action.WHISPER, Action.RALLY, Action.WRATH, Action.DEFECT)):
|
|
413
|
+
if len(cache.text) < Forge.MSG_MAX:
|
|
414
|
+
cache.text += value
|
|
415
|
+
cache.syncIntent()
|
|
416
|
+
return (cache, state, False)
|
|
417
|
+
if kind != 'ENTER':
|
|
418
|
+
return (cache, state, False)
|
|
419
|
+
if current in (Action.WHISPER, Action.RALLY, Action.WRATH, Action.DEFECT):
|
|
420
|
+
submit(cache, state, current)
|
|
421
|
+
else:
|
|
422
|
+
reset(cache)
|
|
423
|
+
return (cache, state, False)
|
|
424
|
+
|
|
425
|
+
def dispatch(cache: UiCache, state: Any, token: Tuple[str, Optional[str]]):
|
|
426
|
+
kind, value = token
|
|
427
|
+
if kind == 'CTRL_C':
|
|
428
|
+
return (cache, state, True)
|
|
429
|
+
if bool(getattr(cache, 'show_lore', False)):
|
|
430
|
+
if kind == 'ARROW':
|
|
431
|
+
offset = int(getattr(cache, 'lore_offset', 0) or 0)
|
|
432
|
+
if value == 'A':
|
|
433
|
+
cache.lore_offset = max(0, offset - 1)
|
|
434
|
+
elif value == 'B':
|
|
435
|
+
cache.lore_offset = offset + 1
|
|
436
|
+
cache.syncIntent()
|
|
437
|
+
return (cache, state, False)
|
|
438
|
+
if kind == 'ENTER' or (kind == 'CH' and value == ' '):
|
|
439
|
+
cache.show_lore = False
|
|
440
|
+
cache.lore_offset = 0
|
|
441
|
+
cache.syncIntent()
|
|
442
|
+
return (cache, state, False)
|
|
443
|
+
if kind == 'CH' and value == ' ' and (cache.focus in (Focus.TABLE_MOVE, Focus.TABLE_LOCK)):
|
|
444
|
+
reset(cache)
|
|
445
|
+
return (cache, state, False)
|
|
446
|
+
if cache.focus == Focus.MENU:
|
|
447
|
+
return handlemenu(cache, state, kind, value)
|
|
448
|
+
if cache.focus == Focus.TABLE_MOVE:
|
|
449
|
+
return handletablemove(cache, state, kind, value)
|
|
450
|
+
if cache.focus == Focus.TABLE_LOCK:
|
|
451
|
+
return handletablelock(cache, state, kind, value)
|
|
452
|
+
if cache.focus == Focus.SPINE:
|
|
453
|
+
return handlespine(cache, state, kind, value)
|
|
454
|
+
return (cache, state, False)
|
|
455
|
+
|
|
456
|
+
def parsekeys(buffer: str):
|
|
457
|
+
out = []
|
|
458
|
+
i = 0
|
|
459
|
+
while i < len(buffer):
|
|
460
|
+
c = buffer[i]
|
|
461
|
+
if c == '\x03':
|
|
462
|
+
out.append(('CTRL_C', None))
|
|
463
|
+
i += 1
|
|
464
|
+
continue
|
|
465
|
+
if c == '\n':
|
|
466
|
+
out.append(('ENTER', None))
|
|
467
|
+
i += 1
|
|
468
|
+
continue
|
|
469
|
+
if c in ('\x7f', '\x08'):
|
|
470
|
+
out.append(('BS', None))
|
|
471
|
+
i += 1
|
|
472
|
+
continue
|
|
473
|
+
if c != '\x1b':
|
|
474
|
+
out.append(('CH', c))
|
|
475
|
+
i += 1
|
|
476
|
+
continue
|
|
477
|
+
if i + 1 >= len(buffer):
|
|
478
|
+
break
|
|
479
|
+
n1 = buffer[i + 1]
|
|
480
|
+
if n1 == 'O' and i + 2 < len(buffer) and (buffer[i + 2] in ('A', 'B', 'C', 'D')):
|
|
481
|
+
out.append(('ARROW', buffer[i + 2]))
|
|
482
|
+
i += 3
|
|
483
|
+
continue
|
|
484
|
+
if n1 == '[':
|
|
485
|
+
j = i + 2
|
|
486
|
+
seq = ''
|
|
487
|
+
while j < len(buffer):
|
|
488
|
+
d = buffer[j]
|
|
489
|
+
if d in ('A', 'B', 'C', 'D'):
|
|
490
|
+
seq += d
|
|
491
|
+
if seq.startswith('1;2') or seq.startswith('2'):
|
|
492
|
+
out.append(('SHIFT_ARROW', d))
|
|
493
|
+
else:
|
|
494
|
+
out.append(('ARROW', d))
|
|
495
|
+
i = j + 1
|
|
496
|
+
break
|
|
497
|
+
if d == '~':
|
|
498
|
+
i = j + 1
|
|
499
|
+
break
|
|
500
|
+
seq += d
|
|
501
|
+
j += 1
|
|
502
|
+
else:
|
|
503
|
+
break
|
|
504
|
+
continue
|
|
505
|
+
i += 1
|
|
506
|
+
return (out, buffer[i:])
|
|
507
|
+
|
|
508
|
+
def initcache(state: Any):
|
|
509
|
+
state = Forge.makeState(state)
|
|
510
|
+
cache = UiCache(feed=[], local_name='')
|
|
511
|
+
cache.focus = Focus.TITLE
|
|
512
|
+
cache.state = state
|
|
513
|
+
cache.pending_request = 'WAITING_STATE'
|
|
514
|
+
if citadel.ash is None:
|
|
515
|
+
citadel.ash = []
|
|
516
|
+
cache.ash = list(citadel.ash)
|
|
517
|
+
cache.feed = list(citadel.ash)
|
|
518
|
+
cache.visible_feed_count = len(cache.feed)
|
|
519
|
+
cache.lore_offset = 0
|
|
520
|
+
cache.intent.Q.self = Forge.Qof(state)
|
|
521
|
+
citadel.bindcache(cache)
|
|
522
|
+
citadel.state = state
|
|
523
|
+
return cache
|
|
524
|
+
|
|
525
|
+
def installdebug():
|
|
526
|
+
return None
|
|
527
|
+
|
|
528
|
+
def bindcache(cache: Any) -> Any:
|
|
529
|
+
return citadel.bindcache(cache)
|
|
530
|
+
|
|
531
|
+
def bindcore(coreobj: Any) -> Any:
|
|
532
|
+
global core
|
|
533
|
+
core = coreobj
|
|
534
|
+
return coreobj
|
|
535
|
+
|
|
536
|
+
def state(value: Any=None) -> Any:
|
|
537
|
+
if value is None:
|
|
538
|
+
return citadel.state
|
|
539
|
+
citadel.surfacestate(value)
|
|
540
|
+
return citadel.state
|
|
541
|
+
|
|
542
|
+
def intent(value: Any) -> Any:
|
|
543
|
+
return citadel.intent(value)
|
|
544
|
+
|
|
545
|
+
def ashfall(value: Any) -> Any:
|
|
546
|
+
return citadel.ashfall(value)
|
|
547
|
+
|
|
548
|
+
def peerdisplaylabel(key: str) -> str:
|
|
549
|
+
return citadel.peerdisplay(key)
|
|
550
|
+
_parse_keys_buffered = parsekeys
|
|
551
|
+
_dispatch_token = dispatch
|
|
552
|
+
_init_cache = initcache
|
|
553
|
+
_install_debug = installdebug
|
|
554
|
+
peer_display_label = peerdisplaylabel
|
|
555
|
+
__all__ = ['Relay', 'citadel', 'bindcache', 'bindcore', 'state', 'intent', 'ashfall', 'peerdisplaylabel', 'parsekeys', 'dispatch', 'initcache', 'installdebug', '_parse_keys_buffered', '_dispatch_token', '_init_cache', '_install_debug', 'peer_display_label']
|