Teaching Python 3.6 with Games

Introduction

  • My goal with this webinar is to share what I’ve learned teaching students to program using Python to create video games, along with a new Arcade library for 2D graphics.
  • So if you are an educator, or if you work with interns and new employees, hopefully you’ll find some ideas to try.
  • If you are more of a visual learner, or if you want to follow up on anything I’m talking about later, you can see my notes for this webinar at: http://2017-craven-webinar.readthedocs.io

Background/Overview

  • I’m a computer science professor at Simpson College in Iowa.
  • I’ve taught an Intro to Programming class using Python and Pygame for many years.
    • I created Program Arcade Games, a website, book, and video series to learn programming.
    • Students learn by creating their own video games.
    • About 1,800 people use it to learn each weekday during the school year.
  • Pygame got to be a limiting factor to make the curriculum better.
    • Buggy, inconsistent, didn’t use new Python 3 features.
    • Students spent too much time learning the ‘oddities’ of Pygame when they could have been learning more programming.
  • Create the Arcade Library
    • Easy library for 2D video games
    • Fix issues with Pygame
    • Faster, OpenGL based
    • Take advantage of Python 3 features like type hinting.
  • I have a new version of my on-line book that I’m working on that uses the Arcade Library instead of Pygame: Learn Python with Arcade Academy

Tip 1: Revise Your Pedagogy

  • Pedagogy (how you teach) should be treated like software.
  • We keep releasing “learn to program” books, as one-and-done books. This is terrible.
  • This applies even if you aren’t writing a “how to program” book. For example, if you are writing docs on your in-house software, your docs on your company’s development procedures.
  • Most books probably haven’t even had one set of people go through them to learn how to program before being released to market.
  • Realize the first release of learning material, just like software, will be terrible.
  • Revise your notes. Teach from the materials, find student questions, update it, repeat.
  • Even the second version will likely be bad. When I create materials, it isn’t until at least the third version I start being happy with the materials.
  • Don’t restart from scratch.
  • After 8 years of revisions, 16 semesters of teaching the class, you can fine tune a class to a thing of beauty. For example, imagine updating a question where you add the text “If you think the answer is zero, go back and reread Section 12.3.” can save you time, the student time, and you end up with a student who understands the material better than last semester’s student. It is engineering.
  • Remember: Any documentation you use watch people use it, take notes on how to make it better, and then engineer it. Make science out of learning.

Tip 2: Use a Good Tool-Chain for Teaching Materials

  • How do you make this constant revision easy?
  • I suggest using Sphinx. Sphinx is the software used to create the official Python documentation. It is a “Static Content Generator.”
    • Note, there are a lot of other Static Content Generators out there you can use for your materials. I’ve also tried using a Wiki. I prefer Sphinx for its Python integration.
  • Sphinx uses Restructured Text, which is much better than editing HTML by hand. You can also make e-books out of it.
  • See this example of a Networking class I did: https://github.com/pvcraven/networking-class/commits/master
  • You can easily build .rst files using Sphinx in PyCharm with the Sphinx task:
_images/sphinx_task.png
  • PyCharm is a great editor to type up your restructured text files, as it can spell check, auto-complete rst directives, and even check and let you know if you have undefined references. You can look up references.
_images/rst_linting.png
  • It is easy to include syntax-highlighted code directly in your materials. You can include code snippets, or have the code in separate files.
print("Simple code snippet")
Including code from a different file
.. literalinclude:: ../../examples/bouncing_ball.py
    :caption: bouncing_ball.py
    :linenos:
  • Store revisions to your materials using Git and a site like GitHub. (Note, BitBucket offers free private repositories if you don’t want to share the source to your materials.)
  • Use Read the Docs to automatically build and host your materials, or use cloud hosting.
  • The end result:
  • Use any computer to update your teaching materials
  • Work with other developers in your team to keep docs up to date using your same in-house VCS.
  • Push the changes and have them automatically built
  • Even use bug/feature tracking to keep track of to-do lists

Tip 3: Parameter and Type Hinting

The beauty of modern editors, is that for most students, you won’t even have to point out that this exists. As soon as you type arcade. when using an IDE like PyCharm, you get a pop-up with the Arcade Library functions. This helps the students quite a bit.

_images/select_function.png

Once you are start typing the function parameters, you also get a pop-up with the parameters and types:

_images/show_parameters.png

Note

Parameter Names are Important

This means the parameter names have to be good. What might make sense to the library programmer does not always make sense to the new programmer trying to figure use the library.

I’ve spent time time tuning the parameter names for the Arcade Library based on my in-person observations of what names seem to work best with students figuring out the library.

If you type the parameters incorrectly, you’ll get an warning before you get any further:

_images/linting1.png

The Arcade Library specifies parameter types using the new type hinting conventions introduced in Python 3.5 via PEP 484 and PEP 526.

The numbers and typing modules in Python help support type hinting. The code to implement type hinting in the Arcade Library looks like this:

def open_window(width: Number, height: Number, window_title: str, resizable: bool = False):

In short, I’ve found these PyCharm features to be very effective with new programmers:

  • Pop-up lists of functions.
  • When typing a function, having the IDE pop up a list of good parameter variable names. IDE choice aside, it is important that the parameter names proven understandable as shown by actual experience observing programmers new to the library.
  • Type hinting. Get some of the advantages of strong typing without the hassle of requiring new programmers to declare variables before using them. (If you’ve forgotten the difference between strong, weak, dynamic, and static typing, I suggest a quick review.)

Note

I hope someday we will also see docs for functions as you type them. Or a link to the HTML docs.

