Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quantum-kittens
GitHub Repository: quantum-kittens/platypus
Path: blob/main/notebooks/ch-demos/first-quantum-game.ipynb
3855 views
Kernel: Python 3

Your First Quantum Game

If you've already read some of this textbook, you should know at least a few quantum gates. If you are wondering what to do with that knowledge, perhaps making a game can be the answer. Making simple games can be a fun way to try out new programming knowledge.

If we are going to make a game, we'll need a game engine. Here we'll introduce a simple game engine that can run here in the Jupyter notebooks of this textbook. With this we'll make make a very simple game based on single qubit gates.

First, we import the game engine.

from qiskit_textbook.games.qiskit_game_engine import QiskitGameEngine

A few simple examples

A minimal example of using this game engine is to simply set all the pixels to a certain colour.

# function called when setting up def start(engine): # just move on to the first frame next_frame(engine) # this is the function that does everything def next_frame (engine): # set all pixels to green for x in range(engine.L): for y in range(engine.L): engine.screen.pixel[x,y].set_color('green') # run the game for an 8x8 screen engine = QiskitGameEngine(start,next_frame,L=8)

Now let's set one specific pixel to a different colour.

# function called when setting up def start(engine): # set a parameter to keep track of the player pixel engine.X = 1 engine.Y = 2 # then move on to the first frame next_frame(engine) # this is the function that does everything def next_frame (engine): # set all pixels to green for x in range(engine.L): for y in range(engine.L): engine.screen.pixel[x,y].set_color('green') # draw the player pixel engine.screen.pixel[engine.X,engine.Y].set_color('red') # run the game engine = QiskitGameEngine(start,next_frame,L=8)

We'll move this around using the arrow buttons.

# this is the function that does everything def next_frame (engine): # change player position if engine.controller['up'].value: engine.Y -= 1 if engine.controller['down'].value: engine.Y += 1 if engine.controller['right'].value: engine.X += 1 if engine.controller['left'].value: engine.X -= 1 # set all pixels to green for x in range(engine.L): for y in range(engine.L): engine.screen.pixel[x,y].set_color('green') # draw the player pixel engine.screen.pixel[engine.X,engine.Y].set_color('red') # run the game engine = QiskitGameEngine(start,next_frame,L=8)

Walking off the edge of the screen results in an error. We can fix this.

# this is the function that does everything def next_frame (engine): # change player position if engine.controller['up'].value: engine.Y -= 1 if engine.controller['down'].value: engine.Y += 1 if engine.controller['right'].value: engine.X += 1 if engine.controller['left'].value: engine.X -= 1 # set all pixels to green for x in range(engine.L): for y in range(engine.L): engine.screen.pixel[x,y].set_color('green') # draw the player pixel Xs = engine.X%engine.L Ys = engine.Y%engine.L engine.screen.pixel[Xs,Ys].set_color('red') # run the game engine = QiskitGameEngine(start,next_frame,L=8)

Here the engine.X and engine.Y coordinates are still allowed to go beyond the screen, but the pixel is displayed such that it wraps back round. We can interpret this as the pixel moving on to another screen.

Now let's move towards giving our pixel a more exciting world to explore. We'll use a function to decide what colour each point should be. We'll start by not changing very much.

import numpy as np def get_color(X,Y): return 'green' # this is the function that does everything def next_frame (engine): # change player position if engine.controller['up'].value: engine.Y -= 1 if engine.controller['down'].value: engine.Y += 1 if engine.controller['right'].value: engine.X += 1 if engine.controller['left'].value: engine.X -= 1 # set all pixels to green for x in range(engine.L): for y in range(engine.L): # get the 'world' coordinates X,Y from the onscreen coordinates x,y X = np.floor(engine.X/engine.L)*engine.L+x Y = np.floor(engine.Y/engine.L)*engine.L+y # set it to whatever colour it should be engine.screen.pixel[x,y].set_color(get_color(X,Y)) # draw the player pixel Xs = engine.X%engine.L Ys = engine.Y%engine.L engine.screen.pixel[Xs,Ys].set_color('red') # run the game engine = QiskitGameEngine(start,next_frame,L=8)

Now let's change get_color to create a beach.

def get_color(X,Y): if X<12: color = 'green' else: color = 'orange' return color # run the game engine = QiskitGameEngine(start,next_frame,L=8)

