-
Notifications
You must be signed in to change notification settings - Fork 11
/
chapter13.html
1059 lines (860 loc) · 87.9 KB
/
chapter13.html
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 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-5459430-3");
pageTracker._trackPageview();
} catch(err) {}</script>
<meta http-equiv="Content-Type" content="text/html;charset=us-ascii" />
<title>IYOCGwP, Chapter 13 - Sonar Treasure Hunt</title>
<link rel="stylesheet" href="inventbook.css" type="text/css" media="all" />
</head>
<body class='chapter13body'>
<table border='0' width='100%'><tr><td><a href='chapter12.html'>Go to Chapter 12 - Cartesian Coordinates</a></td><td align='right'><a href='chapter14.html'>Go to Chapter 14 - Caesar Cipher</a></td></tr></table>
<div style='height: 310px;'><a href='http://www.amazon.com/Invent-Your-Computer-Games-Python/dp/0982106017/'><img src='images/buyad.png' align='right'></a></div>
<div style='height: 350px;'><img src='images/chap13.png'></div>
<div class='inthischapter'><h3 id="TopicsCoveredInThisChapter">Topics Covered In This Chapter:</h3>
<ul>
<li>Data structures.</li>
<li>The <span class='m'>remove()</span> list method.</li>
<li>The <span class='m'>isdigit()</span> string method.</li>
<li>The <span class='m'>sys.exit()</span> function.</li>
</ul></div>
<p>The game in this chapter only introduces a couple new helpful methods that come with Python, the <span class='m'>remove()</span> list method and the <span class='m'>isdigit()</span> string method. But this is the first program which will make use of Cartesian coordinates and the mathematical concepts we learned in chapter 11. This program will also use make use of data structures (which is really just a fancy way of saying variables that contain lists of lists.) As our games become more complicated, we will need to store our data in well-organized ways.</p>
<p>Sonar is a technology that ships use to locate objects under the sea. In this chapter's game, the player places sonar devices at various places in the ocean to locate sunken treasure chests. The sonar devices (in our game) can tell the player how far away a treasure chest is from the sonar device, but not in what direction. But by placing multiple sonar devices down, the player can figure out where exactly the treasure chest is.</p>
<p>There are three chests to collect, but the player has only sixteen sonar devices to use to find them. Imagine that we could not see the treasure chest in the following picture. Because each sonar device can only find the distance but not direction, the possible places the treasure could be is anywhere in a ring around the sonar device (see Figure 13-1).</p>
<p class='centeredImageP'><img src='images/13-1.png' alt='' class='centeredImage' /><br />Figure 13-1: The first sonar device shows a ring<br />of possible places the treasure could be located.</p>
<p class='centeredImageP'><img src='images/13-2.png' alt='' class='centeredImage' /><br />Figure 13-2: Combining the rings of all three sonar<br />devices shows only one possible place for the treasure.</p>
<p>But if we have multiple sonar devices working together, we can narrow it down to an exact place where all the rings intersect each other. (See Figure 13-2)</p>
<h2 id="SampleRun">Sample Run</h2>
<div class='sourceblurb' style='font-size: 75%'>
S O N A R !<br />
<br />
Would you like to view the instructions? (yes/no)<br />
no<br />
1 2 3 4 5<br />
012345678901234567890123456789012345678901234567890123456789<br />
<br />
0 `~~~`~~~`~`~~`~~~~~`~``~~~~`~`~~~`~``~``~~````~`~```~`~~~~`` 0<br />
1 ~`~~~```~~~~`~`~~`~``~`~~```~`~`~~`~`~~~~~~`~`````~`~~`~~~~` 1<br />
2 `~``~``~~~`~``~`~`~``~`````~~~~~~~~~`~`~~`~``~~~~~```~~`~``` 2<br />
3 ``~`~~``~`~``~`~`~`~~`~`~~`~`~``~~~`~``~````~``````~~~~``~`` 3<br />
4 ``~~`~~~``~``~~````~`~`~`~``~~~``~~```~`~~`~~`~`~`~~`~~~~``` 4<br />
5 ~~```~~~`~`~~``~`~``~```~`~~`~~~~~`~~``~`~`~~~`~~`~`~`~`~~~` 5<br />
6 ``~~`````~~~~`~`~~~```~~~~`~~`~~`~~```~~`~~~`~~~``~`~~~``~~~ 6<br />
7 `~`````````~```~``~``~~`~~~~`~~``~``~~~```~`~~`~``~``~~```~~ 7<br />
8 `~````~```~`~~`~~~`~~``~~~``~`~``~~~``~`~`````~`~~```~`~~~~` 8<br />
9 ~```~~`~`~``~``~~``~``~```~`~``~~~~`~`~`~~~`~`~`~`~~~``~~``` 9<br />
10 ```~`~```~``~``~`~~`~``~````~``~~~`~~`~~``~~~~`~~~`~`~~````~ 10<br />
11 ```~```~~~`~```~~`~~~`~`````~`~~`~`~~`~~`~`~~`~~~````~````~` 11<br />
12 ~~~`~`~~~``~~~~~~`~~~``~`~`~~`~`~~`~```~~~```~~`~~`~``~``~`~ 12<br />
13 `~~````~~``~```~~~`~```~`~~~~~~~~~`~~``~~~~~`````~`~`~``~~~~ 13<br />
14 `~~`~`~````~```~`~`~```~~`~~~~`~```~``~``~``~~~````~~``````~ 14<br />
<br />
012345678901234567890123456789012345678901234567890123456789<br />
1 2 3 4 5<br />
You have 16 sonar devices left. 3 treasure chests remaining.<br />
Where do you want to drop the next sonar device? (0-59 0-14) (or type quit)<br />
<span class='sampleruninput'>10 10</span><br />
1 2 3 4 5<br />
012345678901234567890123456789012345678901234567890123456789<br />
<br />
0 `~~~`~~~`~`~~`~~~~~`~``~~~~`~`~~~`~``~``~~````~`~```~`~~~~`` 0<br />
1 ~`~~~```~~~~`~`~~`~``~`~~```~`~`~~`~`~~~~~~`~`````~`~~`~~~~` 1<br />
2 `~``~``~~~`~``~`~`~``~`````~~~~~~~~~`~`~~`~``~~~~~```~~`~``` 2<br />
3 ``~`~~``~`~``~`~`~`~~`~`~~`~`~``~~~`~``~````~``````~~~~``~`` 3<br />
4 ``~~`~~~``~``~~````~`~`~`~``~~~``~~```~`~~`~~`~`~`~~`~~~~``` 4<br />
5 ~~```~~~`~`~~``~`~``~```~`~~`~~~~~`~~``~`~`~~~`~~`~`~`~`~~~` 5<br />
6 ``~~`````~~~~`~`~~~```~~~~`~~`~~`~~```~~`~~~`~~~``~`~~~``~~~ 6<br />
7 `~`````````~```~``~``~~`~~~~`~~``~``~~~```~`~~`~``~``~~```~~ 7<br />
8 `~````~```~`~~`~~~`~~``~~~``~`~``~~~``~`~`````~`~~```~`~~~~` 8<br />
9 ~```~~`~`~``~``~~``~``~```~`~``~~~~`~`~`~~~`~`~`~`~~~``~~``` 9<br />
10 ```~`~```~5`~``~`~~`~``~````~``~~~`~~`~~``~~~~`~~~`~`~~````~ 10<br />
11 ```~```~~~`~```~~`~~~`~`````~`~~`~`~~`~~`~`~~`~~~````~````~` 11<br />
12 ~~~`~`~~~``~~~~~~`~~~``~`~`~~`~`~~`~```~~~```~~`~~`~``~``~`~ 12<br />
13 `~~````~~``~```~~~`~```~`~~~~~~~~~`~~``~~~~~`````~`~`~``~~~~ 13<br />
14 `~~`~`~````~```~`~`~```~~`~~~~`~```~``~``~``~~~````~~``````~ 14<br />
<br />
012345678901234567890123456789012345678901234567890123456789<br />
1 2 3 4 5<br />
Treasure detected at a distance of 5 from the sonar device.<br />
You have 15 sonar devices left. 3 treasure chests remaining.<br />
Where do you want to drop the next sonar device? (0-59 0-14) (or type quit)<br />
<span class='sampleruninput'>15 6</span><br />
1 2 3 4 5<br />
012345678901234567890123456789012345678901234567890123456789<br />
<br />
0 `~~~`~~~`~`~~`~~~~~`~``~~~~`~`~~~`~``~``~~````~`~```~`~~~~`` 0<br />
1 ~`~~~```~~~~`~`~~`~``~`~~```~`~`~~`~`~~~~~~`~`````~`~~`~~~~` 1<br />
2 `~``~``~~~`~``~`~`~``~`````~~~~~~~~~`~`~~`~``~~~~~```~~`~``` 2<br />
3 ``~`~~``~`~``~`~`~`~~`~`~~`~`~``~~~`~``~````~``````~~~~``~`` 3<br />
4 ``~~`~~~``~``~~````~`~`~`~``~~~``~~```~`~~`~~`~`~`~~`~~~~``` 4<br />
5 ~~```~~~`~`~~``~`~``~```~`~~`~~~~~`~~``~`~`~~~`~~`~`~`~`~~~` 5<br />
6 ``~~`````~~~~`~4~~~```~~~~`~~`~~`~~```~~`~~~`~~~``~`~~~``~~~ 6<br />
7 `~`````````~```~``~``~~`~~~~`~~``~``~~~```~`~~`~``~``~~```~~ 7<br />
8 `~````~```~`~~`~~~`~~``~~~``~`~``~~~``~`~`````~`~~```~`~~~~` 8<br />
9 ~```~~`~`~``~``~~``~``~```~`~``~~~~`~`~`~~~`~`~`~`~~~``~~``` 9<br />
10 ```~`~```~5`~``~`~~`~``~````~``~~~`~~`~~``~~~~`~~~`~`~~````~ 10<br />
11 ```~```~~~`~```~~`~~~`~`````~`~~`~`~~`~~`~`~~`~~~````~````~` 11<br />
12 ~~~`~`~~~``~~~~~~`~~~``~`~`~~`~`~~`~```~~~```~~`~~`~``~``~`~ 12<br />
13 `~~````~~``~```~~~`~```~`~~~~~~~~~`~~``~~~~~`````~`~`~``~~~~ 13<br />
14 `~~`~`~````~```~`~`~```~~`~~~~`~```~``~``~``~~~````~~``````~ 14<br />
<br />
012345678901234567890123456789012345678901234567890123456789<br />
1 2 3 4 5<br />
Treasure detected at a distance of 4 from the sonar device.<br />
You have 14 sonar devices left. 3 treasure chests remaining.<br />
Where do you want to drop the next sonar device? (0-59 0-14) (or type quit)<br />
<span class='sampleruninput'>15 10</span><br />
1 2 3 4 5<br />
012345678901234567890123456789012345678901234567890123456789<br />
<br />
0 `~~~`~~~`~`~~`~~~~~`~``~~~~`~`~~~`~``~``~~````~`~```~`~~~~`` 0<br />
1 ~`~~~```~~~~`~`~~`~``~`~~```~`~`~~`~`~~~~~~`~`````~`~~`~~~~` 1<br />
2 `~``~``~~~`~``~`~`~``~`````~~~~~~~~~`~`~~`~``~~~~~```~~`~``` 2<br />
3 ``~`~~``~`~``~`~`~`~~`~`~~`~`~``~~~`~``~````~``````~~~~``~`` 3<br />
4 ``~~`~~~``~``~~````~`~`~`~``~~~``~~```~`~~`~~`~`~`~~`~~~~``` 4<br />
5 ~~```~~~`~`~~``~`~``~```~`~~`~~~~~`~~``~`~`~~~`~~`~`~`~`~~~` 5<br />
6 ``~~`````~~~~`~O~~~```~~~~`~~`~~`~~```~~`~~~`~~~``~`~~~``~~~ 6<br />
7 `~`````````~```~``~``~~`~~~~`~~``~``~~~```~`~~`~``~``~~```~~ 7<br />
8 `~````~```~`~~`~~~`~~``~~~``~`~``~~~``~`~`````~`~~```~`~~~~` 8<br />
9 ~```~~`~`~``~``~~``~``~```~`~``~~~~`~`~`~~~`~`~`~`~~~``~~``` 9<br />
10 ```~`~```~O`~``O`~~`~``~````~``~~~`~~`~~``~~~~`~~~`~`~~````~ 10<br />
11 ```~```~~~`~```~~`~~~`~`````~`~~`~`~~`~~`~`~~`~~~````~````~` 11<br />
12 ~~~`~`~~~``~~~~~~`~~~``~`~`~~`~`~~`~```~~~```~~`~~`~``~``~`~ 12<br />
13 `~~````~~``~```~~~`~```~`~~~~~~~~~`~~``~~~~~`````~`~`~``~~~~ 13<br />
14 `~~`~`~````~```~`~`~```~~`~~~~`~```~``~``~``~~~````~~``````~ 14<br />
<br />
012345678901234567890123456789012345678901234567890123456789<br />
1 2 3 4 5<br />
You have found a sunken treasure chest!<br />
You have 13 sonar devices left. 2 treasure chests remaining.<br />
Where do you want to drop the next sonar device? (0-59 0-14) (or type quit)<br />
<br />
<br />
<i>...skipped over for brevity....</i><br />
<br />
<br />
1 2 3 4 5<br />
012345678901234567890123456789012345678901234567890123456789<br />
<br />
0 `~~~`~~~`~`~~`~~~~~`~``~~~~`~`~~~`~``~``~~````~`~```~`~~~~`` 0<br />
1 ~`~~~```~~~~`~`~~`~``~`~~```~O~`~~`~`~~~~~~`~`````~`~~`~~~~` 1<br />
2 `~``~``~~~`~``~`~`~``~`````~~O~~~O~~`~`~~`~``~~~~~```~~`~``` 2<br />
3 ``~3~~``8`~``~`~`~`~~`~`~~`~`~``~~~`~`O~````~``````~~~~``~`` 3<br />
4 ``~~`~~~``~``~~````~`~`~`~O`~~O``~~```~`~~`~~`~`~`~~`~~~~``` 4<br />
5 ~~```~~~`~`~~``~`~``~```~`~~`~~~~~`~~``~`~`~~~`~~`~`~`~`~~~` 5<br />
6 ``~~`````~~~~`~O~~~```~~~~`~~`~~`~~```~~`~~~`~~~``O`~~~``~~~ 6<br />
7 `~`````````~```~``~``~~`~~~~`~~``~``~~~```~`~~`~``~``~~```~~ 7<br />
8 `~````~```~`~~`~~~`~~``~~~``~`~``~~~``~`O```0`~`~~```~`~~~~` 8<br />
9 ~```~~`~`~``~``~~``~``~```~O~``~~~~`~`~`~~~`~`~`~`~~~``~~``` 9<br />
10 ```~`~```~O`~``O`~~`~``~````~``~~~`~~`~~``~~~~`~~~`~`~~````~ 10<br />
11 ```~```~~~`~```~~`~~~`~`````~`~~`~`~~`~~`~`~~`~~~````~````~` 11<br />
12 ~~~`~`~~~``~~~~~~`~~~``~`~`~~`~`~~`~```~~~```~~`~~`~``~``~`~ 12<br />
13 `~~````~~``~```~~~`~```~`~~~~~~~~~`~~``~~~~~`````~`~`~``~~~~ 13<br />
14 `~~`~`~````~```~`~`~```~~`~~~~`~```~``~``~``~~~````~~``````~ 14<br />
<br />
012345678901234567890123456789012345678901234567890123456789<br />
1 2 3 4 5<br />
Treasure detected at a distance of 4 from the sonar device.<br />
We've run out of sonar devices! Now we have to turn the ship around and head<br />
for home with treasure chests still out there! Game over.<br />
The remaining chests were here:<br />
0, 4<br />
Do you want to play again? (yes or no)<br />
<span class='sampleruninput'>no</span><br />
</div>
<h2 id="SonarsSourceCode">Sonar's Source Code</h2>
<p>Knowing about Cartesian coordinates, number lines, negative numbers, and absolute values will help us out with our Sonar game. If you do not think you understand these concepts, go back to chapter 12. Below is the source code for the game. Type it into a new file, then save the file as <i>sonar.py</i> and run it by pressing the F5 key. You do not need to understand the code to type it in or play the game, the source code will be explained later.</p>
<p>Also, you can download the source code from the book's website at the URL <a href='http://inventwithpython.com/chapter13'>http://inventwithpython.com/chapter13</a>.</p>
<div class='sourcecode'><span class='sourcecodeHeader'>sonar.py</span><br /><span class='sourcecodeSubHeader'>This code can be downloaded from <a href='http://inventwithpython.com/sonar.py'>http://inventwithpython.com/sonar.py</a><br />If you get errors after typing this code in, compare it to the book's code with the online diff tool at <a href='http://inventwithpython.com/diff'>http://inventwithpython.com/diff</a> or email the author at <a href="mailto:[email protected]">[email protected]</a></span><br /><ol start='1'>
<li># Sonar</li>
<li></li>
<li>import random</li>
<li>import sys</li>
<li></li>
<li>def drawBoard(board):</li>
<li> # Draw the board data structure.</li>
<li></li>
<li> hline = ' ' # initial space for the numbers down the left side of the board</li>
<li> for i in range(1, 6):</li>
<li> hline += (' ' * 9) + str(i)</li>
<li></li>
<li> # print the numbers across the top</li>
<li> print(hline)</li>
<li> print(' ' + ('0123456789' * 6))</li>
<li> print()</li>
<li></li>
<li> # print each of the 15 rows</li>
<li> for i in range(15):</li>
<li> # single-digit numbers need to be padded with an extra space</li>
<li> if i < 10:</li>
<li> extraSpace = ' '</li>
<li> else:</li>
<li> extraSpace = ''</li>
<li> print('%s%s %s %s' % (extraSpace, i, getRow(board, i), i))</li>
<li></li>
<li> # print the numbers across the bottom</li>
<li> print()</li>
<li> print(' ' + ('0123456789' * 6))</li>
<li> print(hline)</li>
<li></li>
<li></li>
<li>def getRow(board, row):</li>
<li> # Return a string from the board data structure at a certain row.</li>
<li> boardRow = ''</li>
<li> for i in range(60):</li>
<li> boardRow += board[i][row]</li>
<li> return boardRow</li>
<li></li>
<li>def getNewBoard():</li>
<li> # Create a new 60x15 board data structure.</li>
<li> board = []</li>
<li> for x in range(60): # the main list is a list of 60 lists</li>
<li> board.append([])</li>
<li> for y in range(15): # each list in the main list has 15 single-character strings</li>
<li> # use different characters for the ocean to make it more readable.</li>
<li> if random.randint(0, 1) == 0:</li>
<li> board[x].append('~')</li>
<li> else:</li>
<li> board[x].append('`')</li>
<li> return board</li>
<li></li>
<li>def getRandomChests(numChests):</li>
<li> # Create a list of chest data structures (two-item lists of x, y int coordinates)</li>
<li> chests = []</li>
<li> for i in range(numChests):</li>
<li> chests.append([random.randint(0, 59), random.randint(0, 14)])</li>
<li> return chests</li>
<li></li>
<li>def isValidMove(x, y):</li>
<li> # Return True if the coordinates are on the board, otherwise False.</li>
<li> return x >= 0 and x <= 59 and y >= 0 and y <= 14</li>
<li></li>
<li>def makeMove(board, chests, x, y):</li>
<li> # Change the board data structure with a sonar device character. Remove treasure chests</li>
<li> # from the chests list as they are found. Return False if this is an invalid move.</li>
<li> # Otherwise, return the string of the result of this move.</li>
<li> if not isValidMove(x, y):</li>
<li> return False</li>
<li></li>
<li> smallestDistance = 100 # any chest will be closer than 100.</li>
<li> for cx, cy in chests:</li>
<li> if abs(cx - x) > abs(cy - y):</li>
<li> distance = abs(cx - x)</li>
<li> else:</li>
<li> distance = abs(cy - y)</li>
<li></li>
<li> if distance < smallestDistance: # we want the closest treasure chest.</li>
<li> smallestDistance = distance</li>
<li></li>
<li> if smallestDistance == 0:</li>
<li> # xy is directly on a treasure chest!</li>
<li> chests.remove([x, y])</li>
<li> return 'You have found a sunken treasure chest!'</li>
<li> else:</li>
<li> if smallestDistance < 10:</li>
<li> board[x][y] = str(smallestDistance)</li>
<li> return 'Treasure detected at a distance of %s from the sonar device.' % (smallestDistance)</li>
<li> else:</li>
<li> board[x][y] = 'O'</li>
<li> return 'Sonar did not detect anything. All treasure chests out of range.'</li>
<li></li>
<li></li>
<li>def enterPlayerMove():</li>
<li> # Let the player type in her move. Return a two-item list of int xy coordinates.</li>
<li> print('Where do you want to drop the next sonar device? (0-59 0-14) (or type quit)')</li>
<li> while True:</li>
<li> move = input()</li>
<li> if move.lower() == 'quit':</li>
<li> print('Thanks for playing!')</li>
<li> sys.exit()</li>
<li></li>
<li> move = move.split()</li>
<li> if len(move) == 2 and move[0].isdigit() and move[1].isdigit() and isValidMove(int(move[0]), int(move[1])):</li>
<li> return [int(move[0]), int(move[1])]</li>
<li> print('Enter a number from 0 to 59, a space, then a number from 0 to 14.')</li>
<li></li>
<li></li>
<li>def playAgain():</li>
<li> # This function returns True if the player wants to play again, otherwise it returns False.</li>
<li> print('Do you want to play again? (yes or no)')</li>
<li> return input().lower().startswith('y')</li>
<li></li>
<li></li>
<li>def showInstructions():</li>
<li> print('''Instructions:</li>
<li>You are the captain of the Simon, a treasure-hunting ship. Your current mission</li>
<li>is to find the three sunken treasure chests that are lurking in the part of the</li>
<li>ocean you are in and collect them.</li>
<li></li>
<li>To play, enter the coordinates of the point in the ocean you wish to drop a</li>
<li>sonar device. The sonar can find out how far away the closest chest is to it.</li>
<li>For example, the d below marks where the device was dropped, and the 2's</li>
<li>represent distances of 2 away from the device. The 4's represent</li>
<li>distances of 4 away from the device.</li>
<li></li>
<li> 444444444</li>
<li> 4 4</li>
<li> 4 22222 4</li>
<li> 4 2 2 4</li>
<li> 4 2 d 2 4</li>
<li> 4 2 2 4</li>
<li> 4 22222 4</li>
<li> 4 4</li>
<li> 444444444</li>
<li>Press enter to continue...''')</li>
<li> input()</li>
<li></li>
<li> print('''For example, here is a treasure chest (the c) located a distance of 2 away</li>
<li>from the sonar device (the d):</li>
<li></li>
<li> 22222</li>
<li> c 2</li>
<li> 2 d 2</li>
<li> 2 2</li>
<li> 22222</li>
<li></li>
<li>The point where the device was dropped will be marked with a 2.</li>
<li></li>
<li>The treasure chests don't move around. Sonar devices can detect treasure</li>
<li>chests up to a distance of 9. If all chests are out of range, the point</li>
<li>will be marked with O</li>
<li></li>
<li>If a device is directly dropped on a treasure chest, you have discovered</li>
<li>the location of the chest, and it will be collected. The sonar device will</li>
<li>remain there.</li>
<li></li>
<li>When you collect a chest, all sonar devices will update to locate the next</li>
<li>closest sunken treasure chest.</li>
<li>Press enter to continue...''')</li>
<li> input()</li>
<li> print()</li>
<li></li>
<li></li>
<li>print('S O N A R !')</li>
<li>print()</li>
<li>print('Would you like to view the instructions? (yes/no)')</li>
<li>if input().lower().startswith('y'):</li>
<li> showInstructions()</li>
<li></li>
<li>while True:</li>
<li> # game setup</li>
<li> sonarDevices = 16</li>
<li> theBoard = getNewBoard()</li>
<li> theChests = getRandomChests(3)</li>
<li> drawBoard(theBoard)</li>
<li> previousMoves = []</li>
<li></li>
<li> while sonarDevices > 0:</li>
<li> # Start of a turn:</li>
<li></li>
<li> # show sonar device/chest status</li>
<li> if sonarDevices > 1: extraSsonar = 's'</li>
<li> else: extraSsonar = ''</li>
<li> if len(theChests) > 1: extraSchest = 's'</li>
<li> else: extraSchest = ''</li>
<li> print('You have %s sonar device%s left. %s treasure chest%s remaining.' % (sonarDevices, extraSsonar, len(theChests), extraSchest))</li>
<li></li>
<li> x, y = enterPlayerMove()</li>
<li> previousMoves.append([x, y]) # we must track all moves so that sonar devices can be updated.</li>
<li></li>
<li> moveResult = makeMove(theBoard, theChests, x, y)</li>
<li> if moveResult == False:</li>
<li> continue</li>
<li> else:</li>
<li> if moveResult == 'You have found a sunken treasure chest!':</li>
<li> # update all the sonar devices currently on the map.</li>
<li> for x, y in previousMoves:</li>
<li> makeMove(theBoard, theChests, x, y)</li>
<li> drawBoard(theBoard)</li>
<li> print(moveResult)</li>
<li></li>
<li> if len(theChests) == 0:</li>
<li> print('You have found all the sunken treasure chests! Congratulations and good game!')</li>
<li> break</li>
<li></li>
<li> sonarDevices -= 1</li>
<li></li>
<li> if sonarDevices == 0:</li>
<li> print('We\'ve run out of sonar devices! Now we have to turn the ship around and head')</li>
<li> print('for home with treasure chests still out there! Game over.')</li>
<li> print(' The remaining chests were here:')</li>
<li> for x, y in theChests:</li>
<li> print(' %s, %s' % (x, y))</li>
<li></li>
<li> if not playAgain():</li>
<li> sys.exit()</li>
</ol></div>
<h2 id="DesigningtheProgram">Designing the Program</h2>
<p>Sonar is kind of complicated, so it might be better to type in the game's code and play it a few times first to understand what is going on. After you've played the game a few times, you can kind of get an idea of the sequence of events in this game.</p>
<p>The Sonar game uses lists of lists and other complicated variables. These complicated variables are known as <span class='term'>data structures</span>. Data structures will let us store arrangements of values in a single variable to represent something (such as the locations of the treasure chests in Sonar). We will use data structures for the locations of the treasure chests and dropped sonar devices. One example of a data structure was the <span class='m'>board</span> variable in the Tic Tac Toe chapter.</p>
<p>It is also helpful to write out the things we need our program to do, and come up with some function names that will handle these actions. Remember to name functions after what they specifically do. Otherwise we might end up forgetting a function, or typing in two different functions that do the same thing.</p>
<table class='simpletable centertable' style='width: 570px;'>
<caption>Table 13-1: A list of each function the Sonar game needs.</caption>
<tr><th class='simpletd'>What the code should do.</th><th class='simpletd'>The function that will do it.</th></tr>
<tr><td class='simpletd'>Prints the game board on the screen based on the <span class='m'>board</span> data structure it is passed, including the coordinates along the top, bottom, and left and right sides.</td><td class='simpletd'><span class='m'>drawBoard()</span></td></tr>
<tr><td class='simpletd'>Create a fresh <span class='m'>board</span> data structure.</td><td class='simpletd'><span class='m'>getNewBoard()</span></td></tr>
<tr><td class='simpletd'>Create a fresh <span class='m'>chests</span> data structure that has a number of chests randomly scattered across the game board.</td><td class='simpletd'><span class='m'>getRandomChests()</span></td></tr>
<tr><td class='simpletd'>Check that the XY coordinates that are passed to this function are located on the game board or not.</td><td class='simpletd'><span class='m'>isValidMove()</span></td></tr>
<tr><td class='simpletd'>Let the player type in the XY coordinates of his next move, and keep asking until they type in the coordinates correctly.</td><td class='simpletd'><span class='m'>enterPlayerMove()</span></td></tr>
<tr><td class='simpletd'>Place a sonar device on the game board, and update the <span class='m'>board</span> data structure then return a string that describes what happened.</td><td class='simpletd'><span class='m'>makeMove()</span></td></tr>
<tr><td class='simpletd'>Ask the player if they want to play another game of Sonar.</td><td class='simpletd'><span class='m'>playAgain()</span></td></tr>
<tr><td class='simpletd'>Print out instructions for the game.</td><td class='simpletd'><span class='m' style='white-space:nowrap;'>showInstructions()</span></td></tr>
</table>
<p>These might not be all of the functions we need, but a list like this is a good idea to help you get started with programming your own games. For example, when designing the <span class='m'>drawBoard()</span> function in the Sonar game, we find out we also need a function that does what <span class='m'>getRow()</span> does. Writing out a function once and then calling it twice is preferable to writing out the code twice. The whole point of functions is to reduce duplicate code down to one place, so if we ever need to make changes to that code we only need to change one place in our program.</p>
<h2 id="HowtheCodeWorksLines1to38">How the Code Works: Lines 1 to 38</h2>
<div class='sourcecode'><ol start='1'>
<li># Sonar</li>
<li></li>
<li>import random</li>
<li>import sys</li>
</ol></div>
<p>Here we import two modules, <span class='m'>random</span> and <span class='m'>sys</span>. The <span class='m'>sys</span> module contains the <span class='m'>exit()</span> function, which causes the program to immediately terminate. We will call this function later in our program.</p>
<h3 id="DrawingtheGameBoard">Drawing the Game Board</h3>
<div class='sourcecode'><ol start='6'>
<li>def drawBoard(board):</li>
</ol></div>
<p>The back tick (`) and tilde (~) characters are located next to the 1 key on your keyboard. They resemble the waves of the ocean. Somewhere in this ocean are three treasure chests, but you don't know where. You can figure it out by planting sonar devices, and tell the game program where by typing in the X and Y coordinates (which are printed on the four sides of the screen.)</p>
<p>The <span class='m'>drawBoard()</span> function is the first function we will define for our program. The sonar game's board is an ASCII-art ocean with coordinates going along the X- and Y-axis, and looks like this:</p>
<div class='sourceblurb' style='font-size: 75%'>
1 2 3 4 5<br />
012345678901234567890123456789012345678901234567890123456789<br />
<br />
0 ~~~`~``~~~``~~~~``~`~`~`~`~~`~~~`~~`~``````~~`~``~`~~```~`~` 0<br />
1 `~`~````~~``~`~```~```~```~`~~~``~~`~~~``````~`~``~~``~~`~~` 1<br />
2 ```~~~~`~`~~```~~~``~````~~`~`~~`~`~`~```~~`~``~~`~`~~~~~~`~ 2<br />
3 ~~~~`~~~``~```~``~~`~`~~`~`~~``~````~`~````~```~`~`~`~`````~ 3<br />
4 ~```~~~~~`~~````~~~~```~~~`~`~`~````~`~~`~`~~``~~`~``~`~``~~ 4<br />
5 `~```~`~`~~`~~~```~~``~``````~~``~`~`~~~~`~~``~~~~~~`~```~~` 5<br />
6 ``~~`~~`~``~`````~````~~``~`~~~~`~~```~~~``~`~`~~``~~~```~~~ 6<br />
7 ``~``~~~~~~```~`~```~~~``~`~``~`~~~~~~```````~~~`~~`~~`~~`~~ 7<br />
8 ~~`~`~~```~``~~``~~~``~~`~`~~`~`~```~```~~~```~~~~~~`~`~~~~` 8<br />
9 ```~``~`~~~`~~```~``~``~~~```~````~```~`~~`~~~~~`~``~~~~~``` 9<br />
10 `~~~~```~`~````~`~`~~``~`~~~~`~``~``~```~~```````~`~``~````` 10<br />
11 ~~`~`~~`~``~`~~~````````````````~~`````~`~~``~`~~~~`~~~`~~`~ 11<br />
12 ~~`~~~~```~~~`````~~``~`~`~~``````~`~~``~```````~~``~~~`~~`~ 12<br />
13 `~``````~~``~`~~~```~~~~```~~`~`~~~`~```````~~`~```~``~`~~~~ 13<br />
14 ~~~``~```~`````~~`~`~``~~`~``~`~~`~`~``~`~``~~``~`~``~```~~~ 14<br />
<br />
012345678901234567890123456789012345678901234567890123456789<br />
1 2 3 4 5<br />
</div>
<p>We will split up the drawing in the <span class='m'>drawBoard()</span> function into four steps. First, we create a string variable of the line with 1, 2, 3, 4, and 5 spaced out with wide gaps (to mark the coordinates for 10, 20, 30, 40, and 50). Second, we use that string to display the X-axis coordinates along the top of the screen. Third, we print each row of the ocean along with the Y-axis coordinates on both sides of the screen. And fourth, we print out the X-axis again at the bottom. Having the coordinates on all sides makes it easier for the player to move their finger along the spaces to see where exactly they want to plan a sonar device.</p>
<h3 id="DrawingtheXcoordinatesAlongtheTop">Drawing the X-coordinates Along the Top</h3>
<div class='sourcecode'><ol start='7'>
<li> # Draw the board data structure.</li>
<li></li>
<li> hline = ' ' # initial space for the numbers down the left side of the board</li>
<li> for i in range(1, 6):</li>
<li> hline += (' ' * 9) + str(i)</li>
</ol></div>
<p>Let's look again at the top part of the board, this time with plus signs instead of blank spaces so we can count the spaces easier:</p>
<p class='centeredImageP'><img src='images/13-3.png' alt='' class='centeredImage' /><br />Figure 13-3: The spacing we use for printing the top of the game board.</p>
<p>The numbers on the first line which mark the tens position all have nine spaces in between them, and there are thirteen spaces in front of the 1. We are going to create a string with this line and store it in a variable named <span class='m'>hline</span>.</p>
<div class='sourcecode'><ol start='13'>
<li> # print the numbers across the top</li>
<li> print(hline)</li>
<li> print(' ' + ('0123456789' * 6))</li>
<li> print()</li>
</ol></div>
<p>To print the numbers across the top of the sonar board, we first print the contents of the <span class='m'>hline</span> variable. Then on the next line, we print three spaces (so that this row lines up correctly), and then print the string <span class='m'>'012345678901234567890123456789012345678901234567890123456789'</span>. But this is tedious to type into the source, so instead we type (<span class='m'>'0123456789' * 6</span>) which evaluates to the same string.</p>
<h3 id="DrawingtheRowsoftheOcean">Drawing the Rows of the Ocean</h3>
<div class='sourcecode'><ol start='18'>
<li> # print each of the 15 rows</li>
<li> for i in range(15):</li>
<li> # single-digit numbers need to be padded with an extra space</li>
<li> if i < 10:</li>
<li> extraSpace = ' '</li>
<li> else:</li>
<li> extraSpace = ''</li>
<li> print('%s%s %s %s' % (extraSpace, i, getRow(board, i), i))</li>
</ol></div>
<p>Now we print the each row of the board, including the numbers down the side to label the Y-axis. We use the <span class='m'>for</span> loop to print rows 0 through 14 on the board, along with the row numbers on either side of the board.</p>
<p>We have a small problem. Numbers with only one digit (like 0, 1, 2, and so on) only take up one space when we print them out, but numbers with two digits (like 10, 11, and 12) take up two spaces. This means the rows might not line up and would look like this:</p>
<div class='sourceblurb' style='font-size: 75%'>
8 ~~`~`~~```~``~~``~~~``~~`~`~~`~`~```~```~~~```~~~~~~`~`~~~~` 8<br />
9 ```~``~`~~~`~~```~``~``~~~```~````~```~`~~`~~~~~`~``~~~~~``` 9<br />
10 `~~~~```~`~````~`~`~~``~`~~~~`~``~``~```~~```````~`~``~````` 10<br />
11 ~~`~`~~`~``~`~~~````````````````~~`````~`~~``~`~~~~`~~~`~~`~ 11<br />
</div>
<p>The solution is easy. We just add a space in front of all the single-digit numbers. The <span class='m'>if</span>-<span class='m'>else</span> statement that starts on line 21 does this. We will print the variable <span class='m'>extraSpace</span> when we print the row, and if <span class='m'>i</span> is less than <span class='m'>10</span> (which means it will have only one digit), we assign a single space string to <span class='m'>extraSpace</span>. Otherwise, we set <span class='m'>extraSpace</span> to be a blank string. This way, all of our rows will line up when we print them.</p>
<p>The <span class='m'>getRow()</span> function will return a string representing the row number we pass it. Its two parameters are the board data structure stored in the <span class='m'>board</span> variable and a row number. We will look at this function next.</p>
<h3 id="DrawingtheXcoordinatesAlongtheBottom">Drawing the X-coordinates Along the Bottom</h3>
<div class='sourcecode'><ol start='27'>
<li> # print the numbers across the bottom</li>
<li> print()</li>
<li> print(' ' + ('0123456789' * 6))</li>
<li> print(hline)</li>
</ol></div>
<p>This code is similar to lines 13 to 16. This will print the X-axis coordinates along the bottom of the screen.</p>
<h3 id="GettingtheStateofaRowintheOcean">Getting the State of a Row in the Ocean</h3>
<div class='sourcecode'><ol start='33'>
<li>def getRow(board, row):</li>
<li> # Return a string from the board data structure at a certain row.</li>
<li> boardRow = ''</li>
<li> for i in range(60):</li>
<li> boardRow += board[i][row]</li>
<li> return boardRow</li>
</ol></div>
<p>This function constructs a string called <span class='m'>boardRow</span> from the characters stored in <span class='m'>board</span>. First we set <span class='m'>boardRow</span> to the blank string. The row number (which is the Y coordinate) is passed as a parameter. The string we want is made by concatenating <span class='m'>board[0][row]</span>, <span class='m'>board[1][row]</span>, <span class='m'>board[2][row]</span>, and so on up to <span class='m'>board[59][row]</span>. (This is because the row is made up of 60 characters, from index <span class='m'>0</span> to index <span class='m'>59</span>.)</p>
<p>The <span class='m'>for</span> loop iterates from integers <span class='m'>0</span> to <span class='m'>59</span>. On each iteration the next character in the board data structure is copied on to the end of <span class='m'>boardRow</span>. By the time the loop is done, <span class='m'>boardRow</span> is fully formed, so we return it.</p>
<h2 id="HowtheCodeWorksLines40to62">How the Code Works: Lines 40 to 62</h2>
<p>Now that we have a function to print a given board data structure to the string, let's turn to the other functions that we will need. At the start of the game, we will need to create a new game board data structure (kind of like a blank Tic Tac Toe board) and also place treasure chests randomly around the board. We should also create a function that can tell if the coordinates entered by the player are a valid move or not.</p>
<h3 id="CreatingaNewGameBoard">Creating a New Game Board</h3>
<div class='sourcecode'><ol start='40'>
<li>def getNewBoard():</li>
<li> # Create a new 60x15 board data structure.</li>
<li> board = []</li>
<li> for x in range(60): # the main list is a list of 60 lists</li>
<li> board.append([])</li>
</ol></div>
<p>At the start of each new game, we will need a fresh <span class='m'>board</span> data structure. The <span class='m'>board</span> data structure is a list of lists of strings. The first list represents the X coordinate. Since our game's board is 60 characters across, this first list needs to contain 60 lists. So we create a <span class='m'>for</span> loop that will append 60 blank lists to it.</p>
<div class='sourcecode'><ol start='45'>
<li> for y in range(15): # each list in the main list has 15 single-character strings</li>
<li> # use different characters for the ocean to make it more readable.</li>
<li> if random.randint(0, 1) == 0:</li>
<li> board[x].append('~')</li>
<li> else:</li>
<li> board[x].append('`')</li>
</ol></div>
<p>But <span class='m'>board</span> is more than just a list of 60 blank lists. Each of the 60 lists represents the Y coordinate of our game board. There are 15 rows in the board, so each of these 60 lists must have 15 characters in them. We have another <span class='m'>for</span> loop to add 15 single-character strings that represent the ocean. The "ocean" will just be a bunch of <span class='m'>'~'</span> and <span class='m'>'`'</span> strings, so we will randomly choose between those two. We can do this by generating a random number between <span class='m'>0</span> and <span class='m'>1</span> with a call to <span class='m'>random.randint()</span>. If the return value of <span class='m'>random.randint()</span> is<span class='m'> 0</span>, we add the <span class='m'>'~'</span> string. Otherwise we will add the <span class='m'>'`'</span> string.</p>
<p>This is like deciding which character to use by tossing a coin. And since the return value from <span class='m'>random.randint()</span> will be 0 about half the time, half of the ocean characters will be <span class='m'>'~'</span> and the other half will be <span class='m'>'`'</span>. This will give our ocean a random, choppy look to it.</p>
<p>Remember that the <span class='m'>board</span> variable is a list of 60 lists that have 15 strings. That means to get the string at coordinate 26, 12, we would access <span class='m'>board[26][12]</span>, and not <span class='m'>board[12][26]</span>. The X coordinate is first, then the Y coordinate.</p>
<p>Figure 13-4 is the picture to demonstrate the indexes of a list of lists named <span class='m'>x</span>. The red arrows point to indexes of the inner lists themselves. The image is also flipped on its side to make it easier to read:</p>
<p class='centeredImageP createspace'><img width='570' height='374' src='images/13-4.png' alt='' class='centeredImage' /><br />Figure 13-4: The indexes of a list of lists.</p>
<p class='centeredImageP noncreatespace'><img src='images/13-4.png' alt='' class='centeredImage' /><br />Figure 13-4: The indexes of a list of lists.</p>
<div class='sourcecode'><ol start='51'>
<li> return board</li>
</ol></div>
<p>Finally, we return the <span class='m'>board</span> variable. Remember that in this case, we are returning a reference to the list that we made. Any changes we made to the list (or the lists inside the list) in our function will still be there outside of the function.</p>
<h3 id="CreatingtheRandomTreasureChests">Creating the Random Treasure Chests</h3>
<div class='sourcecode'><ol start='53'>
<li>def getRandomChests(numChests):</li>
<li> # Create a list of chest data structures (two-item lists of x, y int coordinates)</li>
<li> chests = []</li>
<li> for i in range(numChests):</li>
<li> chests.append([random.randint(0, 59), random.randint(0, 14)])</li>
<li> return chests</li>
</ol></div>
<p>Another task we need to do at the start of the game is decide where the hidden treasure chests are. We will represent the treasure chests in our game as a list of lists of two integers. These two integers will be the X and Y coordinates. For example, if the chest data structure was <span class='m'>[[2, 2], [2, 4], [10, 0]]</span>, then this would mean there are three treasure chests, one at 2, 2, another at 2, 4, and a third one at 10, 0.</p>
<p>We will pass the <span class='m'>numChests</span> parameter to tell the function how many treasure chests we want it to generate. We set up a <span class='m'>for</span> loop to iterate this number of times, and on each iteration we append a list of two random integers. The X coordinate can be anywhere from 0 to 59, and the Y coordinate can be from anywhere between 0 and 14. The expression <span class='m'>[random.randint(0, 59), random.randint(0, 14)]</span> that is passed to the append method will evaluate to something like <span class='m'>[2, 2]</span> or <span class='m'>[2, 4]</span> or <span class='m'>[10, 0]</span>. This data structure is then returned.</p>
<h3 id="DeterminingifaMoveisValid">Determining if a Move is Valid</h3>
<div class='sourcecode'><ol start='60'>
<li>def isValidMove(x, y):</li>
<li> # Return True if the coordinates are on the board, otherwise False.</li>
<li> return x >= 0 and x <= 59 and y >= 0 and y <= 14</li>
</ol></div>
<p>The player will type in X and Y coordinates of where they want to drop a sonar device. But they may not type in coordinates that do not exist on the game board. The X coordinates must be between 0 and 59, and the Y coordinate must be between 0 and 14. This function uses a simple expression that uses <span class='m'>and</span> operators to ensure that each part of the condition is <span class='m'>True</span>. If just one is <span class='m'>False</span>, then the entire expression evaluates to <span class='m'>False</span>. This Boolean value is returned by the function.</p>
<h2 id="HowtheCodeWorksLines64to91">How the Code Works: Lines 64 to 91</h2>
<h3 id="PlacingaMoveontheBoard">Placing a Move on the Board</h3>
<div class='sourcecode'><ol start='64'>
<li>def makeMove(board, chests, x, y):</li>
<li> # Change the board data structure with a sonar device character. Remove treasure chests</li>
<li> # from the chests list as they are found. Return False if this is an invalid move.</li>
<li> # Otherwise, return the string of the result of this move.</li>
<li> if not isValidMove(x, y):</li>
<li> return False</li>
</ol></div>
<p>In our Sonar game, the game board is updated to display a number for each sonar device dropped. The number shows how far away the closest treasure chest is. So when the player makes a move by giving the program an X and Y coordinate, we will change the board based on the positions of the treasure chests. This is why our <span class='m'>makeMove()</span> function takes four parameters: the game board data structure, the treasure chests data structures, and the X and Y coordinates.</p>
<p>This function will return the <span class='m'>False</span> Boolean value if the X and Y coordinates if was passed do not exist on the game board. If <span class='m'>isValidMove()</span> returns <span class='m'>False</span>, then <span class='m'>makeMove()</span> will return <span class='m'>False</span>.</p>
<p>If the coordinates land directly on the treasure, <span class='m'>makeMove()</span> will return the string <span class='m'>'You have found a sunken treasure chest!'</span>. If the XY coordinates are within a distance of 9 or less of a treasure chest, we return the string <span class='m'>'Treasure detected at a distance of <span class='m'>%s</span> from the sonar device.'</span> (where <span class='m'>%s</span> is the distance). Otherwise, <span class='m'>makeMove()</span> will return the string <span class='m'>'Sonar did not detect anything. All treasure chests out of range.'</span>.</p>
<div class='sourcecode'><ol start='71'>
<li> smallestDistance = 100 # any chest will be closer than 100.</li>
<li> for cx, cy in chests:</li>
<li> if abs(cx - x) > abs(cy - y):</li>
<li> distance = abs(cx - x)</li>
<li> else:</li>
<li> distance = abs(cy - y)</li>
<li></li>
<li> if distance < smallestDistance: # we want the closest treasure chest.</li>
<li> smallestDistance = distance</li>
</ol></div>
<p>Given the XY coordinates of where the player wants to drop the sonar device, and a list of XY coordinates for the treasure chests (in the <span class='m'>chests</span> list of lists), we will need an algorithm to find out which treasure chest is closest.</p>
<h3 id="AnAlgorithmforFindingtheClosestTreasureChest">An Algorithm for Finding the Closest Treasure Chest</h3>
<p>While the <span class='m'>x</span> and <span class='m'>y</span> variables are just integers (say, <span class='m'>5</span> and <span class='m'>0</span>), together they represent the location on the game board (which is a Cartesian coordinate system) where the player guessed. The <span class='m'>chests</span> variable may have a value such as <span class='m'>[[5, 0], [0, 2], [4, 2]]</span>, that value represents the locations of three treasure chests. Even though these variables are a bunch of numbers, we can visualize it like this:</p>
<p class='centeredImageP'><img src='images/13-5.png' alt='' class='centeredImage' /><br />Figure 13-5: The places on the board that <span class='m'>[[5, 0], [0, 2], [4, 2]]</span> represents.
</p>
<p>We figure out the distance from the sonar device located at 0, 2 with "rings" and the distances around it:</p>
<p class='centeredImageP'><img src='images/13-6.png' alt='' class='centeredImage' /><br />Figure 13-6: The board marked with distances from the 0, 2 position.</p>
<p>But how do we translate this into code for our game? We need a way to represent distance as an expression. Notice that the distance from an XY coordinate is always the larger of two values: the absolute value of the difference of the two X coordinates and the absolute value of the difference of the two Y coordinates.</p>
<p>That means we should subtract the sonar device's X coordinate and a treasure chest's X coordinate, and then take the absolute value of this number. We do the same for the sonar device's Y coordinate and a treasure chest's Y coordinate. The larger of these two values is the distance. Let's look at our example board with rings above to see if this algorithm is correct.</p>
<p>The sonar's X and Y coordinates are 3 and 2. The first treasure chest's X and Y coordinates (first in the list <span class='m'>[[5, 0], [0, 2], [4, 2]]</span> that is) are 5 and 0.</p>
<p>For the X coordinates, <span class='m'>3 - 5</span> evaluates to <span class='m'>-2</span>, and the absolute value of <span class='m'>-2</span> is <span class='m'>2</span>.</p>
<p>For the Y coordinates, <span class='m'>2 - 1</span> evaluates to <span class='m'>1</span>, and the absolute value of <span class='m'>1</span> is <span class='m'>1</span>.</p>
<p>Comparing the two absolute values <span class='m'>2</span> and <span class='m'>1</span>, the larger value is <span class='m'>2</span> so that should be the distance between the sonar device and the treasure chest at coordinates 5, 1. We can look at the board and see that this algorithm works, because the treasure chest at 5,1 is in the sonar device's 2nd ring. Let's quickly compare the other two chests to see if his distances work out correctly also.</p>
<p>The <span class='m'>abs()</span> function returns the absolute value of the number we pass to it. Let's find the distance from the sonar device at 3,2 and the treasure chest at 0,2: <span class='m'>abs(3 - 0)</span> evaluates to <span class='m'>3</span>. <span class='m'>abs(2 - 2)</span> evaluates to <span class='m'>0</span>. <span class='m'>3</span> is larger than <span class='m'>0</span>, so the distance from the sonar device at 3,2 and the treasure chest at 0,2 is <span class='m'>3</span>. We can look at the board and see this is true.</p>
<p>Let's find the distance from the sonar device at 3,2 and the last treasure chest at 4,2. <span class='m'>abs(3 - 4)</span> evaluates to 1. <span class='m'>abs(2 - 2)</span> evaluates to <span class='m'>0</span>. <span class='m'>1</span> is larger than <span class='m'>0</span>, so the distance from the sonar device at 3,2 and the treasure chest at 4,2 is <span class='m'>1</span>. We look at the board and see this is true also.</p>
<p>All three distances worked out correctly, so it seems our algorithm works. The distances from the sonar device to the three sunken treasure chests are <span class='m'>2</span>, <span class='m'>3</span>, and <span class='m'>1</span>. On each guess, we want to know the distance from the sonar device to the closest of the three treasure chest distances. To do this we use a variable called <span class='m'>smallestDistance</span>. Let's look at the code again:</p>
<div class='sourcecode'><ol start='71'>
<li> smallestDistance = 100 # any chest will be closer than 100.</li>
<li> for cx, cy in chests:</li>
<li> if abs(cx - x) > abs(cy - y):</li>
<li> distance = abs(cx - x)</li>
<li> else:</li>
<li> distance = abs(cy - y)</li>
<li></li>
<li> if distance < smallestDistance: # we want the closest treasure chest.</li>
<li> smallestDistance = distance</li>
</ol></div>
<p>You can also use multiple assignment in <span class='m'>for</span> loops. Remember, the assignment statement <span class='m'>a, b = [5, 10]</span> will assign <span class='m'>5</span> to <span class='m'>a</span> and <span class='m'>10</span> to <span class='m'>b</span>. Also, the <span class='m'>for</span> loop <span class='m'>for i in [0, 1, 2, 3, 4]</span> will assign the <span class='m'>i</span> variable the values <span class='m'>0</span> and <span class='m'>1</span> and so on for each iteration.</p>
<p><span class='m'>for cx, cy in chests:</span> combines both of these principles. Because <span class='m'>chests</span> is a list where each item in the list is itself a list of two integers, the first of these integers is assigned to <span class='m'>cx</span> and the second integer is assigned to <span class='m'>cy</span>. So if <span class='m'>chests</span> has the value <span class='m'>[[5, 0], [0, 2], [4, 2]]</span>, <span class='m'>cx</span> will have the value <span class='m'>5</span> and <span class='m'>cy</span> will have the value <span class='m'>0</span> on the first iteration through the loop.</p>
<p>Line 73 determines which is larger: the absolute value of the difference of the X coordinates, or the absolute value of the difference of the Y coordinates. (<span class='m'>abs(cx - x) < abs(cy - y)</span> seems like much easier way to say that, doesn't it?). The <span class='m'>if</span>-else statement assigns the larger of the values to the <span class='m'>distance</span> variable.</p>
<p>So on each iteration of the <span class='m'>for</span> loop, the <span class='m'>distance</span> variable holds the distance of a treasure chest's distance from the sonar device. But we want the shortest (that is, smallest) distance of all the treasure chests. This is where the <span class='m'>smallestDistance</span> variable comes in. Whenever the <span class='m'>distance</span> variable is smaller than <span class='m'>smallestDistance</span>, then the value in <span class='m'>distance</span> becomes the new value of <span class='m'>smallestDistance</span>.</p>
<p>We give <span class='m'>smallestDistance</span> the impossibly high value of <span class='m'>100</span> at the beginning of the loop so that at least one of the treasure chests we find will be put into <span class='m'>smallestDistance</span>. By the time the <span class='m'>for</span> loop has finished, we know that <span class='m'>smallestDistance</span> holds the shortest distance between the sonar device and all of the treasure chests in the game.</p>
<div class='sourcecode'><ol start='81'>
<li> if smallestDistance == 0:</li>
<li> # xy is directly on a treasure chest!</li>
<li> chests.remove([x, y])</li>
<li> return 'You have found a sunken treasure chest!'</li>
</ol></div>
<p>The only time that <span class='m'>smallestDistance</span> is equal to 0 is when the sonar device's XY coordinates are the same as a treasure chest's XY coordinates. This means the player has correctly guessed the location of a treasure chest. We should remove this chest's two-integer list from the <span class='m'>chests</span> data structure with the <span class='m'>remove()</span> list method.</p>
<h2 id="TheremoveListMethod">The <span class='m'>remove()</span> List Method</h2>
<p>The <span class='m'>remove()</span> list method will remove the first occurrence of the value passed as a parameter from the list. For example, try typing the following into the interactive shell:</p>
<div class='sourceblurb'>
>>> x = [42, 5, 10, 42, 15, 42]<br />
>>> x.remove(10)<br />
>>> x<br />
[42, 5, 42, 15, 42]<br />
</div>
<p>You can see that the <span class='m'>10</span> value has been removed from the <span class='m'>x</span> list. The <span class='m'>remove()</span> method removes the first occurrence of the value you pass it, and only the first. For example, type the following into the shell:</p>
<div class='sourceblurb'>
>>> x = [42, 5, 10, 42, 15, 42]<br />
>>> x.remove(42)<br />
>>> x<br />
[5, 10, 42, 15, 42]<br />
</div>
<p>Notice that only the first <span class='m'>42</span> value was removed, but the second and third ones are still there. The <span class='m'>remove()</span> method will cause an error if you try to remove a value that is not in the list:</p>
<div class='sourceblurb'>
>>> x = [5, 42]<br />
>>> x.remove(10)<br />
Traceback (most recent call last):<br />
File "<stdin>", line 1, in <module><br />
ValueError: list.remove(x): x not in list<br />
>>><br />
</div>
<p>After removing the found treasure chest from the <span class='m'>chests</span> list, we return the string <span class='m'>'You have found a sunken treasure chest!'</span> to tell the caller that the guess was correct. Remember that any changes made to the list in a function will exist outside the function as well.</p>
<div class='sourcecode'><ol start='85'>
<li> else:</li>
<li> if smallestDistance < 10:</li>
<li> board[x][y] = str(smallestDistance)</li>
<li> return 'Treasure detected at a distance of %s from the sonar device.' % (smallestDistance)</li>
<li> else:</li>
<li> board[x][y] = 'O'</li>
<li> return 'Sonar did not detect anything. All treasure chests out of range.'</li>
</ol></div>
<p>The <span class='m'>else</span> block executes if <span class='m'>smallestDistance</span> was not <span class='m'>0</span>, which means the player did not guess an exact location of a treasure chest. We return two different strings, depending on if the sonar device was placed within range of any of the treasure chests. If it was, we mark the board with the string version of <span class='m'>smallestDistance</span>. If not, we mark the board with a <span class='m'>'0'</span>.</p>
<h2 class='pagebreaker' id="HowtheCodeWorksLines94to162">How the Code Works: Lines 94 to 162</h2>
<p>The last few functions we need are to let the player enter their move on the game board, ask the player if he wants to play again (this will be called at the end of the game), and print the instructions for the game on the screen (this will be called at the beginning of the game).</p>
<h3 id="GettingthePlayersMove">Getting the Player's Move</h3>
<div class='sourcecode'><ol start='94'>
<li>def enterPlayerMove():</li>
<li> # Let the player type in her move. Return a two-item list of int xy coordinates.</li>
<li> print('Where do you want to drop the next sonar device? (0-59 0-14) (or type quit)')</li>
<li> while True:</li>
<li> move = input()</li>
<li> if move.lower() == 'quit':</li>
<li> print('Thanks for playing!')</li>
<li> sys.exit()</li>
</ol></div>
<p>This function collects the XY coordinates of the player's next move. It has a <span class='m'>while</span> loop so that it will keep asking the player for her next move. The player can also type in <span class='m'>quit</span> in order to quit the game. In that case, we call the <span class='m'>sys.exit()</span> function which immediately terminates the program.</p>
<div class='sourcecode'><ol start='103'>
<li> move = move.split()</li>
<li> if len(move) == 2 and move[0].isdigit() and move[1].isdigit() and isValidMove(int(move[0]), int(move[1])):</li>
<li> return [int(move[0]), int(move[1])]</li>
<li> print('Enter a number from 0 to 59, a space, then a number from 0 to 14.')</li>
</ol></div>
<p>Assuming the player has not typed in <span class='m'>'quit'</span>, we call the <span class='m'>split()</span> method on <span class='m'>move</span> and set the list it returns as the new value of <span class='m'>move</span>. What we expect <span class='m'>move</span> to be is a list of two numbers. These numbers will be strings, because the <span class='m'>split()</span> method returns a list of strings. But we can convert these to integers with the <span class='m'>int()</span> function.</p>
<p>If the player typed in something like <span class='m'>'1 2 3'</span>, then the list returned by <span class='m'>split()</span> would be <span class='m'>['1', '2', '3']</span>. In that case, the expression <span class='m'>len(move) == 2</span> would be <span class='m'>False</span> and the entire expression immediately evaluates to <span class='m'>False</span> (because of short-circuiting as described in chapter 10.)</p>
<p>If the list returned by <span class='m'>split()</span> does have a length of <span class='m'>2</span>, then it will have a <span class='m'>move[0]</span> and <span class='m'>move[1]</span>. We call the string method <span class='m'>isdigit()</span> on those strings. <span class='m'>isdigit()</span> will return <span class='m'>True</span> if the string consists solely of numbers. Otherwise it returns <span class='m'>False</span>. Try typing the following into the interactive shell:</p>
<div class='sourceblurb'>
>>> '42'.isdigit()<br />
True<br />
>>> 'forty'.isdigit()<br />
False<br />
>>> ''.isdigit()<br />
False<br />
>>> 'hello'.isdigit()<br />
False<br />
>>> x = '10'<br />
>>> x.isdigit()<br />
True<br />
>>><br />
</div>
<p>As you can see, both <span class='m'>move[0].isdigit()</span> and <span class='m'>move[1].isdigit()</span> must be <span class='m'>True</span> for the whole condition to be <span class='m'>True</span>. The final part of this expression calls our <span class='m'>move[1]</span> function to check if the XY coordinates exist on the board. If all these expressions are <span class='m'>True</span>, then this function returns a two-integer list of the XY coordinates. Otherwise, the player will be asked to enter coordinates again.</p>
<h3 id="AskingthePlayertoPlayAgain">Asking the Player to Play Again</h3>
<div class='sourcecode'><ol start='109'>
<li>def playAgain():</li>
<li> # This function returns True if the player wants to play again, otherwise it returns False.</li>
<li> print('Do you want to play again? (yes or no)')</li>
<li> return input().lower().startswith('y')</li>
</ol></div>
<p>The <span class='m'>playAgain()</span> function will ask the player if they want to play again, and will keep asking until the player types in a string that begins with <span class='m'>'y'</span>.</p>
<h3 id="PrintingtheGameInstructionsforthePlayer">Printing the Game Instructions for the Player</h3>
<div class='sourcecode'><ol start='115'>
<li>def showInstructions():</li>
<li> print('''Instructions:</li>
<li>You are the captain of the Simon, a treasure-hunting ship. Your current mission</li>
<li>is to find the three sunken treasure chests that are lurking in the part of the</li>
<li>ocean you are in and collect them.</li>
<li></li>
<li>To play, enter the coordinates of the point in the ocean you wish to drop a</li>
<li>sonar device. The sonar can find out how far away the closest chest is to it.</li>
<li>For example, the d below marks where the device was dropped, and the 2's</li>
<li>represent distances of 2 away from the device. The 4's represent</li>
<li>distances of 4 away from the device.</li>
<li></li>
<li> 444444444</li>
<li> 4 4</li>
<li> 4 22222 4</li>
<li> 4 2 2 4</li>
<li> 4 2 d 2 4</li>
<li> 4 2 2 4</li>
<li> 4 22222 4</li>
<li> 4 4</li>
<li> 444444444</li>
<li>Press enter to continue...''')</li>
<li> input()</li>
</ol></div>
<p>The <span class='m'>showInstructions()</span> is just a couple of <span class='m'>print()</span> calls that print multi-line strings. The <span class='m'>input()</span> function just gives the player a chance to press Enter before printing the next string. This is because the screen can only show 25 lines of text at a time.</p>
<div class='sourcecode'><ol start='139'>
<li> print('''For example, here is a treasure chest (the c) located a distance of 2 away</li>
<li>from the sonar device (the d):</li>
<li></li>
<li> 22222</li>
<li> c 2</li>
<li> 2 d 2</li>
<li> 2 2</li>
<li> 22222</li>
<li></li>
<li>The point where the device was dropped will be marked with a 2.</li>
<li></li>
<li>The treasure chests don't move around. Sonar devices can detect treasure</li>
<li>chests up to a distance of 9. If all chests are out of range, the point</li>
<li>will be marked with O</li>
<li></li>
<li>If a device is directly dropped on a treasure chest, you have discovered</li>
<li>the location of the chest, and it will be collected. The sonar device will</li>
<li>remain there.</li>
<li></li>
<li>When you collect a chest, all sonar devices will update to locate the next</li>
<li>closest sunken treasure chest.</li>
<li>Press enter to continue...''')</li>
<li> input()</li>
<li> print()</li>
</ol></div>
<p>This is the rest of the instructions in one multi-line string. After the player presses Enter, the function returns. These are all of the functions we will define for our game. The rest of the program is the main part of our game.</p>
<h2 id="HowtheCodeWorksLines165to217">How the Code Works: Lines 165 to 217</h2>
<p>Now that we are done writing all of the functions our game will need, let's start the main part of the program.</p>
<h3 id="TheStartoftheGame">The Start of the Game</h3>
<div class='sourcecode'><ol start='165'>
<li>print('S O N A R !')</li>
<li>print()</li>
<li>print('Would you like to view the instructions? (yes/no)')</li>
<li>if input().lower().startswith('y'):</li>
<li> showInstructions()</li>
</ol></div>
<p>The expression <span class='m'>input().lower().startswith('y')</span> asks the player if they want to see the instructions, and evaluates to <span class='m'>True</span> if the player typed in a string that began with <span class='m'>'y'</span> or <span class='m'>'Y'</span>. If so, <span class='m'>showInstructions()</span> is called.</p>
<div class='sourcecode'><ol start='171'>
<li>while True:</li>
<li> # game setup</li>
<li> sonarDevices = 16</li>
<li> theBoard = getNewBoard()</li>
<li> theChests = getRandomChests(3)</li>
<li> drawBoard(theBoard)</li>
<li> previousMoves = []</li>
</ol></div>
<p>This <span class='m'>while</span> loop is the main loop for this game. Here are what the variables are for:</p>
<table class='simpletable' style='width: 570px;'>
<caption>Table 13-2: Variables used in the main game loop.</caption>
<tr><th class='simpletd'>Variable</th><th class='simpletd'>Description</th></tr>
<tr><td class='simpletd'>sonarDevices</td><td class='simpletd'>The number of sonar devices (and turns) the player has left.</td></tr>
<tr><td class='simpletd'>theBoard</td><td class='simpletd'>The board data structure we will use for this game.</td></tr>
<tr><td class='simpletd'>theChests</td><td class='simpletd'>The list of chest data structures. <span class='m'>getRandomChests()</span> will return a list of three treasure chests at random places on the board.</td></tr>
<tr><td class='simpletd'>previousMoves</td><td class='simpletd'>A list of all the XY moves that the player has made in the game.</td></tr>
</table>
<h3 id="DisplayingtheGameStatusforthePlayer">Displaying the Game Status for the Player</h3>
<div class='sourcecode'><ol start='179'>
<li> while sonarDevices > 0:</li>
<li> # Start of a turn:</li>
<li></li>
<li> # show sonar device/chest status</li>
<li> if sonarDevices > 1: extraSsonar = 's'</li>
<li> else: extraSsonar = ''</li>
<li> if len(theChests) > 1: extraSchest = 's'</li>
<li> else: extraSchest = ''</li>
<li> print('You have %s sonar device%s left. %s treasure chest%s remaining.' % (sonarDevices, extraSsonar, len(theChests), extraSchest))</li>
</ol></div>
<p>This <span class='m'>while</span> loop executes as long as the player has sonar devices remaining. We want to print a message telling the user how many sonar devices and treasure chests are left. But there is a problem. If there are two or more sonar devices left, we want to print <span class='m'>'2 sonar devices'</span>. But if there is only one sonar device left, we want to print <span class='m'>'1 sonar device'</span> left. We only want the plural form of "devices" if there are multiple sonar devices. The same goes for <span class='m'>'2 treasure chests'</span> and <span class='m'>'1 treasure chest'</span>.</p>
<p>Notice on lines 183 through 186 that we have code after the <span class='m'>if</span> and <span class='m'>else</span> statements' colon. This is perfectly valid Python. Instead of having a block of code after the statement, instead you can just use the rest of the same line to make your code more concise. (Of course, this means you can only have one line of code.) This applies to any statement that uses colons, including <span class='m'>while</span> and <span class='m'>for</span> loops.</p>
<p>So we have two string variables named <span class='m'>extraSsonar</span> and <span class='m'>extraSchest</span>, which are set to <span class='m'>' '</span> (space) if there are multiple sonar devices or treasures chests. Otherwise, they are blank. We use them in the <span class='m'>while</span> statement on line 187.</p>
<h3 id="GettingthePlayersMove">Getting the Player's Move</h3>
<div class='sourcecode'><ol start='189'>
<li> x, y = enterPlayerMove()</li>
<li> previousMoves.append([x, y]) # we must track all moves so that sonar devices can be updated.</li>
<li></li>
<li> moveResult = makeMove(theBoard, theChests, x, y)</li>
<li> if moveResult == False:</li>
<li> continue</li>
</ol></div>
<p>Line 189 uses the multiple assignment trick. <span class='m'>enterPlayerMove()</span> returns a two-item list. The first item will be stored in the <span class='m'>x</span> variable and the second will be stored in the <span class='m'>y</span> variable. We then put these two variables into another two-item list, which we store in the <span class='m'>previousMoves</span> list with the <span class='m'>append()</span> method. This means <span class='m'>previousMoves</span> is a list of XY coordinates of each move the player makes in this game.</p>
<p>The <span class='m'>x</span> and <span class='m'>y</span> variables, along with <span class='m'>theBoard</span> and <span class='m'>theChests</span> (which represent the current state of the game board) are all sent to the <span class='m'>makeMove()</span> function. As we have already seen, this function will make the necessary modifications to the game board. If <span class='m'>makeMove()</span> returns the value <span class='m'>False</span>, then there was a problem with the <span class='m'>x</span> and <span class='m'>y</span> values we passed it. The <span class='m'>continue</span> statement will send the execution back to the start of the <span class='m'>while</span> loop that began on line 179 to ask the player for XY coordinates again.</p>
<h3 id="FindingaSunkenTreasureChest">Finding a Sunken Treasure Chest</h3>
<div class='sourcecode'><ol start='195'>
<li> else:</li>
<li> if moveResult == 'You have found a sunken treasure chest!':</li>
<li> # update all the sonar devices currently on the map.</li>
<li> for x, y in previousMoves:</li>
<li> makeMove(theBoard, theChests, x, y)</li>
<li> drawBoard(theBoard)</li>
<li> print(moveResult)</li>
</ol></div>
<p>If <span class='m'>makeMove()</span> did not return the value <span class='m'>False</span>, it would have returned a string that tells us what were the results of that move. If this string was <span class='m'>'You have found a sunken treasure chest!'</span>, then that means we should update all the sonar devices on the board so they detect the next closest treasure chest on the board. We have the XY coordinates of all the sonar devices currently on the board stored in <span class='m'>previousMoves</span>. So we can just pass all of these XY coordinates to the <span class='m'>makeMove()</span> function again to have it redraw the values on the board.</p>
<p>We don't have to worry about this call to <span class='m'>makeMove()</span> having errors, because we already know all the XY coordinates in <span class='m'>previousMoves</span> are valid. We also know that this call to <span class='m'>makeMove()</span> won't find any new treasure chests, because they would have already been removed from the board when that move was first made.</p>
<p>The <span class='m'>for</span> loop on line 198 also uses the same multiple assignment trick for <span class='m'>x</span> and <span class='m'>y</span> because the items in <span class='m'>previousMoves</span> list are themselves two-item lists. Because we don't print anything here, the player doesn't realize we are redoing all of the previous moves. It just appears that the board has been entirely updated.</p>
<h3 id="CheckingifthePlayerhasWon">Checking if the Player has Won</h3>
<div class='sourcecode'><ol start='203'>
<li> if len(theChests) == 0:</li>
<li> print('You have found all the sunken treasure chests! Congratulations and good game!')</li>
<li> break</li>
</ol></div>
<p>Remember that the <span class='m'>makeMove()</span> function modifies the <span class='m'>theChests</span> list we send it. Because <span class='m'>theChests</span> is a list, any changes made to it inside the function will persist after execution returns from the function. <span class='m'>makeMove()</span> removes items from <span class='m'>theChests</span> when treasure chests are found, so eventually (if the player guesses correctly) all of the treasure chests will have been removed. (Remember, by "treasure chest" we mean the two-item lists of the XY coordinates inside the <span class='m'>theChests</span> list.)</p>
<p>When all the treasure chests have been found on the board and removed from <span class='m'>theChests</span>, the <span class='m'>theChests</span> list will have a length of <span class='m'>0</span>. When that happens, we display a congratulations to the player, and then execute a <span class='m'>break</span> statement to break out of this <span class='m'>while</span> loop. Execution will then move down to line 209 (the first line after the while-block.)</p>