Students often treat the type hinting warnings as errors rather than warnings. This helps a lot with new programmers because they figure out their errors as they do the initial coding, rather than trying to figure out why a 500 line program doesn’t work after they’ve coded it.

Tip 4: Show Students the Linter

Stereotypically, programmers are shown with terrible clothing style choices. In reality, we are all about style. Style of our code. For Python, this is all defined in PEP-8.

This might catch new programmers off-guard. A person might celebrate that feeling of accomplishment when their algorithm works. But get discouraged with the teacher nit-picks their program’s style. New programmers often have issues with:

  • Inconsistent indentation
  • Forgetting to put spaces after commas
  • Huge sections of blank lines in the middle of their program
  • No blank lines in their program

In my experience:

  • Feedback after-the-fact on style issues is mostly ignored, and if it isn’t ignored it just serves to discourage students.
  • Asking students to run a linter before turning in their program is just an extra step that they often skip.

I like Python because it is not so rigid as to discourage new programmers. But I do want to encourage new programmers to learn and adhere to programming standards. With pop-up warnings in the code, we get the best of both worlds. Here is what PyCharm does:

_images/linting2.png

Since it comes from the editor, it seems more “official” than coming from the instructor. It also feels less like a personal insult to their intelligence. Poor students who don’t care about programming can still ignore it. Average to good students can pay attention and write code that conforms to style guidelines.

In my experience, once students got PEP-8 warnings in their editor, the percent of student code that adhered to style guidelines improved.

bouncing_ball.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
"""
Tip 3:
    Function completion
    PEP-8
    Spell checking

Tip 4:
    Use type hinting
"""

# Function completion when you type-to
# Pull up documentation with Ctrl-Q
# If you try setting the background color with only 2 numbers, you'll get an type hint warning
# (Show how to open declaration, and how type hinting is done)
# Forget to put a space after a comma, and get a PEP-8 warning
# This is a program a student wrote his second week of class
# He wrote it and, like many students, also made sure PyCharm showed no issues.
# See that you don't have to declare or create classes to do stuff in arcade!

import arcade

arcade.open_window(800, 600, "Flying V")

arcade.set_background_color((53, 242, 252))

arcade.start_render()

"""Guitar Body"""
arcade.draw_triangle_filled(450, 300, 150, 300, 20, 100, arcade.color.CADMIUM_RED)
arcade.draw_triangle_filled(450, 300, 150, 300, 20, 500, arcade.color.CADMIUM_RED)

"""Guitar Neck"""
arcade.draw_rectangle_filled(450, 300, 350, 50, arcade.color.DEEP_CHAMPAGNE)

"""Guitar Head"""
arcade.draw_triangle_filled(625, 260, 625, 340, 751, 300, arcade.color.BLACK)

"""Guitar Pickups"""
arcade.draw_rectangle_filled(270, 300, 50, 80, arcade.color.SILVER)