We'll now put a single qubit circuit inside this terrain generator, so that we can experiment with single qubit gates by making terrain. For that we'll need some Qiskit tools.

from qiskit import QuantumCircuit from qiskit.quantum_info import Statevector

First, let's see what the results from a circuit look like. Here's an example with just a ry gate for a given angle of rotation.

theta = np.pi/2 qc = QuantumCircuit(1) qc.ry(theta,0) state = Statevector.from_instruction(qc) probs = state.probabilities_dict() print(probs)

Here the angle π/2\pi/2 means that the result of a measurement at the end of this circuit would be equally likely to give 0 or 1. For other angles we can get a bias towards one or the other. This is exactly the kind of behaviour we would expect from these gates, as explained in Chapter 1.

Let's use the probability of the output '1' as a height, and set the colour accordingly.

def get_color(X,Y): # set an angle for ry based on the coordinate theta = X*(np.pi/16) # create and simulate the circuit qc = QuantumCircuit(1) qc.ry(theta,0) state = Statevector.from_instruction(qc) probs = state.probabilities_dict() # get the prob of '1' if '1' in probs: height = probs['1'] else: height = 0 # set colour accordingly if height<0.1: # sea/river color = 'blue' elif height<0.3: # beach color = 'orange' elif height<0.9: # grass color = 'green' else: # mountain color = 'grey' return color # run the game engine = QiskitGameEngine(start,next_frame,L=8)

By adding more complex rotations, and thinking about how these gates combine, we can build on this to create more complex terrain.

import random seed = [random.random() for _ in range(4)] def get_color(X,Y): qc = QuantumCircuit(1) theta1 = (seed[0]*X+seed[1]*Y)*np.pi/16 theta2 = (seed[2]*X-seed[3]*Y)*np.pi/16 qc.ry(theta1,0) qc.rx(theta2,0) state = Statevector.from_instruction(qc) probs = state.probabilities_dict() try: height = probs['1'] except: height = 0 # set colour accordingly if height<0.1: # sea/river color = 'blue' elif height<0.3: # beach color = 'orange' elif height<0.9: # grass color = 'green' else: # mountain color = 'grey' return color # run the game engine = QiskitGameEngine(start,next_frame,L=8)

Now experiment by making your own circuits to generate more interesting terrain, and use it to explore what simple quantum gates can do. Or perhaps make a completely new game of your own!

How to use the game engine

To make full use of the game engine, you'll need all the details of how it works. It gives us the ability to make games for a low-pixel screen (8x8 by default), controlled by a D-pad and five buttons. Note that the screen will only update when a button is pressed.

The game engine is all based around the QiskitGameEngine object. You can call this whatever you like. In the following we will call it engine.

The screen

The pixels can be addressed in the functions using engine.screen. The pixel at position (x,y) is addressed as engine.screen.pixel[x,y]. It has three methods:

  • set_color(color) - The argument color is a string: either 'grey', 'green', 'blue', 'orange' or 'red'.

  • set_brightness(bright) - The argument bright is a Boolean value: False for dim and True for bright.

  • set_text(text) - The argument is text to be displayed.

Note that most pixels won't contain more than a few characters. A larger piece of text can be written on the long pixel at the bottom of the screen, which is accessed with engine.screen.pixel['text'].

The controller

The controller is accessed using engine.controller. Its buttons are addressed using the keys 'down', 'up', 'left', 'right', 'A', 'B', 'X', 'Y' and 'next'. Each is a Jupyter widget button object. Pressing any of these buttons will cause the next_frame function to run. Use the value attribute of each button to determine whether the button has been pressed (True) or not (False).

The game loop

Games are made by defining two functions, start and next_frame. The start function runs when the game begins, and next_frame runs every time a button is pressed to move the game along. Both should have a single argument: the class engine. All parameters required for the game should be defined as attributes to the engine class.

Putting it all together

The game is started by initiating the QiskitGameEngine object with the start and next_frame functions. You can also choose a size other than the default 8x8 grid using the keyword argument L.

QiskitGameEngine(start,next_frame,L=8)

Note that the grid size can be accessed in the start and next_frame functions as engine.L. You can also choose a size other than the default 8x8 grid using the kwarg L.