Flappy bird
The same program in M68K, MIPS, RISC-V.
A playable flappy bird. The bird falls all the time, one tap of the space bar gives it one flap upwards, and the pipes scroll in from the right with their gaps in a different place every game. It ends the moment the bird touches a pipe or the ground, and another tap starts the next one.
Click the Screen panel before you press a key, the same as in Move a square with the keyboard. You can also click on the drawing itself, since the mouse counts as a flap too.
The program is about 800 lines, which is longer than anything else in this course, so the rest of this page walks through it a piece at a time. Each piece is shown on its own where it is explained, and the whole thing sits at the bottom, ready to build and play.
The numbers at the top
Nothing here is code. Every number the program uses more than once, or whose meaning would otherwise
be invisible, is given a name with equ, and that is what makes a game of this size possible to
change at all. HALFGAP equ 32 is the difficulty: a gap is measured from its middle in both
directions, by the drawing and by the hit test alike, so turning that one number down to 24 makes
every gap in the game narrower and nothing else has to know.
Two of these groups repay a closer look. The S_ values are the three states a game can be in, and
they are equs rather than labels because the assembler ignores case, so a constant called
PLAYING and a label called playing would be the same name written twice. The colours are 3-3-2
bytes: three bits of red at the top, three of green, and two of blue at the bottom.
P_CHAR equ 0x10 ; a character, on the Screen and in the transcript
P_NUM equ 0x11 ; a byte as an unsigned decimal number
P_PEN equ 0x20
P_FILL equ 0x21
P_X equ 0x23
P_Y equ 0x24
P_X2 equ 0x25
P_Y2 equ 0x26
P_CMD equ 0x27
P_COL equ 0x29 ; the text cursor, in 8 by 8 cells
P_ROW equ 0x2A
P_KEY equ 0x31 ; 1 while the key whose code is in b is held down
P_MBTN equ 0x42 ; the mouse buttons of the view in b
P_WAIT equ 0x50 ; reading it waits for b hundredths of a second
P_TIME equ 0x52 ; byte b of the hundredths since the run started
C_RECT equ 4
C_ELLIPSE equ 6
C_CLEAR equ 9
C_RESIZE equ 10
C_BUF_ON equ 11
C_PRESENT equ 13
K_ENTER equ 0x0D
K_SPACE equ 0x20
K_UP equ 0x26
K_W equ 0x57
M_NOW equ 0 ; the mouse as it is right now
M_LEFT equ 0x01
WIDTH equ 208
HEIGHT equ 192
TOP equ 8 ; the sky starts under the one text row of the score
TOP16 equ 128 ; TOP * 16
GROUNDY equ 176 ; the top of the ground
GRASSB equ 182
TUFTY equ 174
TUFTB equ 180
TUFTW equ 8
BIRDX equ 40 ; the bird only ever moves up and down
BIRDW equ 16
BIRDH equ 12
BIRDR equ 88 ; BIRDX + MARGIN + BIRDW, its right edge in the world
STARTY equ 1280 ; 80 * 16
RESTY equ 2624 ; (GROUNDY - BIRDH) * 16, where a dead bird lands
MARGIN equ 32 ; a pipe at 0 is this far off the left of the screen
PIPEW equ 24
SPACING equ 80 ; between one pipe and the next
CYCLE equ 240 ; SPACING * 3, what a recycled pipe jumps by
FIRSTX equ 244 ; past the right edge of the screen, lip and all
SPEED equ 2 ; pixels a frame, for everything that scrolls
LIPH equ 6
LIPOUT equ 3
HALFGAP equ 32
GAPMIN equ 48 ; the highest the middle of a gap goes
GAPSPAN equ 88 ; and how far below that it can be
COUNTX equ 48 ; BIRDX + MARGIN - PIPEW: a pipe left of this is passed
GRAV equ 4 ; sixteenths of a pixel, per frame, per frame
FLAPV equ -56 ; the speed one flap gives, upwards
MAXFALL equ 96
PACE equ 4 ; hundredths of a second a frame
; the assembler reads a name without regard to case, so a PLAYING next to the
; playing label below would be one name written twice
S_READY equ 0
S_PLAYING equ 1
S_DEAD equ 2
; colours are 3-3-2 bytes: three bits of red, three of green and two of blue
SKY equ 0x5B
PIPEC equ 0x54
PIPEDK equ 0x2C
GRASS equ 0x79
GRASSDK equ 0x51
SAND equ 0xDA
BODY equ 0xF8
WINGC equ 0xDF
BEAKC equ 0xF0
INK equ 0x20
WHITE equ 0xFF
Setting the Screen up
The Screen is resized first, then cleared, and only then is double buffering turned on. The order
matters: the fill colour a clear uses also becomes the background the text rows are painted on, so
the INK clear is what makes the score row dark for the rest of the run.
After C_BUF_ON nothing the program draws appears until it asks for it, which is what stops the
player seeing half-painted frames.
.org 0x8000
ld a, WIDTH
out (P_X), a
ld a, HEIGHT
out (P_Y), a
ld a, C_RESIZE
out (P_CMD), a
ld a, INK
out (P_FILL), a
ld a, C_CLEAR
out (P_CMD), a ; the fill of a clear becomes the text background too
ld a, C_BUF_ON
out (P_CMD), a ; from here a frame is drawn off screen and shown at once
newgame:
call reset
ld hl, hint
ld a, (best)
call hud
One frame, and the three states
A game is always in one of three states, and state says which: S_READY while the bird hangs
still waiting for the first flap, S_PLAYING, and S_DEAD while it drops to the ground.
The frame loop reads the input once, branches on state, and every branch ends up at draw. So one
frame is one pass through here, and the three states differ only in what they do to the bird and the
pipes on the way past.
The comment above frame is a warning to your future self: the answer from readflap lives in c
for the whole of the frame, so nothing called from here may use c for anything of its own.
; --- one frame ---------------------------------------------------------------
; c carries "a flap began this frame" from the input read to the state machine,
; so nothing called from here is allowed to touch it.
frame:
call readflap
ld c, a
ld a, (state)
cp S_PLAYING
jp z, playing
cp S_DEAD
jp z, dead
; --- waiting to start: the bird hangs still until the first flap --------------
ld a, c
or a
jp z, draw
call seedclock
call newpipes
ld a, S_PLAYING
ld (state), a
ld a, FLAPV
ld (birdv), a
ld hl, scoremsg
xor a
call hud
jp draw
; --- playing -----------------------------------------------------------------
playing:
ld a, c
or a
jr z, fall
ld a, FLAPV ; one tap is one flap, however long the key is held
ld (birdv), a
xor a
ld (wing), a ; and the wing beat starts again with it
fall:
call gravity
call movepipes
call hittest
or a
jp z, draw
ld a, S_DEAD
ld (state), a
ld a, (score)
ld hl, best
cp (hl)
jr c, kept
ld (hl), a
kept:
ld hl, overmsg
ld a, (score)
call hud
jp draw
; --- dead: the bird drops to the ground, then waits for a flap ----------------
dead:
call gravity
ld hl, (birdy)
ld de, RESTY
or a
sbc hl, de
jr c, dfalling
ld hl, RESTY
ld (birdy), hl
xor a
ld (birdv), a
dfalling:
ld a, c
or a
jp z, draw
jp newgame
; --- the frame is painted back to front, then shown all at once ---------------
draw:
call drawsky
ld hl, pipes
call drawpipe
call drawpipe
call drawpipe
call drawground
call drawbird
ld a, C_PRESENT
out (P_CMD), a ; the finished frame appears here, all at once
ld b, PACE
ld c, P_WAIT
in a, (c) ; and this is what sets the frame rate
jp frame
Counting one flap per press
readflap answers 1 only on the frame a press begins, and that is harder than it sounds.
Port 0x31 and port 0x42 both report what is held down right now. A key held for twenty frames
reads as pressed on all twenty of them, which would be twenty flaps from one tap. held is the byte
that turns that into one: a press counts only when the frame before it had nothing down. The answer
travels out in c.
; Input
;------------------------------------------------------------------------------
; Answers a = 1 on the frame a flap begins. Every port here reports what is held
; right now, so the "began" part is this program's: a press only counts when the
; frame before it had nothing pressed.
readflap:
ld c, P_KEY
ld b, K_SPACE
in a, (c)
or a
jr nz, pressed
ld b, K_UP
in a, (c)
or a
jr nz, pressed
ld b, K_W
in a, (c)
or a
jr nz, pressed
ld b, K_ENTER
in a, (c)
or a
jr nz, pressed
ld c, P_MBTN
ld b, M_NOW
in a, (c)
and M_LEFT ; bit 0 of the flags byte is the left button
jr nz, pressed
xor a
ld (held), a
ret
pressed:
ld a, (held)
or a
jr nz, stillheld
ld a, 1
ld (held), a
ret ; this frame is where the press began
stillheld:
xor a
ret
The world: pipes, and where the gaps go
Three pipes make an endless course. CYCLE is SPACING * 3, so a pipe that has scrolled off the
left jumps exactly as far right as a fourth pipe would have been, and the spacing never drifts.
A world 240 wide has nowhere to park three pipes 80 apart off the right of a 208 wide screen, so a
game starts with two of them out of play. The fourth byte of a pipe's record says whether it is
there at all, and newpipes gives it only to the one that starts past the right edge. The drawing
and the hit test skip the other two until they have come round, which is what leaves the first few
seconds of a game with nothing in it but sky. Those two start marked as counted as well, so neither
can take a point on its way past the bird while it is invisible.
randgap has to multiply, and there is no multiply instruction, so mul8 is the shift and add from
the arithmetic lecture: look at the low bit of the multiplier, add the multiplicand if it is set,
double the multiplicand, eight times round. srl a is what feeds it, because rra would rotate the
carry that add hl, de has just left into the number.
The random numbers come from a sixteen bit xorshift, and two of its three shifts cost almost
nothing: the low half of x << 8 is all zero, so that step only changes the high byte, and the high
half of x >> 9 is all zero for the same reason. Only the x << 7 needs a loop. The seed is read
off the clock on the frame of the first flap, so the course depends on when you started playing
rather than on a number written into the program.
; The world
;------------------------------------------------------------------------------
; A new game, keeping the best score of the session.
reset:
ld a, S_READY
ld (state), a
xor a
ld (birdv), a
ld (score), a
ld (wing), a
ld (scroll), a
ld hl, STARTY
ld (birdy), hl
call newpipes
ret
; Three pipes, spread evenly round the world, each with a gap of its own. Only
; the one past the right edge of the screen is in play; the other two wait until
; they have come round, which is what leaves a game's first seconds empty.
newpipes:
ld hl, pipes
ld a, FIRSTX
ld (tuftx), a ; the scratch byte, for the x of the pipe being made
ld b, 3
npnext:
push bc
ld a, (tuftx)
ld (hl), a ; where its left edge is, in the world
inc hl
push hl
call randgap
pop hl
ld (hl), a ; the middle of its gap
inc hl
ld a, (tuftx)
cp FIRSTX
jr c, npwaits ; it starts behind the screen, so it waits its turn
ld (hl), 0 ; not counted yet
inc hl
ld (hl), 1 ; and in play
jr npmade
npwaits:
ld (hl), 1 ; counted, so it cannot score while it is out of play
inc hl
ld (hl), 0 ; and out of play until it comes round from the right
npmade:
inc hl
ld a, (tuftx)
sub SPACING
ld (tuftx), a
pop bc
djnz npnext
ret
; The clock, as the seed, so the course depends on when the player began.
seedclock:
ld c, P_TIME
ld b, 0
in a, (c) ; the low byte of the hundredths since the run started
ld l, a
ld b, 1
in a, (c)
ld h, a
or l
jr nz, seeded
ld hl, 0xACE1 ; a zero seed would stop the generator dead
seeded:
ld (seed), hl
ret
; The middle of the next gap, in a, from a sixteen bit xorshift: one seed always
; lays out the same course. It destroys hl, de and b.
randgap:
call nextrandom
ld a, h ; the high byte, which is the half worth using
ld de, GAPSPAN
call mul8 ; hl = a * GAPSPAN
ld a, h ; whose high byte is nought to GAPSPAN
add a, GAPMIN
ret
; hl = a * de, by the shift and add every machine without a multiply uses
mul8:
ld b, 8
ld hl, 0
mulnext:
srl a ; the low bit of what is left of the multiplier
jr nc, mulskip
add hl, de
mulskip:
ex de, hl
add hl, hl ; de doubles for the next bit
ex de, hl
djnz mulnext
ret
; The next value of a sixteen bit xorshift, in hl
nextrandom:
ld hl, (seed)
ld d, h
ld e, l
ld b, 7
shift7:
add hl, hl
djnz shift7 ; hl = x << 7
ld a, h
xor d
ld h, a
ld a, l
xor e
ld l, a ; x = x ^ (x << 7)
ld a, h
srl a
xor l
ld l, a ; x = x ^ (x >> 9), whose high half is all zero
ld a, l
xor h
ld h, a ; x = x ^ (x << 8), whose low half is all zero
ld (seed), hl
ret
Falling, scrolling and hitting
birdy is a sixteen bit value counting sixteenths of a pixel, and that is not fussiness. A bird
that can only move in whole pixels cannot accelerate smoothly: the slowest fall it could manage is
one pixel a frame, which is already too fast. Sixteenths give the gravity somewhere to accumulate.
birdv is one signed byte, and add a, a then sbc a, a widens it into d by filling that
register with the sign bit, so the pair can be added to hl in a single add hl, de. birdtop
turns the sixteenths back into pixels by shifting right four times, through srl h and rr l so
the bit leaving the high byte arrives at the top of the low one, and what falls off the end is the
fraction the next frame keeps.
The scrolling is byte arithmetic doing the wrapping by itself. A pipe walks from 208 down to 0, and
then 1 minus SPEED is 255, plus CYCLE is 239, which is exactly where the pipe belongs. No
comparison is needed to notice it went off the edge.
; One frame of falling: the speed grows by GRAV and the bird moves by it.
gravity:
ld a, (birdv)
add a, GRAV
bit 7, a
jr nz, capped ; still going up, so there is nothing to cap
cp MAXFALL+1
jr c, capped
ld a, MAXFALL
capped:
ld (birdv), a
ld e, a
add a, a
sbc a, a
ld d, a ; d becomes 0xFF for a negative speed and 0 otherwise
ld hl, (birdy)
add hl, de
bit 7, h
jr nz, bumped ; it went past the top of the screen
ld a, h
or a
jr nz, insky
ld a, l
cp TOP16
jr nc, insky
bumped:
ld hl, TOP16 ; the top of the screen is a bump, not a death
xor a
ld (birdv), a
insky:
ld (birdy), hl
ret
; Everything scrolls left. A pipe that has left the world jumps a whole cycle to
; the right with a new gap, which is why three pipes are an endless course.
movepipes:
ld a, (scroll)
sub SPEED
cp 16
jr c, scrolled
add a, 16 ; the tufts repeat every 16 pixels, so this wrap is invisible
scrolled:
ld (scroll), a
ld hl, pipes
ld b, 3
mpnext:
push bc
push hl ; the record it started at
ld a, (hl)
sub SPEED
ld (hl), a
jr nc, mpcount
add a, CYCLE ; it has left the world, so it comes round again
ld (hl), a
inc hl
push hl
call randgap
pop hl
ld (hl), a ; with a gap of its own
inc hl
ld (hl), 0 ; counting again
inc hl
ld (hl), 1 ; and in play from here on
jr mpdone
mpcount:
cp COUNTX
jr nc, mpdone ; its right edge has not reached the bird yet
inc hl
inc hl
ld a, (hl) ; counted already?
or a
jr nz, mpdone
ld (hl), 1
ld hl, score
inc (hl)
ld a, (hl)
ld hl, scoremsg
call hud
mpdone:
pop hl
ld de, 4
add hl, de ; and on to the record after it
pop bc
djnz mpnext
ret
; Answers a = 1 when the bird is touching a pipe or the ground.
hittest:
call birdtop
ld d, a ; the top of the bird
add a, BIRDH
ld e, a ; and one past its bottom
cp GROUNDY
jr nc, hit ; the ground is met by the whole bird
ld hl, pipes
ld b, 3
htnext:
push bc
ld a, (hl) ; its left edge
inc hl
ld c, (hl) ; the middle of its gap
inc hl
inc hl
ld b, (hl) ; whether it is in play
inc hl ; and on to the record after it
bit 0, b
jr z, htskip ; one out of play is not there to be touched
cp BIRDR
jr nc, htskip ; this pipe is still to the right of the bird
cp COUNTX+1
jr c, htskip ; and this one is already behind it
ld a, c
sub HALFGAP
cp d
jr nc, hitpop ; the bird is above the gap
ld a, c
add a, HALFGAP
cp e
jr c, hitpop ; or below it
htskip:
pop bc
djnz htnext
xor a
ret
hitpop:
pop bc
hit:
ld a, 1
ret
; The bird's top edge in pixels, from the sixteenths it is kept in
birdtop:
ld hl, (birdy)
ld b, 4
btnext:
srl h
rr l
djnz btnext
ld a, l
ret
Drawing
Every shape goes through rect or ellipse, which take their two corners in de and hl and
leave the colours to bothcolours. Painting happens back to front: sky, then ground, then pipes,
then the bird, and finally command 13 to show the lot.
What byte coordinates cannot express is a pipe that is half off the left edge, because its x would
have to be negative. drawpipe settles that before it draws anything. sub MARGIN gives the screen
x and sets the carry if it went below zero, add a, PIPEW gives the right edge, and a pipe whose
right edge is still negative returns without drawing at all. Otherwise the left edge is clamped to 0
and pleft and pright are what every rectangle of that pipe is built from. The right hand side
needs none of this, because the Screen clips a rectangle that runs off that edge on its own.
The score is one text row above the sky, and hud writes it only when it changes: on a new game, on
the first flap, on every point and on a death. The console ports draw at the text cursor and
record in the transcript, so a row rewritten every frame would leave a transcript of two thousand
identical lines. The sky is painted from TOP down and never over that row, which is what leaves it
standing between one call and the next.
; Drawing
;------------------------------------------------------------------------------
; The pen and the fill both become the colour in a
bothcolours:
out (P_FILL), a
out (P_PEN), a
ret
; A filled rectangle from (d, e) to (h, l), in the colours already set. The
; second corner is the one outside the shape, so a rectangle 8 wide ends at x+8.
rect:
ld a, d
out (P_X), a
ld a, e
out (P_Y), a
ld a, h
out (P_X2), a
ld a, l
out (P_Y2), a
ld a, C_RECT
out (P_CMD), a
ret
; The ellipse inside that same rectangle
ellipse:
ld a, d
out (P_X), a
ld a, e
out (P_Y), a
ld a, h
out (P_X2), a
ld a, l
out (P_Y2), a
ld a, C_ELLIPSE
out (P_CMD), a
ret
drawsky:
ld a, SKY
call bothcolours
ld d, 0
ld e, TOP
ld h, WIDTH
ld l, GROUNDY
jp rect
; One pipe, from the record at hl, which moves on to the next one. The Screen's
; coordinates are single bytes, so a pipe half off the left edge is clipped here
; instead of being drawn at a negative x.
drawpipe:
ld a, (hl) ; its left edge in the world
inc hl
ld c, (hl) ; the middle of its gap
inc hl
inc hl
ld b, (hl) ; whether it is in play
inc hl ; and on to the record after it
push hl
bit 0, b
jr z, dpgone
sub MARGIN ; where it is on the screen, which may be off the left
jr nc, dpwhole
add a, PIPEW ; its right edge, which may be off the left as well
jr nc, dpgone
or a
jr z, dpgone
ld d, 0 ; the left edge, clipped to the screen
ld e, a
jr dpedges
dpwhole:
ld d, a
add a, PIPEW
ld e, a
dpedges:
ld a, d
ld (pleft), a
ld a, e
ld (pright), a
ld a, c
sub HALFGAP
ld (ptop), a ; the top of the gap
add a, HALFGAP
add a, HALFGAP
ld (pbot), a ; and the bottom of it
ld a, PIPEC
call bothcolours
call pipeedges
ld e, TOP
ld a, (ptop)
ld l, a
call rect ; the column down from the top of the sky
call pipeedges
ld a, (pbot)
ld e, a
ld l, GROUNDY
call rect ; and the column up from the ground
ld a, PIPEDK
call bothcolours
call lipedges
ld a, (ptop)
ld l, a
sub LIPH
ld e, a
call rect ; the lip that ends at the top of the gap
call lipedges
ld a, (pbot)
ld e, a
add a, LIPH
ld l, a
call rect ; and the one that starts at the bottom of it
dpgone:
pop hl
ret
; d and h, the two x coordinates of the column
pipeedges:
ld a, (pleft)
ld d, a
ld a, (pright)
ld h, a
ret
; the same two, a lip's width wider on each side and clipped the same way
lipedges:
ld a, (pleft)
sub LIPOUT
jr nc, lewide
xor a
lewide:
ld d, a
ld a, (pright)
add a, LIPOUT
ld h, a
ret
; Sand, a band of grass on top of it, and tufts that scroll with the pipes.
drawground:
ld a, SAND
call bothcolours
ld d, 0
ld e, GROUNDY
ld h, WIDTH
ld l, HEIGHT
call rect
ld a, GRASS
call bothcolours
ld d, 0
ld e, GROUNDY
ld h, WIDTH
ld l, GRASSB
call rect
ld a, GRASSDK
call bothcolours
ld a, (scroll)
ld (tuftx), a
ld b, 13
dtnext:
push bc
ld a, (tuftx)
ld d, a
add a, TUFTW
ld h, a
ld e, TUFTY
ld l, TUFTB
call rect
ld a, (tuftx)
add a, 16
ld (tuftx), a
pop bc
djnz dtnext
ret
; The bird: a body, a wing that beats through three positions, an eye and a beak
drawbird:
call birdtop
ld (birdpix), a
ld a, BODY
call bothcolours
ld a, (birdpix)
ld d, BIRDX
ld e, a
ld h, BIRDX+BIRDW
add a, BIRDH
ld l, a
call ellipse
ld a, (state)
cp S_DEAD
jr z, wingset ; a dead bird stops beating
ld a, (wing)
inc a
cp 12
jr c, wingheld
xor a
wingheld:
ld (wing), a
wingset:
ld a, (wing)
rrca
rrca
and 0x03 ; twelve frames, three positions
add a, a
add a, 3
ld c, a ; the wing sits this far down the body
ld a, WINGC
call bothcolours
ld a, (birdpix)
add a, c
ld d, BIRDX+2
ld e, a
ld h, BIRDX+10
add a, 5
ld l, a
call ellipse
ld a, INK
call bothcolours
ld a, (birdpix)
add a, 3
ld d, BIRDX+10
ld e, a
ld h, BIRDX+13
add a, 3
ld l, a
call ellipse
ld a, BEAKC
call bothcolours
ld a, (birdpix)
add a, 6
ld d, BIRDX+13
ld e, a
ld h, BIRDX+20
add a, 4
ld l, a
jp rect
; The one text row, above the sky, rewritten only when it changes: the console
; ports draw on the Screen and record in the transcript at the same time, so a
; line per point is a transcript worth reading and a line per frame is not.
; hud(message in hl, number in a)
hud:
ld (hudnum), a
push hl
ld a, INK
call bothcolours
ld d, 0
ld e, 0
ld h, WIDTH
ld l, TOP
call rect
ld a, WHITE
out (P_PEN), a
xor a
out (P_COL), a
out (P_ROW), a
pop hl
call print
ld a, (hudnum)
out (P_NUM), a
ld a, 10
out (P_CHAR), a
ret
; The zero terminated string at hl
print:
ld a, (hl)
or a
ret z
out (P_CHAR), a
inc hl
jr print
The state, in memory
Everything the game remembers is here, and it is thirty-odd bytes. Each pipe is a four byte record,
and pipes is three of them end to end, walked with hl and add hl, de the way the records
lecture walks any array of records.
; State
;------------------------------------------------------------------------------
.org 0x9000
state: .db 0
birdv: .db 0 ; sixteenths of a pixel a frame, and it fits in a byte
score: .db 0
best: .db 0
held: .db 0 ; was a flap key or button down last frame?
wing: .db 0
scroll: .db 0
birdpix: .db 0 ; the bird's top edge in pixels, for one frame
hudnum: .db 0
tuftx: .db 0
pleft: .db 0 ; the pipe being drawn, after its edges are clipped
pright: .db 0
ptop: .db 0
pbot: .db 0
birdy: .dw STARTY ; sixteenths of a pixel, so gravity is smooth
seed: .dw 0xACE1
; one pipe is its left edge in the world, the middle of its gap, whether it has
; been counted and whether it is in play at all
pipes: .db 0, 0, 0, 0
.db 0, 0, 0, 0
.db 0, 0, 0, 0
hint: .asciz "TAP SPACE, BEST "
scoremsg: .asciz "SCORE "
overmsg: .asciz "GAME OVER, SCORE "
The whole program
Build this one and play it.