Files
  • main.py
  • Engine.py
  • Levels.py
  • License
main.py
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
#    Created by James Wootton
#    Copyright © 2018 University of Basel. All rights reserved.

#import replit
import math
import subprocess
import copy

from Engine import *
from Levels import *

    
# clear screen and put title
ClearScreen ()
print("")
print("")
print("              ██╗  ██╗███████╗██╗     ██╗      ██████╗       ")              
print("              ██║  ██║██╔════╝██║     ██║     ██╔═══██╗    ")                
print("              ███████║█████╗  ██║     ██║     ██║   ██║     ")               
print("              ██╔══██║██╔══╝  ██║     ██║     ██║   ██║     ")               
print("              ██║  ██║███████╗███████╗███████╗╚██████╔╝      ")              
print("              ╚═╝  ╚═╝╚══════╝╚══════╝╚══════╝ ╚═════╝           ")          
print("                                                                   ") 
print("    ██████╗ ██╗   ██╗ █████╗ ███╗   ██╗████████╗██╗   ██╗███╗   ███╗")
print("   ██╔═══██╗██║   ██║██╔══██╗████╗  ██║╚══██╔══╝██║   ██║████╗ ████║")
print("   ██║   ██║██║   ██║███████║██╔██╗ ██║   ██║   ██║   ██║██╔████╔██║")
print("   ██║▄▄ ██║██║   ██║██╔══██║██║╚██╗██║   ██║   ██║   ██║██║╚██╔╝██║")
print("   ╚██████╔╝╚██████╔╝██║  ██║██║ ╚████║   ██║   ╚██████╔╝██║ ╚═╝ ██║")
print("    ╚══▀▀═╝  ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═══╝   ╚═╝    ╚═════╝ ╚═╝     ╚═╝")
print("\n")
print("\n")
print("             A GAMIFIED TUTORIAL FOR QUANTUM PROGRAMMING")
print("                               ")
print("                     For more information visit:")
print("           github.com/decodoku/Quantum_Programming_Tutorial")
print("\n")
print("\n")
print("\n")
print("\n")
print("\n")
print("\n")
input("> Press Enter to continue...\n")
print("  Choose which mode you want to play from the following list")
print("")
print("  1 - Main Tutorial\n      A gamified tutorial for programming quantum computers.\n")
print("  2 - Qubit Swapper\n      A few puzzles dedicated to the task of swapping qubits\n")
print("  3 - Sandbox\n      A chance to do what you want with two qubits.\n")
print("  4 - Image Superposer\n      Write a quantum program to create superpositions of images.\n")
choosing = True
while choosing:
    mode = input("> Input a number to select a mode...\n")
    if mode in ["1","2","3","4"]:
        choosing = False
    else:
        input("> That's not a valid mode. Press Enter to try again...\n")

if mode in ["1","2"]:
    if mode=="1":
            state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro = GetLevelStory()
    elif mode=="2":
            state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro = GetLevelSwaps()
    choosing = True
    while choosing:
        level = input("\n> Select a level (from 1 to "+str(level_num)+")...\n")
        if level.isdigit():
            level = int(level)
            if level in range(1,level_num+1):
                level = int(level)-1
                choosing = False
            else:
                input("> That level does not exist. Press Enter to try again...\n")
        else:
            level = 0
            choosing = False
            input("> That was not a valid level, so we'll start from the beginning...\n")
elif mode=="3":
    state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro = GetLevelSandbox()
    level = 0
elif mode=="4":

    allowed_filenames = ["0000","0001","0010","0011", "0100","0101","0110","0111", "1000","1001","1010","1011", "1100","1101","1110","1111"]

    input("\n> This mode relates to a Jupyter notebook, which you'll find at github.com/decodoku/Quantum_Programming_Tutorial/tree/master/image-superposer...\n")
    input("\n> There you'll find 16 images, all with a bit string as their filename...\n")
    choosing = True
    while choosing :
        string1 = input("\n> Choose one of these images by typing the filename below...\n")
        if string1 in allowed_filenames:
            choosing = False
        else:
            input("> That was not a valid filename. Press Enter to try again...\n")
    choosing = True
    while choosing :
        string2 = input("\n> Choose another image typing the filename below...\n")
        if string2 in allowed_filenames and string2!=string1:
            choosing = False
        else:
            input("> That was not a valid filename. Press Enter to try again...\n")
    input("\n> You'll now write a QISKit program to superpose these two images...\n")
    input("\n> This can then be run on a real machine using the Jupyter notebook...\n")

    state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro = GetLevelSuperposer(string1,string2)
    level = 0

else:
    print("> Oops! You shouldn't have been allowed to pick this mode. Restart the program and try again...")


