9Volt 0.0.1__py3-none-any.whl

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.
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.4
2
+ Name: 9Volt
3
+ Version: 0.0.1
4
+ Summary: A tiny Oblivious Compute halting-machine specimen
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: wcwidth>=0.2
@@ -0,0 +1,11 @@
1
+ HaltingMachine.py,sha256=uFP7vjkHDTU7iowZ8J6zi_swSH3CDSGJWLOQDkR71Xo,292
2
+ Machine/Spacewalk.py,sha256=L-n4-_9j7_BkiqEbc9pPpOPlKK5OesJ7g0X6eJ2UQQI,1847
3
+ Machine/__init__.py,sha256=gWfketXvBDaqOxZrk4DnoRPuCXCrdQO3NTkB8Y-netc,53
4
+ Machine/field.py,sha256=x0HVUWjfyvgSJ4p0K7yfJPmH6Ait_aIQv0a4ZBku1yc,2988
5
+ Machine/inverter.py,sha256=SUyPNGN-q-OnWRucvTqp74yIVvDOWmUTDndds6roJXU,277
6
+ Machine/observer.py,sha256=WsZ1oUMyXItKBPbl34Uj4WfbW7rQod-AiM2dSRluXUA,6286
7
+ 9volt-0.0.1.dist-info/METADATA,sha256=mU-vVUk-OMguM7LHTu5hCg6tALbY_skSkOy3e4ddZcI,160
8
+ 9volt-0.0.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
9
+ 9volt-0.0.1.dist-info/entry_points.txt,sha256=xFGFsuNord-JFO1IXqag_U7lVtBmc_xyg5UA3hJzrlY,55
10
+ 9volt-0.0.1.dist-info/top_level.txt,sha256=KqgqQ1tFPdtcFboqn1xrkVgv7iRX2pjJP8iCFkN1NZM,23
11
+ 9volt-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ HaltingMachine = HaltingMachine:main
@@ -0,0 +1,2 @@
1
+ HaltingMachine
2
+ Machine
HaltingMachine.py ADDED
@@ -0,0 +1,7 @@
1
+ import os, sys
2
+ from Machine.field import main
3
+ if __name__ == "__main__":
4
+ if sys.argv[1:] not in ([], ["Spacewalk.py"], ["Spacewalk"]):
5
+ raise SystemExit("Usage: python HaltingMachine.py [Spacewalk.py]")
6
+ os.environ["HALTING_SPACEWALK"] = "1" if sys.argv[1:] else "0"
7
+ main()
Machine/Spacewalk.py ADDED
@@ -0,0 +1,50 @@
1
+ # Change only this value to adjust Spacewalk's proposal and doorway pace.
2
+ INTERVAL = 3.0
3
+
4
+ POSITIVE = ('000', '100', '110', '111')
5
+ NEGATIVE = ('000', '001', '011', '111')
6
+ LATERAL = frozenset(('010', '101'))
7
+
8
+
9
+ class Spacewalk:
10
+ def __init__(self, inverted=False):
11
+ self.inverted = inverted
12
+ self.state = self.previous = '000'
13
+ self.doorway_at = None
14
+
15
+ @property
16
+ def lane(self):
17
+ return NEGATIVE if self.inverted else POSITIVE
18
+
19
+ @property
20
+ def shell(self):
21
+ return self.state.count('1')
22
+
23
+ def propose(self):
24
+ position = self.lane.index(self.state)
25
+ return self.lane[position + 1] if position < 3 else None
26
+
27
+ def accept(self, incoming, now=0):
28
+ successor = self.propose()
29
+ # ================= LINCHPIN ================= #
30
+ if incoming != successor or successor is None:
31
+ return None
32
+ # ============================================ #
33
+ assert sum(a != b for a, b in zip(self.state, successor)) == 1
34
+ self.previous, self.state = self.state, successor
35
+ if successor == '111': self.doorway_at = now + INTERVAL
36
+ return successor
37
+
38
+ def doorway(self):
39
+ assert self.state == '111'
40
+ self.previous = self.state
41
+ self.inverted = not self.inverted
42
+ self.state, self.doorway_at = '000', None
43
+ return self.state
44
+
45
+ def display(self):
46
+ emblems = ('🪨', '✂️', '📜', '🪨') if self.inverted else ('🪨', '📜', '✂️', '🪨')
47
+ icons = ''.join(glyph + (' ' if glyph == '✂️' else '') for glyph in emblems)
48
+ pointer = ' ' * (4 + 2 * self.shell)
49
+ left, right = (self.state, self.previous) if self.state == '000' and self.previous == '111' else (self.previous, self.state)
50
+ return pointer + '🔻', f'{left} {icons} {right}', pointer + '🔺'
Machine/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """HaltingMachine: observer, inverter, and field."""
Machine/field.py ADDED
@@ -0,0 +1,75 @@
1
+ import os, platform, random, shlex, shutil, signal, subprocess, sys, termios, tty
2
+ from wcwidth import wcswidth
3
+
4
+
5
+ def paint():
6
+ sys.stdout.write("\033[40m\033[97m\033[2J\033[H\033[?25l")
7
+
8
+
9
+ def clear():
10
+ sys.stdout.write("\033[?25h\033[0m\033[2J\033[H"); sys.stdout.flush()
11
+
12
+
13
+ def center(text):
14
+ return " " * max(0, (os.get_terminal_size().columns - wcswidth(text)) // 2) + text
15
+
16
+
17
+ def choose(label, lo, hi, value):
18
+ old = termios.tcgetattr(sys.stdin); tty.setcbreak(sys.stdin.fileno())
19
+ try:
20
+ while True:
21
+ paint(); print("\n\n" + center("HaltingMachine"), "\n", center(label), "", center(str(value)), sep="\n", flush=True)
22
+ key = os.read(sys.stdin.fileno(), 3)
23
+ if key in (b"\r", b"\n"): return value
24
+ if key == b"\x1b[A": value = min(hi, value + 1)
25
+ elif key == b"\x1b[B": value = max(lo, value - 1)
26
+ finally: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old)
27
+
28
+
29
+ def terminal_command(cmd):
30
+ names = ("kitty", "konsole", "gnome-terminal", "xfce4-terminal", "alacritty")
31
+ terminal = next((shutil.which(x) for x in names if shutil.which(x)), None)
32
+ terminal = terminal or shutil.which(os.environ.get("TERMINAL", ""))
33
+ terminal = terminal or shutil.which("x-terminal-emulator") or shutil.which("xterm")
34
+ if not terminal: raise SystemExit("No terminal emulator found")
35
+ name = os.path.basename(os.path.realpath(terminal))
36
+ if name == "gnome-terminal": return [terminal, "--", *cmd]
37
+ if name == "xfce4-terminal": return [terminal, "--command", shlex.join(cmd)]
38
+ if name == "alacritty": return [terminal, "-T", "HaltingMachine", "-e", *cmd]
39
+ if name == "kitty": return [terminal, "--title", "HaltingMachine", *cmd]
40
+ return [terminal, "-e", *cmd]
41
+
42
+
43
+ def spawn(module, index, total):
44
+ cmd = [sys.executable, "-m", module, str(index), str(total)]
45
+ env = os.environ.copy(); env["PYTHONIOENCODING"] = "utf-8"
46
+ if platform.system() == "Darwin":
47
+ line = shlex.join(cmd).replace('"', '\\"')
48
+ subprocess.Popen(["osascript", "-e", f'tell application "Terminal" to do script "{line}"'])
49
+ else: subprocess.Popen(terminal_command(cmd), env=env)
50
+
51
+
52
+ def main():
53
+ old_handlers = {s: signal.getsignal(s) for s in (signal.SIGTSTP, signal.SIGQUIT)}
54
+ for s in old_handlers: signal.signal(s, signal.SIG_IGN)
55
+ try:
56
+ total = choose("OBSERVERS", 1, 10, 3)
57
+ inverted = choose("INVERTERS", 0, total, min(1, total))
58
+ first = total - inverted
59
+ index = random.randrange(total)
60
+ for i in range(total):
61
+ if i != index:
62
+ spawn("Machine.inverter" if i >= first else "Machine.observer", i, total)
63
+ if index >= first:
64
+ from .inverter import run
65
+ else:
66
+ from .observer import run
67
+ run(index, total)
68
+ except (KeyboardInterrupt, EOFError):
69
+ pass
70
+ finally:
71
+ for s, h in old_handlers.items(): signal.signal(s, h)
72
+ clear()
73
+
74
+
75
+ if __name__ == "__main__": main()
Machine/inverter.py ADDED
@@ -0,0 +1,14 @@
1
+ import sys
2
+ from .observer import REVERSE, run as observe
3
+
4
+
5
+ def run(index, total):
6
+ observe(index, total, step=REVERSE, invert_controls=True, invert_mode=True)
7
+
8
+
9
+ def main():
10
+ index, total = map(int, sys.argv[1:3])
11
+ run(index, total)
12
+
13
+
14
+ if __name__ == "__main__": main()
Machine/observer.py ADDED
@@ -0,0 +1,134 @@
1
+ import os, select, signal, socket, sys, termios, time, tty
2
+ from wcwidth import wcswidth
3
+
4
+ ROCK, PAPER, SCISSORS = "🪨", "📜", "✂️"
5
+ FORWARD = {ROCK: PAPER, PAPER: SCISSORS, SCISSORS: ROCK}
6
+ REVERSE = {v: k for k, v in FORWARD.items()}
7
+ ART = {ROCK: ROCK * 4, PAPER: PAPER * 4, SCISSORS: "✂️ " * 4}
8
+ GREEK = ("Γ", "Δ", "Ε", "Θ", "Λ", "Π", "Σ", "Φ", "Ψ", "Ω")
9
+ BASE = 43120
10
+
11
+ def paint():
12
+ sys.stdout.write("\033[40m\033[97m\033[2J\033[H\033[?25l")
13
+
14
+ def clear():
15
+ sys.stdout.write("\033[?25h\033[0m\033[2J\033[H"); sys.stdout.flush()
16
+
17
+ def center(text, width=None):
18
+ width = wcswidth(text) if width is None else width
19
+ return " " * max(0, (os.get_terminal_size().columns - width) // 2) + text
20
+
21
+ def draw(identity, mode, state, inverted=False, genesis=None, cube=None):
22
+ paint(); print("\n\n\n\n" + center(f"{identity} HaltingMachine {identity}"), "\n")
23
+ if genesis is not None:
24
+ print(center("GENESIS"), "\n", center(genesis), flush=True); return
25
+ face = "🙃" if inverted else "🙂"
26
+ halt = f"{face}HALT{face}" if mode == 0 else "HALT"
27
+ repeat = f"{face}REPEAT{face}" if mode else "REPEAT"
28
+ visuals = cube.display() if cube else (ART[state],)
29
+ positions = [center(text, 16 if cube else 8) for text in visuals]
30
+ if cube:
31
+ print(center(halt), center(repeat), *positions, ' ' + center("SPACE TO EXIT"), sep="\n", flush=True)
32
+ else:
33
+ print(center(halt), center(repeat), '', *positions, '', ' ' + center("SPACE TO EXIT"), sep="\n", flush=True)
34
+
35
+ def send(sock, total, payload, skip):
36
+ data = payload.encode("utf-8")
37
+ for i in range(total):
38
+ if i != skip:
39
+ try: sock.sendto(data, ("127.0.0.1", BASE + i))
40
+ except OSError: pass
41
+
42
+ def run(index, total, step=FORWARD, invert_controls=False, invert_mode=False):
43
+ cube = None
44
+ if os.environ.get("HALTING_SPACEWALK") == "1":
45
+ from .Spacewalk import Spacewalk, INTERVAL
46
+ cube = Spacewalk(invert_mode)
47
+ interval = INTERVAL if cube else 1.0
48
+ identity = GREEK[index]
49
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
50
+ sock.setblocking(False); sock.bind(("127.0.0.1", BASE + index))
51
+ seen, state, mode = {index}, ROCK, 0
52
+ def show(genesis=None):
53
+ draw(identity, mode, state, cube.inverted if cube else invert_controls, genesis, cube)
54
+ old = termios.tcgetattr(sys.stdin); tty.setcbreak(sys.stdin.fileno())
55
+ ignored = (signal.SIGTSTP, signal.SIGQUIT)
56
+ handlers = {s: signal.getsignal(s) for s in ignored}
57
+ for s in ignored: signal.signal(s, signal.SIG_IGN)
58
+ hello_at = ready_at = 0.0; emit_at = None if cube else 0.0; shown_seen = -1
59
+ try:
60
+ while True:
61
+ now = time.monotonic()
62
+ if cube and cube.doorway_at is not None and now >= cube.doorway_at:
63
+ cube.doorway()
64
+ mode = int(cube.inverted)
65
+ emit_at = None # Doorway resets to an effectively HALTed relay.
66
+ show()
67
+ if now >= hello_at:
68
+ send(sock, total, f"H{index}", index); hello_at = now + .25
69
+ while True:
70
+ try: msg = sock.recvfrom(64)[0].decode("utf-8")
71
+ except BlockingIOError: break
72
+ if msg.startswith("H"):
73
+ seen.add(int(msg[1:]))
74
+ elif cube or now >= ready_at:
75
+ if cube:
76
+ admitted = cube.accept(msg, now)
77
+ active = (1 - mode) if cube.inverted else mode
78
+ if admitted is not None and active:
79
+ emit_at = now + interval # Admission rephases the repeater.
80
+ elif (admitted is None and active and emit_at is None
81
+ and len(msg) == 3 and set(msg) <= {'0', '1'}):
82
+ emit_at = now + interval # One delayed reply to a rejected Trinity.
83
+ else:
84
+ # ================= LINCHPIN ================= #
85
+ admissible = msg == step[state]
86
+ # ============================================ #
87
+ admitted = msg if admissible else None
88
+ if admitted is not None:
89
+ if not cube:
90
+ state = admitted
91
+ ready_at = emit_at = now + 1.0
92
+ send(sock, total, admitted, index)
93
+ show()
94
+ if len(seen) < total:
95
+ if len(seen) != shown_seen:
96
+ show(f"{len(seen)} / {total}"); shown_seen = len(seen)
97
+ time.sleep(.05); continue
98
+ if ready_at == 0.0:
99
+ show(); ready_at = now + interval
100
+ if not cube: emit_at = ready_at
101
+ elif cube and cube.inverted: emit_at = emit_at or ready_at
102
+ active = (1 - mode) if (cube.inverted if cube else invert_mode) else mode
103
+ if cube:
104
+ if active and emit_at is not None and now >= emit_at:
105
+ proposal = cube.propose()
106
+ if proposal is not None: send(sock, total, proposal, index)
107
+ emit_at = None # One proposal per encounter; listen for another.
108
+ elif active and now >= ready_at and now >= emit_at:
109
+ send(sock, total, step[state], index)
110
+ emit_at = now + 1.0
111
+ ready, _, _ = select.select([sys.stdin], [], [], .05)
112
+ if ready:
113
+ key = os.read(sys.stdin.fileno(), 3)
114
+ if key == b" ": break
115
+ if key in (b"\x1b[A", b"\x1b[B"):
116
+ mode = 1 - mode
117
+ if cube:
118
+ active = (1 - mode) if cube.inverted else mode
119
+ emit_at = time.monotonic() + interval if active else None
120
+ else:
121
+ emit_at = time.monotonic() + 1.0
122
+ show()
123
+ except (KeyboardInterrupt, EOFError):
124
+ pass
125
+ finally:
126
+ for s, h in handlers.items(): signal.signal(s, h)
127
+ termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old)
128
+ clear(); sock.close()
129
+
130
+ def main():
131
+ index, total = map(int, sys.argv[1:3])
132
+ run(index, total)
133
+
134
+ if __name__ == "__main__": main()