From 9c7e59dff36e2f3ac1b59fee872a25d1d269cce3 Mon Sep 17 00:00:00 2001 From: Noor <200412177+learningdungeon@users.noreply.github.com> Date: Fri, 27 Feb 2026 01:06:44 +0500 Subject: [PATCH 1/3] main.py of game --- src/oqd_teaching_demo/ion_game_gui.py | 171 ++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 src/oqd_teaching_demo/ion_game_gui.py diff --git a/src/oqd_teaching_demo/ion_game_gui.py b/src/oqd_teaching_demo/ion_game_gui.py new file mode 100644 index 0000000..8a6fa0e --- /dev/null +++ b/src/oqd_teaching_demo/ion_game_gui.py @@ -0,0 +1,171 @@ +import warnings +warnings.filterwarnings("ignore", category=DeprecationWarning) + +import math +import json +from datetime import datetime +from nicegui import ui +import random +from modules.grover_engine import run_ion_engine + +# --- STATE MANAGEMENT --- +game = { + 'stage': 1, + 'ions': 2, + 'target': '00', + 'moves': [], + 'ion_states': [], + 'probs': {}, + 'status': 'System Initialized.', + 'show_next': False, + 'finished': False, + 'history': [] +} + +def update_ion_visuals(): + """Updates the binary display based on X-gate toggles.""" + states = [0] * game['ions'] + for move in game['moves']: + if move.startswith('X'): + idx = int(move[1:]) + if idx < game['ions']: + states[idx] = 1 - states[idx] + game['ion_states'] = states + +def reset_level(): + """Generates a new target and clears the buffer.""" + game['target'] = format(random.randint(0, (2**game['ions']) - 1), f'0{game["ions"]}b') + game['moves'] = [] + game['probs'] = {} + game['show_next'] = False + game['status'] = f"Mission: Isolate state |{game['target']}>" + update_ion_visuals() + +def add_move(move): + """Adds a pulse to the buffer and refreshes the view.""" + game['moves'].append(move) + game['status'] = f"Pulse {move} added to buffer." + update_ion_visuals() + gui_update.refresh() + +def run_sequence(): + """Executes Grover Engine and uses 'Smart Threshold' to evaluate success.""" + win_prob, probs = run_ion_engine(game['ions'], game['target'], game['moves']) + game['probs'] = probs + + # SMART THRESHOLD: Theoretical max for 1 Grover iteration on N states + N = 2**game['ions'] + theta = math.asin(1 / math.sqrt(N)) + theoretical_max = math.sin(3 * theta)**2 + threshold = theoretical_max * 0.9 # Require 90% of theoretical peak + + if win_prob >= threshold: + game['status'] = f"✨ QUANTUM CONVERGENCE! Signal: {win_prob*100:.1f}%" + game['show_next'] = True + game['history'].append({ + 'stage': game['stage'], + 'target': game['target'], + 'sequence': list(game['moves']), + 'prob': f"{win_prob*100:.1f}%" + }) + else: + game['status'] = f"❌ SIGNAL LOST. Peak: {win_prob*100:.1f}% (Need > {threshold*100:.1f}%)" + game['show_next'] = False + gui_update.refresh() + +def next_stage(): + """Increments stage or triggers Mission Complete.""" + if game['stage'] >= 5: # Final Stage is 5 (6 ions) + game['finished'] = True + else: + game['stage'] += 1 + game['ions'] += 1 + reset_level() + gui_update.refresh() + +def download_report(): + """Exports session history as a JSON file.""" + report = { + 'session_date': datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + 'advocate': 'Noor', + 'results': game['history'] + } + ui.download(json.dumps(report, indent=4).encode(), filename="OQD_Quantum_Report.json") + +# --- UI LAYOUT --- + +@ui.refreshable +def gui_update(): + # 1. MISSION COMPLETE SCREEN + if game['finished']: + with ui.column().classes('w-full items-center p-20 bg-black text-white h-screen'): + ui.label('✨ MISSION COMPLETE ✨').classes('text-h2 text-yellow-400 font-bold mb-4') + ui.label('All 5 Stages Clear. Trap Stability Verified.').classes('text-xl text-blue-300 mb-8') + + with ui.card().classes('bg-slate-900 p-8 border border-blue-500 shadow-2xl items-center w-full max-w-lg'): + ui.label('QUANTUM LOG RECORDED').classes('text-xs text-slate-500 mb-2') + ui.label('Noor, OQD Advocate').classes('text-h5 text-white uppercase tracking-widest') + ui.separator().classes('bg-slate-700 my-4') + ui.button('DOWNLOAD FINAL REPORT', on_click=download_report).classes('w-full h-12').props('color=blue-6 icon=download') + ui.button('RESTART SIMULATION', on_click=lambda: (game.update({'stage':1, 'ions':2, 'finished':False}), reset_level(), gui_update.refresh())).props('flat color=slate-400 mt-4') + return + + # 2. MAIN GAME UI + with ui.column().classes('w-full items-center p-8 bg-slate-900 text-white font-mono'): + with ui.row().classes('w-full justify-between items-center max-w-4xl'): + ui.label('OQD ION-MAZE SUITE').classes('text-h3 text-blue-400 font-bold') + ui.button('REPORT', on_click=download_report).props('flat color=slate-400 icon=description') + + # MISSION STATUS + with ui.card().classes('bg-slate-800 p-6 w-full max-w-2xl border border-blue-900 shadow-2xl mt-4'): + with ui.row().classes('justify-between w-full'): + ui.label(f"STAGE {game['stage']}").classes('text-slate-400') + ui.label(f"TARGET: |{game['target']}>").classes('text-xl text-yellow-400 font-bold') + ui.label(game['status']).classes('text-sm text-blue-200 mt-2 italic') + + if game['moves']: + ui.label("BUFFER: " + " -> ".join(game['moves'])).classes('text-xs text-orange-400 mt-4 font-bold border-t border-slate-700 pt-2') + + # ION TRAP VISUALS + + with ui.row().classes('m-6 p-10 border-2 border-blue-900 rounded-xl bg-black w-full justify-center shadow-inner'): + for i in range(game['ions']): + val = game['ion_states'][i] if i < len(game['ion_states']) else 0 + is_one = val == 1 + color = "text-yellow-400" if is_one else "text-slate-600" + glow = "text-shadow: 0 0 20px #fbbf24;" if is_one else "" + ui.label(f"Ion_{i}:|{val}>").classes(f'mx-4 text-xl font-bold {color}').style(glow) + + # CONTROLS + with ui.row().classes('gap-4 mb-8'): + with ui.button_group(): + for i in range(game['ions']): + ui.button(f"X{i}", on_click=lambda i=i: add_move(f"X{i}")).props('color=blue-grey-10') + ui.button("LOCK", on_click=lambda: add_move("LOCK")).props('color=orange-10 icon=lock') + ui.button("RESET", on_click=lambda: (reset_level(), gui_update.refresh())).props('color=red-10 flat icon=refresh') + + if not game['show_next']: + ui.button("RUN EXPERIMENT", on_click=run_sequence).classes('w-72 h-16 text-lg').props('color=green-7 icon=play_arrow') + else: + ui.button("NEXT STAGE", on_click=next_stage).classes('w-72 h-16 text-lg animate-bounce').props('color=blue-6 icon=arrow_forward') + + # READOUT HISTOGRAM + if game['probs']: + ui.label('PHOTON PROBABILITY DENSITY').classes('mt-10 text-xs text-blue-400 tracking-tighter') + with ui.column().classes('w-full max-w-lg bg-black p-6 rounded-lg border border-slate-800 shadow-2xl'): + N_val = 2**game['ions'] + th_val = (math.sin(3 * math.asin(1/math.sqrt(N_val)))**2) * 0.9 + for s, p in sorted(game['probs'].items()): + is_target = s == game['target'] + is_winner = is_target and p >= th_val + bar_color = "green-5" if is_winner else "yellow-7" if is_target else "blue-grey-9" + text_color = "text-green-400" if is_winner else "text-yellow-400" if is_target else "text-slate-500" + with ui.row().classes('items-center w-full mb-2'): + ui.label(f"|{s}>").classes(f'w-16 text-sm {text_color} font-mono') + ui.linear_progress(value=p).classes('flex-grow h-4 rounded').props(f'color={bar_color} stripe animate' if is_target else f'color={bar_color}') + ui.label(f"{p*100:4.1f}%").classes(f'w-16 text-right text-xs {text_color}') + +# --- INIT --- +reset_level() +gui_update() +ui.run(title="OQD Ion-Maze", dark=True, port=8080) \ No newline at end of file From aeee72873023aee18af653dd1df3a02fade5c42d Mon Sep 17 00:00:00 2001 From: Noor <200412177+learningdungeon@users.noreply.github.com> Date: Fri, 27 Feb 2026 01:07:07 +0500 Subject: [PATCH 2/3] quantum engine --- src/oqd_teaching_demo/quantum_engine.py | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/oqd_teaching_demo/quantum_engine.py diff --git a/src/oqd_teaching_demo/quantum_engine.py b/src/oqd_teaching_demo/quantum_engine.py new file mode 100644 index 0000000..7953c11 --- /dev/null +++ b/src/oqd_teaching_demo/quantum_engine.py @@ -0,0 +1,31 @@ +from qiskit import QuantumCircuit +from qiskit.quantum_info import Statevector + +def run_quantum_level(qubits, target_room, player_moves): + qc = QuantumCircuit(qubits) + qc.h(range(qubits)) + qc.barrier() + + for gate in player_moves: + if gate == 'X0': qc.x(0) + if gate == 'X1': qc.x(1) + if gate == 'X2': qc.x(2) + if gate == 'CZ' and qubits == 2: qc.cz(0, 1) + if gate == 'CCZ' and qubits == 3: qc.ccz(0, 1, 2) + qc.barrier() + + qc.h(range(qubits)) + qc.x(range(qubits)) + if qubits == 2: + qc.cz(0, 1) + else: + qc.h(qubits-1) + qc.mcx(list(range(qubits-1)), qubits-1) + qc.h(qubits-1) + qc.x(range(qubits)) + qc.h(range(qubits)) + + state = Statevector.from_instruction(qc) + probs = state.probabilities_dict() + win_prob = probs.get(target_room[::-1], 0) + return win_prob, probs From 0b771c04c54feaaebc736856f787ed332ca9d65d Mon Sep 17 00:00:00 2001 From: Noor <200412177+learningdungeon@users.noreply.github.com> Date: Fri, 27 Feb 2026 01:07:35 +0500 Subject: [PATCH 3/3] grover engine --- src/oqd_teaching_demo/grover_engine (1).py | 72 ++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/oqd_teaching_demo/grover_engine (1).py diff --git a/src/oqd_teaching_demo/grover_engine (1).py b/src/oqd_teaching_demo/grover_engine (1).py new file mode 100644 index 0000000..6d53602 --- /dev/null +++ b/src/oqd_teaching_demo/grover_engine (1).py @@ -0,0 +1,72 @@ +import numpy as np +from qiskit import QuantumCircuit, transpile +from qiskit_aer import AerSimulator + +def run_ion_engine(num_qubits, target_str, moves): + """ + Core Quantum Engine for the OQD Ion-Maze. + Maps UI 'Pulses' to Qiskit gates and executes a Grover iteration. + """ + # Initialize Simulator + simulator = AerSimulator() + qc = QuantumCircuit(num_qubits) + + # 1. STEP: INITIAL SUPERPOSITION (Preparation) + # Apply Hadamard to all ions to create an equal superposition of all states. + for i in range(num_qubits): + qc.h(i) + + # 2. STEP: USER-DEFINED ORACLE (Phase Marking) + # This is the "Maze" part. The user must rotate the target to |1...1> + # so the LOCK (Multi-Controlled Phase) gate can flip its sign. + for move in moves: + if move.startswith('X'): + idx = int(move[1:]) + if idx < num_qubits: + qc.x(idx) + elif move == 'LOCK': + # MARKER: Flips the phase of the |11...1> state. + if num_qubits > 1: + # Multi-Controlled Phase gate (MCP) with pi rotation + qc.mcp(np.pi, list(range(num_qubits - 1)), num_qubits - 1) + else: + qc.z(0) + + # 3. STEP: GROVER DIFFUSER (Amplitude Amplification) + # Standard reflection about the mean. + # + for i in range(num_qubits): + qc.h(i) + qc.x(i) + + # Reflection about the |0...0> state + if num_qubits > 1: + qc.mcp(np.pi, list(range(num_qubits - 1)), num_qubits - 1) + else: + qc.z(0) + + for i in range(num_qubits): + qc.x(i) + qc.h(i) + + # 4. MEASUREMENT + qc.measure_all() + + # 5. EXECUTION & ENDIANNESS MAPPING + # Transpile for performance + t_qc = transpile(qc, simulator) + job = simulator.run(t_qc, shots=2048) + counts = job.result().get_counts() + + # Convert results to Big-Endian for the OQD UI + # (Qiskit's '001' is actually our Ion_0=1, Ion_1=0, Ion_2=0) + total_shots = sum(counts.values()) + probs = {} + for i in range(2**num_qubits): + # Generate the state string in the order the UI expects (Big-Endian) + ui_state = format(i, f'0{num_qubits}b') + # Reverse it to fetch the data from Qiskit's Little-Endian output + qiskit_key = ui_state[::-1] + probs[ui_state] = counts.get(qiskit_key, 0) / total_shots + + return probs.get(target_str, 0.0), probs \ No newline at end of file