Event-Driven Programming
cpsc 231 - fall 2018
This supplementary handout describes a program execution model
that you might find useful for Assignment #5. Though our official
course text does not formally cover this topic, the stddraw module
does provide us with the means necessary to use this model.
Up to now, the model of execution for the computer programs
we’ve written have been entirely sequential. Program statements are
executed one at a time, in a certain order, obeying your conditional
and iterative statements, until the program terminates. The only
interactions our programs have had with a human have been through
the console via the input() or stdio.read*() functions. While it has
served us well, the key limitation of this model is that we cannot do
anything while waiting for input from the human!
For example, if we wanted to write a program that draws something until the user indicates otherwise, it might look like the listing
below. Although you can interact with the program through the
console, your operating system may start to get unhappy that your
program is not being responsive. To some extent, it’s right, because
no modern application with a graphical interface works this way.1
With Assignment #4, we were kind of
at a “teething” stage in transitioning
to a visual interface for our programs.
It made the most sense to tolerate this
awkwardness for just a little while.
1
sequential-circles.py
import stddraw
radius = 0.5
keep_drawing = True
while keep_drawing:
stddraw.circle(0.5, 0.5, radius)
stddraw.show(0.0)
radius *= 0.8
response = input(’Draw another circle? ’)
keep_drawing = (response == ’y’)
What if we wanted to run a live animation that responds to a
mouse click? Or perhaps alter its behaviour if we press a certain key?
The event-driven programming paradigm provides a means to achieve
this. The main idea is to run an indefinite loop, called an event loop,
that forms the core of your program. On each iteration, check if any
user input events have occurred, respond accordingly, then take care
of other things for a finite amount of time.
Our textbook’s stddraw module provides a simple means for
us to respond to mouse clicks in an event-driven manner. Calling
stddraw.mousePressed() will return True to us if the mouse had
been clicked since the last time we asked. Then we can obtain the
position of the last mouse click, in our stddraw window coordinates,
with stddraw.mouseX() and stddraw.mouseY(). Using these functions, we can turn the bouncing ball example (Program 1.5.6) into a
crude “game” of trying to click on the ball, as shown.
Output of running
sequential-circles.py:
Draw
Draw
Draw
Draw
another
another
another
another
circle?
circle?
circle?
circle?
y
y
y
n
The event-driven algorithm would look
something like this:
• While we want to keep running our
program, repeat:
1. Check for input or other events.
2. Respond to events that occurred.
3. Do some other things for a small,
finite amount of time.
Many windowing system apis will
run the event loop for you and check
for events automatically, invoking
specified functions in your code, called
event handlers, whenever events you’re
interested in occur.
event-driven programming
2
event-driven-ball.py
import stddraw
radius = 0.1
rx = 0.5
# ball x-position
ry = 0.5
# ball y-position
vx = 1.5
# ball x-velocity
vy = 2.3
# ball y-velocity
dt = 0.01
# time step for animation
clicked_on_ball = False
while not clicked_on_ball:
# check for events and respond accordingly
if stddraw.mousePressed():
mx = stddraw.mouseX()
my = stddraw.mouseY()
d_squared = (mx-rx)*(mx-rx) + (my-ry)*(my-ry)
clicked_on_ball = (d_squared < radius * radius)
# animate the ball and bounce off walls
rx += dt * vx
ry += dt * vy
if rx < 0.0 or rx > 1.0:
vx *= -1.0
if ry < 0.0 or ry > 1.0:
vy *= -1.0
stddraw.clear()
stddraw.filledCircle(rx, ry, radius)
stddraw.show(1000.0 * dt)
Similarly, stddraw provides a means to respond to key presses in
an event-driven manner, without going through the console. Calling
stddraw.hasNextKeyTyped() gives us True if a key was pressed since
we last checked,2 and stddraw.nextKeyTyped() gives us the key
as a one-character str object. Try the frustrating little reflex-tester
demonstration program listed below!
event-driven-keys.py
import random
import stddraw
stddraw.setFontSize(300)
matched = False
while not matched:
number = str(random.randrange(1, 10))
stddraw.clear()
stddraw.text(0.5, 0.5, number)
stddraw.show(500.0)
while stddraw.hasNextKeyTyped():
key = stddraw.nextKeyTyped()
matched = matched or (key == number)
Note that key presses are queued up
in a buffer so that we don’t lose keys if
the user is typing faster than the rate at
which you’re running your event loop.
The best way to process key events is to
use a while statement like that shown
in the example program.
2
1 x
XIXO
Th
Figure 3: A Tic-Tac-Toe game in
progress.
Problem 1: Tic-Tac-Toe
Tic-Tac-Toe is another classic two-player game, played on pencil and
paper, that most of us surely learned before elementary school. It is
played on a three-by-three grid of squares as shown in Figure 3, and
the object of the game is to claim a row of three squares on the board,
either horizontally, vertically, or diagonally.
The player who goes first is known as x and the second player
as O. The first player starts by claiming one of the nine squares by
marking it with an x. Then the second player takes an open square
by marking it with an 0. The players continue to alternate turns this
way until one player, the winner, makes a line of three squares in a
row with his or her symbol, or until all nine squares are filled. If the
grid is filled but neither player has a line of three, the game is a draw
(also known as a "cat's game”).
Create a visual and interactive Python program that will facili-
tate a game of Tic-Tac-Toe between two human players. Draw the
board on the screen and wait for the first player to click in one fo the
nine squares, then draw an x in that square. Next, allow the second
player to do the same with an O. Write a message in your game's
window to indicate whose turn it is. Continue until one of the play-
ers wins, or the game ends in a draw. Indicate the the final result of
the game in the graphics window, then wait until the players close
the window (or ask if the players would like to play another game).
OLX
O
Figure 4: Player x has won the game by
forming a line of three.
Inputs: A series of mouse clicks from two human players taking
turns to place their x's and O's.
Outputs: A Tic-Tac-Toe board (which looks like a #) drawn in a win-
dow on your screen with x's and O's drawn as they are placed.
olxo
이x
Figure 5: The game is drawn when all
squares are filled and neither player has
a line of three.
Problem 2: Taffy Tangle
Your goal for this problem is to a visual and interactive Python pro-
gram that allows a human player to play our variant of the match-
three game, Taffy Tangle (or whatever you've christened your game).
Our basic variant of the game will be played on a board measuring
9 rows by 7 columns with six different kinds of "taffies”, as shown
in Figure 6. The player makes moves by swapping adjacent taffies so
that a line (horizontal or vertical) of at least three objects of the same
kind is made. The game ends when no more valid moves remain.
Your program must be able to perform the following functions to
satisfy all requirements of this problem:
>000
1. Generate an initial board (9 rows by 7 columns) filled with ran-
dom selections of the 6 different kinds of taffies, and draw the
board to the screen.
Figure 6: The initial board of taffies in
our game, with one taffy selected in-
side the red box), ready to be swapped.
• Each kind of taffy must be visually distinct from the others in
both colour and shape. (Yours do not need to look like the ones
shown as long as they meet this requirement.)
• The initial board should not contain any lines (horizontal or
vertical) of three or more taffies of the same kind in a row.
2. Allow the player to make moves of swapping two adjacent taffies
on the board by first clicking on one taffy, then clicking on an
adjacent one, while the game is not won or lost.
300011
00000
DODOC
• After the first click, indicate which taffy is "selected" in some
visual way (e.g. as shown in Figure 6).
• Cancel the move if the player then clicks a second taffy that is
not adjacent to the first, selected one.
• Forbid or undo a move that would not result in creating a new
match of three or more taffies.
3. After a valid move from the player, repeatedly eliminate taffies
that are matched by following the sequence of steps below, until
no more matches are found. Update the visual board and pause
briefly after each step so that the player can see what happened.
Figure 7: The green taffy has been
swapped with the yellow one below,
creating two lines of three matches.
O
0000
IDONDOO
OOO
• Eliminate all taffies on the board that are part of a horizontal or
vertical line of three or more taffies of the same kind (Figure 8).
• Allow taffies in each column to "fall" so that it contains a con-
tinuous stack of taffies from the bottom, and no gaps remain in
the middle (Figure 9).
• Refill each column with new, random taffies at the top until all
columns again contain 9 taffies (Figure 10).
4. Keep track of the player's score and display it visually within the
game window. In our basic variant, the score will simply be the
number of taffies the player has eliminated. You may use a more
sophisticated scoring mechanic if you like.
5. Detect when no more valid moves remain, then display a "game
over” message with the player's final score in your game window.
If you don't like losing all the time, you may optionally set a win
condition or goal (e.g. reaching a certain score, perhaps within a
given time limit) and end the game with a victory message once
the condition is met.
Figure 8: The two lines of matching
taffies have been eliminated, leaving a
gap in the middle.
It is entirely up to you to decide how you want to organize your
program into functions, modules, and classes. While it is not strictly
required that you use any or all of these constructs, we are pretty
certain that you will want to use them if you plan to complete this
problem while staying sane. Don't hesitate to ask the instructors if
you need advice for any part of the design process. Good luck!
Figure 9: The taffies above the created
gap have fallen down to fill the empty
space so that every column is a contin-
uous stack of taffies from the bottom of
the board.
Inputs: A series of mouse clicks from the player, desperately trying to
swap taffies on the visual game board.
Outputs: A colourful visual display of taffies being swapped, disap-
pearing, dropping, and reappearing. Don't forget to pause briefly
(e.g. for half a second) after each step!
OD DOOD
OOO TOO
s00000
Figure 10: Random new taffies have
been created to fill every column to the
top again. Note that the three red taffies
should now be eliminated and the
falling and refilling process repeated
until no more matches remain, before
the player makes his or her next move.
Purchase answer to see full
attachment