-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathquantum_rules.py
More file actions
323 lines (260 loc) · 12.1 KB
/
Copy pathquantum_rules.py
File metadata and controls
323 lines (260 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# Superposition move, entangle move stub, measure/collapse logic
"""
quantum_rules.py
Quantum move rules that extend the classical board.
Three quantum moves:
superposition_move(board, engine, piece, sq_a, sq_b)
Split a piece across two squares (H gate).
collapse_piece(board, engine, piece)
Force-measure a superposed piece, collapsing it to one square.
capture_superposed(board, engine, attacker, target_piece, capture_sq)
Attempt to capture a superposed piece; triggers a quantum
measurement — the capture succeeds only if the piece collapses
to the contested square.
All functions mutate board state through board.py's public API and
return a short human-readable string for the game_manager event log.
"""
from __future__ import annotations
from board import Board
# Use unified QuantumBackend from Entanglement.py (replaces Quantum_engin.py)
from Entanglement import QuantumBackend
# ---------------------------------------------------------------------------
# Superposition move
# ---------------------------------------------------------------------------
def superposition_move(board: Board, engine: QuantumBackend,
piece: dict, sq_a: str, sq_b: str) -> str:
"""
Split *piece* into superposition across sq_a and sq_b.
Applies an H gate to the piece's qubit, then sets its positions list
to [sq_a, sq_b] so the board and renderer treat it as a ghost piece
on both squares.
Rules:
- The piece must currently be at sq_a (its classical position).
- sq_b must be a valid destination for that piece type (caller
is responsible for validating this before calling).
- The piece cannot already be superposed or entangled.
Returns a log string.
"""
if piece["superposed"]:
return f"{piece['type'].capitalize()} is already in superposition."
if piece["entangled_with"] or piece.get("entanglement_group") is not None:
return f"{piece['type'].capitalize()} is entangled — cannot split."
engine.apply_hadamard(piece["qubit_id"])
piece["positions"] = [sq_a, sq_b]
piece["superposed"] = True
board._rebuild_map()
symbol = piece["type"].capitalize()
color = piece["color"].capitalize()
return f"{color} {symbol} split -> {sq_a} <-> {sq_b}"
# ---------------------------------------------------------------------------
# Entangle move (stub — waiting for Entanglement.py)
# ---------------------------------------------------------------------------
def entangle_move(board: Board, engine: QuantumBackend,
piece_a: dict, piece_b: dict) -> str:
"""
Link piece_a and piece_b into a Bell state via QuantumBackend.
Creates quantum entanglement between two pieces — when either is
measured, both collapse to the same outcome (correlated measurement).
Rules:
- Both pieces must be classical (not superposed, not entangled)
- Both pieces must be on the board
- After entangling, both pieces share entangled_with list
Returns a log string.
"""
"""
MINIMAL FIX FOR quantum_rules.py
Replace entangle_move() function with this version that:
1. Still does quantum entanglement
2. ALSO creates a movement group so pieces share moves
"""
# Validate both pieces are classical
if piece_a["superposed"] or piece_b["superposed"]:
return "Cannot entangle: one or both pieces are in superposition."
if piece_a["entangled_with"] or piece_b["entangled_with"]:
return "Cannot entangle: one or both pieces are already entangled."
if piece_a["color"] != piece_b["color"]:
return "Cannot entangle: must be your own pieces."
if piece_a is piece_b:
return "Cannot entangle a piece with itself."
if piece_a["type"] == "king" or piece_b["type"] == "king":
return "Kings cannot be entangled."
# Create QUANTUM entanglement
engine.entangle(piece_a["qubit_id"], piece_b["qubit_id"])
piece_a["entangled_with"].append(piece_b["qubit_id"])
piece_b["entangled_with"].append(piece_a["qubit_id"])
# CREATE MOVEMENT GROUP
from entanglement_rules import EntanglementGroup
board.create_entanglement_group([piece_a, piece_b])
sym_a = piece_a["type"].capitalize()
sym_b = piece_b["type"].capitalize()
pos_a = piece_a["positions"][0]
pos_b = piece_b["positions"][0]
return f"Entangled {sym_a}({pos_a}) <-> {sym_b}({pos_b}) [Bell state + movement group]"
def break_entanglement_on_capture(board: Board, captured_piece: dict):
"""
Called when an entangled piece is captured.
Breaks both quantum entanglement AND movement group.
"""
# Handle quantum entanglement (old system)
if captured_piece.get("entangled_with"):
for partner_qubit_id in captured_piece["entangled_with"]:
for p in board.pieces:
if p["qubit_id"] == partner_qubit_id:
if captured_piece["qubit_id"] in p["entangled_with"]:
p["entangled_with"].remove(captured_piece["qubit_id"])
captured_piece["entangled_with"] = []
# Handle movement group (new system)
group_id = captured_piece.get("entanglement_group")
if group_id is not None:
from entanglement_rules import break_entanglement
msg = break_entanglement(captured_piece, board)
return msg if msg else ""
return ""
# ---------------------------------------------------------------------------
# Collapse / force-measure
# ---------------------------------------------------------------------------
def collapse_piece(board: Board, engine: QuantumBackend, piece: dict) -> str:
"""
Force-measure a superposed piece, collapsing it to one square.
Runs the H+measure circuit (via QuantumEngine.measure()), then
discards the square the piece did NOT collapse to.
If the piece is not superposed, this is a no-op.
Returns a log string.
"""
if not piece["superposed"]:
return f"{piece['type'].capitalize()} is not in superposition."
sq_a, sq_b = piece["positions"][0], piece["positions"][1]
result = engine.measure_superposition(piece["qubit_id"]) # 0 → sq_a, 1 → sq_b
collapsed_to = sq_a if result == 0 else sq_b
piece["positions"] = [collapsed_to]
piece["superposed"] = False
board._rebuild_map()
symbol = piece["type"].capitalize()
color = piece["color"].capitalize()
return f"{color} {symbol} collapsed -> {collapsed_to}"
# ---------------------------------------------------------------------------
# Capture of a superposed piece
# ---------------------------------------------------------------------------
def capture_superposed(board: Board, engine: QuantumBackend,
attacker: dict, target_piece: dict,
capture_sq: str) -> str:
"""
Attempt to capture a superposed piece.
Triggers a quantum measurement on target_piece:
- If target_piece collapses TO capture_sq → capture succeeds,
target_piece is removed from the board.
- If target_piece collapses to the OTHER square → capture fails,
target_piece survives at the other square as a classical piece.
Returns a log string describing the outcome.
"""
if not target_piece["superposed"]:
# Classical capture — handled by board.move_piece normally.
return ""
sq_a, sq_b = target_piece["positions"][0], target_piece["positions"][1]
result = engine.measure_superposition(target_piece["qubit_id"])
collapsed_to = sq_a if result == 0 else sq_b
target_piece["positions"] = [collapsed_to]
target_piece["superposed"] = False
atk_sym = attacker["type"].capitalize()
tgt_sym = target_piece["type"].capitalize()
tgt_col = target_piece["color"].capitalize()
if collapsed_to == capture_sq:
board.remove_piece(target_piece)
board.move_piece(attacker, capture_sq)
return (f"{atk_sym} captures {tgt_col} {tgt_sym} -- "
f"collapsed to {capture_sq} [success]")
else:
# Ghost survived at the other square — rebuild the map first so capture_sq
# is seen as empty (ghost is now only at collapsed_to), then move the attacker in
board._rebuild_map()
board.move_piece(attacker, capture_sq)
return (f"{tgt_col} {tgt_sym} collapsed to {collapsed_to} — "
f"{atk_sym} moves to {capture_sq} [ghost survived]")
# =========================================================================
# Standalone test — run with: python quantum_rules.py
# =========================================================================
if __name__ == "__main__":
print("=" * 60)
print(" quantum_rules.py — Quantum Rules Tests")
print("=" * 60)
# --- Test 1: superposition_move ---
b = Board()
e = QuantumBackend() # Use unified QuantumBackend
knight = b.piece_at("b1")
log = superposition_move(b, e, knight, "b1", "c3")
assert knight["superposed"] is True
assert knight["positions"] == ["b1", "c3"]
assert e.is_superposed(knight["qubit_id"])
assert b.piece_at("b1") is knight
assert b.piece_at("c3") is knight
print(f"\n[PASS] Superposition move: {log}")
# --- Test 2: cannot superpose an already-superposed piece ---
log2 = superposition_move(b, e, knight, "b1", "a3")
assert "already" in log2
print(f"[PASS] Double superposition blocked: {log2}")
# --- Test 3: collapse_piece ---
collapse_results = set()
for _ in range(40):
b2 = Board()
e2 = QuantumBackend()
p2 = b2.piece_at("g1") # white knight on g1
superposition_move(b2, e2, p2, "g1", "f3")
log3 = collapse_piece(b2, e2, p2)
assert not p2["superposed"]
assert len(p2["positions"]) == 1
assert p2["positions"][0] in ("g1", "f3")
collapse_results.add(p2["positions"][0])
assert collapse_results == {"g1", "f3"}, "Should collapse to both squares across runs"
print(f"[PASS] collapse_piece: observed both collapse outcomes")
# --- Test 4: collapse on non-superposed piece is a no-op ---
b4 = Board()
e4 = QuantumBackend()
pawn = b4.piece_at("e2")
log4 = collapse_piece(b4, e4, pawn)
assert "not in superposition" in log4
print(f"[PASS] Collapse no-op on classical piece: {log4}")
# --- Test 5: capture_superposed — success case ---
captures = 0
fails = 0
for _ in range(60):
b5 = Board()
e5 = QuantumBackend()
# Put white knight into superposition between b1 and c3
wknight = b5.piece_at("b1")
superposition_move(b5, e5, wknight, "b1", "c3")
# Black attacker on d5 tries to capture on c3
attacker = b5._make_piece("bishop", "black", "d5")
b5.add_piece(attacker)
log5 = capture_superposed(b5, e5, attacker, wknight, "c3")
if "[success]" in log5:
captures += 1
assert b5.piece_at("c3") is attacker
else:
fails += 1
assert wknight in b5.pieces
assert captures > 0 and fails > 0, "Should see both capture outcomes"
print(f"[PASS] capture_superposed: {captures} successes, {fails} failures over 60 runs")
# --- Test 6: entangle_move (real implementation) ---
b6 = Board()
e6 = QuantumBackend()
pa = b6.piece_at("a1") # white rook
pb = b6.piece_at("h1") # white rook
log6 = entangle_move(b6, e6, pa, pb)
assert "Entangled" in log6
assert pa["entangled_with"] == [pb["qubit_id"]]
assert pb["entangled_with"] == [pa["qubit_id"]]
assert e6.is_entangled(pa["qubit_id"])
print(f"[PASS] entangle_move: {log6}")
# --- Test 7: entangled pieces collapse together ---
b7 = Board()
e7 = QuantumBackend()
p7a = b7.piece_at("a2")
p7b = b7.piece_at("b2")
entangle_move(b7, e7, p7a, p7b)
# Measure entangled pair - both should collapse to same outcome
outcome_a, outcomes = e7.measure_entangled(p7a["qubit_id"])
assert outcomes[p7a["qubit_id"]] == outcomes[p7b["qubit_id"]], "Entangled pieces should have same outcome"
print(f"[PASS] Entangled measurement: both collapsed to {outcome_a}")
print("\n" + "=" * 60)
print(" All quantum_rules.py tests passed!")
print("=" * 60)