-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathminiproject-Week4--Pong
654 lines (564 loc) · 21.3 KB
/
miniproject-Week4--Pong
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
#
# Implementation of classic arcade game Pong
#
# -agf
# October 2014
#
# This is the classic game of Pong, more or less, with a few
# bits of juice added, like shaking 'get ready' display, to
# allow the player a second (1.2 seconds, to be exact) to get
# ready for the ball to send when the game starts.
#
# ProTip: the paddles are movable during this time...
#
# Game-restart button restarts the game, running immediately.
#
# There's also a pause mode to pause the game if you need a
# break. I have also implemented a super-deluxe "fancy ball"
# mode, which is about as groundbreaking as it sounds. ;)
#
# Fancyball has eyes that track in the direction the ball
# is traveling. This might be considered cheating by some,
# but I think it's just an interesting additional way to
# indicate the direction the ball's already traveling.
#
# There is a game-reset button to kick the game back to the
# "title screen" where it's not yet running, to take a break,
# whatever. A better version of the game-restart, really.
#
# Also: PC AI player! You can play against the computer.
# Just click the "PC AI" toggle to make the right-side player
# be controlled by a rudimentary, entirely-non-sentient
# intelligence. Some randomness involved, so it can in fact
# lose. Give it a try!
#
# I will mention that the AI addition seems to exacerbate the
# key lag problem on the left/human side. Unlikely there is a
# fix for this as long as we're using Codeskulptor. :|
#
# I added key equivalents for the button toggles (except PC AI):
# Q: Game reset
# R: Game restart
# N: New game
# P: Pause toggle
# F: Fancyball toggle
#
# Key toggles are unavailable in the same ways the buttons
# themselves are, for consistency, eg. can't pause a game
# that's not running. And so on.
#
# I try to comment these things a LOT. Hopefully it helps
# someone. :) Leave your username in the feedback if you
# have questions/comments about anything!
#
# I leave random string text all over the place in these
# projects... which is bad. You should totally gather all
# string text in one place so that any localisation you
# might need for your game down the road is easier. Not
# that THIS game will ever need it; which is why I didn't
# bother cleaning it up.
#
# I wanted to add audio--it's actually pretty easy to
# do--but didn't get around to it.
#
# ENJOY! :)
#
# imports
import simplegui, random, math, time
# initialize globals here
WIDTH = 600
HEIGHT = 400
BALL_RADIUS = 20
PAD_WIDTH = 8
PAD_HEIGHT = 80
HALF_PAD_WIDTH = PAD_WIDTH / 2
HALF_PAD_HEIGHT = PAD_HEIGHT / 2
LEFT = False
RIGHT = True
PAUSE = False
RUNNING = False
# Change FANCYBALL to True here if you want fancy-ball mode
# to be the default-on each time the program's executed:
FANCYBALL = False
PCPLAYS = False
score1 = 0
score2 = 0
ball_pos = [WIDTH/2,HEIGHT/2]
ball_vel = [0,0]
paddle1_vel = 0
paddle2_vel = 0
paddle1_pos = HEIGHT/2
paddle2_pos = HEIGHT/2
# (that's a lot of globals, yeesh)
def spawn_ball(direction):
"""
Initialize ball_pos and ball_vel for new ball
in middle of table.
Direction input controls which direction the
ball is sent each time it's spawned; if direction
is RIGHT, the ball's velocity is upper right,
else upper left.
"""
global ball_pos, ball_vel # these are vectors stored as lists
# timer delay launch of ball
spawn.start()
# ball starts out in the center of the screen
ball_pos = [WIDTH/2,HEIGHT/2]
# negate the velocity to switch X direction
# from right to left
ball_vel[0] = (random.randrange(240, 360)/60.0)
if direction == LEFT:
ball_vel[0] = -ball_vel[0]
# Y direction is always negative to spawn (upwards direction)
# according to the project description/rubric
ball_vel[1] = -(random.randrange(120, 240)/60.0)
def restart():
"""
Restart button will only work if the game's already running.
"""
if RUNNING:
new_game()
# define event handlers
def new_game():
"""
Initializes all the variables necessary for a clean restart
of the game.
One thing I did NOT reset was the fancy-ball setting, so
that players could choose their desired mode during gameplay
and not have to constantly switch it back each time they
restart. It of course resets each time the entire program
is executed. Code is left intact to uncomment if you desire
a different functionality
"""
global paddle1_pos, paddle2_pos, paddle1_vel, paddle2_vel # these are numbers
global score1, score2 # these are ints
global ball_vel
global PAUSE, FANCYBALL, RUNNING, PCPLAYS
# turn off right player PC AI
# PCPLAYS = False
# reset scores, paddle data, ball velocity
score1 = 0
score2 = 0
paddle1_vel = 0
paddle2_vel = 0
paddle1_pos = HEIGHT / 2
paddle2_pos = HEIGHT / 2
ball_vel = [0,0]
# game starts out NOT paused
PAUSE = False
pausebutton.set_text('Pause')
# Game's running
RUNNING = True
# uncomment this next line if you want the
# fancyball mode to reset to plain-ball every
# time you restart:
# FANCYBALL = False
# fancyball()
# start the game with a new ball
spawn_ball(random.choice([RIGHT,LEFT]))
def draw_scores(canvas):
"""
Draws the scores on the screen. I draw them twice,
in white and black, offset, for better visual effect.
"""
canvas.draw_text(str(score1).rjust(2, '0'), (PAD_WIDTH + 20, 40), 36, 'Black', 'monospace')
canvas.draw_text(str(score2).rjust(2, '0'), (WIDTH - PAD_WIDTH - 50, 40), 36, 'Black', 'monospace')
canvas.draw_text(str(score1).rjust(2, '0'), (PAD_WIDTH + 20 - 4, 40 - 2), 36, 'White', 'monospace')
canvas.draw_text(str(score2).rjust(2, '0'), (WIDTH - PAD_WIDTH - 50 - 4, 40 - 2), 36, 'White', 'monospace')
return
def draw_ball(canvas):
"""
Draws the ball. If fancy-ball mode is active, draws the additional
fancy bits as well.
"""
# plain old yellow ball
canvas.draw_circle([ball_pos[0],ball_pos[1]],BALL_RADIUS,2,'Yellow','Yellow')
# fancy? yes!
if FANCYBALL:
# the whites of the eyes
canvas.draw_circle([ball_pos[0] - BALL_RADIUS/2, ball_pos[1] - BALL_RADIUS/3], 6, 2,'Black','White')
canvas.draw_circle([ball_pos[0] + BALL_RADIUS/2, ball_pos[1] - BALL_RADIUS/3], 6, 2,'Black','White')
# look like we look where we're going
# temp vars to get pupil angles
eye_vel0= ball_vel[0]
eye_vel1 = ball_vel[1]
# let's keep the pupils inside the whites...
if eye_vel0 > 2:
eye_vel0 = 2
elif eye_vel0 < -2:
eye_vel0 = -2
if eye_vel1 > 2:
eye_vel1 = 2
elif eye_vel1 < -2:
eye_vel1 = -2
canvas.draw_circle([ball_pos[0] - BALL_RADIUS/2 + eye_vel0,
ball_pos[1] - BALL_RADIUS/3 + eye_vel1], 1, 1, 'Black','Black')
canvas.draw_circle([ball_pos[0] + BALL_RADIUS/2 + eye_vel0,
ball_pos[1] - BALL_RADIUS/3 + eye_vel1], 1, 1, 'Black','Black')
# mouthy mouth-mouth
canvas.draw_circle([ball_pos[0], ball_pos[1] + BALL_RADIUS/2], 6, 2, 'Red','Black')
def pcturn():
"""
PC can play the right side. I basically check
for the ball and move the paddle a random distance
toward the ball's Y value. This allows the PC
player to lose somewhat organically.
You can make it cheat by always setting
the paddle EXACTLY to the Y position of
the ball. Comment out the random.choice()
lines below and uncomment the other two lines
below, for an unbeatable AI opponent. ;)
"""
global paddle2_pos
if paddle2_pos < ball_pos[1]:
#paddle2_pos = ball_pos[1]
paddle2_pos += random.choice([3,4,5,6])
elif paddle2_pos > ball_pos[1]:
#paddle2_pos = ball_pos[1]
paddle2_pos -= random.choice([3,4,5,6])
def draw(canvas):
"""
The draw handler for the game. Where most of the magic happens.
Commented more throughout.
"""
global score1, score2, paddle1_pos, paddle2_pos, ball_pos, ball_vel
# draw mid line and gutters of playfield
canvas.draw_line([WIDTH / 2, 0],[WIDTH / 2, HEIGHT], 1, "White")
canvas.draw_line([PAD_WIDTH, 0],[PAD_WIDTH, HEIGHT], 1, "White")
canvas.draw_line([WIDTH - PAD_WIDTH, 0],[WIDTH - PAD_WIDTH, HEIGHT], 1, "White")
#draw the ball
draw_ball(canvas)
# Let AI play if on
if PCPLAYS:
pcturn()
if not(RUNNING):
# lay down the PONG title
canvas.draw_text('P O N G',
[(WIDTH - frame.get_canvas_textwidth('P O N G',36,'monospace'))/2,
HEIGHT/2 - 30],36,'Black', 'monospace')
canvas.draw_text('P O N G',
[(WIDTH - 3 - frame.get_canvas_textwidth('P O N G',36,'monospace'))/2,
HEIGHT/2 - 3 - 30],36,'White', 'monospace')
# display info on how to start the game, in a flashy way
if int(time.time()*10) % 6 >= 1:
canvas.draw_text('NEW GAME to start',
[(WIDTH - frame.get_canvas_textwidth('NEW GAME to start',20,'monospace'))/2,
HEIGHT/2 + 55],20,'Black', 'monospace')
canvas.draw_text('NEW GAME to start',
[(WIDTH - 3 - frame.get_canvas_textwidth('NEW GAME to start',20,'monospace'))/2,
HEIGHT/2 - 3 + 55],20,'White', 'monospace')
# show me some controls info too
canvas.draw_text('Left Paddle: W/S keys Right Paddle: Up/Down arrows',
[(WIDTH - frame.get_canvas_textwidth('Left Paddle: W/S keys Right Paddle: Up/Down arrows',16,'monospace'))/2,
HEIGHT - 30],16,'Black', 'monospace')
canvas.draw_text('Left Paddle: W/S keys Right Paddle: Up/Down arrows',
[(WIDTH - 2 - frame.get_canvas_textwidth('Left Paddle: W/S keys Right Paddle: Up/Down arrows',16,'monospace'))/2,
HEIGHT - 2 - 30],16,'White', 'monospace')
# Is the game paused? Show a pause message and the scores
if PAUSE:
# draw the scores; I want the scores present while
# the game is paused
draw_scores(canvas)
# flash our pause message by displaying it only on an interval
if int(time.time()*10) % 6 >= 1:
canvas.draw_text('P A U S E D',
[(WIDTH - frame.get_canvas_textwidth('P A U S E D',36,'monospace'))/2,
HEIGHT/2 - 30],36,'Black', 'monospace')
canvas.draw_text('P A U S E D',
[(WIDTH - 4 - frame.get_canvas_textwidth('P A U S E D',36,'monospace'))/2,
HEIGHT/2 - 4 - 30],36,'White', 'monospace')
# we're paused; bail outta the draw handler until we're not
return
# while waiting for the ball to spawn, we disable ball updates
if spawn.is_running():
# waiting for ball to spawn, show a 'get ready' message
# SHAKE IT LIKE A POLAROID PICTURE
# (but only if the game's actually running; this prevents
# the game from showing the message if game-reset button is pressed)
if RUNNING:
gr_xoffset = random.randrange(6) - 3
gr_yoffset = random.randrange(6) - 3
canvas.draw_text('G E T R E A D Y',
[(WIDTH - frame.get_canvas_textwidth('G E T R E A D Y',36,'monospace'))/2 + gr_xoffset,
HEIGHT/2 + gr_yoffset - 30],36,'Black', 'monospace')
canvas.draw_text('G E T R E A D Y',
[(WIDTH - frame.get_canvas_textwidth('G E T R E A D Y',36,'monospace'))/2 - 4 + gr_xoffset,
HEIGHT/2 - 4 + gr_yoffset - 30],36,'White', 'monospace')
else:
# game's not running because we reset, so stop the ball-spawn timer altogether
spawn.stop()
# we resume normal play here, as no pause or spawn in progress
else:
# update ball
# check for horizontal bounds (gutters, paddles)
if (ball_pos[0] + ball_vel[0]) >= (WIDTH - BALL_RADIUS - PAD_WIDTH - 1):
# are we bumping into the right paddle -- take action if so
if (paddle2_pos + HALF_PAD_HEIGHT >= ball_pos[1] >= paddle2_pos - HALF_PAD_HEIGHT):
ball_vel[0] = -ball_vel[0]
ball_vel[0] *= 1.1
ball_vel[1] *= 1.1
# looks like we just hit the gutter, so update the score
# and fire the ball away
else:
score1 += 1
spawn_ball(LEFT)
elif (ball_pos[0] + ball_vel[0]) <= (BALL_RADIUS + PAD_WIDTH):
# are we bumping into the left paddle -- take action if so
if (paddle1_pos + HALF_PAD_HEIGHT >= ball_pos[1] >= paddle1_pos - HALF_PAD_HEIGHT):
ball_vel[0] = -ball_vel[0]
ball_vel[0] *= 1.1
ball_vel[1] *= 1.1
# looks like we just hit the gutter, so update the score
# and fire the ball away
else:
score2 += 1
spawn_ball(RIGHT)
else:
ball_pos[0] += ball_vel[0]
# check for vertical bounds (top/bottom)
if (ball_pos[1] + ball_vel[1]) >= (HEIGHT - BALL_RADIUS - 1):
ball_pos[1] = HEIGHT - BALL_RADIUS - 1
ball_vel[1] = - ball_vel[1]
elif (ball_pos[1] + ball_vel[1]) <= (BALL_RADIUS):
ball_pos[1] = BALL_RADIUS
ball_vel[1] = - ball_vel[1]
else:
ball_pos[1] += ball_vel[1]
# update paddle's vertical position, keep paddle on the screen
# paddle1 (left)
if paddle1_pos + paddle1_vel >= HEIGHT - HALF_PAD_HEIGHT - 1:
paddle1_pos = HEIGHT - HALF_PAD_HEIGHT
elif paddle1_pos + paddle1_vel <= HALF_PAD_HEIGHT:
paddle1_pos = HALF_PAD_HEIGHT
else:
paddle1_pos += paddle1_vel
# paddle2 (right)
if paddle2_pos + paddle2_vel >= HEIGHT - HALF_PAD_HEIGHT - 1:
paddle2_pos = HEIGHT - HALF_PAD_HEIGHT
elif paddle2_pos + paddle2_vel <= HALF_PAD_HEIGHT:
paddle2_pos = HALF_PAD_HEIGHT
else:
paddle2_pos += paddle2_vel
# draw paddles
# paddle1 (left)
canvas.draw_line([PAD_WIDTH - HALF_PAD_WIDTH - 1, paddle1_pos - HALF_PAD_HEIGHT],
[PAD_WIDTH - HALF_PAD_WIDTH - 1, paddle1_pos + HALF_PAD_HEIGHT],
PAD_WIDTH, 'White')
# paddle2 (right)
canvas.draw_line([WIDTH - PAD_WIDTH + HALF_PAD_WIDTH + 1, paddle2_pos - HALF_PAD_HEIGHT],
[WIDTH - PAD_WIDTH + HALF_PAD_WIDTH + 1, paddle2_pos + HALF_PAD_HEIGHT],
PAD_WIDTH, 'White')
# draw scores
# this is a second draw call; the first call happens in
# pause mode, so we need to do it again here to ensure
# it's layered properly with everything else being displayed
draw_scores(canvas)
def keydown(key):
"""
Check for key-DOWN events that affect both the left and right
paddles. W and S for left player (player 1) up and down,
respectively; up-arrow and down-arrow for right player
(player 2), respectively.
The key handling in this isn't great; I consider it a limitation
of Codeskulptor not being able to poll pressed keys directly;
sure, I could code around it, but I shouldn't have to, and
the rubric clearly says to test each key "in isolation" anyway.
I choose a paddle speed of 5 here because that felt right.
The rubric makes no particular demands in this regard.
"""
global paddle1_vel, paddle2_vel
# fancyball toggle
# we'll check these two keys even when in menu screen
# fancyball toggle
if key == simplegui.KEY_MAP['f']:
fancyball()
# new game key
elif key == simplegui.KEY_MAP['n']:
new_game()
# disable keys if not running
if not(RUNNING):
return
#paddle1 (left)
if key == simplegui.KEY_MAP['s']:
paddle1_vel += 5
elif key == simplegui.KEY_MAP['w']:
paddle1_vel -= 5
#paddle2 (right)
if key == simplegui.KEY_MAP['down']:
paddle2_vel += 5
elif key == simplegui.KEY_MAP['up']:
paddle2_vel -= 5
# other keys we wanna check
# restart key
elif key == simplegui.KEY_MAP['r']:
restart()
# pause key
elif key == simplegui.KEY_MAP['p']:
pause()
elif key == simplegui.KEY_MAP['q']:
gamereset()
def keyup(key):
"""
Releasing a key resets the speed of the paddle to zero
for whatever player paddle the key belongs to, stopping
the movement of the paddle.
"""
global paddle1_vel, paddle2_vel
# disable keys if not running
if not(RUNNING):
return
# paddle1 (left)
if key == simplegui.KEY_MAP['s']:
paddle1_vel = 0
elif key == simplegui.KEY_MAP['w']:
paddle1_vel = 0
# paddle2 (right)
if key == simplegui.KEY_MAP['down']:
paddle2_vel = 0
elif key == simplegui.KEY_MAP['up']:
paddle2_vel = 0
def spawner():
"""
Shuts down our ballspawn timer immediately
so it doesn't keep triggering.
"""
spawn.stop()
def pause():
"""
This toggles the pause state of the game,
as well as the pause/resume button text.
Could have broken this text display out like
I did for fancy_text/fancyball(), but it
felt like overkill for a single button text
change.
"""
global PAUSE
if not(RUNNING):
return
PAUSE = not(PAUSE)
if PAUSE:
pausebutton.set_text('Resume [P]')
else:
pausebutton.set_text('[P]ause')
if RUNNING:
spawn.start()
def fancy_text():
"""
Update the fancy-ball toggle button text, as
well as the helper text below the button to
better indicate the current mode as indicated
by the value of FANCYBALL (True or False).
"""
if FANCYBALL:
fancybutton.set_text('Plain Ball [F]')
fancylabel.set_text('FANCY ball selected!')
else:
fancybutton.set_text('[F]ancy Ball')
fancylabel.set_text('Plain ball selected.')
def fancyball():
"""
Toggles fancy-ball mode on/off, updates
the display of the toggle button text and helper
text below the button.
"""
global FANCYBALL
FANCYBALL = not(FANCYBALL)
fancy_text()
def gamereset():
"""
Definitely some duplicated code here, resetting
variables already found in new_game(), but without
any of the ball spawning.
"""
global paddle1_pos, paddle2_pos, paddle1_vel, paddle2_vel # these are numbers
global score1, score2 # these are ints
global ball_vel, ball_pos
global PAUSE, RUNNING, FANCYBALL, PCPLAYS
# reset scores, paddle data, ball velocity
score1 = 0
score2 = 0
paddle1_vel = 0
paddle2_vel = 0
paddle1_pos = HEIGHT / 2
paddle2_pos = HEIGHT / 2
ball_vel = [0,0]
ball_pos = [WIDTH/2,HEIGHT/2]
# game starts out NOT paused
PAUSE = False
pausebutton.set_text('[P]ause')
# reset PC AI button
PCPLAYS = False
pcplayer.set_text('PC AI')
pclabel.set_text('Human opponent selected')
#stop the game running
RUNNING = False
#reset the fancyball
FANCYBALL = False
fancy_text()
def pcplayONOFF():
global PCPLAYS
PCPLAYS = not PCPLAYS
if PCPLAYS:
pcplayer.set_text('Human')
pclabel.set_text('Computer opponent selected')
else:
pcplayer.set_text('PC AI')
pclabel.set_text('Human opponent selected')
# create frame and all the stuff that goes with it
frame = simplegui.create_frame("Pong", WIDTH, HEIGHT)
frame.set_canvas_background('#505050')
frame.add_label('P O N G: The Game')
frame.add_label('Plays forever. No quarters!')
frame.add_label('')
# New Game button
frame.add_button('[N]EW GAME', new_game, 100)
frame.add_label('')
# Restart button
frame.add_button('[R]estart', restart, 100)
frame.add_label('')
# Pause button
pausebutton = frame.add_button('[P]ause', pause, 100)
# whitespace to offset fancy-mode stuff
frame.add_label('')
frame.add_button('Reset/[Q]uit', gamereset, 100)
frame.add_label('')
frame.add_label('Versus mode')
pcplayer = frame.add_button('PC AI', pcplayONOFF, 100)
pclabel = frame.add_label('Human opponent selected')
# fancy mode UI stuff
frame.add_label('')
frame.add_label("'Fancy Ball' mode")
fancybutton = frame.add_button('', fancyball, 100)
fancylabel = frame.add_label('')
fancy_text()
# timers
# controls duration of "get ready" ball-spawn message
spawn = simplegui.create_timer(1200, spawner)
# handlers
frame.set_draw_handler(draw)
frame.set_keydown_handler(keydown)
frame.set_keyup_handler(keyup)
# start the frame
# after this, nothing's the same
frame.start()
print """PONG
----
Controls:
Left paddle: W/S keys
Right paddle: Up/Down arrows
Keyboard shortcuts for menu/buttons:
[N] New game
[R] Restart
[P] Pause/resume
[Q] Quit/reset
[F] Fancy Ball mode
"""
print """The PC AI button toggles a
computer-controlled player that
can play against you!
It always plays the right paddle.
HAVE FUN!
Thanks for playing my game!
All feedback appreciated!
"""
# END #################