forked from artofproblemsolving/pythonbook
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgui.html
More file actions
executable file
·1471 lines (1388 loc) · 108 KB
/
gui.html
File metadata and controls
executable file
·1471 lines (1388 loc) · 108 KB
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
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>16. PyGame — How to Think Like a Computer Scientist: Learning with Python 3 (AoPS Edition)</title>
<link rel="stylesheet" href="_static/style.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/codemirrorEdited.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '1.0',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script type="text/javascript" src="_static/pywindowCodemirrorC.js"></script>
<script type="text/javascript" src="_static/skulpt.min.js"></script>
<script type="text/javascript" src="_static/skulpt-stdlib.js"></script>
<script type="text/javascript" src="_static/aopsmods.js"></script>
<link rel="copyright" title="Copyright" href="copyright.html" />
<link rel="top" title="How to Think Like a Computer Scientist: Learning with Python 3 (AoPS Edition)" href="index.html" />
<link rel="next" title="17. Event-Driven Programming" href="events.html" />
<link rel="prev" title="15. Inheritance" href="inheritance.html" />
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="events.html" title="17. Event-Driven Programming"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="inheritance.html" title="15. Inheritance"
accesskey="P">previous</a> |</li>
<li><a href="index.html">How to Think Like a Computer Scientist: Learning with Python 3 (AoPS Edition)</a> »</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="body">
<div class="line-block">
<div class="line"><br /></div>
</div>
<div class="section" id="pygame">
<span id="index-0"></span><h1>16. PyGame<a class="headerlink" href="#pygame" title="Permalink to this headline">¶</a></h1>
<p>PyGame is a package that is not part of the standard Python distribution, so if you do not
already have it installed (i.e. <tt class="docutils literal"><span class="pre">import</span> <span class="pre">pygame</span></tt> fails), download and install a suitable version from <a class="reference external" href="http://pygame.org/download.shtml">http://pygame.org/download.shtml</a>.
These notes are based on PyGame 1.9.1, the most recent version at the time of writing.</p>
<p>PyGame comes with a substantial set of tutorials, examples, and help, so there is ample
opportunity to stretch yourself on the code. You may need to look around a bit to find
these resources, though: if you’ve installed PyGame on a Windows machine, for example,
they’ll end up in a folder like C:\Python31\Lib\site-packages\pygame\ where you
will find directories for <em>docs</em> and <em>examples</em>.</p>
<div class="section" id="the-game-loop">
<h2>16.1. The game loop<a class="headerlink" href="#the-game-loop" title="Permalink to this headline">¶</a></h2>
<p>The structure of the games we’ll consider always follows this fixed pattern:</p>
<blockquote>
<div><img alt="_images/pygame_structure.png" src="_images/pygame_structure.png" />
</div></blockquote>
<p>In every game, in the <em>setup</em> section we’ll create a window, load and prepare some content, and then
enter the <strong>game loop</strong>. The game loop continuously does four main things:</p>
<ul class="simple">
<li>it <strong>polls</strong> for events — i.e. asks the system whether
events have occurred — and responds appropriately,</li>
<li>it updates whatever internal data structures or objects need changing,</li>
<li>it draws the current state of the game into a (non-visible) surface,</li>
<li>it puts the just-drawn surface on display.</li>
</ul>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 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</pre></div></td><td class="code"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">pygame</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="sd">""" Set up the game and run the main game loop """</span>
<span class="n">pygame</span><span class="o">.</span><span class="n">init</span><span class="p">()</span> <span class="c"># Prepare the pygame module for use</span>
<span class="n">surface_sz</span> <span class="o">=</span> <span class="mi">480</span> <span class="c"># Desired physical surface size, in pixels.</span>
<span class="c"># Create surface of (width, height), and its window.</span>
<span class="n">main_surface</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">set_mode</span><span class="p">((</span><span class="n">surface_sz</span><span class="p">,</span> <span class="n">surface_sz</span><span class="p">))</span>
<span class="c"># Set up some data to describe a small rectangle and its color</span>
<span class="n">small_rect</span> <span class="o">=</span> <span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="mi">150</span><span class="p">,</span> <span class="mi">90</span><span class="p">)</span>
<span class="n">some_color</span> <span class="o">=</span> <span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="c"># A color is a mix of (Red, Green, Blue)</span>
<span class="k">while</span> <span class="k">True</span><span class="p">:</span>
<span class="n">ev</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">poll</span><span class="p">()</span> <span class="c"># Look for any event</span>
<span class="k">if</span> <span class="n">ev</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">pygame</span><span class="o">.</span><span class="n">QUIT</span><span class="p">:</span> <span class="c"># Window close button clicked?</span>
<span class="k">break</span> <span class="c"># ... leave game loop</span>
<span class="c"># Update your game objects and data structures here...</span>
<span class="c"># We draw everything from scratch on each frame.</span>
<span class="c"># So first fill everything with the background color</span>
<span class="n">main_surface</span><span class="o">.</span><span class="n">fill</span><span class="p">((</span><span class="mi">0</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="mi">255</span><span class="p">))</span>
<span class="c"># Overpaint a smaller rectangle on the main surface</span>
<span class="n">main_surface</span><span class="o">.</span><span class="n">fill</span><span class="p">(</span><span class="n">some_color</span><span class="p">,</span> <span class="n">small_rect</span><span class="p">)</span>
<span class="c"># Now the surface is ready, tell pygame to display it!</span>
<span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">flip</span><span class="p">()</span>
<span class="n">pygame</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span> <span class="c"># Once we leave the loop, close the window.</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>This program pops up a window which stays there until we close it:</p>
<blockquote>
<div><img alt="_images/pygame_screenshot01.png" src="_images/pygame_screenshot01.png" />
</div></blockquote>
<p>PyGame does all its drawing onto rectangular <em>surfaces</em>. After initializing PyGame
at line 5, we create a window holding our main surface. The main loop of the game
extends from line 15 to 30, with the following key bits of logic:</p>
<ul class="simple">
<li>First (line 16) we poll to fetch the next event that might be ready for us. This step will
always be followed by some conditional statements that will determine whether
any event that we’re interested in has happened. Polling for the event consumes
it, as far as PyGame is concerned, so we only get one chance to fetch and use
each event. On line 17 we test whether the type of the event is the
predefined constant called pygame.QUIT. This is the event that we’ll see
when the user clicks the close button on the PyGame window. In response to
this event, we leave the loop.</li>
<li>Once we’ve left the loop, the code at line 32 closes window, and we’ll return
from function <tt class="docutils literal"><span class="pre">main</span></tt>. Your program could go on to do other things, or reinitialize
pygame and create another window, but it will usually just end too.</li>
<li>There are different kinds of events — key presses, mouse motion, mouse
clicks, joystick movement, and so on. It is usual that we test and handle all these cases
with new code squeezed in before line 19. The general idea is “handle events
first, then worry about the other stuff”.</li>
<li>At line 20 we’d update objects or data — for example, if we wanted to vary the
color, position, or size of the rectangle we’re about to draw, we’d re-assign
<tt class="docutils literal"><span class="pre">some_color</span></tt>, and <tt class="docutils literal"><span class="pre">small_rect</span></tt> here.</li>
<li>A modern way to write games (now that we have fast computers and fast graphics
cards) is to redraw everything from scratch on every iteration of the game loop. So
the first thing we do at line 24 is fill the entire surface with a background
color. The <tt class="docutils literal"><span class="pre">fill</span></tt> method of a surface takes two arguments — the color to
use for filling, and the rectangle to be filled. But the second argument is
optional, and if it is left out the entire surface is filled.</li>
<li>In line 27 we fill a second rectangle, this time using <tt class="docutils literal"><span class="pre">some_color</span></tt>.
The placement and size of the rectangle are given by the tuple <tt class="docutils literal"><span class="pre">small_rect</span></tt>,
a 4-element tuple <tt class="docutils literal"><span class="pre">(x,</span> <span class="pre">y,</span> <span class="pre">width,</span> <span class="pre">height)</span></tt>.</li>
<li>It is important to understand that the origin of the PyGame’s surface is at the top left
corner (unlike the turtle module that puts its origin in the middle of the screen).
So, if you wanted the rectangle closer to the top of the window, you need to make its
y coordinate smaller.</li>
<li>If your graphics display hardware tries to read from memory at the
same time as the program is writing to that memory, they will interfere with each other,
causing video noise and flicker. To get around this, PyGame keeps two
buffers in the main surface — the <em>back buffer</em> that the program draws to,
while the <em>front buffer</em> is being shown to the user. Each time the program has fully
prepared its back buffer, it flips the back/front role of the two buffers.
So the drawing on lines 24 and 27 does does not
change what is seen on the screen until
we <tt class="docutils literal"><span class="pre">flip</span></tt> the buffers, on line 30.</li>
</ul>
</div>
<div class="section" id="displaying-images-and-text">
<h2>16.2. Displaying images and text<a class="headerlink" href="#displaying-images-and-text" title="Permalink to this headline">¶</a></h2>
<p>To draw an image on the main surface, we load the
image, say a beach ball, into its own new surface.
The main surface has a <tt class="docutils literal"><span class="pre">blit</span></tt> method that copies
pixels from the beach ball surface into its
own surface. When we call <tt class="docutils literal"><span class="pre">blit</span></tt>, we can specify where the beach ball should be placed
on the main surface. The term <strong>blit</strong> is widely used in computer graphics, and means
<em>to make a fast copy of pixels from one area of memory to another</em>.</p>
<p>So in the setup section, before we enter the game
loop, we’d load the image, like this:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">ball</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">image</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="s">"ball.png"</span><span class="p">)</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>and after line 28 in the program above, we’d add this
code to display our image at position (100,120):</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">main_surface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">ball</span><span class="p">,</span> <span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">120</span><span class="p">))</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>To display text, we need do do three things. Before we enter the game loop, we
instantiate a <tt class="docutils literal"><span class="pre">font</span></tt> object:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="highlight"><pre><span class="c"># Instantiate 16 point Courier font to draw text.</span>
<span class="n">my_font</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">font</span><span class="o">.</span><span class="n">SysFont</span><span class="p">(</span><span class="s">"Courier"</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>and after line 28, again, we use the font’s
<tt class="docutils literal"><span class="pre">render</span></tt> method to create a new surface
containing the pixels of the drawn text,
and then, as in the case for images, we blit
our new surface onto the main surface. Notice that <tt class="docutils literal"><span class="pre">render</span></tt>
takes two extra parameters — the second tells
it whether to carefully smooth edges of the text
while drawing (this process is called <em>anti-aliasing</em>),
and the second is the color that
we want the text text be. Here we’ve used <tt class="docutils literal"><span class="pre">(0,0,0)</span></tt>
which is black:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">the_text</span> <span class="o">=</span> <span class="n">my_font</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="s">"Hello, world!"</span><span class="p">,</span> <span class="k">True</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">))</span>
<span class="n">main_surface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">the_text</span><span class="p">,</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">))</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>We’ll demonstrate these two new features by counting
the frames — the iterations of the game loop — and keeping
some timing information. On each frame, we’ll
display the frame count, and the frame rate. We will only update
the frame rate after every 500 frames, when we’ll
look at the timing interval and can do the calculations.</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 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</pre></div></td><td class="code"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">pygame</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">pygame</span><span class="o">.</span><span class="n">init</span><span class="p">()</span> <span class="c"># Prepare the PyGame module for use</span>
<span class="n">main_surface</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">set_mode</span><span class="p">((</span><span class="mi">480</span><span class="p">,</span> <span class="mi">240</span><span class="p">))</span>
<span class="c"># Load an image to draw. Substitute your own.</span>
<span class="c"># PyGame handles gif, jpg, png, etc. image types.</span>
<span class="n">ball</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">image</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="s">"ball.png"</span><span class="p">)</span>
<span class="c"># Create a font for rendering text</span>
<span class="n">my_font</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">font</span><span class="o">.</span><span class="n">SysFont</span><span class="p">(</span><span class="s">"Courier"</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
<span class="n">frame_count</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">frame_rate</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">t0</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">clock</span><span class="p">()</span>
<span class="k">while</span> <span class="k">True</span><span class="p">:</span>
<span class="c"># Look for an event from keyboard, mouse, joystick, etc.</span>
<span class="n">ev</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">poll</span><span class="p">()</span>
<span class="k">if</span> <span class="n">ev</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">pygame</span><span class="o">.</span><span class="n">QUIT</span><span class="p">:</span> <span class="c"># Window close button clicked?</span>
<span class="k">break</span> <span class="c"># Leave game loop</span>
<span class="c"># Do other bits of logic for the game here</span>
<span class="n">frame_count</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">frame_count</span> <span class="o">%</span> <span class="mi">500</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">t1</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">clock</span><span class="p">()</span>
<span class="n">frame_rate</span> <span class="o">=</span> <span class="mi">500</span> <span class="o">/</span> <span class="p">(</span><span class="n">t1</span><span class="o">-</span><span class="n">t0</span><span class="p">)</span>
<span class="n">t0</span> <span class="o">=</span> <span class="n">t1</span>
<span class="c"># Completely redraw the surface, starting with background</span>
<span class="n">main_surface</span><span class="o">.</span><span class="n">fill</span><span class="p">((</span><span class="mi">0</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="mi">255</span><span class="p">))</span>
<span class="c"># Put a red rectangle somewhere on the surface</span>
<span class="n">main_surface</span><span class="o">.</span><span class="n">fill</span><span class="p">((</span><span class="mi">255</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">150</span><span class="p">,</span> <span class="mi">90</span><span class="p">))</span>
<span class="c"># Copy our image to the surface, at this (x,y) posn</span>
<span class="n">main_surface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">ball</span><span class="p">,</span> <span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">120</span><span class="p">))</span>
<span class="c"># Make a new surface with an image of the text</span>
<span class="n">the_text</span> <span class="o">=</span> <span class="n">my_font</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="s">"Frame = {0}, rate = {1:.2f} fps"</span>
<span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">frame_count</span><span class="p">,</span> <span class="n">frame_rate</span><span class="p">),</span> <span class="k">True</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">))</span>
<span class="c"># Copy the text surface to the main surface</span>
<span class="n">main_surface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">the_text</span><span class="p">,</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">))</span>
<span class="c"># Now that everything is drawn, put it on display!</span>
<span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">flip</span><span class="p">()</span>
<span class="n">pygame</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>The frame rate is close to ridiculous —
a lot faster than one’s eye can process frames. (Commercial
video games usually plan their action for 60
frames per second (fps).) Of course, our rate will drop
once we start doing something a little more strenuous inside our game loop.</p>
<blockquote>
<div><img alt="_images/pygame_screenshot02.png" src="_images/pygame_screenshot02.png" />
</div></blockquote>
</div>
<div class="section" id="drawing-a-board-for-the-n-queens-puzzle">
<h2>16.3. Drawing a board for the N queens puzzle<a class="headerlink" href="#drawing-a-board-for-the-n-queens-puzzle" title="Permalink to this headline">¶</a></h2>
<p>We previously solved our N queens puzzle.
For the 8x8 board, one of the solutions was the list <tt class="docutils literal"><span class="pre">[6,4,2,0,5,7,1,3]</span></tt>.
Let’s use that solution as testdata, and now use PyGame to draw that
chessboard with its queens.</p>
<p>We’ll create a new module for the drawing code, called <tt class="docutils literal"><span class="pre">draw_queens.py</span></tt>. When
we have our test case(s) working, we can go back to our solver, import this new module,
and add a call to our new function to draw a board each time a solution is discovered.</p>
<p>We begin with a background of black and red squares
for the board. Perhaps we could create an image that we could
load and draw, but that approach would need
different background images for different size boards.
Just drawing our own red and black rectangles of
the appropriate size sounds like much more fun!</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13</pre></div></td><td class="code"><div class="highlight"><pre><span class="k">def</span> <span class="nf">draw_board</span><span class="p">(</span><span class="n">the_board</span><span class="p">):</span>
<span class="sd">""" Draw a chess board with queens, from the_board. """</span>
<span class="n">pygame</span><span class="o">.</span><span class="n">init</span><span class="p">()</span>
<span class="n">colors</span> <span class="o">=</span> <span class="p">[(</span><span class="mi">255</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">)]</span> <span class="c"># Set up colors [red, black]</span>
<span class="n">n</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">the_board</span><span class="p">)</span> <span class="c"># This is an NxN chess board.</span>
<span class="n">surface_sz</span> <span class="o">=</span> <span class="mi">480</span> <span class="c"># Proposed physical surface size.</span>
<span class="n">sq_sz</span> <span class="o">=</span> <span class="n">surface_sz</span> <span class="o">//</span> <span class="n">n</span> <span class="c"># sq_sz is length of a square.</span>
<span class="n">surface_sz</span> <span class="o">=</span> <span class="n">n</span> <span class="o">*</span> <span class="n">sq_sz</span> <span class="c"># Adjust to exactly fit n squares.</span>
<span class="c"># Create the surface of (width, height), and its window.</span>
<span class="n">surface</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">set_mode</span><span class="p">((</span><span class="n">surface_sz</span><span class="p">,</span> <span class="n">surface_sz</span><span class="p">))</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>Here we precompute <tt class="docutils literal"><span class="pre">sq_sz</span></tt>, the integer
size that each square will be, so that we can fit the squares
nicely into the available window. So if
we’d like the board to be 480x480, and we’re drawing an 8x8
chessboard, then each square will need
to have a size of 60 units. But we
notice that a 7x7 board cannot
fit nicely into 480 — we’re going to
get some ugly border that our squares don’t fill exactly.
So we recompute the surface size to exactly
fit our squares before we create the window.</p>
<p>Now let’s draw the squares, in the game loop.
We’ll need a nested loop: the outer loop will
run over the rows of the chessboard, the
inner loop over the columns:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6
7
8</pre></div></td><td class="code"><div class="highlight"><pre><span class="c"># Draw a fresh background (a blank chess board)</span>
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span> <span class="c"># Draw each row of the board.</span>
<span class="n">c_indx</span> <span class="o">=</span> <span class="n">row</span> <span class="o">%</span> <span class="mi">2</span> <span class="c"># Change starting color on each row</span>
<span class="k">for</span> <span class="n">col</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span> <span class="c"># Run through cols drawing squares</span>
<span class="n">the_square</span> <span class="o">=</span> <span class="p">(</span><span class="n">col</span><span class="o">*</span><span class="n">sq_sz</span><span class="p">,</span> <span class="n">row</span><span class="o">*</span><span class="n">sq_sz</span><span class="p">,</span> <span class="n">sq_sz</span><span class="p">,</span> <span class="n">sq_sz</span><span class="p">)</span>
<span class="n">surface</span><span class="o">.</span><span class="n">fill</span><span class="p">(</span><span class="n">colors</span><span class="p">[</span><span class="n">c_indx</span><span class="p">],</span> <span class="n">the_square</span><span class="p">)</span>
<span class="c"># now flip the color index for the next square</span>
<span class="n">c_indx</span> <span class="o">=</span> <span class="p">(</span><span class="n">c_indx</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="mi">2</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>There are two important ideas in this code: firstly,
we compute the rectangle to be filled
from the <tt class="docutils literal"><span class="pre">row</span></tt> and <tt class="docutils literal"><span class="pre">col</span></tt> loop variables,
multiplying them by the size of the square to
get their position. And, of course, each
square is a fixed width and height. So <tt class="docutils literal"><span class="pre">the_square</span></tt>
represents the rectangle to be filled on the
current iteration of the loop. The second idea
is that we have to alternate colors on
every square. In the earlier setup code we created
a list containing two colors, here we
manipulate <tt class="docutils literal"><span class="pre">c_indx</span></tt> (which will always either have
the value 0 or 1) to start each row on a
color that is different from the previous row’s
starting color, and to switch colors each
time a square is filled.</p>
<p>This (together with the other fragments not shown to flip the surface onto the display) leads
to the pleasing backgrounds like this, for different size boards:</p>
<img alt="_images/pygame_screenshot03.png" src="_images/pygame_screenshot03.png" />
<p>Now, on to drawing the queens! Recall that our
solution <tt class="docutils literal"><span class="pre">[6,4,2,0,5,7,1,3]</span></tt> means that
in column 0 of the board we want a queen at
row 6, at column 1 we want a queen at row 4,
and so on. So we need a loop running over each queen:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="highlight"><pre><span class="k">for</span> <span class="p">(</span><span class="n">col</span><span class="p">,</span> <span class="n">row</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">the_board</span><span class="p">):</span>
<span class="c"># draw a queen at col, row...</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>In this chapter we already have a beach ball image,
so we’ll use that for our queens. In the
setup code before our game loop, we load the ball
image (as we did before), and in the body of
the loop, we add the line:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">surface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">ball</span><span class="p">,</span> <span class="p">(</span><span class="n">col</span> <span class="o">*</span> <span class="n">sq_sz</span><span class="p">,</span> <span class="n">row</span> <span class="o">*</span> <span class="n">sq_sz</span><span class="p">))</span>
</pre></div>
</td></tr></table></div>
<img alt="_images/pygame_screenshot04.png" src="_images/pygame_screenshot04.png" />
</div></blockquote>
<p>We’re getting there, but those queens need to be
centred in their squares! Our problem arises from
the fact that both the ball and the rectangle have
their upper left corner as their reference points.
If we’re going to centre this ball in the square,
we need to give it an extra offset in both the
x and y direction. (Since the ball is round and
the square is square, the offset in the two directions
will be the same, so we’ll just compute a single offset
value, and use it in both directions.)</p>
<p>The offset we need is half the (size of the square less the size
of the ball). So we’ll precompute
this in the game’s setup section, after we’ve loaded the ball
and determined the square size:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">ball_offset</span> <span class="o">=</span> <span class="p">(</span><span class="n">sq_sz</span> <span class="o">-</span> <span class="n">ball</span><span class="o">.</span><span class="n">get_width</span><span class="p">())</span> <span class="o">//</span> <span class="mi">2</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>Now we touch up the drawing code for the ball and we’re done:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">surface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">ball</span><span class="p">,</span> <span class="p">(</span><span class="n">col</span> <span class="o">*</span> <span class="n">sq_sz</span> <span class="o">+</span> <span class="n">ball_offset</span><span class="p">,</span> <span class="n">row</span> <span class="o">*</span> <span class="n">q_sz</span> <span class="o">+</span> <span class="n">ball_offset</span><span class="p">))</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>We might just want to think about what would happen if the ball was bigger than
the square. In that case, <tt class="docutils literal"><span class="pre">ball_offset</span></tt> would become negative.
So it would still be centered in the square - it would just spill
over the boundaries, or perhaps obscure the square entirely!</p>
<p>Here is the complete program:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 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</pre></div></td><td class="code"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">pygame</span>
<span class="k">def</span> <span class="nf">draw_board</span><span class="p">(</span><span class="n">the_board</span><span class="p">):</span>
<span class="sd">""" Draw a chess board with queens, as determined by the the_board. """</span>
<span class="n">pygame</span><span class="o">.</span><span class="n">init</span><span class="p">()</span>
<span class="n">colors</span> <span class="o">=</span> <span class="p">[(</span><span class="mi">255</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">)]</span> <span class="c"># Set up colors [red, black]</span>
<span class="n">n</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">the_board</span><span class="p">)</span> <span class="c"># This is an NxN chess board.</span>
<span class="n">surface_sz</span> <span class="o">=</span> <span class="mi">480</span> <span class="c"># Proposed physical surface size.</span>
<span class="n">sq_sz</span> <span class="o">=</span> <span class="n">surface_sz</span> <span class="o">//</span> <span class="n">n</span> <span class="c"># sq_sz is length of a square.</span>
<span class="n">surface_sz</span> <span class="o">=</span> <span class="n">n</span> <span class="o">*</span> <span class="n">sq_sz</span> <span class="c"># Adjust to exactly fit n squares.</span>
<span class="c"># Create the surface of (width, height), and its window.</span>
<span class="n">surface</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">set_mode</span><span class="p">((</span><span class="n">surface_sz</span><span class="p">,</span> <span class="n">surface_sz</span><span class="p">))</span>
<span class="n">ball</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">image</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="s">"ball.png"</span><span class="p">)</span>
<span class="c"># Use an extra offset to centre the ball in its square.</span>
<span class="c"># If the square is too small, offset becomes negative,</span>
<span class="c"># but it will still be centered :-)</span>
<span class="n">ball_offset</span> <span class="o">=</span> <span class="p">(</span><span class="n">sq_sz</span><span class="o">-</span><span class="n">ball</span><span class="o">.</span><span class="n">get_width</span><span class="p">())</span> <span class="o">//</span> <span class="mi">2</span>
<span class="k">while</span> <span class="k">True</span><span class="p">:</span>
<span class="c"># Look for an event from keyboard, mouse, etc.</span>
<span class="n">ev</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">poll</span><span class="p">()</span>
<span class="k">if</span> <span class="n">ev</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">pygame</span><span class="o">.</span><span class="n">QUIT</span><span class="p">:</span>
<span class="k">break</span><span class="p">;</span>
<span class="c"># Draw a fresh background (a blank chess board)</span>
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span> <span class="c"># Draw each row of the board.</span>
<span class="n">c_indx</span> <span class="o">=</span> <span class="n">row</span> <span class="o">%</span> <span class="mi">2</span> <span class="c"># Alternate starting color</span>
<span class="k">for</span> <span class="n">col</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span> <span class="c"># Run through cols drawing squares</span>
<span class="n">the_square</span> <span class="o">=</span> <span class="p">(</span><span class="n">col</span><span class="o">*</span><span class="n">sq_sz</span><span class="p">,</span> <span class="n">row</span><span class="o">*</span><span class="n">sq_sz</span><span class="p">,</span> <span class="n">sq_sz</span><span class="p">,</span> <span class="n">sq_sz</span><span class="p">)</span>
<span class="n">surface</span><span class="o">.</span><span class="n">fill</span><span class="p">(</span><span class="n">colors</span><span class="p">[</span><span class="n">c_indx</span><span class="p">],</span> <span class="n">the_square</span><span class="p">)</span>
<span class="c"># Now flip the color index for the next square</span>
<span class="n">c_indx</span> <span class="o">=</span> <span class="p">(</span><span class="n">c_indx</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="mi">2</span>
<span class="c"># Now that squares are drawn, draw the queens.</span>
<span class="k">for</span> <span class="p">(</span><span class="n">col</span><span class="p">,</span> <span class="n">row</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">the_board</span><span class="p">):</span>
<span class="n">surface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">ball</span><span class="p">,</span>
<span class="p">(</span><span class="n">col</span><span class="o">*</span><span class="n">sq_sz</span><span class="o">+</span><span class="n">ball_offset</span><span class="p">,</span><span class="n">row</span><span class="o">*</span><span class="n">sq_sz</span><span class="o">+</span><span class="n">ball_offset</span><span class="p">))</span>
<span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">flip</span><span class="p">()</span>
<span class="n">pygame</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">draw_board</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">])</span> <span class="c"># 7 x 7 to test window size</span>
<span class="n">draw_board</span><span class="p">([</span><span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
<span class="n">draw_board</span><span class="p">([</span><span class="mi">9</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span><span class="p">])</span> <span class="c"># 13 x 13</span>
<span class="n">draw_board</span><span class="p">([</span><span class="mi">11</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">9</span><span class="p">])</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>There is one more thing worth reviewing here. The conditional statement on line
50 tests whether the name of the currently executing program is <tt class="docutils literal"><span class="pre">__main__</span></tt>.
This allows us to distinguish whether this module is being run as a main program,
or whether it has been imported elsewhere, and used as a module. If we run this
module in Python, the test cases in lines 51-54 will be executed. However, if we
import this module into another program (i.e. our N queens solver from earlier)
the condition at line 50 will be false, and the statements on lines 51-54 won’t run.</p>
<p>In the section <em class="xref std std-ref">eightqueensmainprog</em> our main program looked like this:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14</pre></div></td><td class="code"><div class="highlight"><pre><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">bd</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">))</span> <span class="c"># Generate the initial permutation</span>
<span class="n">num_found</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">tries</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">num_found</span> <span class="o"><</span> <span class="mi">10</span><span class="p">:</span>
<span class="n">random</span><span class="o">.</span><span class="n">shuffle</span><span class="p">(</span><span class="n">bd</span><span class="p">)</span>
<span class="n">tries</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">has_clashes</span><span class="p">(</span><span class="n">bd</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s">"Found solution {0} in {1} tries."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">bd</span><span class="p">,</span> <span class="n">tries</span><span class="p">))</span>
<span class="n">tries</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">num_found</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>Now we just need two changes. At the top of that program, we
import the module that we’ve been working on here (assume we
called it <tt class="docutils literal"><span class="pre">draw_queens</span></tt>). (You’ll have to ensure that the
two modules are saved in the same folder.) Then after line 10
here we add a call to draw the solution that we’ve just discovered:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">draw_queens</span><span class="o">.</span><span class="n">draw_board</span><span class="p">(</span><span class="n">bd</span><span class="p">)</span>
</pre></div>
</div>
<p>And that gives a very satisfying combination of program that can search for solutions to the N queens problem,
and when it finds each, it pops up the board showing the solution.</p>
</div>
<div class="section" id="sprites">
<h2>16.4. Sprites<a class="headerlink" href="#sprites" title="Permalink to this headline">¶</a></h2>
<p>A sprite is an object that can move about in a game,
and has internal behaviour and state of its own. For example,
a spaceship would be a sprite, the player would be a sprite,
and bullets and bombs would all be sprites.</p>
<p>Object oriented programming (OOP) is ideally suited to a
situation like this: each object can have its own attributes
and internal state, and a couple of methods. Let’s have
some fun with our N queens board. Instead of placing
the queen in her final position, we’d like to drop her in
from the top of the board, and let her fall into position,
perhaps bouncing along the way.</p>
<p>The first encapsulation we need is to turn each of our
queens into an object. We’ll keep a list of all the active
sprites (i.e. a list of queen objects), and arrange two new
things in our game loop:</p>
<ul class="simple">
<li>After handling events, but before drawing, call an <tt class="docutils literal"><span class="pre">update</span></tt>
method on every sprite. This will give each sprite a chance to
modify its internal state in some way — perhaps change its
image, or change its position, or rotate itself, or make itself
grow a bit bigger or a bit smaller.</li>
<li>Once all the sprites have updated themselves, the game loop
can begin drawing - first the background, and then
call a <tt class="docutils literal"><span class="pre">draw</span></tt> method on each sprite in turn, and delegate (hand off)
the task of drawing to the object itself. This is
in line with the OOP idea that we don’t say “Hey, <em>draw</em>,
show this queen!”, but we prefer to say
“Hey, <em>queen</em>, draw yourself!”.</li>
</ul>
<p>We start with a simple object, no movement or animation yet, just scaffolding,
to see how to fit all the pieces together:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15</pre></div></td><td class="code"><div class="highlight"><pre><span class="k">class</span> <span class="nc">QueenSprite</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">img</span><span class="p">,</span> <span class="n">target_posn</span><span class="p">):</span>
<span class="sd">""" Create and initialize a queen for this</span>
<span class="sd"> target position on the board</span>
<span class="sd"> """</span>
<span class="bp">self</span><span class="o">.</span><span class="n">image</span> <span class="o">=</span> <span class="n">img</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target_posn</span> <span class="o">=</span> <span class="n">target_posn</span>
<span class="bp">self</span><span class="o">.</span><span class="n">posn</span> <span class="o">=</span> <span class="n">target_posn</span>
<span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="c"># Do nothing for the moment.</span>
<span class="k">def</span> <span class="nf">draw</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">target_surface</span><span class="p">):</span>
<span class="n">target_surface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">image</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">posn</span><span class="p">)</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>We’ve given the sprite three attributes: an image to be drawn,
a target position, and a current position. If we’re going to
move the spite about, the current position may need to be
different from the target, which is where we want the queen
finally to end up. In this code at this time we’ve done nothing
in the <tt class="docutils literal"><span class="pre">update</span></tt> method, and our <tt class="docutils literal"><span class="pre">draw</span></tt> method (which
can probably remain this simple in future) simply draws itself
at its current position on the surface that is provided
by the caller.</p>
<p>With its class definition in place, we now instantiate our N queens,
put them into a list of sprites, and arrange for the game loop to call
the <tt class="docutils literal"><span class="pre">update</span></tt> and <tt class="docutils literal"><span class="pre">draw</span></tt> methods on each frame. The new bits of
code, and the revised game loop look like this:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 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</pre></div></td><td class="code"><div class="highlight"><pre> <span class="n">all_sprites</span> <span class="o">=</span> <span class="p">[]</span> <span class="c"># Keep a list of all sprites in the game</span>
<span class="c"># Create a sprite object for each queen, and populate our list.</span>
<span class="k">for</span> <span class="p">(</span><span class="n">col</span><span class="p">,</span> <span class="n">row</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">the_board</span><span class="p">):</span>
<span class="n">a_queen</span> <span class="o">=</span> <span class="n">QueenSprite</span><span class="p">(</span><span class="n">ball</span><span class="p">,</span>
<span class="p">(</span><span class="n">col</span><span class="o">*</span><span class="n">sq_sz</span><span class="o">+</span><span class="n">ball_offset</span><span class="p">,</span> <span class="n">row</span><span class="o">*</span><span class="n">sq_sz</span><span class="o">+</span><span class="n">ball_offset</span><span class="p">))</span>
<span class="n">all_sprites</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">a_queen</span><span class="p">)</span>
<span class="k">while</span> <span class="k">True</span><span class="p">:</span>
<span class="c"># Look for an event from keyboard, mouse, etc.</span>
<span class="n">ev</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">poll</span><span class="p">()</span>
<span class="k">if</span> <span class="n">ev</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">pygame</span><span class="o">.</span><span class="n">QUIT</span><span class="p">:</span>
<span class="k">break</span><span class="p">;</span>
<span class="c"># Ask every sprite to update itself.</span>
<span class="k">for</span> <span class="n">sprite</span> <span class="ow">in</span> <span class="n">all_sprites</span><span class="p">:</span>
<span class="n">sprite</span><span class="o">.</span><span class="n">update</span><span class="p">()</span>
<span class="c"># Draw a fresh background (a blank chess board)</span>
<span class="c"># ... same as before ...</span>
<span class="c"># Ask every sprite to draw itself.</span>
<span class="k">for</span> <span class="n">sprite</span> <span class="ow">in</span> <span class="n">all_sprites</span><span class="p">:</span>
<span class="n">sprite</span><span class="o">.</span><span class="n">draw</span><span class="p">(</span><span class="n">surface</span><span class="p">)</span>
<span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">flip</span><span class="p">()</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>This works just like it did before, but our extra work in making objects
for the queens has prepared the way for some more ambitious extensions.</p>
<p>Let us begin with a falling queen object. At any instant, it will have a
velocity i.e. a speed, in a certain direction.
(We are only working with movement in the y direction, but use your imagination!)
So in the object’s <tt class="docutils literal"><span class="pre">update</span></tt> method, we want to change its current position by its velocity.
If our N queens board is floating in space, velocity would stay constant, but hey, here on
Earth we have gravity! Gravity changes the velocity on each time interval, so we’ll want a ball
that speeds up as it falls further. Gravity will be constant for all queens, so we won’t keep
it in the instances — we’ll just make it a variable in our module. We’ll make one other
change too: we will start every queen at the top of the board, so that it can fall towards
its target position. With these changes, we now get the following:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">gravity</span> <span class="o">=</span> <span class="mf">0.0001</span>
<span class="k">class</span> <span class="nc">QueenSprite</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">img</span><span class="p">,</span> <span class="n">target_posn</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">image</span> <span class="o">=</span> <span class="n">img</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target_posn</span> <span class="o">=</span> <span class="n">target_posn</span>
<span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">=</span> <span class="n">target_posn</span>
<span class="bp">self</span><span class="o">.</span><span class="n">posn</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="c"># Start ball at top of its column</span>
<span class="bp">self</span><span class="o">.</span><span class="n">y_velocity</span> <span class="o">=</span> <span class="mi">0</span> <span class="c"># with zero initial velocity</span>
<span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">y_velocity</span> <span class="o">+=</span> <span class="n">gravity</span> <span class="c"># Gravity changes velocity</span>
<span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">posn</span>
<span class="n">new_y_pos</span> <span class="o">=</span> <span class="n">y</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">y_velocity</span> <span class="c"># Velocity moves the ball</span>
<span class="bp">self</span><span class="o">.</span><span class="n">posn</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">new_y_pos</span><span class="p">)</span> <span class="c"># to this new position.</span>
<span class="k">def</span> <span class="nf">draw</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">target_surface</span><span class="p">):</span> <span class="c"># Same as before.</span>
<span class="n">target_surface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">image</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">posn</span><span class="p">)</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>Making these changes gives us a new chessboard in
which each queen starts at the top of its column,
and speeds up, until it drops off the bottom of
the board and disappears forever.
A good start — we have movement!</p>
<p>The next step is to get the ball to bounce when it reaches
its own target position.
It is pretty easy to bounce something — you just change
the sign of its velocity, and it will
move at the same speed in the opposite direction. Of course,
if it is travelling up towards the
top of the board it will be slowed down by gravity.
(Gravity always sucks down!) And you’ll
find it bounces all the way up to where it began from,
reaches zero velocity, and starts falling
all over again. So we’ll have bouncing balls that never settle.</p>
<p>A realistic way to settle the object is to lose some energy (probably to friction)
each time it bounces — so instead of simply reversing the sign of the velocity,
we multiply it by some fractional factor — say -0.65.
This means the ball only retains 65% of its energy on
each bounce, so it will, as in real life,
stop bouncing after a short while, and settle on its “ground”.</p>
<p>The only changes are in the <tt class="docutils literal"><span class="pre">update</span></tt> method, which now looks like this:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12</pre></div></td><td class="code"><div class="highlight"><pre><span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">y_velocity</span> <span class="o">+=</span> <span class="n">gravity</span>
<span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">posn</span>
<span class="n">new_y_pos</span> <span class="o">=</span> <span class="n">y</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">y_velocity</span>
<span class="p">(</span><span class="n">target_x</span><span class="p">,</span> <span class="n">target_y</span><span class="p">)</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target_posn</span> <span class="c"># Unpack the position</span>
<span class="n">dist_to_go</span> <span class="o">=</span> <span class="n">target_y</span> <span class="o">-</span> <span class="n">new_y_pos</span> <span class="c"># How far to our floor?</span>
<span class="k">if</span> <span class="n">dist_to_go</span> <span class="o"><</span> <span class="mi">0</span><span class="p">:</span> <span class="c"># Are we under floor?</span>
<span class="bp">self</span><span class="o">.</span><span class="n">y_velocity</span> <span class="o">=</span> <span class="o">-</span><span class="mf">0.65</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">y_velocity</span> <span class="c"># Bounce</span>
<span class="n">new_y_pos</span> <span class="o">=</span> <span class="n">target_y</span> <span class="o">+</span> <span class="n">dist_to_go</span> <span class="c"># Move back above floor</span>
<span class="bp">self</span><span class="o">.</span><span class="n">posn</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">new_y_pos</span><span class="p">)</span> <span class="c"># Set our new position.</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>Heh, heh, heh! We’re not going to show animated screenshots,
so copy the code into your Python environment and see for yourself.</p>
</div>
<div class="section" id="events">
<h2>16.5. Events<a class="headerlink" href="#events" title="Permalink to this headline">¶</a></h2>
<p>The only kind of event we’re handled so far has been the
QUIT event. But we can also detect keydown and keyup
events, mouse motion, and mousebutton down or up events.
Consult the PyGame documentation and follow the link to Event.</p>
<p>When your program polls for and receives an event
object from PyGame, its event type will determine what secondary
information is available. Each event object carries a
<em>dictionary</em> (which you may only cover in due course in these notes).
The dictionary holds certain <em>keys</em> and <em>values</em> that make
sense for the type of event.</p>
<p>For example, if the type of event is MOUSEMOTION,
we’ll be able to find the mouse position and information about
the state of the mouse buttons in the dictionary
attached to the event. Similarly, if the event is KEYDOWN, we
can learn from the dictionary which key went down, and
whether any modifier keys (shift, control, alt, etc.) are also
down. You also get events when the game window becomes
active (i.e. gets focus) or loses focus.</p>
<p>The event object with type NOEVENT is returned if there are
no events waiting. Events can be printed, allowing you to
experiment and play around. So dropping these lines of code
into the game loop directly after polling for any event is
quite informative:</p>
<blockquote>
<div><div class="highlight-python3"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="highlight"><pre><span class="k">if</span> <span class="n">ev</span><span class="o">.</span><span class="n">type</span> <span class="o">!=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">NOEVENT</span><span class="p">:</span> <span class="c"># Only print if it is interesting!</span>
<span class="nb">print</span><span class="p">(</span><span class="n">ev</span><span class="p">)</span>
</pre></div>
</td></tr></table></div>
</div></blockquote>
<p>With this is place, hit the space bar and the escape key, and
watch the events you get. Click your three
mouse buttons. Move your mouse over the window.
(This causes a vast cascade of events, so you may also
need to filter those out of the printing.)
You’ll get output that looks something like this:</p>
<div class="highlight-pycon"><div class="highlight"><pre><span class="go"><Event(17-VideoExpose {})></span>
<span class="go"><Event(1-ActiveEvent {'state': 1, 'gain': 0})></span>
<span class="go"><Event(2-KeyDown {'scancode': 57, 'key': 32, 'unicode': ' ', 'mod': 0})></span>
<span class="go"><Event(3-KeyUp {'scancode': 57, 'key': 32, 'mod': 0})></span>
<span class="go"><Event(2-KeyDown {'scancode': 1, 'key': 27, 'unicode': '\x1b', 'mod': 0})></span>
<span class="go"><Event(3-KeyUp {'scancode': 1, 'key': 27, 'mod': 0})></span>
<span class="gp">...</span>
<span class="go"><Event(4-MouseMotion {'buttons': (0, 0, 0), 'pos': (323, 194), 'rel': (-3, -1)})></span>
<span class="go"><Event(4-MouseMotion {'buttons': (0, 0, 0), 'pos': (322, 193), 'rel': (-1, -1)})></span>
<span class="go"><Event(4-MouseMotion {'buttons': (0, 0, 0), 'pos': (321, 192), 'rel': (-1, -1)})></span>
<span class="go"><Event(4-MouseMotion {'buttons': (0, 0, 0), 'pos': (319, 192), 'rel': (-2, 0)})></span>
<span class="go"><Event(5-MouseButtonDown {'button': 1, 'pos': (319, 192)})></span>
<span class="go"><Event(6-MouseButtonUp {'button': 1, 'pos': (319, 192)})></span>
<span class="go"><Event(4-MouseMotion {'buttons': (0, 0, 0), 'pos': (319, 191), 'rel': (0, -1)})></span>
<span class="go"><Event(5-MouseButtonDown {'button': 2, 'pos': (319, 191)})></span>
<span class="go"><Event(5-MouseButtonDown {'button': 5, 'pos': (319, 191)})></span>
<span class="go"><Event(6-MouseButtonUp {'button': 5, 'pos': (319, 191)})></span>
<span class="go"><Event(6-MouseButtonUp {'button': 2, 'pos': (319, 191)})></span>
<span class="go"><Event(5-MouseButtonDown {'button': 3, 'pos': (319, 191)})></span>
<span class="go"><Event(6-MouseButtonUp {'button': 3, 'pos': (319, 191)})></span>
<span class="go"> ...</span>
<span class="go"><Event(1-ActiveEvent {'state': 1, 'gain': 0})></span>
<span class="go"><Event(12-Quit {})></span>
</pre></div>
</div>
<p>So let us now make these changes to the code near the top of our game loop:</p>
<blockquote>