while (level<level_num) :
        
    # specifications for this level
    state = copy.deepcopy(state_list[level])
    allowed_gates = copy.deepcopy(allowed_gates_list[level])
    required_gates = copy.deepcopy(allowed_gates)
    qubits_used = copy.deepcopy(qubits_used_list[level])
    success_condition = copy.deepcopy(success_condition_list[level])

    bloch = False # always start a level with Bloch off
    active_qubit = -1 # and no active qubit
    program = [] # and an empty program

    # every puzzle is thought of as one involving two qubits, even if only one of those qubits is involved
    # qubits_used should therefore always be a list of two qubits, the first is the one displayed on the top, and the second is the one on the bottom

    if allowed_gates[qubits_used[0]]=={} : # if no gates are allowed for the qubit on top, we know to only show the qubit on the side
            shown_qubit = 1
    elif allowed_gates[qubits_used[1]]=={} : # and vice versa
            shown_qubit = 0
    else :
            shown_qubit = 2

    success = False
    restart = False
        
    ClearScreen ()
    print("\nLevel "+str(level+1)+"\n")
    for line in intro[level] :
        input("> " + line + "...")

    while success==False and restart==False :

        # ask player to choose an operation
        combined_gates = []
        for qubit in [qubits_used[0],qubits_used[1],"both"]:
            for gate in allowed_gates[qubit].keys() :
                if allowed_gates[qubit][gate]==0 or required_gates[qubit][gate]>0:
                    if gate not in combined_gates:
                        combined_gates.append(gate)
        
        gate = ""
        while gate=="":
 
            message = "> Choose one of the allowed operations from the list above by typing its name below\n> Or type restart to start this puzzle over..."
            given_input = PrintScreen ( message, level, intro, program, state, shown_qubit, active_qubit, bloch, allowed_gates, required_gates )

            if given_input in combined_gates or given_input=='restart' :
                gate = given_input

        # ask player for a qubit to act on (if there is a choice)...
        qubit = ""
        if gate in ["cz","unbloch","restart"]: # for gates that act symmetrically the choice is irrelevant, and so set by default to both
            qubit = "both"
        elif shown_qubit==0 : # if only the top qubit is shown, that's what the gate acts on
            qubit = qubits_used[0]
        elif shown_qubit==1 : # if only the side qubit is shown, that's what the gate acts on
            qubit = qubits_used[1]
        elif gate not in allowed_gates[qubits_used[0]].keys(): # if the gate isn't an allowed one for the top qubit, it acts on the side
            qubit = qubits_used[1]
        elif gate not in allowed_gates[qubits_used[1]].keys(): # and vice-versa
            qubit = qubits_used[0]
        else :
            while qubit not in [qubits_used[0],qubits_used[1]]:
                message = "> Choose a qubit to act on:\n> Type in "+qubits_used[0]+" or "+qubits_used[1]+"..."
                qubit = PrintScreen ( message, level, intro, program, state, shown_qubit, active_qubit, bloch, allowed_gates, required_gates )


        # if it is a gate, apply it
        if gate in ["x","z","h","q","qdg","cz","cx"] :
            # the apply gate function adresses qubits as 0 and 1, so we convert back to this before applying
            if qubit==qubits_used[0] :
                qubit_pos = "0"
            elif qubit==qubits_used[1] :
                qubit_pos = "1"
            else :
                qubit_pos = "both"
            # now apply
            state = ApplyGate( state, gate, qubit_pos )
            # then we write the qiskit commands
            if gate=="cz" :
                program.append("program.cz( qubit["+qubits_used[0]+"], qubit["+qubits_used[1]+"] )")
            elif gate=="cx" :
                if qubit==qubits_used[0] :
                    program.append("program.cx( qubit["+qubits_used[1]+"], qubit["+qubits_used[0]+"] )")
                else :
                    program.append("program.cx( qubit["+qubits_used[0]+"], qubit["+qubits_used[1]+"] )")
            else :
                program.append("program."+gate+"( qubit["+qubit+"] )")

        # if it is a visualization command, apply it
        elif gate=="bloch" :
            bloch = True
            if qubit==qubits_used[0] :
                active_qubit = 0
            elif qubit==qubits_used[1] :
                active_qubit = 1
            elif gate=="unbloch" :
                bloch = false
                active_qubit = -1
        elif gate=="restart" :
            restart = True
        else :
            print("Error: Something's not quite right")


        # log the number of these gates if it is important
        if gate!="restart" :
            if allowed_gates[qubit][gate] > 0 :
                required_gates[qubit][gate] -= 1
                if required_gates[qubit][gate] < 0 :
                    input("> You have used this gate too many times, so the level will restart...\n")
                    restart = True

                
        # see if success conditions are met
        success = True
        # the final state has to be right
        for box, expect in success_condition.items() :
            success =    success and state[box]==expect
        # and the number of ops has to be right
        for qubit in required_gates.keys() :
            for gate in required_gates[qubit].keys() :
                if (required_gates[qubit][gate] > 0) :
                    success = False

        
    if restart==False :
        
        message = "\n> Target achieved.\n\n\n> Press Enter to continue..."
        given_input = PrintScreen ( message, level, intro, program, state, shown_qubit, active_qubit, bloch, {}, required_gates )
                
        # print the outro
        for line in outro[level] :
            input("> " + line + "...")
                
        # interate the level
        level += 1


if mode in ["1","2"] :
    input("> That's all the levels we have for now. Restart the program, or continue your QISKit journey at QISKit.org\n")
elif mode=="3" :
    input("> How are you seeing this?!?!?!?!?!?!?!\n")
elif mode=="4" :
    input("> Now you have your QISKit program. You just need to run the notebook for your image.\n")




    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
Python 3.6.1 (default, Dec 2015, 13:05:11) [GCC 4.8.2] on linux