"""Fret Separators"""
arcade.draw_line(625, 325, 625, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(604, 325, 604, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(583, 325, 583, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(562, 325, 562, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(541, 325, 541, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(520, 325, 520, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(499, 325, 499, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(478, 325, 478, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(457, 325, 457, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(436, 325, 436, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(415, 325, 415, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(394, 325, 394, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(373, 325, 373, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(352, 325, 352, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(331, 325, 331, 275, arcade.color.BROWN, border_width=2)
arcade.draw_line(310, 325, 310, 275, arcade.color.BROWN, border_width=2)

"""Fret Indicators"""
arcade.draw_circle_filled(572.5, 300, 5, arcade.color.LIGHT_BROWN)
arcade.draw_circle_filled(530.5, 300, 5, arcade.color.LIGHT_BROWN)
arcade.draw_circle_filled(383.5, 312, 5, arcade.color.LIGHT_BROWN)
arcade.draw_circle_filled(383.5, 288, 5, arcade.color.LIGHT_BROWN)

"""Strings"""
arcade.draw_line(260, 280, 640, 280, arcade.color.GRAY, border_width=1)
arcade.draw_line(260, 288, 670, 288, arcade.color.GRAY, border_width=1)
arcade.draw_line(260, 296, 700, 296, arcade.color.GRAY, border_width=1)
arcade.draw_line(260, 304, 700, 304, arcade.color.GRAY, border_width=1)
arcade.draw_line(260, 312, 670, 312, arcade.color.GRAY, border_width=1)
arcade.draw_line(260, 320, 640, 320, arcade.color.GRAY, border_width=1)

"""String Exits"""
arcade.draw_circle_filled(640, 280, 2, arcade.color.SILVER)
arcade.draw_circle_filled(670, 288, 2, arcade.color.SILVER)
arcade.draw_circle_filled(700, 296, 2, arcade.color.SILVER)
arcade.draw_circle_filled(700, 304, 2, arcade.color.SILVER)
arcade.draw_circle_filled(670, 312, 2, arcade.color.SILVER)
arcade.draw_circle_filled(640, 320, 2, arcade.color.SILVER)

"""String Entrances"""
arcade.draw_circle_filled(260, 280, 2, arcade.color.BLACK)
arcade.draw_circle_filled(260, 288, 2, arcade.color.BLACK)
arcade.draw_circle_filled(260, 296, 2, arcade.color.BLACK)
arcade.draw_circle_filled(260, 304, 2, arcade.color.BLACK)
arcade.draw_circle_filled(260, 312, 2, arcade.color.BLACK)
arcade.draw_circle_filled(260, 320, 2, arcade.color.BLACK)

"""Volume and Tone Knobs"""
arcade.draw_circle_filled(160, 190, 10, arcade.color.BLACK)
arcade.draw_circle_filled(210, 215, 10, arcade.color.BLACK)
arcade.draw_circle_filled(260, 240, 10, arcade.color.BLACK)
arcade.draw_circle_outline(160, 190, 11, arcade.color.ANTI_FLASH_WHITE, border_width=2)
arcade.draw_circle_outline(210, 215, 11, arcade.color.ANTI_FLASH_WHITE, border_width=2)
arcade.draw_circle_outline(260, 240, 11, arcade.color.ANTI_FLASH_WHITE, border_width=2)

arcade.finish_render()

arcade.run()

Tip 5: Making IDEs Easier

Text editors are simple. Students write a file, and then run the file.

IDEs are more complex. The most common issues for new students:

  • Students must create a project, then add the files to the project. Some students have difficulty with this concept, and even basic file/folder management. If a student just wants to run a two-line for loop example, forcing her to create a project to run it is crazy.
  • Configurable IDEs have a lot of tool windows and other things that can be messed up, making it difficult for a student to get back to the state where they match the instructor’s setup.
  • Students often run the wrong program:
_images/run_the_wrong_program.png

However, the trade-off for this complexity:

  • Code-completion
  • PEP-8 Linting
  • Type hinting
  • Spell checking
  • If an error occurs, the IDE points right at the error without needing to decode a stack trace.

For many years I was in the “text editor” camp. This last year, after seeing the improved code quality with the PEP-8 linting, I’m in the IDE camp.

To mitigate the issues with forcing a project on the student, we create one project to use for the whole class. Students start with a pre-defined set of folders. There is one folder for each lab, and a folder for “scratch” code that students can store quick examples in.

Tip 6: Teach Students to Search for Code Examples

Real-life programming rarely involves writing code from scratch. In fact, feedback we get from recent alumni is that they had not enough experience programming with large pre-existing project.

To help teach this, I provide a lot of code samples:

http://arcade.academy/examples/index.html

Don’t just give students one example and step through it. That’s a snooze-fest. Give lots of example code that form building blocks. Make it easy to navigate. Then give tasks that requires students to go through those code samples and build their own program.

Tip 7: Show Students API Docs

Don’t just show students how to program from your materials. Make it a point to show them the API docs. Have them look up functions out of the API docs and use them:

http://arcade.academy/quick_index.html

Tip 8: Explain IDE Features As You Use Them

  • Moving lines up and down
  • Indent/unindent blocks of code
  • Multi-cursor
  • Go to definition

Results

Playlist with some of the games:

https://www.youtube.com/playlist?list=PLUjR0nhln8ub1tPayFjz7w-LCTQ_gYs7V

Note that these aren’t paint-by-number programs. There is a lot of creativity in what the students have put together.

Here is a breakout program that a student did:

The code mostly passes the linter check, and for a first-semester programming student is a rather impressive feat:

example.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
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
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
"""
Lab 12 - Final Project
Audio from http://opengameart.org
"""
import arcade
import random

SPRITE_SCALING = 0.5
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
MOVEMENT_SPEED = 8
BALL_SPEED = 4

INSTRUCTION_PAGE = 0
GAME_RUNNING = 1
LEVEL_COMPLETE_PAGE = 2
GAME_OVER = 3
GAME_END = 4


def create_power_up(x, y, power_up_list, all_sprites_list):
    power_up = None
    power_up_type = random.randrange(3)
    if power_up_type == 0:
        # Image from Game Freezer: http://www.gamesfreezer.eu/2015_06_01_archive.html
        power_up = arcade.Sprite("images/extra_life.png", 0.4 * SPRITE_SCALING)
    if power_up_type == 1:
        # Image from Prek-8.com:
        power_up = arcade.Sprite("images/add_balls.png", 0.4 * SPRITE_SCALING)
    if power_up_type == 2:
        # image from ClipartFest: https://clipartfest.com/categories/view/
        power_up = arcade.Sprite("images/speed_up.png", 0.4 * SPRITE_SCALING)
    power_up.power_up_value = power_up_type
    power_up.change_y = -2
    power_up.center_x = x
    power_up.center_y = y
    power_up_list.append(power_up)
    all_sprites_list.append(power_up)
    

class Ball(arcade.Sprite):

    def __init__(self, all_sprites_list, power_up_list, wall_list,
                 brick_list, blue_brick_list, orange_brick_list, wall_brick_list,
                 player_sprite, application):
        # Image from Kenney.nl
        super().__init__("images/ball.png", 2 * SPRITE_SCALING)
        self.all_sprites_list = all_sprites_list
        self.wall_list = wall_list
        self.brick_list = brick_list
        self.blue_brick_list = blue_brick_list
        self.orange_brick_list = orange_brick_list
        self.wall_brick_list = wall_brick_list
        self.power_up_list = power_up_list
        self.player_sprite = player_sprite
        self.brick_sound = arcade.load_sound("sounds/brick_hit.wav")
        self.center_x = self.player_sprite.center_x
        self.center_y = 0.05 * SCREEN_HEIGHT + SCREEN_HEIGHT/15
        self.change_x = random.randrange(-3, 4)
        self.change_y = BALL_SPEED
        self.application = application

    def update(self):

        self.center_x += self.change_x

        orange_brick_hit_list = arcade.check_for_collision_with_list(self, self.orange_brick_list)
        for brick in orange_brick_hit_list:
            # Image from Kenney.nl
            blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
            blue_brick.center_x = brick.center_x
            blue_brick.center_y = brick.center_y
            self.all_sprites_list.append(blue_brick)
            self.brick_list.append(blue_brick)
            self.blue_brick_list.append(blue_brick)
            brick.kill()
            arcade.play_sound(self.brick_sound)
            self.application.score += 1
            if self.change_x > 0:
                self.right = brick.left
            else:
                self.left = brick.right
        if len(orange_brick_hit_list) > 0:
            self.change_x *= -1

        blue_brick_hit_list = arcade.check_for_collision_with_list(self, self.blue_brick_list)
        for brick in blue_brick_hit_list:
            brick.kill()
            arcade.play_sound(self.brick_sound)
            self.application.score += 1
            if random.randrange(10) == 0:
                create_power_up(self.center_x, self.center_y, self.power_up_list, self.all_sprites_list)
            if self.change_x > 0:
                self.right = brick.left
            else:
                self.left = brick.right

        if len(blue_brick_hit_list) > 0:
            self.change_x *= -1

        wall_hit_list = arcade.check_for_collision_with_list(self, self.wall_list)
        for wall in wall_hit_list:
            if self.change_x > 0:
                self.right = wall.left
            else:
                self.left = wall.right
        if len(wall_hit_list) > 0:
            self.change_x *= -1

        wall_brick_hit_list = arcade.check_for_collision_with_list(self, self.wall_brick_list)
        for wall_brick in wall_brick_hit_list:
            if self.change_x > 0:
                self.right = wall_brick.left
            else:
                self.left = wall_brick.right
        if len(wall_brick_hit_list) > 0:
            self.change_x *= -1

        self.center_y += self.change_y

        orange_brick_hit_list = arcade.check_for_collision_with_list(self, self.orange_brick_list)
        for brick in orange_brick_hit_list:
            # Image from Kenney.nl
            blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
            blue_brick.center_x = brick.center_x
            blue_brick.center_y = brick.center_y
            self.all_sprites_list.append(blue_brick)
            self.brick_list.append(blue_brick)
            self.blue_brick_list.append(blue_brick)
            brick.kill()
            arcade.play_sound(self.brick_sound)
            if self.change_y > 0:
                self.top = brick.bottom
            else:
                self.bottom = brick.top
            self.application.score += 1
        if len(orange_brick_hit_list) > 0:
            self.change_y *= -1

        blue_brick_hit_list = arcade.check_for_collision_with_list(self, self.blue_brick_list)
        for brick in blue_brick_hit_list:
            brick.kill()
            arcade.play_sound(self.brick_sound)
            if random.randrange(10) == 0:
                create_power_up(self.center_x, self.center_y, self.power_up_list, self.all_sprites_list)
            if self.change_y > 0:
                self.top = brick.bottom
            else:
                self.bottom = brick.top
            self.application.score += 1
        if len(blue_brick_hit_list) > 0:
            self.change_y *= -1

        wall_hit_list = arcade.check_for_collision_with_list(self, self.wall_list)
        for wall in wall_hit_list:
            if self.change_y > 0:
                self.top = wall.bottom
            else:
                self.bottom = wall.top
        if len(wall_hit_list) > 0:
            self.change_y *= -1

        wall_brick_hit_list = arcade.check_for_collision_with_list(self, self.wall_brick_list)
        for wall_brick in wall_brick_hit_list:
            if self.change_y > 0:
                self.top = wall_brick.bottom
            else:
                self.bottom = wall_brick.top
        if len(wall_brick_hit_list) > 0:
            self.change_y *= -1

    def get_score(self):
        arcade.draw_text("Score: " + str(self.score), 40, 10, arcade.color.WHITE, 24)


class MyApplication(arcade.Window):
    """ Main application class. """
    def __init__(self, width, height):

        super().__init__(width, height)
        # Sprite lists
        self.all_sprites_list = None

        # Set up the player.

        self.score = 0
        self.player_sprite = None
        self.player_sound = arcade.load_sound("sounds/player_hit.wav")
        self.wall_list = None
        self.brick_list = None
        self.wall_brick_list = None
        self.blue_brick_list = None
        self.orange_brick_list = None
        self.power_up_value = None
        self.power_up_list = None
        self.ball = None
        self.ball_list = None
        self.lives = 3
        self.next_level = False
        self.level = None
        self.current_state = INSTRUCTION_PAGE

    def setup(self):

        # Sprite lists
        self.score = 0
        self.all_sprites_list = arcade.SpriteList()
        self.wall_list = arcade.SpriteList()
        self.brick_list = arcade.SpriteList()
        self.blue_brick_list = arcade.SpriteList()
        self.orange_brick_list = arcade.SpriteList()
        self.wall_brick_list = arcade.SpriteList()
        self.power_up_list = arcade.SpriteList()
        self.ball_list = arcade.SpriteList()
        self.level = 0
        self.next_level = False
        # Image from Kenney.nl
        self.player_sprite = arcade.Sprite("images/player.png",
                                           0.25 * SPRITE_SCALING)
        self.player_sprite.center_x = 0.5 * SCREEN_WIDTH
        self.player_sprite.center_y = 0.075 * SCREEN_HEIGHT
        self.all_sprites_list.append(self.player_sprite)

        arcade.set_background_color(arcade.color.AMAZON)

        # -- Set up the boundary walls.
        for y in range(18, 600, 32):
            # Image from Kenney.nl
            wall = arcade.Sprite("images/wall.png", 0.5 * SPRITE_SCALING)
            wall.center_x = 18
            wall.center_y = y
            self.all_sprites_list.append(wall)
            self.wall_list.append(wall)

        for y in range(18, 600, 32):
            # Image from Kenney.nl
            wall = arcade.Sprite("images/wall.png", 0.5 * SPRITE_SCALING)
            wall.center_x = 582
            wall.center_y = y
            self.all_sprites_list.append(wall)
            self.wall_list.append(wall)

        for x in range(12, 600, 32):
            # Image from Kenney.nl
            wall = arcade.Sprite("images/wall.png", 0.5 * SPRITE_SCALING)
            wall.center_x = x
            wall.center_y = 596
            self.all_sprites_list.append(wall)
            self.wall_list.append(wall)

    def level_1(self):
        self.next_level = False
        ball = Ball(self.all_sprites_list, self.power_up_list,
                    self.wall_list, self.brick_list, self.blue_brick_list,
                    self.orange_brick_list, self.wall_brick_list, self.player_sprite, self)
        self.ball_list.append(ball)
        self.all_sprites_list.append(ball)

        for row in range(4):
            for column in range(-(8 - 2 * row), 0):
                # Image from Kenney.nl
                blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
                blue_brick.center_x = SCREEN_WIDTH / 10 * row + SCREEN_WIDTH / 2
                blue_brick.center_y = 30 * column + 590
                self.all_sprites_list.append(blue_brick)
                self.brick_list.append(blue_brick)
                self.blue_brick_list.append(blue_brick)

        for column in range(4):
            for row in range(-2 * column, 0):
                # Image from Kenney.nl
                blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
                blue_brick.center_x = 60 * column + 60
                blue_brick.center_y = 30 * row + 590
                self.all_sprites_list.append(blue_brick)
                self.brick_list.append(blue_brick)
                self.blue_brick_list.append(blue_brick)

    def level_2(self):
        self.next_level = False
        ball = Ball(self.all_sprites_list, self.power_up_list,
                    self.wall_list, self.brick_list, self.blue_brick_list,
                    self.orange_brick_list, self.wall_brick_list, self.player_sprite, self)
        self.ball_list.append(ball)
        self.all_sprites_list.append(ball)

        for row in range(4):
            for column in range(0, (4 - row)):
                # Image from Kenney.nl
                blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
                blue_brick.center_x = SCREEN_WIDTH / 10 * row + 70
                blue_brick.center_y = 30 * column + 300
                self.all_sprites_list.append(blue_brick)
                self.brick_list.append(blue_brick)
                self.blue_brick_list.append(blue_brick)

        for row in range(4):
            for column in range(-(4 - row), 0):
                # Image from Kenney.nl
                blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
                blue_brick.center_x = SCREEN_WIDTH / 10 * row + 70
                blue_brick.center_y = 30 * column + 590
                self.all_sprites_list.append(blue_brick)
                self.brick_list.append(blue_brick)
                self.blue_brick_list.append(blue_brick)

        for column in range(5):
            for row in range(0, column):
                # Image from Kenney.nl
                blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
                blue_brick.center_x = 60 * column + 290
                blue_brick.center_y = 30 * row + 300
                self.all_sprites_list.append(blue_brick)
                self.brick_list.append(blue_brick)
                self.blue_brick_list.append(blue_brick)

        for column in range(5):
            for row in range(-column, 0):
                # Image from Kenney.nl
                blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
                blue_brick.center_x = 60 * column + 290
                blue_brick.center_y = 30 * row + 590
                self.all_sprites_list.append(blue_brick)
                self.brick_list.append(blue_brick)
                self.blue_brick_list.append(blue_brick)

        for column in range(5):
            # Image from Kenney.nl
            wall_brick = arcade.Sprite("images/black_brick.png", 0.6 * SPRITE_SCALING)
            wall_brick.center_x = 60 * column + 180
            wall_brick.center_y = 430
            self.all_sprites_list.append(wall_brick)
            self.wall_brick_list.append(wall_brick)

    def level_3(self):
        self.next_level = False
        ball = Ball(self.all_sprites_list, self.power_up_list,
                    self.wall_list, self.brick_list, self.blue_brick_list,
                    self.orange_brick_list, self.wall_brick_list, self.player_sprite, self)
        self.ball_list.append(ball)
        self.all_sprites_list.append(ball)
        # Image from Kenney.nl
        orange_brick = arcade.Sprite("images/orange_brick.png", 0.6 * SPRITE_SCALING)
        orange_brick.center_x = SCREEN_WIDTH / 2
        orange_brick.center_y = 3 * SCREEN_HEIGHT / 4
        self.all_sprites_list.append(orange_brick)
        self.brick_list.append(orange_brick)
        self.orange_brick_list.append(orange_brick)

        for row in range(3):
            # Image from Kenney.nl
            blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
            blue_brick.center_x = 240
            blue_brick.center_y = 30 * row + 420
            self.all_sprites_list.append(blue_brick)
            self.brick_list.append(blue_brick)
            self.blue_brick_list.append(blue_brick)

        for row in range(3):
            # Image from Kenney.nl
            blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
            blue_brick.center_x = 360
            blue_brick.center_y = 30 * row + 420
            self.all_sprites_list.append(blue_brick)
            self.brick_list.append(blue_brick)
            self.blue_brick_list.append(blue_brick)
        # Image from Kenney.nl
        blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
        blue_brick.center_x = 300
        blue_brick.center_y = 420
        self.all_sprites_list.append(blue_brick)
        self.brick_list.append(blue_brick)
        self.blue_brick_list.append(blue_brick)
        # Image from Kenney.nl
        blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
        blue_brick.center_x = 300
        blue_brick.center_y = 480
        self.all_sprites_list.append(blue_brick)
        self.brick_list.append(blue_brick)
        self.blue_brick_list.append(blue_brick)

        for row in range(5):
            # Image from Kenney.nl
            orange_brick = arcade.Sprite("images/orange_brick.png", 0.6 * SPRITE_SCALING)
            orange_brick.center_x = 180
            orange_brick.center_y = 30 * row + 390
            self.all_sprites_list.append(orange_brick)
            self.brick_list.append(orange_brick)
            self.orange_brick_list.append(orange_brick)

        for row in range(5):
            # Image from Kenney.nl
            orange_brick = arcade.Sprite("images/orange_brick.png", 0.6 * SPRITE_SCALING)
            orange_brick.center_x = 420
            orange_brick.center_y = 30 * row + 390
            self.all_sprites_list.append(orange_brick)
            self.brick_list.append(orange_brick)
            self.orange_brick_list.append(orange_brick)

        for column in range(3):
            # Image from Kenney.nl
            orange_brick = arcade.Sprite("images/orange_brick.png", 0.6 * SPRITE_SCALING)
            orange_brick.center_x = 60 * column + 240
            orange_brick.center_y = 390
            self.all_sprites_list.append(orange_brick)
            self.brick_list.append(orange_brick)
            self.orange_brick_list.append(orange_brick)

        for column in range(3):
            # Image from Kenney.nl
            orange_brick = arcade.Sprite("images/orange_brick.png", 0.6 * SPRITE_SCALING)
            orange_brick.center_x = 60 * column + 240
            orange_brick.center_y = 510
            self.all_sprites_list.append(orange_brick)
            self.brick_list.append(orange_brick)
            self.orange_brick_list.append(orange_brick)

        for row in range(7):
            # Image from Kenney.nl
            blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
            blue_brick.center_x = 120
            blue_brick.center_y = 30 * row + 360
            self.all_sprites_list.append(blue_brick)
            self.brick_list.append(blue_brick)
            self.blue_brick_list.append(blue_brick)

        for row in range(7):
            # Image from Kenney.nl
            blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
            blue_brick.center_x = 480
            blue_brick.center_y = 30 * row + 360
            self.all_sprites_list.append(blue_brick)
            self.brick_list.append(blue_brick)
            self.blue_brick_list.append(blue_brick)

        for column in range(5):
            # Image from Kenney.nl
            blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
            blue_brick.center_x = 60 * column + 180
            blue_brick.center_y = 360
            self.all_sprites_list.append(blue_brick)
            self.brick_list.append(blue_brick)
            self.blue_brick_list.append(blue_brick)

        for column in range(5):
            # Image from Kenney.nl
            blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
            blue_brick.center_x = 60 * column + 180
            blue_brick.center_y = 540
            self.all_sprites_list.append(blue_brick)
            self.brick_list.append(blue_brick)
            self.blue_brick_list.append(blue_brick)

    def level_4(self):
        self.next_level = False
        ball = Ball(self.all_sprites_list, self.power_up_list,
                    self.wall_list, self.brick_list, self.blue_brick_list,
                    self.orange_brick_list, self.wall_brick_list, self.player_sprite, self)
        self.ball_list.append(ball)
        self.all_sprites_list.append(ball)

        for row in range(5):
            for column in range(8):
                # Image from Kenney.nl
                blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
                blue_brick.center_x = SCREEN_WIDTH / 10 * column + 90
                blue_brick.center_y = 30 * row + 430
                self.all_sprites_list.append(blue_brick)
                self.brick_list.append(blue_brick)
                self.blue_brick_list.append(blue_brick)

        for column in range(4):
            # Image from Kenney.nl
            wall_brick = arcade.Sprite("images/black_brick.png", 0.6 * SPRITE_SCALING)
            wall_brick.center_x = 120 * column + 110
            wall_brick.center_y = 220
            wall_brick.change_x = -2
            self.all_sprites_list.append(wall_brick)
            self.wall_brick_list.append(wall_brick)

        for column in range(4):
            # Image from Kenney.nl
            wall_brick = arcade.Sprite("images/black_brick.png", 0.6 * SPRITE_SCALING)
            wall_brick.center_x = 120 * column + 130
            wall_brick.center_y = 280
            wall_brick.change_x = 2
            self.all_sprites_list.append(wall_brick)
            self.wall_brick_list.append(wall_brick)

    def level_5(self):
        self.next_level = False
        ball = Ball(self.all_sprites_list,  self.power_up_list,
                    self.wall_list, self.brick_list, self.blue_brick_list,
                    self.orange_brick_list, self.wall_brick_list, self.player_sprite, self)
        self.ball_list.append(ball)
        self.all_sprites_list.append(ball)
        # Image from Kenney.nl
        orange_brick = arcade.Sprite("images/orange_brick.png", 0.6 * SPRITE_SCALING)
        orange_brick.center_x = 300
        orange_brick.center_y = 350
        self.all_sprites_list.append(orange_brick)
        self.brick_list.append(orange_brick)
        self.orange_brick_list.append(orange_brick)

        for row in range(3):
            # Image from Kenney.nl
            blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
            blue_brick.center_x = 240
            blue_brick.center_y = 30 * row + 320
            self.all_sprites_list.append(blue_brick)
            self.brick_list.append(blue_brick)
            self.blue_brick_list.append(blue_brick)

        for row in range(3):
            # Image from Kenney.nl
            blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
            blue_brick.center_x = 360
            blue_brick.center_y = 30 * row + 320
            self.all_sprites_list.append(blue_brick)
            self.brick_list.append(blue_brick)
            self.blue_brick_list.append(blue_brick)
        # Image from Kenney.nl
        blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
        blue_brick.center_x = 300
        blue_brick.center_y = 320
        self.all_sprites_list.append(blue_brick)
        self.brick_list.append(blue_brick)
        self.blue_brick_list.append(blue_brick)
        # Image from Kenney.nl
        blue_brick = arcade.Sprite("images/blue_brick.png", 0.6 * SPRITE_SCALING)
        blue_brick.center_x = 300
        blue_brick.center_y = 380
        self.all_sprites_list.append(blue_brick)
        self.brick_list.append(blue_brick)
        self.blue_brick_list.append(blue_brick)

        for row in range(5):
            # Image from Kenney.nl
            orange_brick = arcade.Sprite("images/orange_brick.png", 0.6 * SPRITE_SCALING)
            orange_brick.center_x = 180
            orange_brick.center_y = 30 * row + 290
            self.all_sprites_list.append(orange_brick)
            self.brick_list.append(orange_brick)
            self.orange_brick_list.append(orange_brick)

        for row in range(5):
            # Image from Kenney.nl
            orange_brick = arcade.Sprite("images/orange_brick.png", 0.6 * SPRITE_SCALING)
            orange_brick.center_x = 420
            orange_brick.center_y = 30 * row + 290
            self.all_sprites_list.append(orange_brick)
            self.brick_list.append(orange_brick)
            self.orange_brick_list.append(orange_brick)

        for column in range(3):
            # Image from Kenney.nl
            orange_brick = arcade.Sprite("images/orange_brick.png", 0.6 * SPRITE_SCALING)
            orange_brick.center_x = 60 * column + 240
            orange_brick.center_y = 290
            self.all_sprites_list.append(orange_brick)
            self.brick_list.append(orange_brick)
            self.orange_brick_list.append(orange_brick)

        for column in range(3):
            # Image from Kenney.nl
            orange_brick = arcade.Sprite("images/orange_brick.png", 0.6 * SPRITE_SCALING)
            orange_brick.center_x = 60 * column + 240
            orange_brick.center_y = 410
            self.all_sprites_list.append(orange_brick)
            self.brick_list.append(orange_brick)
            self.orange_brick_list.append(orange_brick)

        for column in range(4):
            # Image from Kenney.nl
            wall_brick = arcade.Sprite("images/black_brick.png", 0.6 * SPRITE_SCALING)
            wall_brick.center_x = 140 * column + 90
            wall_brick.center_y = 220
            wall_brick.change_x = 2
            wall_brick.change_y = 0
            self.all_sprites_list.append(wall_brick)
            self.wall_brick_list.append(wall_brick)

        for column in range(4):
            # Image from Kenney.nl
            wall_brick = arcade.Sprite("images/black_brick.png", 0.6 * SPRITE_SCALING)
            wall_brick.center_x = 140 * column + 90
            wall_brick.center_y = 480
            wall_brick.change_x = -2
            wall_brick.change_y = 0
            self.all_sprites_list.append(wall_brick)
            self.wall_brick_list.append(wall_brick)
        # Image from Kenney.nl
        wall_brick = arcade.Sprite("images/black_brick.png", 0.6 * SPRITE_SCALING)
        wall_brick.center_x = 90
        wall_brick.center_y = 350
        wall_brick.change_x = 0
        wall_brick.change_y = -2
        self.all_sprites_list.append(wall_brick)
        self.wall_brick_list.append(wall_brick)
        # Image from Kenney.nl
        wall_brick = arcade.Sprite("images/black_brick.png", 0.6 * SPRITE_SCALING)
        wall_brick.center_x = 510
        wall_brick.center_y = 350
        wall_brick.change_x = 0
        wall_brick.change_y = 2
        self.all_sprites_list.append(wall_brick)
        self.wall_brick_list.append(wall_brick)

    def add_life(self):
        self.lives += 1

    def add_balls(self):
        for i in range(3):
            ball = Ball(self.all_sprites_list, self.power_up_list,
                        self.wall_list, self.brick_list, self.blue_brick_list,
                        self.orange_brick_list, self.wall_brick_list, self.player_sprite, self)
            self.ball_list.append(ball)
            self.all_sprites_list.append(ball)

    def speed_up(self):
        for ball in self.ball_list:
            ball.change_y += 1

    def draw_instructions_page(self):
        arcade.draw_text("Welcome to Brick Breaker!", 50, 500, arcade.color.WHITE, 32)
        arcade.draw_text("Press SPACE to Continue", 55, 350, arcade.color.WHITE, 32)
        self.lives = 3

    def draw_game(self):
        """
        Draw all the sprites, along with the score.
        """
        # Draw all the sprites.
        self.all_sprites_list.draw()
        # Draw the score in the bottom left.
        Ball.get_score(self)
        arcade.draw_text("Level " + str(self.level), 455, 10, arcade.color.WHITE, 24)
        arcade.draw_text(str(self.lives) + " Lives left", 230, 10, arcade.color.WHITE, 24)

    def draw_level_complete(self):
        # Draw at the end of every level
        arcade.draw_text("Level  " + str(self.level), 150, 400, arcade.color.WHITE, 64)
        arcade.draw_text("Complete!", 100, 290, arcade.color.WHITE, 64)
        arcade.draw_text("Press Space to Continue", 65, 210, arcade.color.WHITE, 32)

    def draw_game_over(self):
        arcade.draw_text("Game Over", 80, 330, arcade.color.WHITE, 64)
        arcade.draw_text("Press R to Restart", 80, 220, arcade.color.WHITE, 40)
        arcade.draw_text("Press Q to Quit", 120, 140, arcade.color.WHITE, 40)

    def draw_game_end(self):
        arcade.draw_text("Congratulations!", 50, 520, arcade.color.WHITE, 52)
        arcade.draw_text("You Won!", 90, 420, arcade.color.WHITE, 72)
        arcade.draw_text("Your Final Score was ", 40, 300, arcade.color.WHITE, 40)
        arcade.draw_text(str(self.score), 280, 240, arcade.color.WHITE, 40)
        arcade.draw_text("Press R to Restart", 80, 140, arcade.color.WHITE, 40)
        arcade.draw_text("Press Q to Quit", 120, 70, arcade.color.WHITE, 40)

    def on_draw(self):

        arcade.start_render()
        # Draw all the sprites.
        self.all_sprites_list.draw()
        if self.current_state == INSTRUCTION_PAGE:
            self.draw_instructions_page()

        elif self.current_state == GAME_RUNNING:
            self.draw_game()

        elif self.current_state == LEVEL_COMPLETE_PAGE:
            self.draw_level_complete()

        elif self.current_state == GAME_END:
            self.draw_game_end()

        else:
            self.draw_game_over()
        Ball.get_score(self)

        if len(self.brick_list) == 0 and self.next_level:
            if self.level < 5:
                self.level += 1
                if self.level == 1:
                    self.level_1()
                elif self.level == 2:
                    self.level_2()
                elif self.level == 3:
                    self.level_3()
                elif self.level == 4:
                    self.level_4()
                elif self.level == 5:
                    self.level_5()

            else:
                self.draw_game_end()

    def on_key_press(self, key, modifiers):
        """Called whenever a key is pressed. """

        if key == arcade.key.Q:
            MyApplication.close(self)
        elif key == arcade.key.SPACE and len(self.brick_list) == 0:
            if not self.next_level:
                if self.level < 6:
                    self.current_state = GAME_RUNNING
                    self.draw_game()
                    self.next_level = True
                else:
                    self.current_state = GAME_END
        elif key == arcade.key.R:
            self.current_state = INSTRUCTION_PAGE
            self.draw_instructions_page()
            MyApplication.setup(self)
        elif key == arcade.key.LEFT or key == arcade.key.A:
            self.player_sprite.change_x = -MOVEMENT_SPEED
        elif key == arcade.key.RIGHT or key == arcade.key.D:
            self.player_sprite.change_x = MOVEMENT_SPEED

    def on_key_release(self, key, modifiers):
        if key == arcade.key.LEFT or key == arcade.key.A \
                or key == arcade.key.RIGHT or key == arcade.key.D:
            self.player_sprite.change_x = 0

    def animate(self, delta_time):

        for ball in self.ball_list:
            if ball.center_y < 0:
                ball.kill()
                if self.lives > 0 and len(self.ball_list) == 0:
                    self.lives -= 1
                    ball = Ball(self.all_sprites_list, self.power_up_list,
                                self.wall_list, self.brick_list, self.blue_brick_list,
                                self.orange_brick_list, self.wall_brick_list, self.player_sprite, self)
                    self.ball_list.append(ball)
                    self.all_sprites_list.append(ball)
                elif self.lives <= 0:
                    for power_up in self.power_up_list:
                        power_up.kill()
                    self.draw_game_over()
                    self.current_state = GAME_OVER

        for power_up in self.power_up_list:
            if power_up.top <= 0:
                power_up.kill()

        if len(self.brick_list) == 0 and self.level > 0:
            if not self.next_level:
                for wall_brick in self.wall_brick_list:
                    wall_brick.kill()
                for ball in self.ball_list:
                    ball.kill()
                for power_up in self.power_up_list:
                    power_up.kill()
                self.current_state = LEVEL_COMPLETE_PAGE
                self.draw_level_complete()

        wall_player_hit_list = arcade.check_for_collision_with_list(self.player_sprite, self.wall_list)
        for wall in wall_player_hit_list:
            if self.player_sprite.right >= wall.left and self.player_sprite.center_x > SCREEN_WIDTH / 2:
                self.player_sprite.right = wall.left

            elif self.player_sprite.left <= wall.right:
                self.player_sprite.left = wall.right

        for wall_brick in self.wall_brick_list:
            if self.current_state != GAME_RUNNING:
                wall_brick.change_x = 0
                wall_brick.change_y = 0
            elif self.level == 4:
                if wall_brick.left < 70:
                    for brick in self.wall_brick_list:
                        brick.change_x *= -1
                elif wall_brick.right > 550:
                    for brick in self.wall_brick_list:
                        brick.change_x *= -1
            elif self.level == 5:
                if wall_brick.center_x < 90 and wall_brick.change_x < 0:
                    wall_brick.change_x = 0
                    wall_brick.change_y = -2
                elif wall_brick.center_y < 220 and wall_brick.change_y < 0:
                    wall_brick.change_x = 2
                    wall_brick.change_y = 0
                elif wall_brick.center_x > 510 and wall_brick.change_x > 0:
                    wall_brick.change_x = 0
                    wall_brick.change_y = 2
                elif wall_brick.center_y > 480 and wall_brick.change_y > 0:
                    wall_brick.change_x = -2
                    wall_brick.change_y = 0

        ball_hit_list = arcade.check_for_collision_with_list(self.player_sprite, self.ball_list)
        for ball in ball_hit_list:
            arcade.play_sound(self.player_sound)
            if ball.change_y < 0:
                ball.change_y *= -1
            if ball.center_x < self.player_sprite.center_x:
                ball.change_x = random.randrange(-5, -2)
            elif ball.center_x > self.player_sprite.center_x:
                ball.change_x = random.randrange(3, 6)
            else:
                ball.change_x = 0

        power_up_hit_list = arcade.check_for_collision_with_list(self.player_sprite, self.power_up_list)
        for power_up in power_up_hit_list:
            power_up.kill()
            if power_up.power_up_value == 0:
                self.add_life()

            elif power_up.power_up_value == 1:
                self.add_balls()

            elif power_up.power_up_value == 2:
                self.speed_up()

        self.all_sprites_list.update()
        self.ball_list.update()
        self.player_sprite.update()
        self.wall_brick_list.update()

window = MyApplication(SCREEN_WIDTH, SCREEN_HEIGHT)
window.setup()

arcade.run()