You are on page 1of 371

Trang 1

Phát minh của riêng bạn


Trò chơi máy tính với Python
Ấn bản lần 2
Al Sweigart
Trang 2
Bản quyền © 2008, 2009, 2010 bởi Albert Sweigart
Một số quyền được bảo lưu. "Phát minh trò chơi máy tính của riêng bạn với Python" ("Phát minh
với Python ") được cấp phép theo Giấy phép Creative Commons Ghi công-Phi thương mại-
Chia sẻ Giấy phép Hoa Kỳ tương tự 3.0.
Bạn được tự do:
Để chia sẻ - sao chép, phân phối, hiển thị và thực hiện công việc
To Remix - để tạo tác phẩm phái sinh
Trong các điều kiện sau:
Ghi công - Bạn phải ghi công việc theo cách được chỉ định bởi
tác giả hoặc người cấp phép (nhưng không phải bằng bất cứ cách nào gợi ý rằng họ chứng thực bạn
hoặc bạn
sử dụng công việc). (Có thể bao gồm tiêu đề và tên tác giả trong bất kỳ đoạn trích nào về điều này
công việc.)
Phi thương mại - Bạn không được sử dụng tác phẩm này cho mục đích thương mại.
Chia sẻ tương tự - Nếu bạn thay đổi, chuyển đổi hoặc xây dựng dựa trên công việc này, bạn có thể
phân phối công việc kết quả chỉ theo cùng một giấy phép hoặc tương tự với công việc này.
Tóm tắt này được đặt ở đây:
http://creativecommons.org/licenses/by-nc-sa/3.0/us/
Việc sử dụng hợp pháp của bạn và các quyền khác không bị ảnh hưởng bởi những điều trên. Đây là
một
tóm tắt dễ đọc của con người về Bộ luật pháp (giấy phép đầy đủ), được đặt ở đây:
http://creativecommons.org/licenses/by-nc-sa/3.0/us/legalcode
Phiên bản sách 13
Sê-ri 980-0-9821060-1-3
Ấn bản lần 2

Trang 3
Đối với Caro, với nhiều tình yêu hơn
hơn bao giờ tôi biết tôi đã có.

Trang 4

Lưu ý cho phụ huynh và đồng nghiệp


Lập trình viên
Cảm ơn bạn đã đọc cuốn sách này. Động lực của tôi để viết cuốn sách này đến từ một khoảng cách
Tôi thấy trong các tài liệu ngày nay cho trẻ em thích học lập trình. Tôi bắt đầu lập trình
khi tôi 9 tuổi bằng ngôn ngữ BASIC với một cuốn sách tương tự cuốn sách này. Trong thời gian
Trong quá trình viết này, tôi đã nhận ra một ngôn ngữ hiện đại như Python đã tạo ra như thế nào
lập trình dễ dàng hơn và linh hoạt hơn cho một thế hệ lập trình viên mới. Con trăn có một
đường cong học tập nhẹ nhàng trong khi vẫn là một ngôn ngữ nghiêm túc được sử dụng bởi các lập
trình viên
chuyên nghiệp
Bộ sách lập trình hiện nay dành cho trẻ em mà tôi đã thấy rơi vào hai loại.
Đầu tiên, những cuốn sách không dạy lập trình nhiều như "phần mềm tạo trò chơi" hay
ngôn ngữ bị câm để làm cho việc lập trình trở nên "dễ dàng" (đến mức nó không còn
lập trình). Hoặc thứ hai, họ dạy lập trình như một cuốn sách giáo khoa toán học: tất cả
nguyên tắc và khái niệm với ít ứng dụng cho người đọc. Cuốn sách này có một
cách tiếp cận khác nhau: hiển thị mã nguồn cho các trò chơi ngay trước mặt và giải thích
nguyên tắc lập trình từ các ví dụ.
Tôi cũng đã cung cấp sách này theo giấy phép Creative Commons, cho phép
bạn tạo bản sao và phân phối cuốn sách này (hoặc trích đoạn) với sự cho phép đầy đủ của tôi, miễn là
như ghi công cho tôi vẫn còn nguyên và nó được sử dụng cho mục đích phi thương mại. (Xem
trang bản quyền.) Tôi muốn làm cho cuốn sách này trở thành một món quà cho một thế giới đã mang
lại cho tôi rất nhiều.
Cảm ơn bạn một lần nữa vì đã đọc cuốn sách này, và vui lòng gửi email cho tôi bất kỳ câu hỏi hoặc
bình luận.
Al Sweigart
al@inventwithpython.com
Toàn bộ văn bản của cuốn sách này có sẵn ở định dạng HTML hoặc PDF tại:
http://inventwithpython.com

Trang 5
Truyện tranh khủng long được sao chép với sự cho phép. Cảm ơn Ryan!

Cuốn sách này dành cho ai?


Lập trình không khó. Nhưng thật khó để tìm thấy những tài liệu học tập dạy bạn làm những điều thú vị với
lập trình. Các sách máy tính khác đi qua nhiều chủ đề mà hầu hết các lập trình viên mới không cần. Cuốn sách này sẽ
dạy bạn cách lập trình trò chơi máy tính của riêng bạn. Bạn sẽ học được một kỹ năng hữu ích và có những trò chơi thú vị để thể
hiện

Cuốn sách này là dành cho:
● Những người mới bắt đầu hoàn thành, những người muốn tự dạy lập trình máy tính, ngay cả khi họ không có trước đó

kinh nghiệm lập trình.


● Trẻ em và thanh thiếu niên muốn học lập trình máy tính bằng cách tạo trò chơi. Trẻ em từ 9 hoặc 10 tuổi

tuổi nên có thể theo cùng.


● Người lớn và giáo viên muốn dạy người khác lập trình.

● Bất cứ ai, trẻ hay già, muốn học cách lập trình bằng cách học lập trình chuyên nghiệp

ngôn ngữ.

Trang 6

Mục lục
Danh sách mã nguồn
xin chào
21
đoán
30
jokes
51
chuồn chuồn
58
buggy
83
coinFlips.py
87
hangman.py
103
tictactoe.py
150
truefalsefizz.py
172
bagels.py
184
sonar.py
213
mật mã
244
Reversi.py
261
aisim1.py
292
aisim2.py
294
aisim3.py
299
pygameHelloWorld.py 309
hình ảnh động
324
va chạm
339
pygameInput.py
348
spitesAndSound.py
360
dodger.py
371
1
Cài đặt Python
1
Tải xuống và cài đặt Python
2
Bắt đầu Python
4
Làm thế nào để sử dụng cuốn sách này
4
Các chương trình nổi bật
5
Số dòng và không gian
5
Tóm lược
7
2
Vỏ tương tác
số 8
Một số công cụ toán học đơn giản
số 8
Đánh giá biểu thức
11
Lưu trữ các giá trị trong biến
12

Trang 7
Sử dụng nhiều hơn một biến
15
Tóm lược
16
3
Chuỗi và Chương trình đầu tiên của bạn
18
Dây
18
Nối chuỗi
19
Viết chương trình trong Trình chỉnh sửa tệp của IDLE
20
Chào thế giới!
20
Chương trình "Hello World" hoạt động như thế nào
23
Tóm lược
26
4
Đoán số
28
Trò chơi "Đoán số"
28
Chạy mẫu của "Đoán số"
29
Đoán mã nguồn của số
29
Các khẩu Statement
31
Các random.randint () Chức năng
32
Truyền các đối số cho các hàm
34
Khối
36
Kiểu dữ liệu Boolean
37
Toán tử so sánh
37
Điều kiện
38
Thử nghiệm với Booleans, Toán tử so sánh và Điều kiện 38
Vòng lặp với các báo cáo trong khi
41
Người chơi đoán
41
nếu Tuyên bố
44
Rời khỏi vòng lặp sớm với tuyên bố phá vỡ
45
Kiểm tra xem người chơi có thắng không
46
Tóm tắt: Chính xác thì lập trình là gì?
47
Một trang web để theo dõi chương trình
48
5
Truyện cười
50
Tận dụng tối đa bản in ()
50
Chạy thử truyện cười
50
Mã nguồn của Joke
51
Nhân vật thoát
52

Trang 8
Báo giá và Báo giá kép
53
Các cuối khoá Luận
54
Tóm lược
55
6
Cõi rồng
56
Giới thiệu chức năng
56
Chạy mẫu của vương quốc rồng
57
Mã nguồn của Dragon Realm
57
báo cáo def
60
Toán tử Boolean
61
Giá trị trả về
65
Phạm vi biến đổi
65
Thông số
68
Đặt định nghĩa hàm ở đâu
70
Hiển thị kết quả trò chơi
71
Dấu hai chấm :
73
Chương trình thực sự bắt đầu ở đâu
73
Thiết kế chương trình
75
Tóm lược
76
7
Sử dụng trình gỡ lỗi
77
Lỗi!
77
Bắt đầu trình gỡ lỗi
78
Bước
80
Các nút Đi và Thoát
81
Bước qua và bước ra
81
Tìm lỗi
83
Điểm phá vỡ
86
Tóm lược
88
số 8
Biểu đồ dòng chảy
89
Cách chơi "Hangman"
89
Chạy mẫu của "Hangman"
89
Nghệ thuật ASCII
91
Thiết kế chương trình với sơ đồ
92
Tạo biểu đồ dòng chảy
93

Trang 9
Tóm tắt: Tầm quan trọng của việc lên kế hoạch cho trò chơi
100
9
Người treo cổ
102
Mã nguồn của Hangman
103
Chuỗi nhiều dòng
107
Không biến đổi
108
Danh sách
108
Thay đổi giá trị của các mục danh sách với phân công chỉ mục
110
Danh sách kết nối
110
Các trong điều hành
111
Xóa các mục khỏi danh sách với del Statements
112
Danh sách danh sách
113
Phương pháp
114
Các phương thức liệt kê ngược () và append ()
115
Sự khác biệt giữa phương thức và chức năng
116
Các split () Chức năng
116
Các phạm vi () và list () Chức năng
120
cho các vòng lặp
121
Tuyên bố elif ("Khác nếu")
127
Đánh giá các chức năng chúng tôi xác định
131
Thực hiện các thay đổi mới cho chương trình Hangman
132
Từ điển
139
Bộ từ cho Hangman
142
Các random.choice () Chức năng
143
Nhiều bài tập
145
Tóm lược
147
10 Tic Tắc
148
Chạy mẫu của Tic Tac Toe
149
Mã nguồn của Tic Tac Toe
150
Thiết kế chương trình
154
Trò chơi AI
156
Danh sách tài liệu tham khảo
162
Đánh giá ngắn mạch
170
Các None Giá trị
175

Trang 10
Tóm tắt: Tạo trí tuệ nhân tạo chơi trò chơi
182
11 bánh mì tròn
183
Chạy mẫu
184
Mã nguồn của Bagel
184
Thiết kế chương trình
186
Các random.shuffle () Chức năng
188
Toán tử chuyển nhượng tăng cường
190
Các loại () Danh sách Phương
192
Các join () Chuỗi Phương pháp
192
Nội suy chuỗi
194
Tóm tắt: Làm tốt ở Bagels
198
12 tọa độ Descartes
200
Lưới và tọa độ Descartes
201
Số âm
202
Thủ thuật toán học
204
Giá trị tuyệt đối và abs () Chức năng
206
Hệ thống tọa độ của màn hình máy tính
207
Tóm tắt: Sử dụng Toán này trong Trò chơi
208
13 cuộc săn lùng kho báu Sonar
209
Chạy mẫu
210
Mã nguồn của Sonar
213
Thiết kế chương trình
218
Các remove () Danh sách Phương
229
Tóm tắt: Đánh giá về trò chơi Sonar của chúng tôi
238
14 Mật mã Caesar
239
Về mật mã
239
Mật mã Caesar
240
ASCII và sử dụng số cho chữ cái
241
Các chr () và ord () Chức năng
242
Chạy mẫu của Mật mã Caesar
243
Mã nguồn của Caesar
244
Các isalpha () Chuỗi Phương pháp
247
Các phương thức chuỗi isupper () và islower ()
248

Trang 11
Lực lượng vũ phu
251
Tóm tắt: Xem xét Chương trình Mật mã Caesar của chúng tôi
253
15 Reversi
256
Cách chơi Reversi
255
Chạy mẫu
257
Mã nguồn của Reversi
260
Các bool () Chức năng
276
Tóm tắt: Đánh giá trò chơi Reversi
290
16 Mô phỏng AI
29
Trò chơi "Máy tính so với máy tính"
29
Mã nguồn AISim1.py
292
Mã nguồn AISim2.py
294
Tỷ lệ phần trăm
296
Các vòng () Chức năng
297
So sánh các thuật toán AI khác nhau
299
Mã nguồn AISim3.py
299
Học những điều mới bằng cách chạy thử nghiệm mô phỏng
304
17 Đồ họa và Hoạt hình
306
Cài đặt Pygame
307
Xin chào thế giới ở Pygame
308
Xin chào mã nguồn thế giới
308
Nhập mô-đun Pygame
311
Biến tham chiếu cửa hàng tham chiếu đến các đối tượng
313
Màu sắc trong Pygame
313
Phông chữ và Hàm pygame.font.SysFont ()
315
Thuộc tính
316
Hàm xây dựng và hàm type () .
317
Kiểu dữ liệu pygame.PixelArray
321
Sự kiện và Vòng lặp trò chơi
322
Hoạt hình
324
Mã nguồn của chương trình hoạt hình
324
Một số sửa đổi nhỏ
335
Tóm tắt: Lập trình Pygame
335

Trang 12
18 Phát hiện va chạm và đầu vào
337
Mã nguồn của Chương trình Phát hiện Va chạm
337
Chức năng phát hiện va chạm
341
Các pygame.time.Clock Object và đánh dấu () Phương pháp
344
Mã nguồn của chương trình nhập bàn phím
348
Các colliderect () Phương pháp
353
Tóm tắt: Phát hiện va chạm và đầu vào Pygame
353
19 âm thanh và hình ảnh
358
Tập tin hình ảnh và âm thanh
360
Chương trình âm thanh và âm thanh
360
Mã nguồn của Chương trình Sprites và Âm thanh
360
Thiết lập cửa sổ và cấu trúc dữ liệu
364
Các pygame.transform.scale () Chức năng
364
Tóm tắt: Các trò chơi với Đồ họa và Âm thanh
368
20 Dodger
369
Đánh giá các kiểu dữ liệu Pygame cơ bản
370
Mã nguồn của Dodger
371
Triển khai mã cheat
392
Sửa đổi trò chơi Dodger
397
Tóm tắt: Tạo trò chơi của riêng bạn
397
Phụ lục A
Sự khác biệt giữa Python 2 và 3
399
Phụ lục B
Báo cáo, chức năng và phương pháp tham khảo
403
Phụ lục C
Chạy chương trình Python mà không cần cài đặt Python
404
Phụ lục D
Thông báo lỗi phổ biến trong Python
407
Bảng chú giải
411
Giới thiệu về tác giả
421
Trang 13

Trang 14

Trang 15
Các chủ đề được đề cập trong Chương này:
● Tải xuống và cài đặt trình thông dịch Python.
● Sử dụng shell tương tác của IDLE để chạy hướng dẫn.
● Cách sử dụng cuốn sách này.

● Trang web của cuốn sách tại http://inventwithpython.com

Xin chào! Đây là một cuốn sách sẽ dạy bạn cách lập trình bằng cách chỉ cho bạn cách tạo
trò chơi máy tính. Khi bạn tìm hiểu cách các trò chơi trong cuốn sách này hoạt động, bạn sẽ có
thể tạo
trò chơi của riêng bạn. Tất cả những gì bạn cần là một máy tính, một số phần mềm được gọi là
Python
Thông dịch viên, và cuốn sách này. Phần mềm bạn cần là miễn phí và bạn có thể tải xuống từ
Internet.
Khi tôi còn là một đứa trẻ, tôi đã tìm thấy một cuốn sách như thế này dạy tôi cách viết các
chương trình đầu tiên của mình
và các trò chơi. Đó là niềm vui và dễ dàng. Bây giờ là một người lớn, tôi vẫn có máy tính lập
trình thú vị,
và tôi được trả tiền cho nó. Nhưng ngay cả khi bạn không trở thành lập trình viên máy tính khi
bạn phát triển
lên, lập trình là một kỹ năng hữu ích và thú vị để có.
Máy tính là máy móc rất hữu ích. Tin tốt là học lập trình
máy tính rất dễ Nếu bạn có thể đọc cuốn sách này, bạn có thể lập trình một máy tính. Một máy
tính
chương trình chỉ là một loạt các hướng dẫn được chạy bởi một máy tính, giống như một cuốn
truyện chỉ là một
toàn bộ câu đọc của người đọc.
Các hướng dẫn này giống như các hướng dẫn từng chặng mà bạn có thể nhận được khi đi bộ đến
nhà bạn. (Rẽ trái ở nơi có ánh sáng, đi bộ hai khối, tiếp tục đi bộ cho đến khi bạn tìm thấy cái
đầu tiên
ngôi nhà màu xanh bên phải.) Máy tính làm theo từng chỉ dẫn mà bạn đưa ra theo thứ tự
mà bạn cho nó Trò chơi điện tử không có gì ngoài chương trình máy tính. (Và rất
1

Trang 16
chương trình máy tính thú vị!)
Trong cuốn sách này, bất kỳ từ nào bạn cần biết sẽ trông như thế này . Ví dụ: từ
"chương trình" được định nghĩa trong đoạn trước.
Để nói cho máy tính biết bạn muốn nó làm gì, bạn viết một chương trình bằng ngôn ngữ
Máy tính hiểu. Ngôn ngữ lập trình mà cuốn sách này dạy có tên là Python.
Có nhiều ngôn ngữ lập trình khác nhau bao gồm BASIC, Java, Python, Pascal,
Haskell và C ++ (phát âm là "c plus plus").
Khi tôi còn là một đứa trẻ, hầu hết mọi người đều học lập trình BASIC như ngôn ngữ đầu tiên
của họ. Nhưng
ngôn ngữ lập trình mới đã được phát minh kể từ đó, bao gồm cả Python. Python là
thậm chí còn dễ học hơn BASIC và đó là ngôn ngữ lập trình nghiêm túc được sử dụng bởi
lập trình viên máy tính chuyên nghiệp. Nhiều người lớn sử dụng Python trong công việc của họ
(và khi
lập trình chỉ để giải trí).
Một vài trò chơi đầu tiên chúng ta sẽ cùng nhau tạo ra trong cuốn sách này có lẽ sẽ đơn giản
so với các game bạn đã chơi trên Xbox, Playstation hoặc Wii. Họ không có
Đồ họa hoặc âm nhạc ưa thích nhưng đó là vì chúng nhằm dạy bạn những điều cơ bản. Họ
cố tình đơn giản để chúng ta có thể tập trung vào việc học lập trình. Trò chơi không cần phải
phức tạp để vui vẻ Hangman, Tic Tac Toe và tạo mã bí mật rất đơn giản để
chương trình nhưng cũng rất vui
Chúng ta cũng sẽ học cách làm cho máy tính giải quyết một số vấn đề toán học trong Python
vỏ. (Đừng lo lắng nếu bạn không biết nhiều về toán học. Nếu bạn biết cách thêm và
nhân lên, bạn biết đủ toán để làm lập trình. Lập trình là vấn đề
giải quyết nói chung hơn là giải quyết các vấn đề toán học.)
Tải xuống và cài đặt Python
Trước khi chúng tôi có thể bắt đầu lập trình, bạn sẽ cần cài đặt phần mềm Python; đặc biệt
trình thông dịch Python. (Bạn có thể cần nhờ người lớn giúp đỡ ở đây.) Trình thông dịch là một
chương trình hiểu các hướng dẫn mà bạn sẽ viết bằng ngôn ngữ Python. Không có
thông dịch viên, máy tính của bạn sẽ không hiểu các hướng dẫn này và các chương trình của bạn
sẽ không
công việc. (Chúng ta sẽ chỉ gọi "trình thông dịch Python" là "Python" kể từ bây giờ.)
Vì chúng tôi sẽ viết các trò chơi của mình bằng ngôn ngữ Python, chúng tôi cần tải xuống
Python
đầu tiên, từ trang web chính thức của ngôn ngữ lập trình Python,
http://www.python.org
Tôi sẽ cung cấp cho bạn các hướng dẫn để cài đặt Python trên Microsoft Windows chứ không
phải
bởi vì đó là hệ điều hành yêu thích của tôi nhưng vì rất có thể đó là hệ điều hành
hệ thống mà máy tính của bạn đang chạy. Bạn có thể muốn sự giúp đỡ của người khác
tải xuống và cài đặt phần mềm Python.
Khi bạn truy cập python.org, bạn sẽ thấy một danh sách các liên kết ở bên trái (Giới thiệu, Tin
tức,
Tài liệu, Tải xuống, v.v.) Nhấp vào liên kết Tải xuống để đi đến tải xuống
2

Trang 17
sau đó tìm tệp có tên Python 3.1 Windows Installer (Windows nhị phân -
không bao gồm nguồn) và nhấp vào liên kết của nó để tải xuống Python cho Windows.
Hình 1-1: Nhấp vào liên kết trình cài đặt Windows để tải xuống Python cho Windows từ http://www.python.org
Nhấp đúp vào tệp python-3.1.msi mà bạn vừa tải xuống để khởi động Python
trình cài đặt. (Nếu nó không bắt đầu, hãy thử nhấp chuột phải vào tệp và chọn Cài đặt.) Sau khi
trình cài đặt khởi động, nhấp vào nút Tiếp theo và chỉ chấp nhận các lựa chọn trong trình cài đặt
khi bạn đi
(không cần thực hiện bất kỳ thay đổi nào). Khi cài đặt kết thúc, bấm Kết thúc .
Lưu ý quan trọng! Hãy chắc chắn cài đặt Python 3 chứ không phải Python 2. Các chương trình
trong này
sách sử dụng Python 3 và bạn sẽ gặp lỗi nếu bạn cố chạy chúng với Python 2.
Việc cài đặt cho Mac OS cũng tương tự. Thay vì tải xuống tệp .msi từ
Thay vào đó, trang web Python, tải xuống tệp Mac Installer Disk Image. Liên kết đến đây
tệp sẽ trông giống như "Ảnh đĩa Mac Installer (3.1.1)" trên "Tải xuống Python
Phần mềm "trang web.
Nếu hệ điều hành của bạn là Ubuntu, bạn có thể cài đặt Python bằng cách mở một thiết bị đầu
cuối
cửa sổ (nhấp vào Ứng dụng> Phụ kiện> Thiết bị đầu cuối) và nhập sudo apt-get
cài đặt python3 sau đó nhấn Enter. Bạn sẽ cần nhập mật khẩu gốc để
cài đặt Python, vì vậy hãy yêu cầu người sở hữu máy tính nhập mật khẩu này.
Có thể có một phiên bản Python mới hơn có sẵn hơn 3.1. Nếu vậy, chỉ cần tải xuống
phiên bản mới nhất. Các chương trình trò chơi trong cuốn sách này sẽ hoạt động giống
nhau. Nếu bạn có bất kỳ
có vấn đề, bạn luôn có thể Google để "cài đặt Python trên <hệ điều hành của bạn
tên> ". Python là một ngôn ngữ rất phổ biến, vì vậy bạn không gặp khó khăn gì trong việc tìm
kiếm trợ giúp.
Hướng dẫn bằng video về cách cài đặt Python có sẵn từ trang web của cuốn sách này tại
http://inventwithpython.com/ideo/.
3
1 - Cài đặt Python

Trang 18
Bắt đầu Python
Nếu hệ điều hành của bạn là Windows XP, bạn sẽ có thể chạy Python bằng cách chọn
Bắt đầu> Chương trình> Python 3.1> IDLE (GUI GUI). Khi nó chạy nó nên
trông giống như hình 1-2. (Nhưng các hệ điều hành khác nhau sẽ trông hơi
khác nhau.)
Hình 1-2: Vỏ tương tác của chương trình IDLE trên Windows.
Đứng IDLE cho tôi nteractive D eve L opment E nvironment. Môi trường phát triển
là phần mềm giúp bạn dễ dàng viết chương trình Python. Chúng tôi sẽ sử dụng IDLE để nhập
chương trình của chúng tôi và chạy chúng.
Cửa sổ xuất hiện khi bạn chạy IDLE lần đầu tiên được gọi là shell tương tác. Một cái vỏ
là một chương trình cho phép bạn gõ hướng dẫn vào máy tính. Shell Python cho phép bạn
gõ hướng dẫn Python và shell gửi các hướng dẫn này đến phần mềm được gọi là
Trình thông dịch Python để thực hiện. Chúng ta có thể gõ các hướng dẫn Python vào shell và,
bởi vì
Shell là tương tác, máy tính sẽ đọc hướng dẫn của chúng tôi và trả lời theo một cách nào đó.
(Lý tưởng theo cách mà chúng ta mong đợi nhưng điều đó sẽ phụ thuộc vào việc chúng ta có viết
đúng không
hướng dẫn.)
Làm thế nào để sử dụng cuốn sách này
Có một vài điều bạn nên hiểu về cuốn sách này trước khi bắt đầu.
"Invent with Python" khác với các sách lập trình khác vì nó tập trung vào
mã nguồn hoàn chỉnh cho các trò chơi khác nhau. Thay vì dạy bạn các khái niệm lập trình
và để bạn tự tìm ra cách tạo ra những trò chơi thú vị với những khái niệm đó, cuốn sách này
cho bạn thấy những trò chơi thú vị và sau đó giải thích cách chúng được kết hợp với nhau.
4

Trang 19
Các chương trình nổi bật
Hầu hết các chương bắt đầu với một chương trình mẫu của chương trình đặc trưng. Chạy mẫu
này cho thấy
cho bạn biết đầu ra của chương trình trông như thế nào, với những gì người dùng nhập vào được
hiển thị dưới dạng in đậm .
Điều này sẽ cho bạn ý tưởng về trò chơi hoàn chỉnh sẽ như thế nào khi bạn bước vào
mã và chạy nó
Một số chương cũng hiển thị mã nguồn hoàn chỉnh của trò chơi, nhưng hãy nhớ rằng: bạn không
phải nhập từng dòng mã ngay bây giờ. Thay vào đó, bạn có thể đọc chương đầu tiên để
hiểu những gì từng dòng mã làm và sau đó thử nhập nó sau.
Bạn cũng có thể tải xuống tệp mã nguồn từ trang web của cuốn sách này. Truy cập URL
http://inventwithpython.com/source và làm theo hướng dẫn để tải xuống nguồn
mã tập tin.
Số dòng và không gian
Khi tự nhập mã nguồn, không nhập số dòng xuất hiện tại
bắt đầu của mỗi dòng. Ví dụ, nếu bạn thấy điều này trong cuốn sách:
9. số = ngẫu nhiên.randint (1, 20)
Bạn không cần nhập "9." ở phía bên trái, hoặc không gian ngay sau nó.
Chỉ cần gõ nó như thế này:
số = ngẫu nhiên.randint (1, 20)
Những con số này chỉ được sử dụng để cuốn sách này có thể tham khảo các dòng cụ thể trong
mã.
Họ không phải là một phần của chương trình thực tế.
Ngoài các số dòng, hãy chắc chắn nhập mã chính xác như nó xuất hiện. Thông báo rằng
một số dòng không bắt đầu ở cạnh ngoài cùng bên trái của trang, nhưng được thụt lề bởi bốn
hoặc
tám không gian. Hãy chắc chắn để đặt đúng số lượng khoảng trắng ở đầu mỗi dòng. (Từ
mỗi ký tự trong IDLE có cùng chiều rộng, bạn có thể đếm số lượng khoảng trắng bằng cách đếm
số lượng ký tự bên trên hoặc bên dưới dòng bạn đang xem.)
Ví dụ: bạn có thể thấy rằng dòng thứ hai được thụt vào bởi bốn khoảng trắng vì bốn dòng
các ký tự (" whil ") trên dòng trên nằm trên không gian thụt lề. Dòng thứ ba là
được thụt lề bởi bốn khoảng trắng khác (bốn ký tự, " nếu n " nằm trên dòng thứ ba
không gian thụt lề):
5
1 - Cài đặt Python

Trang 20
trong khi đoán <10:
nếu số == 42:
in ('Xin chào')
Văn bản gói trong cuốn sách này
Một số dòng mã quá dài để phù hợp với một dòng trên trang và văn bản của mã
sẽ quấn quanh để dòng tiếp theo. Khi bạn nhập những dòng này vào trình chỉnh sửa tệp, hãy
nhập
mã tất cả trên một dòng mà không cần nhấn Enter.
Bạn có thể biết khi nào một dòng mới bắt đầu bằng cách nhìn vào số dòng ở bên trái của
mã. Ví dụ: mã bên dưới chỉ có hai dòng mã, mặc dù dòng đầu tiên
bao bọc xung quanh:
1. in ('Đây là dòng đầu tiên! Xxxxxxxxxxxxxxx
xxxxxxxxxxxx ')
2. in ('Đây là dòng thứ hai!')
Truy tìm chương trình trực tuyến
Bạn có thể truy cập http://inventwithpython.com/traces để xem dấu vết qua từng
các chương trình trong cuốn sách này. Theo dõi một chương trình có nghĩa là bước qua từng
dòng mã,
theo cùng một cách mà một máy tính sẽ thực thi nó. Trang web dấu vết có ghi chú và
lời nhắc hữu ích ở mỗi bước của dấu vết để giải thích chương trình đang làm gì, để nó có thể
giúp bạn hiểu rõ hơn tại sao các chương trình này hoạt động theo cách họ làm.
Kiểm tra mã của bạn trực tuyến
Một số trò chơi trong cuốn sách này hơi dài. Mặc dù nó rất hữu ích để học
Python bằng cách gõ mã nguồn cho các trò chơi này, bạn có thể vô tình mắc lỗi chính tả
Điều đó khiến các chương trình trò chơi của bạn bị sập. Nó có thể không rõ ràng nơi lỗi đánh
máy.
Bạn có thể sao chép và dán văn bản mã nguồn của mình vào công cụ tìm khác biệt trực tuyến
trên sách
trang mạng. Công cụ tìm khác biệt sẽ hiển thị bất kỳ sự khác biệt giữa mã nguồn trong sách và
mã nguồn bạn đã nhập. Đây là một cách dễ dàng để tìm thấy bất kỳ lỗi chính tả trong chương
trình của bạn.
Sao chép và dán văn bản là một kỹ năng máy tính rất hữu ích, đặc biệt là đối với máy tính
lập trình. Có một video hướng dẫn về sao chép và dán tại trang web của cuốn sách này tại
http://inventwithpython.com/ideo/.
Công cụ tìm khác biệt trực tuyến có tại trang web này: http://inventwithpython.com/diff. Một video
6

Trang 21
hướng dẫn cách sử dụng công cụ tìm khác biệt có sẵn từ trang web của cuốn sách này tại
http://inventwithpython.com/ideo/.
Tóm lược
Chương này đã giúp bạn bắt đầu với phần mềm Python bằng cách hiển thị cho bạn
trang web python.org nơi bạn có thể tải xuống miễn phí. Sau khi cài đặt và bắt đầu
Phần mềm Python IDLE, chúng tôi sẽ sẵn sàng học lập trình bắt đầu trong chương tiếp theo.
Trang web của cuốn sách này tại http://inventwithpython.com có thêm thông tin về mỗi
các chương, bao gồm một trang web theo dõi trực tuyến có thể giúp bạn hiểu chính xác những gì
từng dòng của chương trình làm.
7
1 - Cài đặt Python
Trang 22
Các chủ đề được đề cập trong Chương này:
● Số nguyên và số dấu phẩy động
● Biểu thức

● Giá trị

● Người vận hành

● Đánh giá biểu thức

● Lưu trữ giá trị trong các biến

Trước khi bắt đầu viết trò chơi trên máy tính, chúng ta nên học một số chương trình cơ bản
khái niệm đầu tiên. Các khái niệm này là các giá trị, toán tử, biểu thức và biến. Chúng tôi sẽ
không
bắt đầu lập trình trong chương này, nhưng biết những khái niệm này và tên của sự vật sẽ
làm cho việc học lập trình dễ dàng hơn nhiều Điều này là do phần lớn lập trình chỉ được xây
dựng trên
một vài khái niệm đơn giản kết hợp với nhau để tạo ra các chương trình tiên tiến.
Hãy bắt đầu bằng cách học cách sử dụng shell tương tác của Python.
Một số công cụ toán học đơn giản
Để mở IDLE trên Windows, nhấp vào Bắt đầu> Chương trình> Python 3.1> IDLE (Python
GUI) . Với IDLE mở, hãy thực hiện một số phép toán đơn giản với Python. Vỏ tương tác có thể
làm việc giống như một máy tính. Gõ 2 + 2 vào vỏ và nhấn phím Enter trên của bạn
bàn phím. (Trên một số bàn phím, đây là phím RETURN.) Như bạn có thể thấy trong Hình 2-1,
máy tính sẽ trả lời với số 4; tổng 2 + 2.
số 8

Trang 23
Hình 2-1: Nhập 2 + 2 vào vỏ.
Như bạn có thể thấy, chúng ta có thể sử dụng shell Python giống như một máy tính. Đây không
phải là một chương trình bởi
bởi vì chúng ta chỉ đang học những điều cơ bản ngay bây giờ. Dấu + báo cho máy tính thêm
các số 2 và 2. Để trừ các số, hãy sử dụng dấu - và để nhân các số, hãy sử dụng một
dấu hoa thị ( * ), như vậy:
Khi được sử dụng theo cách này, +, -, * và / được gọi là toán tử vì chúng cho biết
máy tính để thực hiện các hoạt động được chỉ định trên các số xung quanh chúng.
Số nguyên và số dấu phẩy động
Trong lập trình (và cả trong toán học), các số nguyên như 4, 0 và 99 được gọi
số nguyên . Các số có phân số hoặc dấu thập phân (như 3.5 và 42.1 và 5.0) không phải là số
số nguyên. Trong Python, số 5 là một số nguyên, nhưng nếu chúng ta viết nó là 5.0 thì nó sẽ
không phải là một số nguyên
số nguyên. Các số có dấu thập phân được gọi là số dấu phẩy động . Trong
toán học, 5.0 vẫn được coi là một số nguyên và giống như số 5, nhưng trong
lập trình máy tính máy tính coi bất kỳ số nào có dấu thập phân là không phải là một
số nguyên.
Bảng 2-1: Toán học khác nhau
toán tử trong Python.
2+2
thêm vào
2-2
phép trừ
2*2
phép nhân
2/2
bộ phận
9
2 - Vỏ tương tác

Trang 24
Biểu thức
Hãy thử gõ một số vấn đề toán học này vào trình bao, nhấn phím Enter sau mỗi lần.
2+2+2+2+2
8*6
10-5 + 6
2+2
Hình 2-2 là lớp vỏ tương tác trong IDLE sẽ trông như thế nào sau khi bạn nhập vào
hướng dẫn ở trên.
Hình 2-2: Cửa sổ IDLE trông như thế nào sau khi nhập hướng dẫn.
Những bài toán này được gọi là
biểu thức. Máy tính có thể giải quyết
hàng triệu vấn đề này trong vài giây
Biểu thức được tạo thành từ các giá trị (
số) được kết nối bởi các nhà khai thác (
dấu hiệu toán học). Hãy học chính xác những gì
giá trị và toán tử là.
Như bạn có thể thấy cuối cùng
biểu hiện trong ví dụ trên, bạn
có thể đặt bất kỳ số lượng không gian trong
giữa các số nguyên và các toán tử này. (Nhưng hãy chắc chắn luôn luôn bắt đầu ngay từ đầu
của dòng, không có khoảng trắng ở phía trước.)
Số là một loại giá trị. Số nguyên là một loại số. Nhưng, mặc dù số nguyên là
số, không phải tất cả các số là số nguyên. (Ví dụ: phân số và số có số thập phân
các điểm như 2,5 là các số không phải là số nguyên.)
Hình 2-3: Một biểu thức được tạo thành từ các giá trị và toán tử.
10

Trang 25
Điều này giống như cách một con mèo là một loại vật nuôi, nhưng không phải tất cả thú cưng đều là
mèo. Ai đó có thể có một con vật cưng
chó hoặc một con thằn lằn thú cưng. Một biểu thức được tạo thành từ các giá trị (chẳng hạn như
các số nguyên như 8 và 6)
được kết nối bởi một toán tử (chẳng hạn như dấu * nhân). Một giá trị duy nhất của chính nó cũng

coi một biểu thức.
Trong chương tiếp theo, chúng ta sẽ tìm hiểu về cách làm việc với văn bản trong các biểu
thức. Python không
giới hạn chỉ là số. Nó không chỉ là một máy tính ưa thích!
Đánh giá biểu thức
Khi một máy tính giải được biểu thức 10 + 5 và nhận giá trị 15 , chúng ta nói nó có
đánh giá biểu thức. Đánh giá một biểu thức làm giảm biểu thức thành một
giá trị, giống như giải một bài toán làm giảm bài toán xuống một con số duy nhất: câu trả lời.
Các biểu thức 10 + 5 và 10 + 3 + 2 có cùng giá trị, vì cả hai đều
đánh giá đến 15 . Ngay cả các giá trị đơn lẻ cũng được coi là biểu thức: Biểu thức 15 đánh giá
đến giá trị 15 .
Tuy nhiên, nếu bạn chỉ cần gõ 5+ vào trình vỏ tương tác, bạn sẽ nhận được thông báo lỗi.
>>> 5+
Lỗi cú pháp: cú pháp không hợp lệ
Lỗi này xảy ra vì 5 + không phải là biểu thức. Biểu thức có giá trị
được kết nối bởi các toán tử, nhưng toán tử + luôn mong muốn kết nối hai thứ trong Python.
Chúng tôi chỉ cho nó một. Đây là lý do tại sao thông báo lỗi xuất hiện. Một lỗi cú pháp có nghĩa

rằng máy tính không hiểu hướng dẫn mà bạn đã đưa ra vì bạn đã gõ nó
không chính xác Python sẽ luôn hiển thị thông báo lỗi nếu bạn nhập một hướng dẫn rằng nó
không thể hiểu.
Điều này có vẻ không quan trọng, nhưng rất nhiều chương trình máy tính không chỉ nói
Máy tính phải làm gì, nhưng cũng biết chính xác làm thế nào để bảo máy tính làm việc đó.
Biểu thức bên trong các biểu thức khác
Biểu thức cũng có thể chứa các biểu thức khác. Ví dụ: trong biểu thức 2 + 5
+ 8 , phần 2 + 5 là biểu thức riêng của nó. Python đánh giá 2 + 5 đến 7 , vì vậy bản gốc
biểu thức trở thành 7 + 8 . Python sau đó đánh giá biểu thức này thành 15 .
Hãy nghĩ về một biểu hiện như là một chồng bánh kếp. Nếu bạn đặt hai chồng bánh kếp
cùng nhau, bạn vẫn có một chồng bánh kếp. Và một đống lớn bánh kếp có thể được tạo thành
những chồng bánh nhỏ hơn được ghép lại với nhau. Biểu thức có thể được kết hợp với nhau
để tạo thành các biểu thức lớn hơn theo cùng một cách. Nhưng dù biểu hiện có lớn đến đâu thì nó
cũng
ước tính cho một câu trả lời duy nhất, giống như 2 + 5 + 8 ước tính thành 15 .
11
2 - Vỏ tương tác

Trang 26
Lưu trữ các giá trị trong biến
Khi chúng tôi lập trình, chúng tôi thường muốn lưu các giá trị mà biểu thức của chúng tôi đánh
giá thành
vì vậy chúng ta có thể sử dụng chúng sau này trong chương trình. Chúng ta có thể lưu trữ các giá
trị trong các biến .
Hãy nghĩ về các biến như một hộp có thể chứa các giá trị. Bạn có thể lưu trữ các giá trị bên trong
các biến
với dấu = (được gọi là toán tử gán ). Ví dụ: để lưu trữ giá trị 15 trong một
biến có tên "spam", nhập spam = 15 vào shell:
>>> thư rác = 15
>>>
Bạn có thể nghĩ về biến như một
hộp có giá trị 15 bên trong của nó (như
thể hiện trong hình 2-4). Biến
Tên "thư rác" là nhãn trên hộp (vì vậy
chúng ta có thể nói một biến từ một biến khác)
và giá trị được lưu trữ trong nó giống như một giá trị nhỏ
lưu ý bên trong hộp.
Khi bạn nhấn Enter, bạn sẽ không thấy
bất cứ điều gì đáp lại, ngoài một
dòng trống. Trừ khi bạn thấy một lỗi
tin nhắn, bạn có thể cho rằng
hướng dẫn đã được thực hiện
thành công Dấu nhắc >>> tiếp theo sẽ
xuất hiện để bạn có thể gõ trong hướng dẫn tiếp theo.
Hướng dẫn này (được gọi là câu lệnh gán ) tạo thư rác biến và
lưu trữ giá trị 15 trong đó. Không giống như biểu thức, câu lệnh là hướng dẫn không
ước tính cho bất kỳ giá trị nào, đó là lý do tại sao không có giá trị được hiển thị trên dòng tiếp
theo trong trình bao.
Có thể khó hiểu khi biết hướng dẫn nào là biểu thức và hướng dẫn nào
các câu lệnh. Chỉ cần nhớ rằng nếu hướng dẫn ước tính thành một giá trị, thì đó là một
biểu hiện. Nếu hướng dẫn không, thì đó là một tuyên bố.
Một câu lệnh gán được viết dưới dạng một biến, theo sau là dấu = bằng, theo sau
bởi một biểu thức. Giá trị mà biểu thức ước tính được lưu trữ bên trong biến.
Giá trị 15 của chính nó là một biểu thức. Biểu thức được tạo thành từ một giá trị duy nhất của
chính nó là
dễ đánh giá. Những biểu thức này chỉ đánh giá giá trị của chính nó. Ví dụ:
biểu thức 15 đánh giá thành 15!
Hãy nhớ rằng, các biến lưu trữ giá trị, không phải biểu thức. Ví dụ: nếu chúng ta có tuyên bố,
spam = 10 + 5 , sau đó biểu thức 10 + 5 trước tiên sẽ được ước tính thành 15 và sau đó
giá trị 15 sẽ được lưu trữ trong biến, thư rác .
Hình 2-4: Các biến giống như các hộp có thể chứa các giá trị trong đó.
12

Trang 27
Lần đầu tiên bạn lưu trữ một giá trị bên trong một biến bằng cách sử dụng câu lệnh gán,
Python sẽ tạo biến đó. Mỗi lần sau đó, một câu lệnh gán sẽ chỉ
thay thế giá trị được lưu trữ trong biến.
Bây giờ hãy xem liệu chúng ta đã tạo biến của mình đúng chưa. Nếu chúng ta gõ spam vào shell
bằng
chính nó, chúng ta sẽ thấy giá trị nào được lưu trữ bên trong thư rác biến .
>>> thư rác = 15
>>> thư rác
15
>>>
Bây giờ, thư rác ước tính giá trị bên trong biến, 15.
Và đây là một bước ngoặt thú vị. Nếu bây giờ chúng tôi nhập spam + 5 vào shell, chúng tôi sẽ
nhận được
số nguyên 20 , như vậy.
>>> thư rác = 15
>>> thư rác + 5
20
>>>
Điều đó có vẻ kỳ lạ nhưng thật có ý nghĩa khi chúng ta nhớ rằng chúng ta đặt giá trị của thư rác
đến 15 . Vì chúng tôi đã đặt giá trị của thư rác biến thành 15 , viết thư rác + 5 giống như
viết biểu thức 15 + 5 .
Nếu bạn cố gắng sử dụng một biến trước khi nó được tạo, Python sẽ báo lỗi cho bạn
bởi vì không có biến nào như vậy tồn tại Điều này cũng xảy ra nếu bạn gõ nhầm tên của
Biến đổi.
Chúng ta có thể thay đổi giá trị được lưu trữ trong một biến bằng cách nhập một câu lệnh gán
khác.
Ví dụ: thử các cách sau:
>>> thư rác = 15
>>> thư rác + 5
20
>>> thư rác = 3
>>> thư rác + 5
số 8
>>>
Lần đầu tiên chúng tôi nhập spam + 5 , biểu thức ước tính là 20 , vì chúng tôi đã lưu trữ
13
2 - Vỏ tương tác

Trang 28
giá trị 15 bên trong thư rác biến . Nhưng khi chúng ta nhập spam = 3 , giá trị 15 là
thay thế hoặc ghi đè, với giá trị 3 . Bây giờ, khi chúng tôi nhập spam + 5 , biểu thức
ước tính là 8 vì giá trị của thư rác bây giờ là 3.
Để tìm ra giá trị hiện tại bên trong một biến, chỉ cần nhập tên biến vào
Vỏ.
Bây giờ đây là một cái gì đó thú vị. Bởi vì một biến chỉ là một tên cho một giá trị, chúng ta có
thể
viết biểu thức với các biến như thế này:
>>> thư rác = 15
>>> thư rác + thư rác
30
>>> thư rác - thư rác
0
>>>
Khi thư rác biến có giá trị nguyên 15 được lưu trong đó, hãy nhập thư rác + thư rác
tương tự như nhập 15 + 15 , ước tính là 30 . Và thư rác - thư rác cũng vậy
như 15-15 , trong đó đánh giá để 0 . Các biểu thức trên sử dụng thư rác biến hai lần.
Bạn có thể sử dụng các biến nhiều lần như bạn muốn trong biểu thức. Hãy nhớ rằng Python
sẽ đánh giá một tên biến với giá trị được lưu trữ bên trong biến đó, mỗi lần
biến được sử dụng.
Chúng tôi thậm chí có thể sử dụng giá trị trong biến thư rác để gán thư rác một giá trị mới:
>>> thư rác = 15
>>> thư rác = thư rác + 5
20
>>>
Câu lệnh gán spam = spam + 5 giống như nói, "giá trị mới của
biến thư rác sẽ là giá trị hiện tại của thư rác cộng với năm. "Hãy nhớ rằng biến trên
phía bên trái của dấu = sẽ được gán giá trị mà biểu thức ở phía bên phải
đánh giá để. Chúng tôi cũng có thể tiếp tục tăng giá trị trong thư rác lên 5 lần:
>>> thư rác = 15
>>> thư rác = thư rác + 5
>>> thư rác = thư rác + 5
>>> thư rác = thư rác + 5
>>> thư rác
30
14

Trang 29
>>>
Ghi đè biến
Thay đổi giá trị được lưu trữ bên trong một biến là dễ dàng. Chỉ cần thực hiện một nhiệm vụ
khác
câu lệnh có cùng biến. Hãy xem điều gì xảy ra khi bạn nhập mã sau đây
vào vỏ tương tác:
>>> thư rác = 42
>>> in (spam)
42
>>> thư rác = 'Xin chào'
>>> in (spam)
xin chào
Ban đầu, biến thư rác có số nguyên 42 được đặt bên trong nó. Đây là lý do tại sao đầu tiên
in (spam) in ra 42 . Nhưng khi chúng tôi thực thi spam = 'Xin chào' , giá trị 42 là
ném ra khỏi biến và bị quên vì giá trị chuỗi 'Hello' mới được đặt bên trong
các thư rác biến.
Thay thế giá trị trong một biến bằng một giá trị mới được gọi là ghi đè giá trị. Nó là
điều quan trọng cần biết là giá trị cũ bị lãng quên vĩnh viễn. Nếu bạn muốn nhớ điều này
giá trị để bạn có thể sử dụng nó sau này trong chương trình của mình, lưu trữ nó trong một biến
khác trước
ghi đè giá trị:
>>> thư rác = 42
>>> in (spam)
42
>>> oldSpam = thư rác
>>> thư rác = 'Xin chào'
>>> in (spam)
xin chào
>>> in (cũSpam)
42
Trong ví dụ trên, trước khi ghi đè giá trị trong thư rác , chúng tôi lưu trữ giá trị đó trong một
biến có tên oldSpam .
Sử dụng nhiều hơn một biến
Khi chúng tôi lập trình, chúng tôi sẽ không luôn muốn bị giới hạn chỉ một biến. Thường thì
chúng ta sẽ
cần sử dụng nhiều biến.
15
2 - Vỏ tương tác

Trang 30
Ví dụ: hãy gán các giá trị khác nhau cho hai biến có tên là trứng và fizz , như
vì thế:
>>> xì hơi = 10
>>> trứng = 15
Bây giờ biến fizz có 10 bên trong nó và trứng có 15 bên trong nó.
Hình 2-5: Biến "fizz" và "egg" có các giá trị được lưu trữ trong chúng.
Không thay đổi giá trị trong biến thư rác của chúng tôi , hãy thử gán một giá trị mới cho
biến thư rác. Nhập spam = fizz + trứng vào vỏ rồi nhập spam vào
Shell để xem giá trị mới của thư rác. Bạn có thể đoán nó sẽ là gì không?
>>> xì hơi = 10
>>> trứng = 15
>>> spam = fizz + trứng
>>> thư rác
25
>>>
Giá trị trong thư rác hiện là 25 vì khi chúng tôi thêm fizz và trứng, chúng tôi sẽ thêm
giá trị được lưu trữ bên trong fizz và trứng .
Tóm lược
Trong chương này, bạn đã học những điều cơ bản về cách viết hướng dẫn Python. Nhu cầu
Python
bạn nói chính xác những gì cần làm theo một cách nghiêm ngặt, bởi vì máy tính không có điểm
chung
ý nghĩa và chỉ hiểu hướng dẫn rất đơn giản. Bạn đã học được rằng Python có thể
16

Trang 31
đánh giá các biểu thức (nghĩa là giảm biểu thức thành một giá trị) và đó
biểu thức là các giá trị (chẳng hạn như 2 hoặc 5 ) kết hợp với các toán tử (chẳng hạn
như + hoặc - ). Bạn có
cũng học được rằng bạn có thể lưu trữ các giá trị bên trong các biến để sử dụng chúng sau này.
Trong chương tiếp theo, chúng ta sẽ tìm hiểu một số khái niệm cơ bản hơn, và sau đó bạn sẽ
sẵn sàng để lập trình
17
2 - Vỏ tương tác

Trang 32
Các chủ đề được đề cập trong Chương này:
● Luồng thực hiện
● Chuỗi

● Nối chuỗi

● Kiểu dữ liệu (như chuỗi hoặc số nguyên)

● Sử dụng IDLE để viết mã nguồn.

● Lưu và chạy các chương trình trong IDLE.

● Hàm print () .

● Hàm đầu vào () .

● Nhận xét

● Viết hoa biến

● Trường hợp nhạy cảm

● Biến ghi đè

Bây giờ đã đủ số nguyên và toán. Python không chỉ là một máy tính. Hiện nay
Hãy xem Python có thể làm gì với văn bản. Trong chương này, chúng ta sẽ học cách lưu trữ văn
bản trong
các biến, kết hợp văn bản với nhau và hiển thị chúng trên màn hình. Nhiều chương trình của
chúng tôi
sẽ sử dụng văn bản để hiển thị các trò chơi của chúng tôi cho người chơi và người chơi sẽ nhập
văn bản vào
các chương trình thông qua bàn phím. Chúng tôi cũng sẽ thực hiện chương trình đầu tiên của
chúng tôi, chào đón người dùng
với dòng chữ "Xin chào thế giới!" và hỏi tên người dùng.
Dây
Trong Python, chúng tôi làm việc với các đoạn văn bản nhỏ gọi là chuỗi . Chúng tôi có thể lưu
trữ các giá trị chuỗi
bên trong các biến giống như chúng ta có thể lưu trữ các giá trị số bên trong các biến. Khi chúng ta

18

Trang 33
chuỗi, chúng tôi đặt chúng ở giữa hai dấu ngoặc đơn ('), như thế này:
>>> thư rác = 'xin chào'
>>>
Các dấu ngoặc đơn ở đó chỉ để báo cho máy tính biết chuỗi bắt đầu và kết thúc ở đâu
(và không phải là một phần của giá trị chuỗi).
Bây giờ, nếu bạn nhập thư rác vào trình bao, bạn sẽ thấy nội dung của biến thư rác
( chuỗi 'xin chào' .) Điều này là do Python sẽ đánh giá một biến với giá trị được lưu trữ
bên trong biến.
>>> thư rác = 'xin chào'
>>> thư rác
'xin chào'
>>>
Chuỗi có thể có hầu hết bất kỳ ký tự hoặc đăng nhập vào chúng cũng như khoảng trắng và số.
(Chuỗi không thể có dấu ngoặc đơn bên trong chúng mà không sử dụng ký tự thoát. Thoát
các ký tự được mô tả sau.) Đây là tất cả các ví dụ về chuỗi:
'xin chào'
'Chào bạn!'
'Albert'
'BẾP'
'7 quả táo, 14 quả cam, 3 quả chanh'
'Một thời gian dài trước đây trong một thiên hà xa, rất xa ...'
'O * & # wY% * & OCfsdYO * & gfC% YO * &% 3yc8r2'
Như chúng ta đã làm với các giá trị số trong chương trước, chúng ta cũng có thể đặt các giá trị
chuỗi trong
biểu thức. Ví dụ: biểu thức 4 * 2 + 3 là biểu thức có số
các giá trị sẽ ước tính cho số nguyên 11 .
Nối chuỗi
Bạn có thể thêm một chuỗi vào cuối chuỗi khác bằng cách sử dụng toán tử + , được gọi là
nối chuỗi. Hãy thử nhập 'Xin chào' + 'Thế giới!' vào vỏ:
>>> 'Xin chào' + 'Thế giới!'
'Chào thế giới!'
>>>
19
3 - Chuỗi

Trang 34
Để giữ các chuỗi riêng biệt, hãy đặt một khoảng trắng ở cuối chuỗi 'Xin chào' , trước chuỗi
trích dẫn duy nhất, như thế này:
>>> 'Xin chào' + 'Thế giới!'
'Chào thế giới!'
>>>
Chuỗi và số nguyên là các loại dữ liệu khác nhau . Tất cả các giá trị có một kiểu dữ liệu. Kiểu
dữ liệu
của giá trị 'Xin chào' là một chuỗi. Kiểu dữ liệu của giá trị 5 là một số nguyên. Kiểu dữ liệu
của dữ liệu cho chúng ta biết (và máy tính) đó là loại dữ liệu gì.
Viết chương trình trong Trình chỉnh sửa tệp của IDLE
Cho đến bây giờ chúng tôi đã gõ từng hướng dẫn vào vỏ tương tác. Khi nào
chúng tôi viết chương trình mặc dù, chúng tôi gõ một số hướng dẫn và để chúng chạy tất cả cùng
một lúc.
Hãy viết chương trình đầu tiên của chúng tôi!
Tên của chương trình cung cấp shell tương tác được gọi là IDLE,
Môi trường DeveLopement tương tác. IDLE cũng có một phần khác gọi là trình soạn thảo tệp.
Nhấp vào menu Tệp ở đầu cửa sổ Python Shell và chọn Cửa sổ mới .
Một cửa sổ trống mới sẽ xuất hiện để chúng tôi nhập chương trình của chúng tôi. Cửa sổ này
là tệp
biên tập viên .
Hình 3-1: Cửa sổ soạn thảo tệp.

Chào thế giới!


Một truyền thống cho các lập trình viên học một ngôn ngữ mới là tạo ra chương trình đầu tiên
của họ
hiển thị dòng chữ "Xin chào thế giới!" trên màn hình. Chúng tôi sẽ tạo chương trình Hello World
của riêng mình
hiện nay.
20

Trang 35
xin chào
Mã này có thể được tải xuống từ http://inventwithpython.com/hello.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. # Chương trình này nói xin chào và hỏi tên tôi.
2. in ('Xin chào thế giới!')
3. in ('Tên bạn là gì?')
4. myName = input ()
5. in ('Rất vui được gặp bạn,' + myName)
Chương trình IDLE sẽ cung cấp cho các loại hướng dẫn khác nhau màu sắc khác nhau. Sau khi
bạn là
Nhập xong mã này vào, cửa sổ sẽ trông như thế này:
Hình 3-3: Cửa sổ soạn thảo tệp sẽ trông như thế này sau khi bạn nhập mã.
Khi bạn vào chương trình của mình, đừng nhập số
ở bên trái của mã. Họ ở đó để chúng ta có thể tham khảo
đến từng dòng theo số trong lời giải thích của chúng tôi. Nếu bạn nhìn vào
góc dưới bên phải của cửa sổ soạn thảo tập tin, nó sẽ
cho bạn biết con trỏ nào hiện đang bật.
Nhập văn bản sau vào trình chỉnh sửa tệp mới
cửa sổ. Chúng tôi gọi văn bản này là mã nguồn của chương trình
bởi vì nó chứa các hướng dẫn mà Python sẽ
làm theo để xác định chính xác chương trình nên ứng xử như thế nào (Hãy nhớ, đừng gõ vào
số dòng!)
LƯU Ý QUAN TRỌNG! Chương trình sau đây sẽ được chạy bởi Python 3
trình thông dịch, không phải Python 2.6 (hoặc bất kỳ phiên bản 2.x nào khác). Hãy chắc chắn
rằng bạn có chính xác
phiên bản Python đã cài đặt. (Nếu bạn đã cài đặt Python 2, bạn có thể có Python 3
được cài đặt cùng lúc.) Để tải xuống Python 3, hãy truy cập
http://python.org/doad/release/3.1.1/ và cài đặt phiên bản này.
Hình 3-2: Phía dưới bên phải của tệp
cửa sổ soạn thảo cho bạn biết con trỏ ở đâu
Là. Con trỏ hiện đang ở dòng 12.
21
3 - Chuỗi

Trang 36
Lưu chương trình của bạn
Hướng dẫn bằng video về cách sử dụng trình chỉnh sửa tệp có sẵn từ trang web của cuốn sách
này tại
http://inventwithpython.com/ideo/.
Nếu bạn gặp một lỗi giống như thế này:
Chào thế giới!
Tên của bạn là gì?
Albert
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "C: /Python26/test1.py", dòng 4, trong <mô-đun>
myName = input ()
Tệp "<chuỗi>", dòng 1, trong <mô-đun>
TênError: tên 'Albert' không được xác định
... thì điều này có nghĩa là bạn đang chạy chương trình với Python 2, thay vì Python 3. Bạn
có thể cài đặt Python 3 hoặc chuyển đổi mã nguồn trong cuốn sách này sang Python 2. Phụ lục A
Khi bạn đã
đã nhập của bạn
mã nguồn, lưu
nó để bạn
sẽ không phải
gõ lại từng cái
thời gian chúng ta bắt đầu
IDLE. Làm như vậy,
chọn tập tin
thực đơn ở đầu
Trình chỉnh sửa tệp
cửa sổ, và sau đó
nhấp vào Lưu với tên .
Lưu dưới dạng
cửa sổ nên
mở. Đi vào
hello.py trong
Hộp tên tệp
sau đó nhấn Save .
(Xem hình 3-4.)
Thỉnh thoảng bạn nên lưu chương trình của mình khi bạn nhập chúng. Theo cách đó, nếu
máy tính gặp sự cố hoặc bạn vô tình thoát khỏi IDLE, chỉ gõ phím bạn đã thực hiện
kể từ lần lưu cuối cùng của bạn sẽ bị mất. Nhấn Ctrl-S để lưu tệp của bạn một cách nhanh chóng
mà không cần sử dụng
chuột nào cả.
Hình 3-4: Lưu chương trình.
22

Trang 37
liệt kê sự khác biệt giữa Python 2 và 3 mà bạn sẽ cần cho cuốn sách này.
Mở các chương trình bạn đã lưu
Để tải một chương trình đã lưu, chọn Tệp> Mở . Làm điều đó ngay bây giờ và trong cửa sổ
xuất hiện chọn hello.py và nhấn nút Mở . Chương trình hello.py đã lưu của bạn nên
mở trong cửa sổ Trình chỉnh sửa tệp.
Bây giờ là lúc để chạy chương trình của chúng tôi. Từ menu Tệp, chọn Chạy> Chạy Mô-
đun hoặc
chỉ cần nhấn phím F5 trên bàn phím của bạn. Chương trình của bạn sẽ chạy trong cửa sổ shell
xuất hiện khi bạn lần đầu tiên bắt đầu IDLE. Hãy nhớ rằng, bạn phải nhấn F5 từ tệp
cửa sổ của trình soạn thảo, không phải cửa sổ của vỏ tương tác.
Khi chương trình của bạn hỏi tên của bạn, hãy tiếp tục và nhập nó như trong Hình 3-5:
Hình 3-5: Vỏ tương tác trông như thế nào khi chạy chương trình "Hello World".
Bây giờ, khi bạn nhấn Enter, chương trình sẽ chào bạn ( người dùng ) theo tên.
Xin chúc mừng! Bạn đã viết chương trình đầu tiên của bạn. Bây giờ bạn là một máy tính bắt đầu
lập trình viên. (Bạn có thể chạy lại chương trình này nếu muốn bằng cách nhấn lại F5.)
Chương trình "Hello World" hoạt động như thế nào
Chương trình này hoạt động như thế nào? Chà, mỗi dòng mà chúng tôi nhập vào là một chỉ dẫn
cho
máy tính được Python diễn giải theo cách mà máy tính sẽ hiểu. Một
chương trình máy tính rất giống như một công thức. Thực hiện bước đầu tiên trước, sau đó đến
bước thứ hai, v.v.
cho đến khi bạn đạt đến cuối cùng Mỗi hướng dẫn được thực hiện theo trình tự, bắt đầu từ chính
đầu chương trình và làm việc xuống danh sách hướng dẫn. Sau khi chương trình thực thi
dòng hướng dẫn đầu tiên, nó di chuyển và thực hiện dòng thứ hai, sau đó là dòng thứ ba, và vì
vậy
trên.
Chúng tôi gọi các hướng dẫn sau của chương trình từng bước là luồng thực hiện , hoặc
chỉ thực hiện ngắn gọn.
Bây giờ chúng ta hãy nhìn vào từng dòng chương trình của chúng tôi để xem nó đang làm gì, bắt đầu
với
23
3 - Chuỗi

Trang 38
dòng số 1.
Bình luận
1. # Chương trình này nói xin chào và hỏi tên tôi.
Dòng này được gọi là một bình luận . Bất kỳ văn bản nào sau dấu # (được gọi là dấu thăng ) là
một lời bình luận. Nhận xét không dành cho máy tính, nhưng dành cho bạn, lập trình viên. Các
máy tính bỏ qua chúng. Chúng được sử dụng để nhắc nhở bạn về những gì chương trình làm
hoặc nói
những người khác có thể xem mã của bạn, mã của bạn đang cố làm gì.
Các lập trình viên thường đặt một nhận xét ở đầu mã của họ để đặt tiêu đề cho chương trình của
họ.
Chương trình IDLE hiển thị các bình luận màu đỏ để giúp họ nổi bật.
Chức năng
Một chức năng giống như một chương trình nhỏ bên trong chương trình của bạn. Nó chứa các
dòng mã
được thực hiện từ trên xuống dưới. Python cung cấp một số hàm dựng sẵn mà chúng ta có thể
sử dụng. Điều tuyệt vời về các chức năng là chúng ta chỉ cần biết chức năng đó làm gì,
nhưng không làm thế nào nó làm điều đó. (Bạn cần biết rằng hàm print () hiển thị văn bản trên
màn hình, nhưng bạn không cần biết làm thế nào để làm điều này.)
Một cuộc gọi hàm là một đoạn mã thông báo chương trình của chúng ta chạy mã bên trong
chức năng. Ví dụ, chương trình của bạn có thể gọi hàm print () bất cứ khi nào bạn muốn
hiển thị một chuỗi trên màn hình. Hàm print () lấy chuỗi bạn nhập vào giữa
dấu ngoặc đơn làm đầu vào và hiển thị văn bản trên màn hình. Bởi vì chúng tôi muốn hiển thị
Chào thế giới! trên màn hình, chúng ta gõ tên chức năng in , theo sau là một
mở ngoặc đơn, theo sau là 'Xin chào thế giới!' chuỗi và dấu ngoặc đơn đóng.
Các print () Chức năng
2. in ('Xin chào thế giới!')
3. in ('Tên bạn là gì?')
Dòng này là một cuộc gọi đến chức năng in , thường được viết là print () (với chuỗi là
được in đi bên trong dấu ngoặc đơn).
Chúng tôi thêm dấu ngoặc đơn vào cuối tên hàm để làm rõ rằng chúng tôi đang đề cập đến một
Hàm có tên print () , không phải là biến có tên print . Các dấu ngoặc ở cuối
chức năng cho chúng tôi biết chúng tôi đang nói về một chức năng, giống như các trích dẫn xung
quanh
số '42' cho chúng tôi biết rằng chúng tôi đang nói về chuỗi '42' chứ không phải số nguyên 42 .
Dòng 3 là một lệnh gọi hàm print () khác . Lần này, chương trình hiển thị "Bạn là gì
Tên?"
24

Trang 39
Các đầu vào () Chức năng
4. myName = input ()
Dòng này có một câu lệnh gán với một biến ( myName ) và một hàm gọi
( đầu vào () ). Khi đầu vào () được gọi, chương trình sẽ đợi đầu vào; cho người dùng nhập
bản văn. Chuỗi văn bản mà người dùng nhập (tên của bạn) trở thành giá trị đầu ra của hàm.
Giống như các biểu thức, các lệnh gọi hàm ước tính thành một giá trị duy nhất. Giá trị mà hàm
đánh giá cuộc gọi được gọi là giá trị trả về. Trong trường hợp này, giá trị trả về của đầu vào ()
Hàm là chuỗi mà người dùng đã nhập - tên của họ. Nếu người dùng gõ vào Albert,
Hàm gọi hàm input () ước tính cho chuỗi 'Albert' .
Hàm có tên input () không cần bất kỳ đầu vào nào (không giống như hàm print () ),
đó là lý do tại sao không có gì ở giữa các dấu ngoặc đơn.
5. in ('Rất vui được gặp bạn,' + myName)
Trên dòng cuối cùng, chúng ta có một hàm print () một lần nữa. Lần này, chúng tôi sử dụng toán
tử cộng
( + ) để nối chuỗi 'Rất vui được gặp bạn' và chuỗi được lưu trữ trong
các Myname biến, đó là tên mà người dùng nhập vào của chúng tôi vào chương trình. Đây là
cách
chúng tôi nhận được chương trình để chào đón chúng tôi bằng tên.
Kết thúc chương trình
Khi chương trình thực thi dòng cuối cùng, nó dừng lại. Tại thời điểm này, nó đã chấm dứt hoặc
đã thoát và tất cả các biến bị máy tính quên, bao gồm cả chuỗi chúng tôi đã lưu trữ
trong tên của tôi . Nếu bạn thử chạy lại chương trình với một tên khác, như Carolyn, thì nó
sẽ nghĩ đó là tên của bạn
Chào thế giới!
Tên của bạn là gì?
Carolyn
Rất vui được gặp bạn, Carolyn
Hãy nhớ rằng, máy tính chỉ thực hiện chính xác những gì bạn lập trình nó để làm. Trong đó, đầu
tiên của chúng tôi
chương trình, nó được lập trình để hỏi bạn tên của bạn, cho phép bạn nhập một chuỗi, và sau đó
nói
xin chào và hiển thị chuỗi bạn đã gõ
Nhưng máy tính thì câm. Chương trình không quan tâm nếu bạn nhập tên của bạn, một người
nào đó
tên khác, hoặc chỉ là một cái gì đó ngớ ngẩn. Bạn có thể nhập bất cứ thứ gì bạn muốn và máy
tính
sẽ đối xử với nó theo cùng một cách:
25
3 - Chuỗi

Trang 40
Chào thế giới!
Tên của bạn là gì?
phân
Rất vui được gặp bạn
Tên biến
Máy tính không quan tâm bạn đặt tên cho biến của mình là gì, nhưng bạn nên làm vậy. Bố thí
tên biến phản ánh loại dữ liệu họ chứa giúp dễ hiểu hơn
những gì một chương trình làm. Thay vì tên , chúng ta có thể gọi biến này
abrahamLincoln hoặc nAmE . Máy tính sẽ chạy chương trình giống nhau (miễn là bạn
luôn sử dụng abrahamLincoln hoặc nAmE ).
Tên biến (cũng như mọi thứ khác trong Python) phân biệt chữ hoa chữ thường. Trường hợp-
nhạy cảm có nghĩa là cùng một tên biến trong một trường hợp khác được coi là hoàn toàn
tên biến riêng biệt. Vì vậy, thư rác , SPAM , Spam và sPAM được coi là bốn
các biến khác nhau trong Python. Mỗi cái có thể chứa các giá trị riêng của chúng.
Đó là một ý tưởng tồi để có các biến có vỏ khác nhau trong chương trình của bạn. Nếu bạn lưu
trữ
Tên đầu tiên trong biến tên và họ của bạn trong biến TÊN , nó sẽ rất
khó hiểu khi bạn đọc mã của bạn vài tuần sau khi bạn viết nó lần đầu tiên. Đã tên có nghĩa là đầu
tiên và
TÊN có nghĩa là cuối cùng, hoặc cách khác xung quanh?
Nếu bạn vô tình chuyển đổi tên và biến NAME, thì chương trình của bạn sẽ vẫn
chạy (nghĩa là nó sẽ không có bất kỳ lỗi cú pháp nào) nhưng nó sẽ chạy không chính xác. Loại lỗ
hổng này trong
mã của bạn được gọi là một lỗi . Nó rất phổ biến để vô tình tạo ra lỗi trong chương trình của bạn
trong khi bạn viết chúng Đây là lý do tại sao điều quan trọng là tên biến bạn chọn thực hiện
giác quan.
Nó cũng giúp viết hoa tên biến nếu chúng bao gồm nhiều hơn một từ. Nếu bạn lưu trữ
một chuỗi những gì bạn đã có cho bữa sáng trong một biến, tên biến
whatIHadFor Breakfast ThisMorning dễ đọc hơn nhiều so với
whatihadforb ERICthismorning . Đây là một quy ước (có nghĩa là, một tùy chọn
nhưng cách làm tiêu chuẩn) trong lập trình Python. (Mặc dù tốt hơn nữa sẽ là
một cái gì đó đơn giản, như ngày nay ăn sáng . Viết hoa chữ cái đầu tiên của mỗi từ trong
tên biến làm cho chương trình dễ đọc hơn.
Tóm lược
Bây giờ chúng ta đã học được cách xử lý văn bản, chúng ta có thể bắt đầu tạo các chương trình
người dùng có thể chạy và tương tác với. Điều này rất quan trọng vì văn bản là cách chính của
người dùng và
máy tính sẽ liên lạc với nhau. Người chơi sẽ nhập văn bản vào chương trình
thông qua bàn phím với chức năng input () . Và máy tính sẽ hiển thị văn bản trên
26

Trang 41
màn hình khi hàm print () được thực thi.
Chuỗi chỉ là một loại dữ liệu khác nhau mà chúng ta có thể sử dụng trong các chương trình của
mình. Chúng ta có thể sử dụng dấu +
Toán tử để nối các chuỗi với nhau. Sử dụng toán tử + để nối hai chuỗi
cùng nhau tạo thành một chuỗi mới cũng giống như sử dụng toán tử + để thêm hai số nguyên để
tạo thành một chuỗi
số nguyên mới (tổng).
Trong chương tiếp theo, chúng ta sẽ tìm hiểu thêm về các biến để chương trình của chúng ta sẽ
nhớ văn bản và số mà người chơi nhập vào chương trình. Một khi chúng ta có
đã học cách sử dụng văn bản, số và biến, chúng tôi sẽ sẵn sàng bắt đầu tạo trò chơi.
27
3 - Chuỗi

Trang 42
Các chủ đề được đề cập trong Chương này:
● báo cáo nhập khẩu
● Mô-đun

● Đối số

● trong khi báo cáo

● Điều kiện

● Khối

● Booleans

● Toán tử so sánh

● Sự khác biệt giữa = và == .

● nếu báo cáo

● Các phá vỡ từ khóa.

● Các hàm str () và int () .

● Hàm Random.randint () .

Trò chơi "Đoán số"


Chúng tôi sẽ thực hiện một trò chơi "Đoán số". Trong trò chơi này, máy tính sẽ nghĩ
của một số ngẫu nhiên từ 1 đến 20 và yêu cầu bạn đoán số đó. Bạn chỉ có sáu
đoán, nhưng máy tính sẽ cho bạn biết nếu dự đoán của bạn quá cao hoặc quá thấp. Nếu bạn đoán
số trong vòng sáu lần thử, bạn thắng.
Đây là một trò chơi hay để bạn bắt đầu vì nó sử dụng các số ngẫu nhiên, các vòng lặp và
đầu vào từ người dùng trong một chương trình khá ngắn. Khi bạn viết trò chơi này, bạn sẽ học
cách
chuyển đổi giá trị thành các loại dữ liệu khác nhau (và tại sao bạn cần phải làm điều này).
28

Trang 43
Bởi vì chương trình này là một trò chơi, chúng tôi sẽ gọi người dùng là người chơi , nhưng từ
"người dùng"
cũng sẽ đúng
Chạy mẫu của "Đoán số"
Đây là những gì trò chơi của chúng tôi sẽ trông giống như người chơi khi chương trình được
chạy. Văn bản đó
các kiểu người chơi được in đậm .
Xin chào! Tên của bạn là gì?
Albert
Chà, Albert, tôi đang nghĩ về một con số từ 1
và 20.
Hãy đoán.
10
Dự đoán của bạn quá cao.
Hãy đoán.
2
Dự đoán của bạn quá thấp.
Hãy đoán.
4
Làm tốt lắm, Albert! Bạn đoán số của tôi trong 3
đoán!
Nhập mã này chính xác như nó xuất hiện ở đây, và sau đó lưu nó bằng cách nhấp vào menu Tệp
và sau đó lưu dưới dạng . Đặt cho nó một tên tệp như đoán từ xa, sau đó chạy nó bằng cách
nhấn phím F5. Đừng
lo lắng nếu bạn không hiểu mã bây giờ, tôi sẽ giải thích từng bước một.
Đoán mã nguồn của số
Đây là mã nguồn cho trò chơi Đoán số của chúng tôi. Khi bạn nhập mã này vào
trình chỉnh sửa tập tin, hãy chắc chắn chú ý đến khoảng cách ở phía trước của một số dòng. Một
số
dòng có bốn hoặc tám khoảng trống ở phía trước chúng. Sau khi bạn đã nhập mã, hãy lưu
tập tin dưới dạng đoán . Bạn có thể chạy chương trình từ trình chỉnh sửa tệp bằng cách nhấn
F5. Nếu bạn thấy một
thông báo lỗi, kiểm tra xem bạn đã gõ chương trình chính xác như đã viết.
Nếu bạn không muốn nhập tất cả mã này, bạn có thể tải xuống từ trang web của cuốn sách này
tại
URL http://inventwithpython.com/ch CHƯƠNG4.
Lưu ý quan trọng! Hãy chắc chắn chạy chương trình này với Python 3 chứ không phải Python
2.
các chương trình trong cuốn sách này sử dụng Python 3 và bạn sẽ gặp lỗi nếu bạn cố chạy chúng
với Python
2. Bạn có thể nhấp vào Trợ giúp và sau đó Giới thiệu về IDLE để tìm hiểu phiên bản Python nào
của bạn
có.
29
4 - Đoán số
Trang 44
đoán
Mã này có thể được tải xuống từ http://inventwithpython.com/guess.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. # Đây là một trò chơi đoán số.
2. nhập ngẫu nhiên
3.
4. đoánTaken = 0
5.
6. in ('Xin chào! Tên bạn là gì?')
7. myName = input ()
số 8.
9. số = ngẫu nhiên.randint (1, 20)
10. in ('Chà,' + myName + ', tôi đang nghĩ về một con số
từ 1 đến 20. ')
11.
12. trong khi đoánTaken <6:
13. in ('Hãy đoán.') # Có bốn khoảng trắng trong
mặt trước in.
14. đoán = đầu vào ()
15. đoán = int (đoán)
16.
17. đoánesaken = đoánesaken + 1
18.
19. nếu đoán <số:
20.
print ('Dự đoán của bạn quá thấp.') # Có tám
khoảng trống phía trước bản in.
21.
22. nếu đoán> số:
23.
in ('Dự đoán của bạn quá cao.')
24.
25. nếu đoán == số:
26.
phá vỡ
27.
28. nếu đoán == số:
29. đoánesaken = str (đoánesaken)
30. in ('Công việc tốt,' + myName + '! Bạn đoán tôi
số trong '+ đoánTaken +' đoán! ')
31.
32. nếu đoán! = Số:
33. số = str (số)
34. print ('Không. Số tôi đã nghĩ đến là' +
con số)
Mặc dù chúng tôi đang nhập mã nguồn vào một cửa sổ soạn thảo tệp mới, chúng tôi có thể
trở về shell để nhập các hướng dẫn riêng lẻ để xem những gì họ làm. Các
shell tương tác rất tốt để thử nghiệm các hướng dẫn khác nhau khi chúng ta không
chạy một chương trình. Bạn có thể quay lại vỏ tương tác bằng cách nhấp vào cửa sổ của nó hoặc
vào
nút thanh tác vụ của nó. Trong Windows hoặc Mac OS X, thanh tác vụ hoặc thanh công cụ nằm
ở dưới cùng của
màn. Trên Linux, thanh tác vụ có thể được đặt dọc theo phía trên màn hình.
30
Trang 45
Nếu chương trình dường như không hoạt động sau khi bạn đã nhập, hãy kiểm tra xem bạn đã nhập
chưa
mã chính xác như nó xuất hiện trong cuốn sách này. Bạn cũng có thể sao chép và dán mã của
mình vào
công cụ "khác biệt" trực tuyến tại http://inventwithpython.com/diff. Công cụ tìm khác biệt sẽ chỉ
cho bạn cách
mã khác với mã nguồn trong cuốn sách này. Trong trình chỉnh sửa tệp, nhấn Ctrl-A để "Chọn
Tất cả "văn bản bạn đã nhập, sau đó nhấn Ctrl-C để sao chép văn bản vào bảng tạm. Sau đó, dán
văn bản này bằng cách nhấp vào trường văn bản của công cụ tìm khác biệt trên trang web và
nhấp vào "So sánh"
cái nút. Trang web sẽ cho bạn thấy bất kỳ sự khác biệt giữa mã của bạn và mã trong này
sách.
Có một công cụ tìm khác nhau cho mỗi chương trình trong cuốn sách này trên
http://inventwithpython.com
trang mạng. Hướng dẫn bằng video về cách sử dụng công cụ tìm khác biệt có sẵn từ trang web
của cuốn sách này tại
http://inventwithpython.com/ideo/.
Các khẩu Statement
Hãy lần lượt xem xét từng dòng mã để xem chương trình này hoạt động như thế nào.
1. # Đây là một trò chơi đoán số.
Dòng này là một bình luận. Nhận xét đã được giới thiệu trong chương trình Hello World của
chúng tôi tại
Chương 3. Hãy nhớ rằng Python sẽ bỏ qua mọi thứ sau dấu # . Điều này chỉ nhắc nhở
chúng tôi những gì chương trình này làm.
2. nhập ngẫu nhiên
Đây là một báo cáo nhập khẩu . Báo cáo không phải là chức năng (lưu ý rằng không nhập
cũng không ngẫu nhiên có dấu ngoặc đơn sau tên của nó). Hãy nhớ rằng, báo cáo là hướng dẫn

thực hiện một số hành động nhưng không đánh giá đến một giá trị. Bạn đã thấy các tuyên bố:
câu lệnh gán lưu một giá trị vào một biến (nhưng câu lệnh không đánh giá
bất cứ điều gì).
Trong khi Python bao gồm nhiều hàm dựng sẵn, một số hàm tồn tại riêng biệt
các chương trình gọi là mô-đun. Các mô-đun là các chương trình Python có chứa các chức năng
bổ sung.
Chúng tôi sử dụng các chức năng của các mô-đun này bằng cách đưa chúng vào các chương
trình của chúng tôi với
báo cáo nhập khẩu . Trong trường hợp này, chúng tôi đang nhập mô-đun ngẫu nhiên .
Câu lệnh nhập được tạo thành từ khóa nhập theo sau là mô-đun
Tên. Cùng với nhau, từ khóa và tên mô-đun tạo nên tuyên bố. Dòng 2 sau đó là một
nhập khẩu tuyên bố rằng hàng nhập khẩu module tên ngẫu nhiên , trong đó có nhiều
các hàm liên quan đến số ngẫu nhiên. (Chúng ta sẽ sử dụng một trong các chức năng này sau để

máy tính đưa ra một con số ngẫu nhiên để chúng ta đoán.)
4. đoánTaken = 0
31
4 - Đoán số
Trang 46
Dòng này tạo ra một biến mới có tên là đoánesaken . Chúng tôi sẽ lưu trữ số lượng
đoán người chơi thực hiện trong biến này. Vì người chơi đã không đưa ra bất kỳ dự đoán nào cho
đến nay,
chúng tôi lưu trữ số nguyên 0 ở đây.
6. in ('Xin chào! Tên bạn là gì?')
7. myName = input ()
Các dòng 6 và 7 giống như các dòng trong chương trình Hello World mà chúng ta đã thấy trong
Chương 3. Các lập trình viên thường sử dụng lại mã từ các chương trình khác của họ khi họ cần
chương trình để làm một cái gì đó mà họ đã được mã hóa trước đó.
Dòng 6 là một hàm gọi đến hàm print () . Hãy nhớ rằng một chức năng giống như một
chương trình nhỏ mà chương trình của chúng tôi chạy và khi chương trình của chúng tôi gọi một
chức năng thì nó chạy chương trình này
chương trình nhỏ. Mã bên trong hàm print () hiển thị chuỗi bạn đã truyền
bên trong dấu ngoặc đơn trên màn hình.
Khi hai dòng này kết thúc thực thi, chuỗi là tên của người chơi sẽ được lưu trữ
trong biến myName . (Hãy nhớ rằng, chuỗi có thể không thực sự là tên của người chơi. Đó là
bất cứ chuỗi nào người chơi gõ vào. Máy tính bị câm và chỉ cần làm theo
chương trình không có vấn đề gì.)
Các random.randint () Chức năng
9. số = ngẫu nhiên.randint (1, 20)
Trong Dòng 9, chúng ta gọi một hàm mới có tên randint () , và sau đó lưu giá trị trả về trong
một biến có tên là số . Hãy nhớ rằng các lệnh gọi hàm là biểu thức vì chúng
đánh giá một giá trị. Chúng tôi gọi giá trị này là giá trị trả về của hàm gọi.
Vì hàm randint () được cung cấp bởi mô-đun ngẫu nhiên , chúng tôi đứng trước nó
với ngẫu nhiên. (đừng quên khoảng thời gian!) để nói với chương trình của chúng tôi rằng
hàm randint ()
là trong mô-đun ngẫu nhiên.
Hàm randint () sẽ trả về một số nguyên ngẫu nhiên giữa (và bao gồm) cả hai
số nguyên chúng tôi cung cấp cho nó. Ở đây, chúng tôi cung cấp cho nó các số nguyên 1 và 20
giữa các dấu ngoặc đơn
theo tên hàm (cách nhau bằng dấu phẩy). Số nguyên ngẫu nhiên randint ()
lợi nhuận được lưu trữ trong một biến có tên là số - đây là số bí mật mà người chơi đang thử
đoán.
Chỉ trong chốc lát, hãy quay lại trình vỏ tương tác và nhập nhập ngẫu nhiên vào
nhập mô-đun ngẫu nhiên. Sau đó nhập Random.randint (1, 20) để xem những gì
chức năng gọi đánh giá đến. Nó sẽ trả về một số nguyên trong khoảng từ 1 đến 20 . Bây giờ nhập
cùng một mã một lần nữa và lệnh gọi hàm có thể sẽ trả về một số nguyên khác nhau. Đây là
32

Trang 47
bởi vì mỗi lần hàm randint () được gọi, nó sẽ trả về một số ngẫu nhiên,
giống như khi bạn lắc xí ngầu bạn sẽ nhận được một số ngẫu nhiên mỗi lần.
>>> nhập ngẫu nhiên
>>> ngẫu nhiên.randint (1, 20)
12
>>> ngẫu nhiên.randint (1, 20)
18
>>> ngẫu nhiên.randint (1, 20)
3
>>> ngẫu nhiên.randint (1, 20)
18
>>> ngẫu nhiên.randint (1, 20)
7
>>>
Bất cứ khi nào chúng tôi muốn thêm tính ngẫu nhiên vào các trò chơi của mình, chúng tôi có thể
sử dụng randint ()
chức năng. Và chúng tôi sử dụng tính ngẫu nhiên trong hầu hết các trò chơi. (Hãy nghĩ về việc có
bao nhiêu trò chơi trên bảng
xúc xắc.)
Bạn cũng có thể thử các phạm vi số khác nhau bằng cách thay đổi các đối số. Dành cho
ví dụ: nhập Random.randint (1, 4) để chỉ nhận các số nguyên từ 1 đến 4
(bao gồm cả 1 và 4 ). Hoặc thử Random.randint (1000, 2000) để lấy số nguyên
từ 1000 đến 2000 . Dưới đây là một ví dụ về cách gọi Random.randint ()
chức năng và xem những gì giá trị nó trả về. Kết quả bạn nhận được khi bạn gọi
Hàm Random.randint () có thể sẽ khác (sau tất cả là ngẫu nhiên).
>>> ngẫu nhiên.randint (1, 4)
3
>>> ngẫu nhiên.randint (1, 4)
4
>>> ngẫu nhiên.randint (1000, 2000)
1294
>>> ngẫu nhiên.randint (1000, 2000)
1585
>>>
Chúng ta có thể thay đổi mã của trò chơi một chút để làm cho trò chơi hoạt động khác đi. Thử
thay đổi dòng 9 và 10 từ đây:
9. số = ngẫu nhiên.randint (1, 20 )
10. in ('Chà,' + tên + ', tôi đang nghĩ về một con số
từ 1 đến 20. ')
33
4 - Đoán số

Trang 48
vào những dòng này:
9. số = ngẫu nhiên.randint (1, 100 )
10. in ('Chà,' + tên + ', tôi đang nghĩ về một con số
từ 1 đến 100. ')
Và bây giờ máy tính sẽ nghĩ về một số nguyên trong khoảng từ 1 đến 100 . Thay đổi dòng 9 sẽ
thay đổi phạm vi của số ngẫu nhiên, nhưng nhớ thay đổi dòng 10 để trò chơi
cũng cho người chơi biết phạm vi mới thay vì phạm vi cũ.
Chức năng gọi bên trong Mô-đun
Nhân tiện, hãy chắc chắn nhập ngẫu nhiên.randint (1, 20) và không chỉ randint (1,
20) hoặc máy tính sẽ không biết tìm trong mô-đun ngẫu nhiên cho randint ()
chức năng và bạn sẽ gặp một lỗi như thế này:
>>> randint (1, 20)
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "<stdin>", dòng 1, trong <mô-đun>
NameError: tên 'randint' không được xác định
>>>
Hãy nhớ rằng, chương trình của bạn cần chạy nhập ngẫu nhiên trước khi có thể gọi
Hàm Random.randint () . Đây là lý do tại sao báo cáo nhập khẩu thường đi tại
bắt đầu chương trình.
Truyền các đối số cho các hàm
Các giá trị nguyên giữa các dấu ngoặc đơn trong Random.randint (1, 20)
chức năng gọi được gọi là đối số. Đối số là các giá trị được truyền cho hàm
khi hàm được gọi. Luận cứ cho biết chức năng làm thế nào để hành xử. Giống như
đầu vào của người chơi thay đổi cách chương trình của chúng tôi hoạt động, đối số là đầu vào
cho các chức năng.
Một số hàm yêu cầu bạn chuyển các giá trị cho chúng khi bạn gọi chúng. Ví dụ, nhìn
tại các cuộc gọi chức năng này:
đầu vào()
in ('Xin chào')
ngẫu nhiên.randint (1, 20)
Hàm input () không có đối số nhưng lệnh gọi hàm print () có một và
lệnh gọi hàm randint () có hai. Khi chúng tôi có nhiều hơn một đối số, chúng tôi sẽ tách ra
34

Trang 49
mỗi dấu phẩy, như bạn có thể thấy trong ví dụ này. Các lập trình viên nói rằng các đối số
được phân cách (nghĩa là tách biệt) bằng dấu phẩy. Đây là cách máy tính biết nơi một
giá trị kết thúc và bắt đầu khác.
Nếu bạn truyền quá nhiều hoặc quá ít đối số trong một lệnh gọi hàm, Python sẽ hiển thị một
thông báo lỗi, như bạn có thể thấy dưới đây. Trong ví dụ này, trước tiên chúng ta gọi là randint
() với
chỉ có một đối số (quá ít) và sau đó chúng tôi gọi là randint () với ba đối số (quá
nhiều).
>>> ngẫu nhiên.randint (1)
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "<pyshell # 1>", dòng 1, trong <mô-đun>
ngẫu nhiên.randint (1)
TypeError: randint () mất đúng 3 vị trí
đối số (2 cho)
>>> ngẫu nhiên.randint (1, 2, 3)
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "<pyshell # 2>", dòng 1, trong <mô-đun>
ngẫu nhiên.randint (1, 2, 3)
TypeError: randint () mất đúng 3 vị trí
đối số (4 cho)
>>>
Lưu ý rằng thông báo lỗi cho biết chúng tôi đã chuyển 2 và 4 đối số thay vì 1 và 3. Điều này
là bởi vì Python luôn vượt qua một đối số phụ, vô hình. Đối số này nằm ngoài
phạm vi của cuốn sách này và bạn không phải lo lắng về nó.
Đón tiếp người chơi
Dòng 10 và 12 chào đón người chơi và cho họ biết về trò chơi, sau đó bắt đầu cho phép
Người chơi đoán số bí mật. Dòng 10 khá đơn giản, nhưng dòng 12 giới thiệu một cách hữu ích
khái niệm gọi là vòng lặp.
10. in ('Chà,' + myName + ', tôi đang nghĩ về một con số
từ 1 đến 20. ')
Trong Dòng 10, hàm print () chào đón người chơi bằng tên và nói với họ rằng
máy tính đang nghĩ về một con số ngẫu nhiên.
Nhưng chờ đã - tôi không nói rằng hàm print () chỉ mất một chuỗi sao? Nó có thể trông giống
như
Có nhiều hơn một chuỗi ở đó. Nhưng nhìn vào dòng cẩn thận. Các dấu cộng cộng
ba chuỗi để đánh giá xuống một chuỗi và đó là một chuỗi in ()
chức năng in. Nó có thể trông giống như dấu phẩy đang phân tách các chuỗi, nhưng nếu bạn nhìn
gần gũi, bạn thấy rằng dấu phẩy nằm trong dấu ngoặc kép và một phần của chuỗi.
35
4 - Đoán số

Trang 50
Vòng lặp
Dòng 12 có một thứ gọi là câu lệnh while , biểu thị sự bắt đầu của một
trong khi lặp lại. Vòng lặp là một phần của mã được thực thi lặp đi lặp lại. Nhưng trước khi
chúng ta
có thể tìm hiểu về các vòng lặp trong khi chúng ta cần tìm hiểu một vài khái niệm khác
trước. Những khái niệm đó
là các khối, booleans, toán tử so sánh, điều kiện và cuối cùng là câu lệnh while .
Khối
Một khối là một hoặc nhiều dòng mã được nhóm lại với cùng một lượng tối thiểu
của vết lõm. Bạn có thể biết nơi một khối bắt đầu và kết thúc bằng cách nhìn vào dòng
thụt lề (nghĩa là số khoảng trắng ở phía trước của dòng).
Một khối bắt đầu khi một dòng được thụt vào bởi bốn khoảng trắng. Bất kỳ dòng nào sau đây
cũng là
thụt lề bởi bốn không gian là một phần của khối. Một khối trong một khối bắt đầu khi một dòng

thụt vào với bốn khoảng trắng khác (cho tổng cộng tám khoảng trắng ở phía trước của
dòng). Khối
kết thúc khi có một dòng mã có cùng độ thụt trước khi khối bắt đầu.
Dưới đây là sơ đồ của mã với các khối được phác thảo và đánh số. Các không gian có
hình vuông màu đen điền vào để làm cho chúng dễ dàng hơn để đếm.
Hình 4-1: Khối và vết lõm của chúng. Các chấm đen đại diện cho không gian.
Ví dụ, nhìn vào mã ở trên. Các không gian đã được thay thế bằng hình vuông tối
làm cho chúng dễ đếm hơn Dòng 12 có một vết lõm bằng 0 và không nằm trong bất kỳ khoảng
trống nào
khối. Dòng 13 có một vết lõm gồm bốn khoảng trắng. Vì vết lõm này lớn hơn
thụt dòng trước, chúng ta có thể nói rằng một khối mới đã bắt đầu. Dòng 14, 15, 17 và 19
cũng có bốn không gian để thụt lề. Cả hai dòng này có cùng số lượng
thụt lề như dòng trước, vì vậy chúng tôi biết họ ở trong cùng một khối. (Chúng tôi không tính
dòng trống khi chúng ta tìm kiếm thụt lề.)
36

Trang 51
Dòng 20 có một vết lõm gồm tám khoảng trắng. Tám không gian là hơn bốn không gian, vì vậy
chúng tôi
biết một khối mới đã bắt đầu. Đây là một khối nằm trong một khối khác.
Dòng 22 chỉ có bốn khoảng trắng. Dòng trước dòng 22 có số lượng không gian lớn hơn.
Vì vết lõm đã giảm, chúng tôi biết rằng khối đó đã kết thúc. Dòng 22 nằm trong
cùng khối với các dòng khác với bốn khoảng trắng.
Dòng 23 tăng thụt đầu dòng lên tám khoảng trắng, do đó, một khối mới đã bắt đầu.
Tóm lại, dòng 12 không nằm trong bất kỳ khối nào. Các dòng 13 đến 23 tất cả trong một khối
(được đánh dấu bằng
khoanh tròn 1). Dòng 20 nằm trong một khối trong một khối (được đánh dấu bằng 2 được
khoanh tròn). Và dòng 23 là duy nhất
dòng trong một khối khác trong một khối (được đánh dấu bằng 3 được khoanh tròn).
Khi bạn nhập mã vào IDLE, mỗi chữ cái có cùng chiều rộng. Bạn có thể đếm số
các chữ cái ở trên hoặc dưới dòng để xem bạn đã đặt bao nhiêu khoảng trống trước dòng đó
mã.
Trong hình này, các dòng mã bên trong hộp 1 đều nằm trong cùng một khối và khối 2 và 3
nằm trong khối 1. Khối 1 được thụt vào với ít nhất bốn khoảng trắng từ lề trái và
khối 2 và 3 được thụt vào tám khoảng trắng từ lề trái. Một khối có thể chứa chỉ một
hàng. Lưu ý rằng khối 2 và 3 chỉ có một dòng.
Kiểu dữ liệu Boolean
Kiểu dữ liệu Boolean chỉ có hai giá trị: Đúng hoặc Sai . Những giá trị này là trường hợp-
nhạy cảm và chúng không phải là giá trị chuỗi; nói cách khác, bạn không đặt 'ký tự trích dẫn
xung quanh chúng. Chúng tôi sẽ sử dụng các giá trị Boolean với các toán tử so sánh để tạo thành
các điều kiện.
(Xem bên dưới.)
Toán tử so sánh
Trong dòng 12 của chương trình của chúng tôi, dòng mã chứa câu lệnh while :
12. trong khi đoánTaken <6:
Biểu thức theo sau từ khóa while ( đoánesaken <6 ) chứa hai
các giá trị (giá trị trong biến đoán đoán và giá trị nguyên 6 ) được kết nối bởi
một toán tử ( dấu < , dấu "nhỏ hơn"). Dấu < được gọi là so sánh
Nhà điều hành .
Toán tử so sánh được sử dụng để so sánh hai giá trị và đánh giá là True hoặc
Giá trị Boolean sai . Danh sách tất cả các toán tử so sánh nằm trong Bảng 4-1.
37
4 - Đoán số

Trang 52
Điều kiện
Một điều kiện là một biểu thức kết hợp hai giá trị với toán tử so sánh
(chẳng hạn như < hoặc > ) và ước tính giá trị Boolean. Một điều kiện chỉ là một tên khác cho một
biểu thức đánh giá là Đúng hoặc Sai . Bạn sẽ tìm thấy một danh sách so sánh khác
toán tử trong Bảng 4-1.
Các điều kiện luôn luôn đánh giá theo giá trị Boolean - Đúng hoặc Sai . Ví dụ:
điều kiện trong mã của chúng tôi, đoán <6 câu hỏi "là giá trị được lưu trữ trong
đoán được lấy ít hơn số 6 ? "Nếu vậy, thì điều kiện ước tính là Đúng . Nếu
không, điều kiện đánh giá là Sai .
Trong trường hợp chương trình Đoán số của chúng tôi, trong dòng 4, chúng tôi đã lưu trữ giá
trị 0 trong
đoán . Vì 0 nhỏ hơn 6 , điều kiện này ước tính giá trị Boolean
của Đúng . Hãy nhớ rằng, một điều kiện chỉ là một tên cho một biểu thức sử dụng so sánh
các toán tử như < hoặc ! = .
Thử nghiệm với Booleans, Toán tử so sánh,
và điều kiện
Nhập các biểu thức sau vào vỏ tương tác để xem kết quả Boolean của chúng:
>>> 0 <6
Thật
>>> 6 <0
Sai
>>> 50 <10
Sai
>>> 10 <11
Thật
>>> 10 <10
Bảng 4-1: Toán tử so sánh.
Ký hiệu toán tử
Tên người vận hành
<
Ít hơn
>
Lớn hơn
<=
Ít hơn hoặc bằng
>=
Lớn hơn hoặc bằng
==
Tương đương với
!=
Không bằng
38

Trang 53
Sai
Điều kiện 0 <6 trả về giá trị Boolean Đúng vì số 0 nhỏ hơn
số 6 . Nhưng vì 6 không nhỏ hơn 0 nên điều kiện 6 <0 ước tính thành Sai .
50 không nhỏ hơn 10 , vì vậy 50 <10 là Sai . 10 nhỏ hơn 11 , vì vậy 10 <11 là Đúng .
Nhưng còn 10 <10 thì sao? Tại sao nó đánh giá thành Sai? Đó là sai vì
số 10 không nhỏ hơn số 10 . Chúng có cùng kích thước. Nếu là con gái
tên Alice có chiều cao tương đương với một cậu bé tên Bob, bạn sẽ không nói rằng Alice cao
hơn
hơn Bob hoặc Alice ngắn hơn Bob. Cả hai tuyên bố đó đều sai.
Hãy thử nhập một số điều kiện vào trình bao để xem các toán tử so sánh này hoạt động như thế
nào:
>>> 10 == 10
Thật
>>> 10 == 11
Sai
>>> 11 == 10
Sai
>>> 10! = 10
Sai
>>> 10! = 11
Thật
>>> 'Xin chào' == 'Xin chào'
Thật
>>> 'Xin chào' == 'Tạm biệt'
Sai
>>> 'Xin chào' == 'HELLO'
Sai
>>> 'Tạm biệt'! = 'Xin chào'
Thật
Lưu ý sự khác biệt giữa toán tử gán ( = ) và so sánh "bằng"
toán tử ( == ). Dấu bằng ( = ) được sử dụng để gán giá trị cho biến và bằng
Dấu ( == ) được sử dụng trong biểu thức để xem hai giá trị có bằng nhau không. Thật dễ dàng để
vô tình sử dụng cái này khi bạn định sử dụng cái kia, vì vậy hãy cẩn thận với những gì bạn nhập
vào.
Hai giá trị là các kiểu dữ liệu khác nhau sẽ luôn không bằng nhau. Dành cho
ví dụ, hãy thử nhập nội dung sau vào shell tương tác:
>>> 42 == 'Xin chào'
Sai
>>> 42! = 'Xin chào'
39
4 - Đoán số

Trang 54
Thật
Vòng lặp với các báo cáo trong khi
Câu lệnh while đánh dấu sự bắt đầu của một vòng lặp. Đôi khi trong các chương trình của chúng
tôi, chúng tôi
muốn chương trình làm đi làm lại nhiều lần. Khi thực hiện đạt đến một
Câu lệnh while , nó đánh giá điều kiện bên cạnh từ khóa while . Nếu điều kiện
ước tính là True , việc thực thi di chuyển bên trong khối while. (Trong chương trình của chúng
tôi, trong khi-
khối bắt đầu trên dòng 13.) Nếu điều kiện ước tính thành Sai , việc thực thi di chuyển tất cả
cách qua khối trong khi. (Trong chương trình của chúng tôi, dòng đầu tiên sau khối while là
dòng 28.)
12. trong khi đoánTaken <6:
Hình 4-2: Điều kiện của vòng lặp while.
Hình 4-2 cho thấy cách thực thi chảy tùy thuộc vào điều kiện. Nếu điều kiện
ước tính là True (lần đầu tiên thực hiện, vì giá trị của đoán đoán là
0 ), thực thi sẽ nhập khối while tại dòng 13 và tiếp tục đi xuống. Sau chương trình
đến cuối khối while, thay vì đi xuống dòng tiếp theo, nó nhảy ngược lên
đến dòng của câu lệnh while (dòng 12). Sau đó, nó đánh giá lại điều kiện và nếu nó là True
40

Trang 55
chúng ta nhập khối while một lần nữa.
Đây là cách vòng lặp hoạt động. Miễn là điều kiện là True , chương trình sẽ giữ
thực thi mã bên trong khối while liên tục cho đến khi chúng ta đạt đến cuối thời gian-
khối và điều kiện là Sai . Và, cho đến khi đoánTaken bằng hoặc lớn hơn
6 , chúng tôi sẽ tiếp tục lặp.
Hãy nghĩ về câu lệnh while khi nói, "trong khi điều kiện này là đúng, hãy tiếp tục lặp
thông qua mã trong khối này ".
Bạn có thể làm cho trò chơi này khó hơn hoặc dễ hơn bằng cách thay đổi số lần đoán của người
chơi
được. Tất cả bạn phải làm là thay đổi dòng này:
12. trong khi đoánTaken < 6 :
vào dòng này:
12. trong khi đoánTaken < 4 :
... và bây giờ người chơi chỉ được bốn lần đoán thay vì sáu lần đoán. Bằng cách thiết lập
điều kiện để đoánTaken <4 , chúng tôi đảm bảo rằng mã bên trong vòng lặp chỉ chạy bốn
lần thay vì sáu. Điều này làm cho trò chơi khó khăn hơn nhiều. Để làm cho trò chơi dễ dàng hơn,
đặt điều kiện để đoánTaken <8 hoặc đoánesaken <10 , điều này sẽ gây ra
vòng lặp để chạy nhiều lần hơn trước và chấp nhận nhiều dự đoán hơn từ người chơi.
Tất nhiên, nếu chúng ta loại bỏ dòng 17 hoàn toàn thì đoán đoán sẽ không bao giờ
tăng và điều kiện sẽ luôn luôn đúng . Điều này sẽ cung cấp cho người chơi không giới hạn
số lần đoán.
Người chơi đoán
Dòng 13 đến 17 yêu cầu người chơi đoán số bí mật là gì và cho phép họ nhập số
phỏng đoán. Chúng tôi lưu trữ dự đoán này trong một biến và sau đó chuyển đổi giá trị chuỗi đó
thành một số nguyên
giá trị.
13. in ('Hãy đoán.') # Có bốn khoảng trắng trong
mặt trước in.
14. đoán = đầu vào ()
Chương trình hiện yêu cầu chúng tôi đoán. Chúng tôi gõ vào dự đoán của chúng tôi và số đó
được lưu trữ trong
một biến có tên là đoán .
41
4 - Đoán số

Trang 56
Chuyển đổi Strings đến số nguyên với int () Chức năng
15. đoán = int (đoán)
Trong dòng 15, chúng ta gọi một hàm mới gọi là int () . Hàm int () lấy một
tranh luận. Hàm input () trả về một chuỗi văn bản mà người chơi đã nhập. Nhưng trong chúng ta
chương trình, chúng ta sẽ muốn một số nguyên, không phải là một chuỗi. Nếu người chơi
nhập 5 như dự đoán của họ,
Hàm input () sẽ trả về giá trị chuỗi '5' chứ không phải giá trị nguyên 5 . Nhớ lại
rằng Python coi chuỗi '5' và số nguyên 5 là các giá trị khác nhau. Vì vậy, int ()
Hàm sẽ lấy giá trị chuỗi chúng ta cung cấp cho nó và trả về dạng giá trị nguyên của nó.
Hãy thử nghiệm hàm int () trong trình vỏ tương tác. Hãy thử gõ
tiếp theo:
>>> int ('42 ')
42
>>> int (42)
42
>>> int ('xin chào')
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "<pyshell # 4>", dòng 1, trong <mô-đun>
int ('bốn mươi hai')
ValueError: chữ không hợp lệ cho int () với cơ sở
10: 'xin chào'
>>> int ('bốn mươi hai')
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "<pyshell # 5>", dòng 1, trong <mô-đun>
int ('bốn mươi hai')
ValueError: chữ không hợp lệ cho int () với cơ sở
10: 'bốn mươi hai'
>>> int ('42 ')
42
>>> 3 + int ('2')
5
Chúng ta có thể thấy rằng lệnh gọi int ('42 ') sẽ trả về giá trị nguyên 42 và int đó
(42) sẽ làm tương tự (mặc dù việc chuyển đổi một số nguyên thành một số nguyên là vô nghĩa).
Tuy nhiên, mặc dù bạn có thể truyền một chuỗi cho hàm int () , bạn không thể chỉ truyền
bất kỳ chuỗi. Ví dụ: chuyển 'hello' cho int () (giống như chúng ta làm trong int ('hello')
cuộc gọi) sẽ dẫn đến một lỗi. Chuỗi chúng ta truyền cho int () phải được tạo thành từ các số.
Số nguyên chúng ta truyền cho int () cũng phải là số, chứ không phải là văn bản, đó là lý do tại
sao
42

Trang 57
int ('bốn mươi hai') cũng tạo ra lỗi. Điều đó nói rằng, hàm int () hơi
tha thứ- nếu chuỗi của chúng ta có khoảng trắng ở hai bên, nó vẫn sẽ chạy mà không gặp
lỗi. Đây là lý do tại sao
cuộc gọi int ('42') hoạt động.
Dòng 3 + int ('2') hiển thị biểu thức thêm số nguyên 3 vào giá trị trả về
của int ('2') (cũng đánh giá là 2 ). Biểu thức ước tính thành 3 + 2 , trong đó
sau đó ước lượng thành 5. Vì vậy, mặc dù chúng tôi không thể thêm một số nguyên và một chuỗi
( 3 + '2'
sẽ hiển thị cho chúng tôi một lỗi), chúng tôi có thể thêm một số nguyên vào một chuỗi đã được
chuyển đổi thành một
số nguyên.
Hãy nhớ rằng, quay lại chương trình của chúng tôi trên dòng 15, biến đoán ban đầu giữ chuỗi
giá trị của những gì người chơi đã gõ. Chúng tôi sẽ ghi đè giá trị chuỗi được lưu trữ theo dự
đoán với
giá trị nguyên được trả về bởi hàm int () . Điều này là do sau này chúng ta sẽ so sánh
đoán của người chơi với số ngẫu nhiên mà máy tính đưa ra. Chúng tôi chỉ có thể so sánh
hai giá trị nguyên để xem nếu một giá trị lớn hơn (nghĩa là cao hơn) hoặc nhỏ hơn (nghĩa là thấp
hơn) so với
khác Chúng ta không thể so sánh giá trị chuỗi với giá trị số nguyên để xem giá trị chuỗi lớn hơn
hay nhỏ hơn
so với cái khác, ngay cả khi giá trị chuỗi đó là số, chẳng hạn như '5' .
Trong trò chơi Đoán số của chúng tôi, nếu người chơi nhập vào một số không phải là số,
sau đó hàm gọi int () sẽ dẫn đến lỗi và chương trình sẽ bị sập. Mặt khác
Các trò chơi trong cuốn sách này, chúng tôi sẽ thêm một số mã để kiểm tra các điều kiện lỗi như
thế này và
cung cấp cho người chơi một cơ hội khác để nhập một phản ứng chính xác.
Lưu ý rằng việc gọi int (đoán) không thay đổi giá trị trong biến đoán . Các
mã int (đoán) là một biểu thức ước lượng thành dạng giá trị nguyên của chuỗi
được lưu trữ trong biến đoán. Chúng ta phải gán giá trị trả về này để đoán để thay đổi
giá trị đoán cho một số nguyên với dòng đầy đủ này: đoán = int (đoán)
Biến tăng
17. đoánesaken = đoánesaken + 1
Khi người chơi đã đoán, chúng tôi muốn tăng số lần đoán
nhớ người chơi lấy.
Lần đầu tiên chúng ta vào khối vòng lặp, đoán là có giá trị 0 . Con trăn
sẽ lấy giá trị này và thêm 1 vào nó. 0 + 1 là 1 . Sau đó, Python sẽ lưu trữ giá trị mới của 1
để đoánTaken .
Hãy nghĩ về dòng 17 là có nghĩa, " biến đoán đoán phải là nhiều hơn một
những gì nó đã là ".
Khi chúng ta thêm 1 vào một giá trị số nguyên, các lập trình viên nói rằng họ đang tăng giá trị
(bởi vì nó đang tăng lên một). Khi chúng ta trừ đi một giá trị, chúng ta
giảm giá trị (vì nó đang giảm đi một). Lần tiếp theo khối vòng lặp
43
4 - Đoán số

Trang 58
các vòng lặp xung quanh, đoánTaken sẽ có giá trị là 1 và sẽ được tăng lên thành
giá trị 2 .
Là Guess Guess quá thấp?
Dòng 19 và 20 kiểm tra xem số mà người chơi đoán có nhỏ hơn bí mật không
số ngẫu nhiên mà máy tính đưa ra. Nếu vậy, thì chúng tôi muốn nói với người chơi rằng
dự đoán của họ quá thấp bằng cách in thông báo này lên màn hình.
nếu Tuyên bố
19. nếu đoán <số:
20.
print ('Dự đoán của bạn quá thấp.') # Có
tám khoảng trống phía trước bản in.
Dòng 19 bắt đầu một nếu tuyên bố với từ khóa, nếu . Bên cạnh từ khóa if là
điều kiện. Dòng 20 bắt đầu một khối mới (bạn có thể biết vì độ thụt đã tăng
từ dòng 19 đến dòng 20.) Khối theo sau từ khóa if được gọi là khối if. An
nếu tuyên bố được sử dụng nếu bạn chỉ muốn một chút mã để thực thi nếu điều kiện là đúng.
Dòng 19 có một nếu tuyên bố với điều kiện đoán <số . Nếu điều kiện
ước tính thành True , sau đó mã trong khối if được thực thi. Nếu điều kiện là Sai ,
sau đó mã trong khối if được bỏ qua.
Hình 4-3: câu lệnh if và while.
Giống như câu lệnh while , if
tuyên bố cũng có một từ khóa, theo sau
bởi một điều kiện, và sau đó là một khối mã.
Xem hình 4-3 để so sánh
hai tuyên bố.
Các nếu tác phẩm tuyên bố gần như
cùng một cách như một tuyên bố trong khi quá. Nhưng
Không giống như khối while, thực thi
không nhảy trở lại nếu tuyên bố tại
kết thúc khối if. Nó chỉ tiếp tục
xuống dòng tiếp theo. Nói cách khác, nếu
khối sẽ không lặp.
Nếu điều kiện là True , thì tất cả
các dòng bên trong khối if- được thực thi. Các
chỉ dòng bên trong khối if này trên dòng 19 là
một lệnh gọi hàm print () .
44

Trang 59
Nếu số nguyên mà người chơi nhập vào nhỏ hơn số nguyên ngẫu nhiên mà máy tính nghĩ ra,
chương trình hiển thị Dự đoán của bạn quá thấp . Nếu số nguyên người chơi nhập vào là
bằng hoặc lớn hơn số nguyên ngẫu nhiên (trong trường hợp này, điều kiện bên cạnh if
từ khóa sẽ là Sai ), sau đó khối này sẽ bị bỏ qua.
Là Guess Guess quá cao?
Các dòng từ 22 đến 26 trong chương trình của chúng tôi kiểm tra xem dự đoán của người chơi có
quá lớn hoặc chính xác không
bằng số bí mật.
22. nếu đoán> số:
23.
in ('Dự đoán của bạn quá cao.')
Nếu dự đoán của người chơi lớn hơn số nguyên ngẫu nhiên, chúng tôi sẽ nhập khối if theo sau
những nếu tuyên bố. Dòng print () cho người chơi biết rằng dự đoán của họ quá lớn.
Rời khỏi vòng lặp sớm với tuyên bố phá vỡ
25. nếu đoán == số:
26.
phá vỡ
Điều này nếu điều kiện của câu lệnh kiểm tra xem dự đoán có bằng số nguyên ngẫu nhiên
không. Nếu
đó là, chúng ta nhập dòng 26, khối if theo sau nó.
Dòng bên trong khối if là một câu lệnh break báo cho chương trình ngay lập tức
nhảy ra khỏi khối while đến dòng đầu tiên sau khi kết thúc khối while. (Giờ nghỉ
tuyên bố không làm phiền tái kiểm tra trong khi điều kiện vòng lặp, nó chỉ bùng nổ
ngay.)
Câu lệnh break chỉ là từ khóa break , không có điều kiện hoặc dấu hai chấm
(dấu hiệu).
Nếu dự đoán của người chơi không bằng số nguyên ngẫu nhiên, chúng tôi sẽ không thoát ra khỏi
thời gian-
khối, chúng ta sẽ đạt đến đáy của khối while. Khi chúng ta chạm đến đáy của
khối while, chương trình sẽ lặp lại từ đầu và kiểm tra lại điều kiện
( đoánTaken <6 ). Nhớ sau khi đoánTaken = đoánesaken +
1 dòng mã được thực thi, giá trị mới của đoánesaken là 1 . Vì 1 nhỏ hơn 6 ,
chúng ta vào vòng lặp một lần nữa
Nếu người chơi tiếp tục đoán quá thấp hoặc quá cao, giá trị của đoán được thực hiện sẽ
thay đổi thành 2 , rồi 3 , rồi 4 , rồi 5 , 6 . Nếu người chơi đoán đúng số,
điều kiện trong câu lệnh if đoán == sẽ là True và chúng ta sẽ có
45
4 - Đoán số

Trang 60
thực hiện câu lệnh break . Nếu không, chúng tôi tiếp tục lặp. Nhưng khi
đoánesaken có số 6 được lưu trữ, điều kiện của câu lệnh while là Sai ,
vì 6 không nhỏ hơn 6 . Bởi vì điều kiện của câu lệnh while là Sai , chúng tôi sẽ không
nhập vào vòng lặp và thay vào đó nhảy đến cuối khối while.
Các dòng mã còn lại chạy khi người chơi đoán xong (vì
Người chơi đoán đúng số, hoặc do người chơi hết số lần đoán). Các
Lý do người chơi thoát khỏi vòng lặp trước sẽ xác định xem họ thắng hay thua trò chơi, và
chương trình sẽ hiển thị thông báo phù hợp trên màn hình cho cả hai trường hợp.
Kiểm tra xem người chơi có thắng không
28. nếu đoán == số:
Không giống như mã trong dòng 25, dòng này không có thụt đầu dòng, có nghĩa là khối while có
đã kết thúc và đây là dòng đầu tiên bên ngoài khối while. Khi chúng tôi rời khối while , chúng tôi
đã làm như vậy bởi vì điều kiện của câu lệnh while là Sai (khi người chơi chạy
ngoài dự đoán) hoặc nếu chúng tôi thực hiện câu lệnh break (khi người chơi đoán
số chính xác). Với dòng 28, kiểm tra lại để xem người chơi có đoán đúng không. Nếu vậy, chúng
tôi
nhập khối if sau.
29. đoánesaken = str (đoánesaken)
30. in ('Công việc tốt,' + myName + '! Bạn đoán tôi
số trong '+ đoánTaken +' đoán! ')
Dòng 29 và 30 nằm trong khối if. Họ chỉ thực hiện nếu điều kiện trong nếu
câu lệnh trên dòng 28 là True (nghĩa là, nếu người chơi đoán đúng máy tính
con số).
Trong dòng 29 (tương tự như mã đoán = int (đoán) trên dòng 15), chúng tôi gọi
hàm mới str () , trả về dạng chuỗi của một đối số. Chúng tôi sử dụng cái này
Hàm vì chúng tôi muốn thay đổi giá trị số nguyên trong đoánesaken thành chuỗi của nó
phiên bản vì chúng tôi chỉ có thể sử dụng chuỗi trong các cuộc gọi để in () .
Dòng 29 nói với người chơi rằng họ đã thắng, và họ đã đoán được bao nhiêu lần. Để ý
trong dòng này, chúng tôi thay đổi giá trị đoánTaken thành một chuỗi bởi vì chúng tôi chỉ có thể
thêm chuỗi vào chuỗi khác. Nếu chúng ta cố gắng thêm một chuỗi vào một số nguyên, thì Python
thông dịch viên sẽ hiển thị một lỗi.
Kiểm tra xem người chơi bị mất
32. nếu đoán! = Số:
46

Trang 61
Trong Dòng 32, chúng tôi sử dụng toán tử so sánh ! = Với điều kiện if của câu lệnh để
có nghĩa là "không bằng." Nếu giá trị đoán của người chơi thấp hơn hoặc cao hơn (và
do đó, không bằng) số được máy tính chọn, khi đó điều kiện này ước tính là
Đúng và chúng ta nhập khối theo sau câu lệnh if này trên dòng 33.
Các dòng 33 và 34 nằm trong khối if và chỉ thực hiện nếu điều kiện là True .
33. số = str (số)
34. print ('Không. Số tôi đã nghĩ đến là' +
con số)
Trong khối này, chúng tôi cho người chơi biết con số đó là gì vì họ không đoán đúng.
Nhưng trước tiên, chúng ta phải lưu trữ phiên bản chuỗi số là giá trị mới của số .
Dòng này cũng nằm trong khối if và chỉ thực thi nếu điều kiện là True . Tại đây
điểm, chúng tôi đã đạt đến cuối mã, và chương trình kết thúc.
Xin chúc mừng! Chúng tôi vừa lập trình trò chơi thực sự đầu tiên của chúng tôi!
Tóm tắt: Chính xác thì lập trình là gì?
Nếu ai đó hỏi bạn, "Chính xác thì lập trình là gì?" bạn có thể nói gì với
họ? Lập trình chỉ là hành động viết mã cho các chương trình, nghĩa là tạo ra
các chương trình có thể được thực thi bởi một máy tính.
"Nhưng chính xác thì chương trình là gì?" Khi bạn thấy ai đó đang sử dụng chương trình máy
tính (cho
ví dụ, chơi trò chơi Đoán số của chúng tôi), tất cả những gì bạn thấy là một số văn bản xuất hiện
trên
màn. Chương trình quyết định văn bản chính xác sẽ hiển thị trên màn hình (được gọi là
đầu ra ), dựa trên hướng dẫn của nó (nghĩa là chương trình) và trên văn bản mà trình phát
gõ trên bàn phím (được gọi là đầu vào ). Chương trình có rất cụ thể
hướng dẫn về văn bản để hiển thị người dùng. Một chương trình chỉ là một bộ sưu tập các
hướng dẫn.
"Những loại hướng dẫn?" Chỉ có một vài loại hướng dẫn khác nhau, thực sự.
Biểu thức, được tạo thành từ các giá trị được kết nối bởi các toán tử. Biểu hiện là tất cả
được đánh giá xuống một giá trị duy nhất, như 2 + 2 ước tính thành 4 hoặc 'Xin chào' + '' +
'Thế giới' đánh giá là 'Xin chào thế giới' . Các cuộc gọi chức năng cũng là một phần của biểu
thức
bởi vì họ tự đánh giá một giá trị duy nhất và giá trị này có thể được kết nối bằng
Toán tử đến các giá trị khác. Khi biểu thức bên cạnh từ khóa if và while , chúng tôi
cũng gọi họ là điều kiện.
Các câu lệnh gán, chỉ đơn giản là lưu trữ các giá trị trong các biến để chúng ta có thể nhớ
các giá trị sau này trong chương trình của chúng tôi.
if , while và break là các câu lệnh điều khiển luồng bởi vì chúng quyết định cái nào
47
4 - Đoán số

Trang 62
hướng dẫn được thực hiện. Luồng thực thi thông thường cho một chương trình là bắt đầu tại
đầu và thực hiện từng lệnh đi xuống từng cái một. Nhưng những tuyên bố kiểm soát dòng chảy
có thể khiến luồng bỏ qua hướng dẫn, lặp lại hướng dẫn hoặc thoát ra khỏi các vòng lặp.
Các lệnh gọi hàm cũng thay đổi luồng thực thi bằng cách nhảy đến điểm bắt đầu của hàm.
Hàm print () hiển thị văn bản trên màn hình. Ngoài ra, đầu vào () chức năng
có thể nhận văn bản từ người dùng thông qua bàn phím. Điều này được gọi là I / O (phát âm
giống như
các chữ cái, "eye-oh"), vì nó liên quan đến đầu vào và đầu ra của chương trình.
Và đó là nó, chỉ bốn điều đó. Tất nhiên, có nhiều chi tiết về bốn người đó.
các loại hướng dẫn. Trong cuốn sách này, bạn sẽ tìm hiểu về các kiểu dữ liệu và toán tử mới, mới
báo cáo kiểm soát dòng chảy bên cạnh if , while và break , và một số chức năng mới. Ở đó
cũng là các loại I / O khác nhau (đầu vào từ chuột, và xuất ra âm thanh và đồ họa
và hình ảnh thay vì chỉ văn bản.)
Đối với người sử dụng chương trình của bạn, họ thực sự chỉ quan tâm đến loại cuối cùng đó, I /
O. Các
người dùng gõ trên bàn phím và sau đó nhìn thấy những thứ trên màn hình hoặc nghe những thứ
từ
diễn giả. Nhưng đối với máy tính để tìm ra những điểm tham quan để hiển thị và những âm
thanh để chơi, nó
cần một chương trình và các chương trình chỉ là một loạt các hướng dẫn mà bạn, lập trình viên,
đã được viết.
Một trang web để theo dõi chương trình
Nếu bạn có quyền truy cập Internet và trình duyệt web, bạn có thể truy cập trang web của cuốn
sách này tại
http://inventwithpython.com/traces bạn sẽ tìm thấy một trang theo dõi từng trang
các chương trình trong cuốn sách này. Bằng cách làm theo cùng với dòng theo dõi, nó có thể trở
nên nhiều hơn
rõ ràng những gì chương trình Đoán số làm. Trang web này chỉ hiển thị một mô phỏng của
Điều gì xảy ra khi chương trình được chạy. Không có mã thực tế đang thực sự được thực thi.
48

Trang 63
Hình 4-4: Trang web truy tìm.
Phía bên trái của trang web hiển thị mã nguồn và dòng được tô sáng là dòng
mã sắp được thực thi. Bạn thực hiện dòng này và di chuyển đến dòng tiếp theo
nhấp vào nút "Tiếp theo". Bạn cũng có thể quay lại một bước bằng cách nhấp vào nút "Trước
đó",
hoặc nhảy trực tiếp đến một bước bằng cách gõ nó vào ô trắng và nhấp vào nút "Nhảy".
Ở bên phải của trang web, có ba phần. "Giá trị biến hiện tại"
phần hiển thị cho bạn từng biến đã được gán một giá trị, cùng với chính giá trị đó.
Phần "Ghi chú" sẽ cho bạn một gợi ý về những gì đang xảy ra trên dòng được tô sáng.
Phần "Đầu ra chương trình" hiển thị đầu ra từ chương trình và đầu vào được gửi
đến chương trình. (Trang web này tự động nhập văn bản vào chương trình khi chương trình
hỏi.)
Vì vậy, hãy truy cập từng trang web này và nhấp vào nút "Tiếp theo" và "Trước đó" để theo dõi
thông qua chương trình như chúng tôi đã làm ở trên.
Hướng dẫn bằng video về cách sử dụng công cụ theo dõi trực tuyến có sẵn từ cuốn sách này
trang web tại http://inventwithpython.com/ideo/.
49
4 - Đoán số

Trang 64
Các chủ đề được đề cập trong Chương này:
● Sử dụng đối số từ khóa cuối của print () để bỏ qua dòng mới.
● Thoát ký tự.

● Sử dụng dấu ngoặc đơn và dấu ngoặc kép cho chuỗi.

Tận dụng tối đa bản in ()


Hầu hết các trò chơi trong cuốn sách này sẽ có văn bản đơn giản cho đầu vào và đầu ra. Đầu vào

người dùng gõ trên bàn phím và nhập vào máy tính. Đầu ra là văn bản
hiển thị trên màn hình. Trong Python, hàm print () có thể được sử dụng để hiển thị
đầu ra văn bản trên màn hình. Chúng ta đã học cách cơ bản của việc sử dụng hàm print () ,
nhưng có nhiều hơn để tìm hiểu về cách các chuỗi và print () hoạt động trong Python.
Chạy thử truyện cười
Bạn nhận được gì khi vượt qua người tuyết với ma cà rồng?
Băng giá!
Nha sĩ gọi khoang của phi hành gia là gì?
Một lỗ đen!
Tiếng gõ cửa.
50

Trang 65
Ai đó?
Gián đoạn bò.
Gián đoạn bò-MOO!

Mã nguồn của Joke


Đây là mã nguồn cho chương trình truyện cười ngắn của chúng tôi. Nhập nó vào trình chỉnh sửa
tập tin và lưu
nó như jokes.py . Nếu bạn không muốn nhập mã này, bạn cũng có thể tải xuống nguồn
mã từ trang web của cuốn sách này tại URL http://inventwithpython.com/ch CHƯƠNG5.
Lưu ý quan trọng! Hãy chắc chắn chạy chương trình này với Python 3 chứ không phải Python
2.
các chương trình trong cuốn sách này sử dụng Python 3 và bạn sẽ gặp lỗi nếu bạn cố chạy chúng
với Python
2. Bạn có thể nhấp vào Trợ giúp và sau đó Giới thiệu về IDLE để tìm hiểu phiên bản Python nào
của bạn
có.
jokes
Mã này có thể được tải xuống từ http://inventwithpython.com/jokes.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. in ('Bạn nhận được gì khi vượt qua người tuyết với
ma cà rồng? ')
2. đầu vào ()
3. in ('Băng giá!')
4. in ()
5. in ('Nha sĩ gọi khoang của phi hành gia là gì?')
6. đầu vào ()
7. in ('Một lỗ đen!')
8. in ()
9. in ('Knock gõ.')
10. đầu vào ()
11. in ("Ai ở đó?")
12. đầu vào ()
13. in ('Gián đoạn bò.')
14. đầu vào ()
15. in ('Ngắt bò wh', end = '')
16. in ('- MOO!')
Đừng lo lắng nếu bạn không hiểu mọi thứ trong chương trình. Chỉ cần lưu và chạy
chương trình. Hãy nhớ rằng, nếu chương trình của bạn có lỗi trong đó, bạn có thể sử dụng công
cụ tìm khác biệt trực tuyến tại
http://inventwithpython.com/ch CHƯƠNG5.
Mã hoạt động như thế nào
Hãy xem mã cẩn thận hơn.
51
5 - Truyện cười

Trang 66
1. in ('Bạn nhận được gì khi vượt qua người tuyết với
ma cà rồng? ')
2. đầu vào ()
3. in ('Băng giá!')
4. in ()
Ở đây chúng ta có ba lệnh gọi hàm print () . Bởi vì chúng tôi không muốn nói với người chơi
dòng cú đấm của trò đùa là gì, chúng ta có lệnh gọi hàm input () sau lần in đầu tiên
() . Người chơi có thể đọc dòng đầu tiên, nhấn Enter, sau đó đọc dòng punch.
Người dùng vẫn có thể nhập chuỗi và nhấn Enter, nhưng vì chúng tôi không lưu trữ chuỗi này
trong bất kỳ biến nào, chương trình sẽ quên nó đi và chuyển sang dòng mã tiếp theo.
Cuộc gọi hàm print () cuối cùng không có đối số chuỗi. Điều này nói với chương trình chỉ
in một dòng trống. Các dòng trống có thể hữu ích để giữ cho văn bản của chúng tôi không bị bó
lại
cùng với nhau.
Nhân vật thoát
5. in ('Nha sĩ gọi khoang của phi hành gia là gì?')
6. đầu vào ()
7. in ('Một lỗ đen!')
8. in ()
Trong bản in đầu tiên () ở trên, bạn sẽ nhận thấy rằng chúng ta có dấu gạch chéo ngay trước bản
đơn
trích dẫn (đó là dấu nháy đơn). Dấu gạch chéo ngược này (\ là dấu gạch chéo ngược, / là dấu
gạch chéo về phía trước) cho chúng ta biết
rằng bức thư ngay sau đó là một nhân vật thoát . Một nhân vật thoát giúp chúng ta in ra
các chữ cái khó nhập mã nguồn. Có một số lối thoát khác nhau
các ký tự, nhưng trong lệnh gọi của chúng tôi để in () ký tự thoát là trích dẫn duy nhất.
Chúng ta phải có một ký tự thoát trích dẫn bởi vì nếu không thì Python
thông dịch viên sẽ nghĩ rằng trích dẫn này có nghĩa là kết thúc chuỗi. Nhưng chúng tôi muốn báo
giá này
là một phần của chuỗi. Khi chúng tôi in chuỗi này, dấu gạch chéo ngược sẽ không hiển thị.
Một số nhân vật thoát khác
Điều gì nếu bạn thực sự muốn hiển thị một dấu gạch chéo ngược? Dòng mã này sẽ không hoạt
động:
>>> in ('Anh ấy bay đi trong một màu xanh lá cây
máy bay trực thăng.')
Đó in () gọi hàm sẽ hiển thị như sau:
52

Trang 67
Anh bay đi trong một chiếc trực thăng màu xanh lá cây.
Điều này là do "t" trong "teal" được xem là một nhân vật thoát ra kể từ khi nó xuất hiện sau một
dấu gạch chéo ngược. Ký tự thoát t mô phỏng việc nhấn phím Tab trên bàn phím của bạn. Bỏ
trốn
các ký tự ở đó để các chuỗi có thể có các ký tự không thể gõ vào.
Thay vào đó, hãy thử dòng này:
>>> in ('Anh ấy bay đi trong một teal màu xanh lá cây
máy bay trực thăng.')
Dưới đây là danh sách các ký tự thoát trong Python:
Báo giá và Báo giá kép
Các chuỗi không phải luôn nằm ở giữa các dấu ngoặc đơn trong Python. Bạn cũng có thể đặt
chúng ở giữa hai dấu ngoặc kép. Hai dòng này in cùng một điều:
>>> in ('Xin chào thế giới')
Chào thế giới
>>> in ("Xin chào thế giới")
Chào thế giới
Nhưng bạn không thể trộn dấu ngoặc kép. Dòng này sẽ cung cấp cho bạn một lỗi nếu bạn cố
gắng sử dụng chúng:
>>> in ('Xin chào thế giới ")
Cú pháp: EOL trong khi quét trích dẫn đơn
chuỗi
>>>
Bảng 5-1: Nhân vật thoát
Thoát khỏi nhân vật thực sự được in
\
Dấu gạch chéo ngược (\)
\'
Trích dẫn đơn (')
\"
Trích dẫn kép (")
\n
Dòng mới
\t
Chuyển hướng
53
5 - Truyện cười

Trang 68
Tôi thích sử dụng dấu ngoặc đơn vì tôi không phải giữ phím shift trên
bàn phím để gõ chúng. Việc nhập dễ dàng hơn và máy tính cũng không quan tâm.
Nhưng hãy nhớ, giống như bạn phải sử dụng ký tự thoát \ 'để có một trích dẫn trong một
chuỗi được bao quanh bởi các dấu ngoặc đơn, bạn cần ký tự thoát \ "để có dấu ngoặc kép
trong một chuỗi được bao quanh bởi dấu ngoặc kép. Ví dụ, nhìn vào hai dòng này:
>>> in ('Tôi đã hỏi mượn xe của Abe cho
tuần. Anh ta nói, "Chắc chắn." ')
Tôi hỏi mượn xe của Abe trong một tuần. Anh nói
"Chắc chắn rồi."
>>> in ("Anh ấy nói, \" Tôi không thể tin rằng bạn để anh ấy
mượn xe của bạn. \ "")
Anh nói: "Tôi không thể tin rằng bạn cho anh ấy mượn của bạn
xe hơi."
Bạn có nhận thấy rằng trong chuỗi trích dẫn đơn, bạn không cần phải thoát dấu ngoặc kép,
và trong chuỗi trích dẫn kép bạn không cần thoát dấu ngoặc đơn? Con trăn
thông dịch viên đủ thông minh để biết rằng nếu một chuỗi bắt đầu bằng một loại trích dẫn thì loại
kia
loại trích dẫn không có nghĩa là chuỗi kết thúc.
Các cuối khoá Luận
9. in ('Knock gõ.')
10. đầu vào ()
11. in ("Ai ở đó?")
12. đầu vào ()
13. in ('Gián đoạn bò.')
14. đầu vào ()
15. in ('Ngắt bò wh', end = '')
16. in ('- MOO!')
Bạn có nhận thấy tham số thứ hai trên dòng in 15 () không? Thông thường, print () thêm
một ký tự dòng mới đến cuối chuỗi nó in. (Đây là lý do tại sao một bản in trống ()
Hàm sẽ chỉ in một dòng mới.) Nhưng hàm print () có thể tùy chọn có một giây
tham số (có tên kết thúc.) Chuỗi trống chúng tôi đang truyền được gọi là từ khóa
lập luận . Tham số kết thúc có một tên cụ thể và để truyền một đối số cho điều này
tham số cụ thể chúng ta cần sử dụng cú pháp end = .
Lưu ý rằng khi bạn nhập từ khóa và đối số từ khóa, bạn chỉ sử dụng một =
ký tên. Đó là kết thúc = '' , và không kết thúc == '' .
Bằng cách chuyển một chuỗi trống cho phần cuối, chúng ta báo cho hàm print () không thêm
dòng mới
ở cuối chuỗi, nhưng thay vào đó thêm một chuỗi trống. Đây là lý do tại sao '-MOO!' xuất hiện
tiếp theo
54

Trang 69
đến dòng trước, thay vì trên dòng riêng của nó. Không có dòng mới được in sau
'Ngắt chuỗi bò' .
Tóm lược
Chương này trình bày ngắn gọn về cách phần mềm (bao gồm các chương trình Python của chúng
tôi) chạy trên
máy vi tính. Python là ngôn ngữ lập trình cấp cao hơn mà trình thông dịch Python (đó
là, phần mềm Python bạn đã tải xuống và cài đặt) chuyển đổi thành máy
ngôn ngữ. Ngôn ngữ máy là 1 và 0 tạo nên hướng dẫn cho máy tính của bạn
có thể hiểu và xử lý.
Phần còn lại của chương này khám phá những cách khác nhau mà bạn có thể sử dụng hàm print
() .
Ký tự thoát được sử dụng cho các ký tự khó hoặc không thể nhập vào
mã bằng bàn phím. Các ký tự thoát được gõ vào chuỗi bắt đầu bằng dấu gạch chéo ngược
\ theo sau là một chữ cái duy nhất cho ký tự thoát. Ví dụ: \ n sẽ in ra một
dòng mới. Để hiển thị dấu gạch chéo ngược, bạn sẽ sử dụng ký tự thoát \.
Hàm print () tự động nối thêm một ký tự dòng mới vào cuối của
chuỗi chúng tôi vượt qua nó sẽ được hiển thị trên màn hình. Hầu hết thời gian, đây là một phím
tắt hữu ích.
Nhưng đôi khi chúng ta không muốn một nhân vật mới vào cuối. Để thay đổi điều này, chúng tôi
vượt qua
kết thúc từ khóa đối số với một chuỗi trống. Ví dụ: để in "thư rác" lên màn hình
không có ký tự dòng mới, bạn sẽ gọi print ('spam', end = '') .
Bằng cách thêm cấp độ kiểm soát này vào văn bản chúng tôi hiển thị trên màn hình, chúng tôi có
nhiều hơn nữa
cách linh hoạt để hiển thị văn bản trên màn hình theo cách chính xác mà chúng ta muốn.
55
5 - Truyện cười

Trang 70
Các chủ đề được đề cập trong Chương này:
● Các thời gian mô-đun.
● Hàm time.s ngủ () .

● Các lợi nhuận từ khóa.

● Tạo các chức năng riêng của chúng tôi với từ khóa def .

● Các và và hay và không boolean nhà khai thác.

● Bảng chân lý

● Phạm vi biến (Toàn cầu và Địa phương)

● Tham số và đối số

● Biểu đồ dòng chảy


Giới thiệu chức năng
Chúng tôi đã sử dụng hai hàm trong các chương trình trước của chúng tôi: input () và print () .
Trong các chương trình trước đây của chúng tôi, chúng tôi đã gọi các hàm này để thực thi mã
bên trong
các chức năng này. Trong chương này, chúng tôi sẽ viết các chức năng của riêng mình để các
chương trình của chúng tôi gọi. Một
chức năng giống như một chương trình nhỏ nằm trong chương trình của chúng tôi. Nhiều lần
trong một chương trình, chúng tôi
muốn chạy mã chính xác nhiều lần. Thay vì gõ mã này một số
lần, chúng ta có thể đặt mã đó bên trong một hàm và gọi hàm đó nhiều lần. Cái này có
lợi ích bổ sung mà nếu chúng tôi mắc lỗi, chúng tôi chỉ có một vị trí trong mã để thay đổi

Trò chơi chúng tôi sẽ tạo để giới thiệu các chức năng được gọi là "Vương quốc rồng" và cho
phép
Người chơi đoán giữa hai hang động giữ ngẫu nhiên kho báu hoặc số phận nhất định.
56

Trang 71
Cách chơi "Vương quốc rồng"
Trong trò chơi này, người chơi ở trong một vùng đất đầy rồng. Những con rồng sống trong hang
động với
đống lớn kho báu của họ được thu thập. Một số con rồng rất thân thiện và sẽ chia sẻ
kho báu với bạn Những con rồng khác tham lam và đói khát, và sẽ ăn bất cứ ai xâm nhập
hang động của họ. Người chơi ở phía trước hai hang động, một với một con rồng thân thiện và
cái còn lại với
một con rồng đói. Người chơi được lựa chọn giữa hai.
Mở cửa sổ soạn thảo tệp mới bằng cách nhấp vào menu Tệp , sau đó nhấp vào Mới
Cửa sổ . Trong cửa sổ trống xuất hiện, nhập mã nguồn và lưu nguồn
mã dưới dạng dragon.py . Sau đó chạy chương trình bằng cách nhấn F5.
Chạy mẫu của vương quốc rồng
Bạn đang ở trong một vùng đất đầy những con rồng. Trước mặt bạn,
bạn thấy hai hang động. Trong một hang động, rồng rất thân thiện.
và sẽ chia sẻ kho báu của anh ấy với bạn. Con rồng khác
là tham lam và đói, và sẽ ăn bạn trong tầm nhìn.
Bạn sẽ đi vào hang nào? (1 hoặc 2)
1
Bạn đến gần hang ...
Trời tối và ma quái ...
Một con rồng lớn nhảy ra trước mặt bạn! Anh ấy mở hàm
và ...
Yêu tinh bạn xuống trong một vết cắn!
Bạn có muốn chơi lại không? (có hay không)
Không

Mã nguồn của Dragon Realm


Đây là mã nguồn của trò chơi Dragon Realm. Nhập mã nguồn là một tuyệt vời
cách để làm quen với mã. Nhưng nếu bạn không muốn thực hiện thao tác gõ này, bạn có thể tải
xuống
mã nguồn từ trang web của cuốn sách này tại URL http://inventwithpython.com/ch CHƯƠNG6.
Có các hướng dẫn trên trang web sẽ cho bạn biết cách tải xuống và mở
tập tin mã nguồn. Bạn có thể sử dụng công cụ tìm khác biệt trực tuyến trên trang web để kiểm tra
xem có lỗi nào không
trong mã của bạn.
Một điều cần biết khi bạn đọc qua đoạn mã dưới đây: Các khối theo def
các dòng định nghĩa một hàm, nhưng mã trong khối đó không chạy cho đến khi hàm được gọi.
Mã không thực thi từng dòng trong chương trình này theo thứ tự từ trên xuống. Cái này sẽ
giải thích chi tiết hơn sau này trong chương này.
57
6 - Cõi rồng

Trang 72
Lưu ý quan trọng! Hãy chắc chắn chạy chương trình này với Python 3 chứ không phải Python 2.
các chương trình trong cuốn sách này sử dụng Python 3 và bạn sẽ gặp lỗi nếu bạn cố chạy chúng
với Python
2. Bạn có thể nhấp vào Trợ giúp và sau đó Giới thiệu về IDLE để tìm hiểu phiên bản Python nào
của bạn
có.
chuồn chuồn
Mã này có thể được tải xuống từ http://inventwithpython.com/dragon.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. nhập ngẫu nhiên
2. thời gian nhập khẩu
3.
4. def displayIntro ():
5. in ('Bạn đang ở trên một hành tinh đầy rồng. Ở phía trước
của bạn,')
6. in ('bạn thấy hai hang. Trong một hang, rồng là
thân thiện')
7. in ('và sẽ chia sẻ kho báu của anh ấy với bạn.
con rồng khác ')
8. in ('là tham lam và đói, và sẽ ăn bạn trên
thị giác.')
9. in ()
10.
11. def selectCave ():
12. hang = ''
13. trong khi cave! = '1' và cave! = '2':
14.
in ('hang nào bạn sẽ đi vào? (1 hoặc 2)')
15.
cave = đầu vào ()
16.
17. hang trở về
18.
19. def checkCave (chọnCave):
20. in ('Bạn đến gần hang ...')
21. thời gian ngủ (2)
22. in ('Trời tối và ma quái ...')
23. thời gian ngủ (2)
24. in ('Một con rồng lớn nhảy ra trước mặt bạn! Anh ấy
mở hàm và ... ')
25. in ()
26. thời gian ngủ (2)
27.
28. FriendlyCave = Random.randint (1, 2)
29.
30. nếu được chọnCave == str (FriendlyCave):
31.
in ('Cung cấp cho bạn kho báu của anh ấy!')
32. khác:
33.
in ('Gõ bạn xuống trong một vết cắn!')
34.
35. playAgain = 'có'
36. trong khi playAgain == 'yes' hoặc playAgain == 'y':
37.
38. displayIntro ()
58

Trang 73
39.
40. caveNumber = selectCave ()
41.
42. checkCave (caveNumber)
43.
44. in ('Bạn có muốn chơi lại không? (Có hoặc không)')
45. playAgain = input ()

Mã hoạt động như thế nào


Hãy xem mã nguồn chi tiết hơn.
1. nhập ngẫu nhiên
2. thời gian nhập khẩu
Ở đây chúng tôi có hai báo cáo nhập khẩu . Chúng tôi nhập mô-đun ngẫu nhiên như chúng tôi đã
làm trong
Đoán trò chơi số. Trong Dragon Realm, chúng tôi cũng sẽ muốn một số chức năng liên quan đến
thời gian
rằng thời gian mô-đun bao gồm, vì vậy chúng tôi sẽ nhập khẩu đó là tốt.
Xác định displayIntro () Chức năng
4. def displayIntro ():
5. in ('Bạn đang ở trên một hành tinh đầy rồng. Ở phía trước
của bạn,')
6. in ('bạn thấy hai hang. Trong một hang, rồng là
thân thiện')
7. in ('và sẽ chia sẻ kho báu của anh ấy với bạn. Cái khác
rồng ')
8. in ('là tham lam và đói, và sẽ ăn bạn trên
thị giác.')
9. in ()
Hình 6-1 cho thấy một loại mới của
tuyên bố, tuyên bố def . Các
tuyên bố def được tạo thành từ def
từ khóa, theo sau là tên hàm
với dấu ngoặc đơn, và sau đó là dấu hai chấm
( dấu : ký). Có một khối sau
câu lệnh được gọi là khối def.
Hình 6-1: Các bộ phận của một tuyên bố def .
59
6 - Cõi rồng
Trang 74
báo cáo def
Câu lệnh def không phải là một cuộc gọi đến một hàm có tên displayIntro () . Thay vào đó, def
tuyên bố có nghĩa là chúng ta đang tạo hoặc xác định một hàm mới mà chúng ta có thể gọi sau
này trong
chương trình. Sau khi chúng ta xác định hàm này, chúng ta có thể gọi nó giống như cách chúng
ta gọi các hàm khác.
Khi chúng ta gọi hàm này, mã bên trong khối def sẽ được thực thi.
Chúng tôi cũng nói rằng chúng tôi xác định các biến khi chúng tôi tạo chúng bằng một câu lệnh
gán. Các
mã spam = 42 định nghĩa thư rác biến .
Hãy nhớ rằng, câu lệnh def không thực thi mã ngay bây giờ, nó chỉ định nghĩa những gì
mã được thực thi khi chúng ta gọi hàm displayIntro () sau trong chương trình.
Khi thực hiện chương trình đạt đến một câu lệnh def , nó sẽ nhảy xuống đến cuối của def-
khối. Chúng tôi sẽ nhảy trở lại đỉnh của khối def khi displayIntro ()
chức năng được gọi là. Sau đó, nó sẽ thực thi tất cả các câu lệnh print () bên trong khối def. Vì
thế
chúng tôi gọi chức năng này khi chúng tôi muốn hiển thị "Bạn đang ở trên một hành tinh đầy
rồng ..."
giới thiệu cho người dùng.
Khi chúng ta gọi hàm displayIntro () , việc thực thi chương trình sẽ nhảy đến
bắt đầu hàm trên dòng 5. Khi khối của hàm kết thúc, thực thi chương trình
trở về dòng được gọi là hàm.
Chúng tôi sẽ giải thích tất cả các chức năng mà chương trình này sẽ sử dụng trước khi chúng tôi
giải thích chính
một phần của chương trình. Có thể hơi khó hiểu khi học chương trình theo thứ tự mà nó
hành quyết. Nhưng hãy nhớ rằng khi chúng ta xác định các chức năng, họ chỉ ngồi im lặng
xung quanh chờ đợi để được gọi vào hành động.
Xác định chooseCave () Chức năng
11. def selectCave ():
Ở đây chúng ta đang định nghĩa một hàm khác gọi là selectCave . Mã trong chức năng này
sẽ nhắc người dùng chọn hang nào họ nên vào.
12. hang = ''
13. trong khi cave! = '1' và cave! = '2':
Bên trong hàm selectCave () , chúng ta tạo một biến mới gọi là cave và lưu trữ một
chuỗi trống trong đó. Sau đó chúng tôi sẽ bắt đầu một khi vòng lặp. Điều này trong khi điều kiện
tuyên bố
chứa một toán tử mới mà chúng ta chưa từng thấy trước khi gọi và . Giống như - hoặc * là
toán tử toán học và == hoặc ! = là toán tử so sánh, toán tử và toán tử là một
toán tử boolean.
60

Trang 75
Toán tử Boolean
Logic Boolean xử lý những thứ là đúng hoặc sai. Đây là lý do tại sao dữ liệu boolean
loại chỉ có hai giá trị, Đúng và Sai . Các câu lệnh Boolean luôn luôn đúng hoặc
sai. Nếu tuyên bố không đúng, thì đó là sai. Và nếu tuyên bố không sai, thì đó là
thật.
Toán tử Boolean so sánh hai giá trị boolean khác nhau và đánh giá với một boolean duy nhất
giá trị. Bạn có nhớ cách toán tử * sẽ kết hợp hai giá trị nguyên và tạo ra một
giá trị nguyên mới (tích của hai số nguyên ban đầu)? Và bạn cũng nhớ
cách toán tử + có thể kết hợp hai chuỗi và tạo ra một giá trị chuỗi mới (
nối hai chuỗi gốc)? Các và điều hành boolean kết hợp hai
giá trị boolean để tạo ra một giá trị boolean mới. Đây là cách và toán tử hoạt động.
Hãy nghĩ về câu "Mèo có râu và chó có đuôi". Câu này đúng
bởi vì "mèo có râu" là đúng và "chó có đuôi" cũng đúng.
Nhưng câu "Mèo có râu và chó có cánh". sẽ là sai Cũng
mặc dù "mèo có râu" là đúng, chó không có cánh, vì vậy "chó có cánh" là sai.
Toàn bộ câu chỉ đúng nếu cả hai phần đều đúng vì hai phần được kết nối bởi
từ "và." Nếu một hoặc cả hai phần là sai, thì toàn bộ câu là sai.
Các và nhà điều hành trong Python hoạt động theo cách này quá. Nếu các giá trị boolean ở cả hai
phía của
các và từ khóa là thật , thì biểu thức với các nhà điều hành và đánh giá lại để Đúng .
Nếu một trong hai giá trị boolean là Sai hoặc cả hai giá trị boolean đều sai , thì
biểu thức ước lượng thành Sai .
Đánh giá một biểu thức có chứa các toán tử Boolean
Vì vậy, hãy nhìn vào dòng 13 một lần nữa:
13. trong khi cave! = '1' và cave! = '2':
Điều kiện này được tạo thành từ hai biểu thức được kết nối bởi toán tử và boolean.
Trước tiên chúng tôi đánh giá các biểu thức này để nhận các giá trị boolean (nghĩa
là Đúng hoặc Sai ) của chúng.
Sau đó, chúng tôi đánh giá các giá trị boolean với toán tử và .
Giá trị chuỗi được lưu trữ trong cave khi chúng ta lần đầu tiên thực hiện điều này trong khi câu
lệnh là trống
chuỗi, '' . Chuỗi trống không bằng chuỗi '1' , vì vậy phía bên trái ước tính
Đúng . Chuỗi trống cũng không bằng chuỗi '2' , vì vậy phía bên phải ước tính là
Đúng . Vì vậy, điều kiện sau đó biến thành Đúng và Đúng . Bởi vì cả hai giá trị boolean
là True , điều kiện cuối cùng được đánh giá là True . Và bởi vì tuyên bố while
điều kiện là True , việc thực hiện chương trình đi vào khối while.
61
6 - Cõi rồng

Trang 76
Tất cả điều này được thực hiện bởi trình thông dịch Python, nhưng điều quan trọng là phải hiểu làm
thế nào
thông dịch viên làm điều này. Hình ảnh này cho thấy các bước về cách người phiên dịch đánh
giá
điều kiện (nếu giá trị của hang là chuỗi trống):
while cave! = '1' và cave! = '2':
while ''! = '1' và cave! = '2':
trong khi True và cave! = '2':
trong khi True và ''! = '2':
trong khi Đúng và Đúng:
trong khi Đúng:
Thử nghiệm với và và hoặc Người vận hành
Hãy thử nhập nội dung sau vào vỏ tương tác:
>>> Đúng và Đúng
Thật
>>> Đúng và Sai
Sai
>>> Sai và đúng
Sai
>>> Sai và Sai
Sai
Có hai toán tử boolean khác. Người tiếp theo là hoặc toán tử. Nhà điều hành hoặc
hoạt động tương tự như và, ngoại trừ nó sẽ đánh giá thành True nếu một trong hai giá trị boolean
là Đúng . Lần duy nhất toán tử hoặc toán tử đánh giá thành Sai là nếu cả hai boolean
các giá trị là Sai .
Câu "Mèo có râu hoặc chó có cánh". là đúng. Mặc dù chó không
có cánh, khi chúng ta nói "hoặc" chúng tôi muốn nói rằng một trong hai phần là đúng. Câu "Mèo
có râu hoặc chó có đuôi. "cũng đúng. (Hầu hết thời gian chúng ta nói" điều này HOẶC
rằng ", chúng tôi muốn nói một điều là đúng nhưng điều khác là sai. Trong lập trình," hoặc "có
nghĩa là
rằng một trong hai điều là đúng, hoặc có thể cả hai điều đó đều đúng.)
62

Trang 77
Hãy thử nhập nội dung sau vào vỏ tương tác:
>>> Đúng hay Đúng
Thật
>>> Đúng hay Sai
Thật
>>> Sai hoặc đúng
Thật
>>> Sai hoặc Sai
Sai
Thử nghiệm với người không khai thác
Toán tử boolean thứ ba thì không . Toán tử không khác nhau
toán tử chúng ta đã thấy trước đây, bởi vì nó chỉ hoạt động trên một giá trị, không phải hai. Chỉ

giá trị ở bên phải của từ khóa không , và không có ở bên trái. Nhà điều hành không
đánh giá là Đúng là Sai và sẽ đánh giá Sai là Đúng .
Hãy thử nhập nội dung sau vào vỏ tương tác:
>>> không đúng
Sai
>>> không sai
Thật
>>> Đúng không
Cú pháp: cú pháp không hợp lệ (<pyshell # 0>, dòng 1)
Lưu ý rằng nếu chúng ta đặt giá trị boolean ở phía bên trái của toán tử không dẫn đến kết quả là
lỗi cú pháp.
Chúng ta có thể sử dụng cả hai và và không khai thác trong một biểu thức duy nhất. Hãy thử
gõ True
và không sai vào vỏ:
>>> Đúng và không sai
Thật
Thông thường biểu thức Đúng và Sai sẽ đánh giá thành Sai . Nhưng sự thật
và không biểu thức Sai đánh giá là Đúng . Điều này là do không sai đánh giá
True , biến biểu thức thành True và True , đánh giá thành True .
63
6 - Cõi rồng

Trang 78
Bàn chân lý
Nếu bạn quên cách các toán tử boolean hoạt động, bạn có thể nhìn vào các biểu đồ này,
được gọi là bảng chân lý :
Lấy dữ liệu đầu vào
14.
in ('hang nào bạn sẽ đi vào? (1 hoặc 2)')
15.
cave = đầu vào ()
Tại đây, người chơi được yêu cầu vào hang nào họ chọn để vào bằng cách nhập 1 hoặc 2 và
nhấn Enter. Bất cứ chuỗi nào người chơi gõ sẽ được lưu trữ trong hang . Sau khi mã này là
thực hiện, chúng tôi nhảy trở lại đầu câu lệnh while và kiểm tra lại điều kiện.
Hãy nhớ rằng dòng là:
13. trong khi cave! = '1' và cave! = '2':
Nếu điều kiện này ước tính là True , chúng tôi sẽ nhập lại khối while và hỏi
người chơi cho một số hang động để vào. Nhưng nếu người chơi gõ 1 hoặc 2, thì giá trị hang
sẽ là '1' hoặc '2' . Điều này gây ra điều kiện để đánh giá thành Sai và
Bảng 6-1: Bảng chân lý và toán tử.
Một

B
là toàn bộ tuyên bố
Đúng và Đúng là
Thật
Đúng và Sai là
Sai
Sai và đúng là
Sai
Sai và sai là
Sai
Bảng 6-2: Các hoặc bảng chân lý điều hành của.
Một
hoặc B
là toàn bộ tuyên bố
Đúng hay Đúng là
Thật
Đúng hay Sai là
Thật
Sai hoặc đúng là
Thật
Sai hoặc sai là
Sai
Bảng 6-3: Bảng chân lý của người vận hành.
không phải A

Toàn bộ tuyên bố
không đúng

Sai
không sai là
Thật
64

Trang 79
thực hiện chương trình sẽ tiếp tục trong vòng lặp while.
Lý do chúng tôi có một vòng lặp ở đây là vì người chơi có thể đã gõ 3 hoặc 4 hoặc
XIN CHÀO. Chương trình của chúng tôi không có ý nghĩa về điều này, vì vậy nếu người chơi
không nhập 1 hoặc 2, thì
chương trình lặp lại và hỏi lại người chơi. Trong thực tế, máy tính sẽ kiên nhẫn hỏi
Người chơi cho số hang lặp đi lặp lại cho đến khi người chơi gõ 1 hoặc 2. Khi nào
Người chơi làm điều đó, điều kiện của khối trong khi sẽ là Sai và chúng tôi sẽ nhảy xuống
qua khối while và tiếp tục với chương trình.
Giá trị trả về
17. hang trở về
Đây là từ khóa return , chỉ xuất hiện bên trong def-blocks. Nhớ như thế nào
Hàm input () trả về giá trị chuỗi mà người chơi đã nhập? Hoặc làm thế nào randint
Hàm () sẽ trả về một giá trị nguyên ngẫu nhiên? Hàm của chúng ta cũng sẽ trả về một giá trị. Nó
trả về chuỗi được lưu trữ trong hang động .
Điều này có nghĩa là nếu chúng ta có một dòng mã như spam = selectCave () , thì mã
bên trong selectCave () sẽ được thực thi và lệnh gọi hàm sẽ đánh giá
giá trị trả về của selectCave () . Giá trị trả về sẽ là chuỗi '1' hoặc chuỗi
'2' . (Chúng tôi trong khi đảm bảo vòng lặp mà chooseCave () sẽ chỉ trở lại hoặc là '1' hoặc
'2' .)
Các lợi nhuận từ khóa chỉ được tìm thấy bên trong def-khối. Khi tuyên bố trở lại là
thực hiện, chúng tôi ngay lập tức nhảy ra khỏi khối def. (Điều này giống như cách phá vỡ
câu lệnh sẽ làm cho chúng ta nhảy ra khỏi một khối trong khi.) Việc thực hiện chương trình di
chuyển trở lại
dòng đã được gọi là hàm.
Bạn cũng có thể tự sử dụng từ khóa return để thoát ra khỏi hàm, chỉ cần
như phá vỡ từ khóa sẽ thoát ra khỏi một khi vòng lặp.
Phạm vi biến đổi
Giống như các giá trị trong các biến của chương trình của chúng ta bị lãng quên sau khi chương
trình kết thúc,
các biến được tạo bên trong hàm bị quên sau khi thực thi rời khỏi hàm.
Không chỉ vậy, nhưng khi thực thi bên trong hàm, chúng ta không thể thay đổi các biến
bên ngoài hàm, hoặc các biến bên trong các hàm khác. Phạm vi của biến là đây
phạm vi mà các biến có thể được sửa đổi. Các biến duy nhất mà chúng ta có thể sử dụng bên
trong một hàm
là những cái chúng ta tạo bên trong hàm (hoặc các biến tham số, được mô tả sau).
Đó là, phạm vi của biến nằm trong khối của hàm. Phạm vi của các biến
được tạo bên ngoài các chức năng nằm ngoài tất cả các chức năng trong chương trình.
Không chỉ vậy, nhưng nếu chúng ta có một biến có tên là thư rác được tạo bên ngoài hàm và
65
6 - Cõi rồng

Trang 80
chúng ta tạo một biến có tên là spam bên trong hàm, trình thông dịch Python sẽ
coi chúng là hai biến riêng biệt. Điều đó có nghĩa là chúng ta có thể thay đổi giá trị của thư rác
bên trong hàm và điều này sẽ không thay đổi biến thư rác nằm ngoài
chức năng. Điều này là do các biến này có phạm vi khác nhau, phạm vi toàn cầu và
phạm vi địa phương.
Phạm vi toàn cầu và phạm vi địa phương
Chúng tôi có tên cho các phạm vi này. Phạm vi bên ngoài của tất cả các chức năng được gọi
là toàn cầu
phạm vi . Phạm vi bên trong của một chức năng được gọi là phạm vi địa phương . Toàn bộ
chương trình có
chỉ có một phạm vi toàn cầu và mỗi chức năng có một phạm vi cục bộ của riêng nó.
Các biến được định nghĩa trong phạm vi toàn cục có thể được đọc bên ngoài và bên trong các
hàm, nhưng có thể
chỉ được sửa đổi bên ngoài tất cả các chức năng. Các biến được định nghĩa trong phạm vi cục bộ
của hàm có thể
chỉ được đọc hoặc sửa đổi bên trong chức năng đó.
Cụ thể, chúng ta có thể đọc giá trị của các biến toàn cục từ phạm vi cục bộ, nhưng
cố gắng thay đổi giá trị trong một biến toàn cục từ phạm vi cục bộ sẽ để lại
biến toàn cầu không thay đổi. Những gì Python thực sự làm là tạo một biến cục bộ với
cùng tên với biến toàn cục. Nhưng Python sẽ coi đây là hai cái khác nhau
biến.
Nhìn vào ví dụ này để xem điều gì xảy ra khi bạn cố gắng thay đổi một biến toàn cục từ
trong phạm vi địa phương. Hãy nhớ rằng mã trong hàm funky () không chạy cho đến khi
Hàm funky () được gọi. Các ý kiến giải thích những gì đang xảy ra:
# Khối này không chạy cho đến khi funky () được gọi:
def funky ():
# Chúng tôi đọc giá trị của biến toàn cục:
in (thư rác) # 42
# Chúng tôi tạo một biến cục bộ có tên là "thư rác"
# thay vì thay đổi giá trị của toàn cầu
# biến "thư rác":
thư rác = 99
# Tên "thư rác" hiện chỉ địa phương
# biến chỉ cho phần còn lại của điều này
# chức năng:
in (thư rác) # 99
# Một biến toàn cầu có tên là "spam":
thư rác = 42
# Gọi hàm funky ():
sôi nổi ()
66

Trang 81
# Biến toàn cục không thay đổi trong funky ():
in (thư rác) # 42
Điều quan trọng là phải biết khi nào một biến được xác định bởi vì đó là cách chúng ta biết
phạm vi của biến. Một biến được định nghĩa lần đầu tiên chúng ta sử dụng nó trong một câu lệnh
gán.
Khi chương trình lần đầu tiên thực thi dòng:
12. hang = ''
... hang biến được xác định.
Nếu chúng ta gọi hàm selectCave () hai lần, giá trị được lưu trữ trong biến đầu tiên
thời gian sẽ không được nhớ lần thứ hai Điều này là do khi thực hiện trái
các chooseCave () chức năng (có nghĩa là, trái chooseCave () 's phạm vi địa phương), các hang
động
biến bị lãng quên và phá hủy. Nhưng nó sẽ được định nghĩa lại khi chúng ta gọi hàm
lần thứ hai vì dòng 12 sẽ được thực hiện lại.
Điều quan trọng cần nhớ là giá trị của một biến trong phạm vi cục bộ không phải là
ghi nhớ ở giữa các cuộc gọi chức năng.
Xác định checkCave () Chức năng
19. def checkCave (chọnCave):
Bây giờ chúng ta đang định nghĩa một hàm khác có tên checkCave () . Lưu ý rằng chúng tôi đặt
văn bản được chọn Lưu ở giữa các dấu ngoặc đơn. Các tên biến ở giữa
dấu ngoặc đơn được gọi là tham số .
Hãy nhớ rằng, đối với một số hàm như cho str () hoặc randint () , chúng ta sẽ vượt qua một
đối số ở giữa các dấu ngoặc đơn:
>>> str (5)
'5'
>>> ngẫu nhiên.randint (1, 20)
14
Khi chúng ta gọi checkCave () , chúng ta cũng sẽ truyền một giá trị cho nó làm đối số. Khi nào
thực thi di chuyển bên trong hàm checkCave () , một biến mới có tên là selectCave
sẽ được gán giá trị này. Đây là cách chúng ta truyền các giá trị biến cho các hàm vì các hàm
không thể đọc các biến bên ngoài hàm (nghĩa là nằm ngoài phạm vi cục bộ của hàm).
67
6 - Cõi rồng
Trang 82
Các tham số là các biến cục bộ được xác định khi hàm được gọi. Giá trị
được lưu trong tham số là đối số được truyền trong lệnh gọi hàm.
Thông số
Ví dụ, đây là một chương trình ngắn thể hiện các tham số. Hãy tưởng tượng chúng ta đã có một
chương trình ngắn trông như thế này:
def sayHello (tên):
in ('Xin chào,' + tên)
in ('Nói xin chào với Alice.')
choáng váng = 'Alice'
sayHello (choáng váng)
in ('Đừng quên nói xin chào với Bob.')
sayHello ('Bob')
Nếu chúng tôi chạy chương trình này, nó sẽ trông như thế này:
Xin chào Alice.
Xin chào, Alice
Đừng quên nói xin chào với Bob.
Xin chào Bob
Chương trình này gọi một hàm chúng ta đã tạo, sayHello () và đầu tiên chuyển giá trị
trong biến fizzy như là một đối số với nó. (Chúng tôi lưu trữ các chuỗi 'Alice' ở ga .)
Sau đó, chương trình gọi lại hàm sayHello () , chuyển chuỗi 'Bob' thành một
tranh luận.
Giá trị trong biến fizzy và chuỗi 'Bob' là đối số. các tên
biến trong sayHello () là một tham số. Sự khác biệt giữa các đối số và
tham số là các đối số là các giá trị được truyền trong một lệnh gọi hàm và các tham số là
biến cục bộ lưu trữ các đối số. Có thể dễ dàng hơn chỉ cần nhớ rằng điều trong
giữa các dấu ngoặc trong câu lệnh def là một tham số và điều ở giữa
dấu ngoặc trong lệnh gọi hàm là một đối số.
Thay vào đó, chúng ta có thể đã sử dụng biến fizzy bên trong hàm sayHello ()
sử dụng một tham số. (Điều này là do phạm vi cục bộ vẫn có thể thấy các biến trong toàn cầu
phạm vi.) Nhưng sau đó chúng ta sẽ phải nhớ gán cho biến fizzy một chuỗi
thời gian trước khi chúng ta gọi hàm sayHello () . Các thông số làm cho chương trình của chúng
tôi đơn giản hơn.
Nhìn vào mã này:
def nóiHello ():
68

Trang 83
in ('Xin chào,' + có ga)
in ('Nói xin chào với Alice.')
choáng váng = 'Alice'
nói xin chào ()
in ('Đừng quên nói xin chào với Bob.')
nói xin chào ()
Khi chúng tôi chạy mã này, nó trông như thế này:
Xin chào Alice.
Xin chào, Alice
Đừng quên nói xin chào với Bob.
Xin chào, Alice
Hàm sayHello () của chương trình này không có tham số, nhưng sử dụng toàn cục
biến có ga trực tiếp. Hãy nhớ rằng bạn có thể đọc các biến toàn cục bên trong các hàm,
bạn không thể sửa đổi giá trị được lưu trữ trong biến.
Không có tham số, chúng ta phải nhớ đặt biến fizzy trước khi gọi
nói xin chào () . Trong chương trình này, chúng tôi quên làm như vậy, vì vậy lần thứ hai chúng
tôi gọi
sayHello () giá trị của fizzy vẫn là 'Alice' . Sử dụng tham số làm cho chức năng
gọi đơn giản hơn để làm, đặc biệt là khi các chương trình của chúng tôi rất lớn và có nhiều chức
năng.
Biến cục bộ và biến toàn cục có cùng tên
Bây giờ hãy nhìn vào chương trình sau, có một chút khác biệt. Để làm cho nó rõ ràng để xem,
biến toàn cục đã được viền với một dòng và biến cục bộ đã được viền với
chấm.
thư rác def (myName):
in ('Xin chào,' + myName)
myName = 'Bánh quế'
in ('Tên mới của bạn là' + myName)
tên tôi = 'Albert'
thư rác (tên tôi)
in ('Howdy,' + myName)
Nếu chúng tôi chạy chương trình này, nó sẽ trông như thế này:
Xin chào, Albert
Tên mới của bạn là bánh quế
69
6 - Cõi rồng

Trang 84
Ôi, Albert
Chương trình này xác định một biến mới gọi là myName và lưu trữ chuỗi 'Albert' trong
nó Sau đó, chương trình gọi hàm spam () , truyền giá trị trong myName dưới dạng
tranh luận. Việc thực thi di chuyển đến hàm spam () . Tham số trong thư rác () cũng là
được đặt tên myName và có giá trị đối số được gán cho nó. Hãy nhớ rằng, tên tôi bên trong
các thư rác () function (phạm vi địa phương) được coi là một biến khác so với Myname
biến ngoài hàm (phạm vi toàn cầu).
Hàm sau đó in 'Xin chào, Albert' , và sau đó trên dòng tiếp theo thay đổi
giá trị trong myName thành 'Bánh quế' . Hãy nhớ rằng, điều này chỉ thay đổi myName cục bộ
biến đó là bên trong hàm. Biến myName toàn cầu nằm ngoài hàm
vẫn có giá trị 'Albert' được lưu trữ trong đó.
Bây giờ chức năng in ra 'Tên mới của bạn là Bánh quế' , bởi vì
Biến myName trong phạm vi cục bộ đã thay đổi thành 'Bánh quế' . Việc thực hiện có
đạt đến cuối của hàm, vì vậy nó nhảy ngược xuống nơi gọi hàm. Các
myName cục bộ bị phá hủy và lãng quên. Dòng tiếp theo sau đó là in ('Howdy,'
+ myName) , sẽ hiển thị Howdy, Albert .
Hãy nhớ rằng, myName bên ngoài các hàm (nghĩa là trong phạm vi toàn cầu) vẫn có
giá trị 'Albert' , không phải 'Bánh quế' . Điều này là do myName trong phạm vi toàn cầu và
các Myname trong thư rác () 's phạm vi địa phương là các biến khác nhau, mặc dù họ có
cùng tên.
Đặt định nghĩa hàm ở đâu
Định nghĩa của hàm (trong đó chúng ta đặt câu lệnh def và khối def) phải đến
trước khi bạn gọi hàm. Điều này giống như cách bạn phải gán giá trị cho một biến trước đó
bạn có thể sử dụng biến. Nếu bạn đặt lệnh gọi hàm trước định nghĩa hàm, bạn sẽ
gặp lỗi Nhìn vào mã này:
nói lời tạm biệt()
def sayoodBye ():
in ('Tạm biệt!')
Nếu bạn cố chạy nó, Python sẽ cung cấp cho bạn một thông báo lỗi giống như thế này:
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "C: \ Python31 \ foo.py", dòng 1, trong <mô-đun>
nói lời tạm biệt()
70

Trang 85
TênError: tên 'sayoodBye' không được xác định
Để khắc phục điều này, hãy đặt định nghĩa hàm trước khi gọi hàm:
def sayoodBye ():
in ('Tạm biệt!')
nói lời tạm biệt()
Hiển thị kết quả trò chơi
Quay lại mã nguồn của trò chơi:
20. in ('Bạn đến gần hang ...')
21. thời gian ngủ (2)
Chúng tôi hiển thị một số văn bản cho trình phát và sau đó gọi hàm time.s ngủ () .
Hãy nhớ làm thế nào trong lệnh gọi randint () của chúng ta , hàm randint () nằm bên trong
mô-đun ngẫu nhiên ? Trong trò chơi Dragon Realm, chúng tôi cũng đã nhập mô-đun thời
gian . Các
mô-đun thời gian có một chức năng gọi là ngủ () sẽ tạm dừng chương trình trong một vài
giây Chúng ta truyền giá trị số nguyên 2 làm đối số cho hàm time.s ngủ () cho
bảo nó tạm dừng chính xác 2 giây.
22. in ('Trời tối và ma quái ...')
23. thời gian ngủ (2)
Ở đây chúng tôi in thêm một số văn bản và chờ thêm 2 giây nữa. Những khoảng dừng ngắn
thêm hồi hộp cho trò chơi, thay vì hiển thị tất cả các văn bản cùng một lúc. Trong những câu
chuyện cười của chúng tôi
chương trình, chúng tôi đã gọi hàm input () để đợi cho đến khi người chơi nhấn phím Enter.
Ở đây, người chơi không cần phải làm gì cả ngoại trừ chờ đợi.
24. in ('Một con rồng lớn nhảy ra trước mặt bạn! Anh ấy
mở hàm và ... ')
25. in ()
26. thời gian ngủ (2)
Chuyện gì xảy ra tiếp theo? Và làm thế nào để chương trình quyết định những gì xảy ra?
71
6 - Cõi rồng
Trang 86
Quyết định hang nào có Rồng thân thiện
28. FriendlyCave = Random.randint (1, 2)
Bây giờ chúng ta sẽ có chương trình chọn ngẫu nhiên hang nào có thân thiện
rồng trong đó. Cuộc gọi của chúng ta đến hàm Random.randint () sẽ trả về số nguyên 1
hoặc số nguyên 2 và lưu trữ giá trị này trong một biến có tên là FriendlyCave .
30. nếu được chọnCave == str (FriendlyCave):
31.
in ('Cung cấp cho bạn kho báu của anh ấy!')
Ở đây chúng tôi kiểm tra xem số nguyên của hang chúng tôi đã chọn ( '1' hay '2' ) có bằng với
hang không
chọn ngẫu nhiên để có con rồng thân thiện. Nhưng chờ đã, giá trị trong selectCave là một
chuỗi (vì input () trả về chuỗi) và giá trị trong FriendlyCave là một số nguyên
(vì Random.randint () trả về số nguyên). Chúng ta không thể so sánh chuỗi và số nguyên
với dấu == , vì chúng sẽ luôn khác nhau ( '1' không bằng 1 ).
So sánh giá trị của các loại dữ liệu khác nhau với toán tử == sẽ luôn luôn đánh giá
Sai .
Vì vậy, chúng ta đang truyền FriendlyCave cho hàm str () , trả về chuỗi
giá trị của FriendlyCave.
Điều kiện trong điều này nếu câu lệnh thực sự so sánh là chuỗi trong
chọnCave và chuỗi được trả về bởi hàm str () . Chúng ta cũng có thể có cái này
thay vào đó:
if int (chọnCave) == FriendlyCave:
Sau đó, nếu điều kiện tuyên bố của sẽ so sánh giá trị số nguyên được trả về bởi các int
Hàm () cho giá trị nguyên trong FriendlyCave . Giá trị trả về của int ()
Hàm là dạng số nguyên của chuỗi được lưu trữ trong selectCave .
Nếu nếu đánh giá lại tình trạng tuyên bố để Đúng , chúng tôi nói với các cầu thủ mà họ đã giành
được
kho báu.
32. khác:
33.
in ('Gõ bạn xuống trong một vết cắn!')
Dòng 32 có một từ khóa mới. Các từ khóa khác luôn luôn xuất hiện sau khối if. Các
khác-block rằng sau các thực thi từ khóa khác nếu điều kiện trong khi tuyên bố là
72

Trang 87
Sai . Hãy nghĩ về nó như cách nói của chương trình, "Nếu điều kiện này là đúng thì hãy thực thi
khối if hoặc cách khác thực thi khối khác. "
Nhớ đặt dấu hai chấm (dấu:) sau từ khóa khác.
Dấu hai chấm :
Bạn có thể nhận thấy rằng chúng ta luôn đặt dấu hai chấm ở cuối if , other , while , và
tuyên bố def . Dấu hai chấm đánh dấu sự kết thúc của tuyên bố và cho chúng ta biết rằng dòng
tiếp theo
nên là sự khởi đầu của một khối mới.
Chương trình thực sự bắt đầu ở đâu
35. playAgain = 'có'
Đây là dòng đầu tiên không phải là câu lệnh def hoặc bên trong khối def. Dòng này là nơi
chương trình của chúng tôi thực sự bắt đầu. Các câu lệnh def trước chỉ đơn thuần xác định các
hàm, nó
đã không chạy mã bên trong các chức năng. Các chương trình phải luôn xác định chức năng
trước
chức năng có thể được gọi. Điều này giống hệt như cách các biến phải được xác định bằng một
câu lệnh gán trước biến có thể được sử dụng trong chương trình.
36. trong khi playAgain == 'yes' hoặc playAgain == 'y':
Đây là sự khởi đầu của một trong khi vòng lặp. Chúng tôi vào vòng lặp nếu playAgain bằng
hoặc "có" hoặc "y" . Lần đầu tiên chúng ta đến với điều này trong khi tuyên bố, chúng ta vừa
mới
đã gán giá trị chuỗi 'có' cho biến playAgain . Điều đó có nghĩa là tình trạng này
sẽ là Đúng .
Gọi các chức năng trong chương trình của chúng tôi
38. displayIntro ()
Ở đây chúng ta gọi hàm displayIntro () . Đây không phải là một hàm Python, nó là của chúng tôi
chức năng mà chúng tôi đã xác định trước đó trong chương trình của chúng tôi. Khi chức năng
này được gọi, chương trình
thực thi nhảy đến dòng đầu tiên trong hàm displayIntro () trên dòng 5. Khi tất cả các
các dòng trong hàm được thực hiện, thực thi nhảy xuống dòng sau cái này.
40. caveNumber = selectCave ()
Dòng này cũng gọi một chức năng mà chúng tôi tạo ra. Hãy nhớ rằng selectCave ()
73
6 - Cõi rồng

Trang 88
Chức năng cho phép người chơi gõ vào hang mà họ chọn đi vào. Khi dòng hang trở về
trong hàm này thực thi, thực thi chương trình nhảy xuống ở đây và cục bộ
giá trị của hang biến là giá trị trả về của hàm này. Giá trị trả về được lưu trữ trong một
biến mới có tên caveNumber . Sau đó, việc thực hiện di chuyển đến dòng tiếp theo.
42. checkCave (caveNumber)
Dòng này gọi hàm checkCave () của chúng tôi với đối số giá trị của caveNumber.
Không chỉ thực hiện nhảy đến dòng 20, mà giá trị được lưu trữ trong caveNumber được sao chép
vào
tham số được chọnCave bên trong hàm checkCave () . Đây là chức năng mà
sẽ hiển thị 'Cung cấp cho bạn kho báu của anh ấy!' hoặc 'Yêu cầu bạn xuống
một vết cắn! ' , tùy thuộc vào hang mà người chơi chọn đi vào.
Yêu cầu người chơi chơi lại
44. in ('Bạn có muốn chơi lại không? (Có hoặc không)')
45. playAgain = input ()
Sau khi trò chơi đã được chơi, người chơi được hỏi liệu họ có muốn chơi lại không. Các
biến playAgain lưu trữ chuỗi mà người dùng đã nhập. Sau đó, chúng ta đến cuối của
trong khi khối, vì vậy chương trình kiểm tra lại các trong khi điều kiện tuyên bố của: trong khi
playAgain == 'có' hoặc playAgain == 'y'
Sự khác biệt là, bây giờ giá trị của playAgain bằng với bất kỳ chuỗi nào mà người chơi
gõ vào. Nếu người chơi gõ vào chuỗi 'có' hoặc 'y' , thì chúng tôi sẽ nhập vào vòng lặp
lại ở dòng 38.
Nếu người chơi gõ vào 'không' hoặc 'n' hoặc thứ gì đó ngớ ngẩn như 'Abraham Lincoln' ,
sau đó điều kiện của câu lệnh while sẽ là Sai và chúng ta sẽ đi đến dòng tiếp theo
sau khối while. Nhưng vì không còn dòng nào sau block-block, chương trình
chấm dứt.
Nhưng hãy nhớ rằng, chuỗi 'CÓ' khác với chuỗi 'có' . Nếu người chơi gõ
trong chuỗi 'CÓ' , thì điều kiện của câu lệnh while sẽ đánh giá thành Sai và
chương trình vẫn sẽ chấm dứt.
Chúng tôi vừa hoàn thành trò chơi thứ hai của chúng tôi! Trong trò chơi Dragon Realm, chúng
tôi đã sử dụng rất nhiều
những gì chúng ta đã học được trong trò chơi "Đoán số" và cũng chọn ra một vài thủ thuật mới.
Nếu bạn không hiểu một số khái niệm trong chương trình này, thì hãy đọc tóm tắt tại
kết thúc chương này hoặc xem lại từng dòng mã nguồn hoặc thử thay đổi nguồn
mã và xem chương trình thay đổi như thế nào. Trong chương tiếp theo, chúng tôi sẽ không tạo ra
một trò chơi, nhưng một
chương trình máy tính sẽ tạo mã bí mật từ các tin nhắn thông thường và cũng giải mã
mã bí mật trở lại tin nhắn ban đầu.
74

Trang 89
Chúng tôi đã đi qua mã nguồn từ trên xuống dưới. Nếu bạn muốn đi qua
mã nguồn theo thứ tự thực thi chảy, sau đó kiểm tra trang web theo dõi trực tuyến
cho chương trình này tại URL http://inventwithpython.com/traces/dragon.html.
Thiết kế chương trình
Dragon Realm là một game khá đơn giản. Các trò chơi khác trong cuốn sách này sẽ nhiều hơn
một chút
phức tạp. Đôi khi nó giúp viết ra mọi thứ bạn muốn trò chơi hoặc chương trình của bạn
để làm trước khi bạn bắt đầu viết mã. Điều này được gọi là "thiết kế chương trình."
Ví dụ, nó có thể giúp vẽ biểu đồ dòng chảy . Biểu đồ dòng chảy là một hình ảnh cho thấy
mọi hành động có thể xảy ra trong trò chơi của chúng tôi và theo thứ tự nào. Thông thường
chúng ta sẽ
tạo biểu đồ luồng trước khi viết chương trình của chúng tôi, để chúng tôi nhớ viết mã cho từng
chương trình
điều đó xảy ra trong trò chơi Hình 6-2 là biểu đồ dòng chảy cho Dragon Realm.
Hình 6-2: Biểu đồ dòng chảy cho trò chơi Dragon Realm.
Để xem những gì xảy ra trong trò chơi, hãy đặt ngón tay của bạn vào ô "Bắt đầu" và làm theo
75
6 - Cõi rồng

Trang 90
mũi tên từ hộp này sang hộp khác. Ngón tay của bạn giống như thực hiện chương trình.
Ngón tay của bạn sẽ vạch ra một con đường từ hộp này sang hộp khác, cho đến khi cuối cùng
ngón tay của bạn chạm vào
"Kết thúc" hộp. Như bạn có thể thấy, khi bạn đến hộp "Kiểm tra rồng thân thiện hay đói",
chương trình có thể vào hộp "Người chơi thắng" hoặc hộp "Người chơi thua". Dù bằng cách nào,
cả hai đường dẫn sẽ kết thúc tại ô "Yêu cầu chơi lại" và từ đó chương trình sẽ
hoặc kết thúc hoặc hiển thị giới thiệu cho người chơi một lần nữa.
Tóm lược
Trong trò chơi "Vương quốc rồng", chúng tôi đã tạo ra các chức năng của riêng mình, đó là phần
chính của
chương trình được gọi là. Bạn có thể nghĩ về các chức năng như các chương trình nhỏ trong
chương trình của chúng tôi. Mật mã
bên trong chức năng được chạy khi chương trình của chúng ta gọi chức năng đó. Bằng cách phá
mã của chúng tôi
vào các hàm, chúng ta có thể sắp xếp mã của mình thành các phần nhỏ hơn và dễ hiểu
hơn. Chúng tôi
cũng có thể chạy cùng một mã bằng cách đặt nó bên trong một hàm, thay vì gõ từng mã
thời gian chúng tôi muốn chạy mã đó.
Các đầu vào cho các hàm là các đối số chúng ta vượt qua khi chúng ta thực hiện một cuộc gọi
hàm. Các
Hàm gọi chính nó ước tính một giá trị gọi là giá trị trả về. Giá trị trả về là
đầu ra của hàm.
Chúng tôi cũng đã học về phạm vi thay đổi. Các biến được tạo bên trong hàm
tồn tại trong phạm vi cục bộ và các biến được tạo bên ngoài tất cả các hàm tồn tại trong toàn cầu
phạm vi. Mã trong phạm vi toàn cầu không thể sử dụng các biến cục bộ. Nếu một biến cục bộ có
cùng tên với một biến trong phạm vi toàn cầu, Python coi nó là một biến riêng biệt
biến và gán giá trị mới cho biến cục bộ sẽ không thay đổi giá trị trong
biến toàn cầu.
Phạm vi biến có vẻ phức tạp, nhưng chúng rất hữu ích để tổ chức
các hàm như các đoạn mã tách biệt với phần còn lại của hàm. Bởi vì mỗi
Hàm có phạm vi cục bộ của riêng nó, chúng tôi có thể chắc chắn rằng mã trong một hàm sẽ
không
gây ra lỗi trong các chức năng khác.
Tất cả các chương trình không cần thiết đều sử dụng các hàm vì chúng rất hữu ích, bao gồm cả
phần còn lại của
trò chơi trong cuốn sách này. Bằng cách hiểu cách các chức năng hoạt động, chúng ta có thể tiết
kiệm cho mình rất nhiều
gõ và làm cho chương trình của chúng tôi dễ đọc hơn sau này.
76

Trang 91
Các chủ đề được đề cập trong Chương này:
● 3 loại lỗi khác nhau
● Trình gỡ lỗi của IDLE

● Bước vào, qua và ra

● Đi và thoát

● Điểm phá vỡ

Lỗi!
"Trong hai lần tôi đã được hỏi, 'Xin cầu nguyện, ông Babbage, nếu bạn đưa vào
Máy sai số liệu, câu trả lời đúng sẽ ra? ' Tôi không thể đúng
để hiểu các loại ý tưởng có thể kích động như vậy
câu hỏi. "
-Charles Babbage, nhà toán học, triết gia, nhà phát minh người Anh thế kỷ 19
và kỹ sư cơ khí, người khởi nguồn khái niệm lập trình
máy vi tính.
http://en.wikipedia.org/wiki/Charles_Babbage
Nếu bạn nhập sai mã, máy tính sẽ không cung cấp cho bạn chương trình phù hợp. Một
chương trình máy tính sẽ luôn làm những gì bạn nói với nó, nhưng những gì bạn bảo chương
trình làm
có thể không giống như những gì bạn muốn chương trình làm. Một lỗi là tên gọi khác của
một lỗi hoặc vấn đề trong một chương trình máy tính. Lỗi xảy ra khi lập trình viên không có
77

Trang 92
suy nghĩ cẩn thận về chính xác những gì chương trình đang làm. Có ba loại lỗi
điều đó có thể xảy ra với chương trình của bạn:
● Lỗi cú pháp là một loại lỗi xuất phát từ lỗi chính tả trong chương trình của bạn. Khi mà

Trình thông dịch Python thấy lỗi cú pháp, đó là do mã của bạn không được viết đúng
Ngôn ngữ Python. Một chương trình Python thậm chí chỉ có một lỗi cú pháp sẽ không chạy.
● Lỗi thời gian chạy là lỗi xảy ra trong khi chương trình đang chạy (nghĩa là

thi công). Chương trình sẽ hoạt động cho đến khi gặp lỗi dòng mã,
và sau đó chương trình kết thúc với một thông báo lỗi (điều này được gọi là sự cố ).
Trình thông dịch Python sẽ hiển thị một cái gì đó gọi là "truy nguyên" và hiển thị dòng
vấn đề xảy ra ở đâu
● Lỗi ngữ nghĩa là lỗi khó sửa nhất. Lỗi này không làm sập chương trình,

và chương trình có vẻ hoạt động tốt. Tuy nhiên, nó không làm những gì lập trình viên
dành cho chương trình để làm. Ví dụ, nếu lập trình viên muốn biến
tổng là tổng của các giá trị trong các biến a , b và c nhưng ghi tổng = a
+ b * c , sau đó giá trị trong tổng sẽ sai. Điều này sẽ không gây ra chương trình
sụp đổ ngay lập tức, nhưng có thể hoặc không thể khiến một số mã khác bị sập sau này
bởi vì giá trị bất ngờ trong tổng số .
Tìm lỗi trong chương trình của chúng tôi có thể khó, nếu bạn thậm chí nhận thấy chúng! Khi
chạy
chương trình của bạn, bạn có thể phát hiện ra rằng đôi khi các chức năng không được gọi khi
chúng
giả sử là, hoặc có thể chúng được gọi quá nhiều lần. Bạn có thể mã điều kiện cho một
trong khi vòng lặp sai, do đó nó lặp sai số lần. (Một vòng lặp trong chương trình của bạn
không bao giờ thoát ra là một loại lỗi được gọi là vòng lặp vô hạn . Để dừng chương trình này,
bạn có thể nhấn Ctrl-C trong trình vỏ tương tác.) Bất kỳ điều nào trong số này có thể xảy ra
nhầm
trong mã của bạn nếu bạn không cẩn thận.
Thật khó để tìm ra cách mã của bạn có thể tạo ra lỗi vì tất cả các dòng
mã được thực thi rất nhanh và các giá trị trong các biến thay đổi thường xuyên. Một
trình gỡ lỗi là một chương trình cho phép bạn thực hiện từng dòng mã của mình (cùng một dòng
để Python thực thi chúng) và hiển thị giá trị nào được lưu trữ trong tất cả các biến.
Trình gỡ lỗi cho phép bạn xem từng dòng mã ảnh hưởng đến chương trình của bạn như thế
nào. Điều này có thể rất
hữu ích để tìm ra chính xác những gì chương trình đang làm.
Có thể tìm thấy hướng dẫn bằng video về cách sử dụng trình gỡ lỗi đi kèm với IDLE trên cuốn
sách này
trang web tại http://inventwithpython.com/ideo/
Bắt đầu trình gỡ lỗi
Trong IDLE, hãy tiếp tục và mở trò chơi Dragon Realm mà bạn đã thực hiện trong chương trước.
Trong trình vỏ tương tác, bấm vào Tệp và sau đó Mở , sau đó chọn dragon.py (hoặc
bất cứ điều gì bạn đặt tên cho tập tin khi bạn lưu nó).
Sau khi mở tệp dragon.py , nhấp vào mục menu Gỡ lỗi ở đầu
shell tương tác, sau đó bấm Trình gỡ lỗi để làm cho cửa sổ Kiểm soát gỡ lỗi xuất hiện
(Hình 7-1).
78

Trang 93
Hình 7-1: Cửa sổ Debug Control.
Bây giờ khi bạn chạy trò chơi Dragon Realm (bằng cách nhấn F5 hoặc nhấp vào Chạy , sau
đó Chạy
Mô-đun trong menu trên cùng của cửa sổ soạn thảo tệp), chương trình gỡ lỗi sẽ được kích
hoạt. Điều này
được gọi là chạy một chương trình "theo trình gỡ lỗi". Trong cửa sổ Kiểm soát gỡ lỗi, kiểm tra
Các hộp kiểm nguồn và Globals . Sau đó chạy chương trình bằng cách nhấn F5 trong trình
chỉnh sửa tệp
cửa sổ (Hình 7-2).
Hình 7-2: Chạy trò chơi Dragon Realm dưới trình gỡ lỗi.
Khi bạn chạy các chương trình Python với trình gỡ lỗi được kích hoạt, chương trình sẽ dừng
trước khi nó thực thi dòng mã đầu tiên. Nếu bạn nhấp vào thanh tiêu đề của cửa sổ trình chỉnh sửa
tệp (và
79
7 - Sử dụng trình gỡ lỗi

Trang 94
bạn đã chọn hộp kiểm Nguồn trong cửa sổ Kiểm soát gỡ lỗi), dòng đầu tiên của
mã được tô màu xám. Ngoài ra, cửa sổ Kiểm soát gỡ lỗi cho thấy bạn đang ở dòng 1,
đó là dòng ngẫu nhiên nhập khẩu .
Trình gỡ lỗi cho phép bạn thực thi một dòng hoặc mã tại một thời điểm (được gọi là "bước"). Để
thực thi
một lệnh đơn, nhấp vào nút Bước trong Cửa sổ gỡ lỗi. Đi trước và nhấp vào
Bước nút một lần. Điều này sẽ khiến trình thông dịch Python thực thi nhập ngẫu nhiên
hướng dẫn, và sau đó dừng lại trước khi nó thực hiện lệnh tiếp theo. Kiểm soát gỡ lỗi
cửa sổ sẽ thay đổi để hiển thị rằng bạn hiện đang ở dòng 2, dòng thời gian nhập .
Bước
Bước là quá trình thực hiện một hướng dẫn của chương trình tại một thời điểm. Đang làm
điều này cho phép bạn thấy những gì xảy ra sau khi chạy một dòng mã, có thể giúp bạn hình
dung
ra một lỗi xuất hiện đầu tiên trong các chương trình của bạn.
Cửa sổ Debug Control sẽ hiển thị cho bạn dòng nào sắp được thực thi khi bạn
nhấp vào nút Bước trong cửa sổ Kiểm soát gỡ lỗi. Cửa sổ này cũng sẽ cho bạn biết những gì
số dòng nó được bật và hiển thị cho bạn hướng dẫn chính nó.
Nhấp vào nút Bước một lần nữa để chạy hướng dẫn thời gian nhập . Trình gỡ lỗi sẽ
thực hiện quy tắc nhập này và sau đó chuyển sang dòng 4. Trình gỡ lỗi đã bỏ qua dòng 3
bởi vì nó là một dòng trống Lưu ý rằng bạn chỉ có thể bước về phía trước với trình gỡ lỗi, bạn
không thể đi ngược.
Nhấp vào nút Bước ba lần nữa. Điều này sẽ thực thi ba câu lệnh def
xác định các chức năng. Trình gỡ lỗi bỏ qua các khối def của các hàm này bởi vì chúng tôi
chỉ xác định các chức năng, không gọi chúng. Khi bạn xác định các chức năng này, chúng sẽ
xuất hiện trong khu vực Globals của cửa sổ Debug Control.
Văn bản bên cạnh tên hàm trong khu vực Toàn cầu sẽ trông giống như
"<chức năng kiểm tra Lưu ở 0x012859B0>". Tên mô-đun cũng có vẻ khó hiểu
văn bản bên cạnh chúng, chẳng hạn như "<mô-đun 'ngẫu nhiên' từ 'C: \\ Python25 \\ lib \\
Random.pyc'>". Điều này
chỉ hữu ích cho các lập trình viên Python nâng cao và bạn không cần biết cái này là gì
có nghĩa là để gỡ lỗi chương trình của bạn. Chỉ cần thấy rằng các chức năng và mô-đun là có
trong
Khu vực toàn cầu sẽ cho bạn biết nếu chức năng đã được xác định hoặc mô-đun đã được nhập.
Bạn cũng có thể bỏ qua các dòng __builtins__, __doc__ và __name__ trong khu vực Toàn cầu.
Trình gỡ lỗi bây giờ sẽ là (sau khi nhấp vào Bước bốn lần) tại dòng 35, playAgain =
dòng 'có' . Khi bạn bấm Bước để thực thi dòng này, biến playAgain sẽ là
được tạo và sẽ hiển thị trong khu vực Toàn cầu. Bên cạnh nó sẽ là giá trị được lưu trữ trong này
biến, đó là chuỗi 'có' . Trình gỡ lỗi cho phép bạn xem các giá trị của tất cả
các biến trong chương trình khi chương trình chạy. Điều này có thể rất hữu ích nếu bạn cần sửa
chữa
chương trình của bạn.
Vùng toàn cầu trong cửa sổ Kiểm soát gỡ lỗi là nơi chứa tất cả các biến toàn cục
80

Trang 95
lưu trữ. Biến toàn cục là biến được tạo bên ngoài bất kỳ hàm nào (đó
là, trong phạm vi toàn cầu). Ngoài ra còn có một khu vực địa phương , cho bạn thấy phạm vi
địa phương
các biến và giá trị của chúng. Vùng cục bộ sẽ chỉ có các biến trong đó khi chương trình
thực hiện là bên trong của một chức năng. Vì chúng tôi vẫn ở trong phạm vi toàn cầu, khu vực
này trống.
Trình gỡ lỗi Python (và hầu hết tất cả các trình gỡ lỗi) chỉ cho phép bạn tiến lên phía trước
chương trình. Khi bạn đã thực hiện một lệnh, bạn không thể lùi lại và hoàn tác
chỉ dẫn.
Các nút Đi và Thoát
Nếu bạn cảm thấy mệt mỏi khi nhấp vào nút bước này nhiều lần và chỉ muốn chương trình
để chạy bình thường, nhấp vào nút Bắt đầu ở đầu cửa sổ Kiểm soát gỡ lỗi. Điều này sẽ nói
chương trình chạy như thể bạn không bật trình gỡ lỗi.
Nếu bạn muốn chấm dứt chương trình trong khi chương trình đang chạy, chỉ cần nhấp vào nút
Thoát tại
trên cùng của cửa sổ Debug Control. Chương trình sẽ ngay lập tức thoát ra. Điều này có thể là
tiện dụng nếu bạn muốn dừng chương trình và bắt đầu gỡ lỗi từ đầu một lần nữa.
Bước vào, qua và ra
Bắt đầu chương trình Dragon Realm với trình gỡ lỗi và tiếp tục bước (bằng cách nhấp vào
Nút bước trong cửa sổ Kiểm soát gỡ lỗi) cho đến khi trình gỡ lỗi ở dòng 38 (lệnh gọi đến
dòng displayIntro () ). Khi bạn bấm Bước một lần nữa, trình gỡ lỗi sẽ nhảy vào đây
gọi hàm và xuất hiện trên dòng 5 (dòng đầu tiên trong khối def của displayIntro ()
chức năng. Loại bước chúng ta đã và đang làm được gọi là bước vào , bởi vì nó sẽ
bước vào các cuộc gọi chức năng.
81
7 - Sử dụng trình gỡ lỗi

Trang 96
Hình 7-3: Tiếp tục bước cho đến khi bạn đạt đến dòng 38.
Nếu bạn bấm Bước thêm một vài lần nữa, bạn sẽ thấy đầu ra của lệnh gọi hàm print ()
xuất hiện trong cửa sổ vỏ tương tác một lần. Khi bạn bước qua bản in cuối cùng ()
gọi hàm trong hàm displayIntro () , trình gỡ lỗi sẽ quay trở lại đầu tiên
dòng (dòng 40) sau khi gọi hàm.
Nhấp vào Bước một lần nữa để bước vào chức năng chọn lựa. Tiếp tục bước qua
mã cho đến khi bạn thực hiện chức năng gọi hàm raw_input () . Chương trình sẽ chờ
cho đến khi bạn gõ một phản hồi vào shell, giống như khi bạn chạy chương trình bình
thường. nếu bạn
Hãy thử nhấp vào nút Bước ngay bây giờ, sẽ không có gì xảy ra vì chương trình sẽ đợi
phản ứng.
Nhập phản hồi bằng cách nhấp lại vào cửa sổ shell tương tác và nhập hang nào
bạn muốn vào Bạn phải bấm vào dòng dưới cùng trong vỏ trước khi gõ. Nếu bạn là
gõ nhưng không xuất hiện trên màn hình (và con trỏ nhấp nháy không phải là dưới nào
hang bạn sẽ đi vào? (1 hoặc 2) văn bản), sau đó bạn chưa nhấp vào lần cuối
dòng của cửa sổ vỏ.
Khi bạn nhấn phím Enter để nhập phản hồi của mình, trình gỡ lỗi sẽ tiếp tục bước
dòng mã một lần nữa. Thay vì nhấp vào Bước, hãy thử nhấp vào nút Ra trên Gỡ lỗi
Cửa sổ điều khiển. Điều này được gọi là bước ra , bởi vì nó sẽ khiến trình gỡ lỗi bước
trên nhiều dòng như nó cần cho đến khi nó nhảy ra khỏi chức năng mà nó đã ở.
ví dụ: nếu bạn ở trong hàm displayIntro () trên dòng 6, nhấp vào Out sẽ
82

Trang 97
Trình gỡ lỗi tiếp tục bước cho đến khi chức năng kết thúc và trở lại dòng sau
cuộc gọi đến displayIntro () . Bước ra có thể giúp bạn không phải bấm Bước qua
và hơn một lần nữa để nhảy ra khỏi chức năng.
Nếu bạn không ở trong một hàm (nghĩa là bạn đang ở trong phạm vi toàn cầu) và bạn bấm Out,
trình gỡ lỗi sẽ thực thi tất cả các dòng còn lại trong chương trình (chính xác như thể bạn đã bấm
nút Bắt đầu).
Loại bước cuối cùng được thực hiện bằng nút Over trong cửa sổ Kiểm soát gỡ lỗi và
nó là để bước qua các cuộc gọi chức năng. Bước qua có nghĩa là trình gỡ lỗi sẽ không bước
vào các cuộc gọi chức năng. Thay vào đó, trình gỡ lỗi thực thi tất cả mã bên trong hàm cùng một
lúc
và chỉ dừng lại ở dòng sau khi gọi hàm. Điều này rất hữu ích nếu bạn không muốn bước
thông qua mỗi dòng duy nhất bên trong hàm.
Bây giờ bạn biết năm nút ở đầu cửa sổ Kiểm soát gỡ lỗi làm gì. Đây là
một bản tóm tắt:
● Đi - Thực thi phần còn lại của mã như bình thường hoặc cho đến khi đạt đến điểm dừng. (Phá

vỡ
điểm được mô tả sau.)
● Bước - Bước một dòng mã. Nếu đường dây là một lời gọi hàm, trình gỡ lỗi sẽ bước vào

chức năng.
● Trên - Bước một dòng mã. Nếu đường dây là một cuộc gọi chức năng, trình gỡ lỗi sẽ không

bước
vào chức năng, nhưng thay vào đó bước qua cuộc gọi.
● Ra ngoài - Tiếp tục bước qua các dòng mã cho đến khi trình gỡ lỗi rời khỏi chức năng

trong khi Out đã được bấm. Bước này ra khỏi chức năng.


● Thoát - Kết thúc chương trình ngay lập tức.

Tìm lỗi
Sử dụng trình gỡ lỗi là một cách tốt để tìm ra nguyên nhân gây ra lỗi trong chương trình của
bạn. Như
một ví dụ, đây là một chương trình nhỏ có lỗi. Chương trình đi kèm với một
vấn đề bổ sung ngẫu nhiên cho người dùng để giải quyết. Trong cửa sổ shell tương tác, bấm vào
Tệp, sau đó Cửa sổ mới để mở cửa sổ soạn thảo tệp mới. Nhập chương trình này vào đó
cửa sổ và lưu chương trình dưới dạng buggy.py .
buggy
1. nhập ngẫu nhiên
2. số1 = ngẫu nhiên.randint (1, 10)
3. số2 = ngẫu nhiên.randint (1, 10)
4. in ('Thế nào là' + str (số1) + '+' + str (số2) +
'?')
5. câu trả lời = đầu vào ()
6. nếu câu trả lời == number1 + number2:
7. in ('Đúng!')
8. khác:
9. in ('Không! Câu trả lời là' + str (số1 +
83
7 - Sử dụng trình gỡ lỗi

Trang 98
số 2))
Nhập chương trình chính xác như ở trên, ngay cả khi bạn đã có thể biết lỗi là gì.
Sau đó thử chạy chương trình bằng cách nhấn F5. Đây là một trò chơi số học đơn giản mà
đưa ra hai số ngẫu nhiên và yêu cầu bạn thêm chúng. Đây là những gì nó có thể nhìn
như khi bạn chạy chương trình:
5 + 1 là gì?
6
Không! Câu trả lời là 6
Điều đó không đúng! Chương trình này có một lỗi ngữ nghĩa trong đó. Ngay cả khi người dùng
gõ vào
Câu trả lời đúng, chương trình nói rằng họ sai.
Bạn có thể nhìn vào mã và suy nghĩ kỹ về nơi nó bị lỗi. Nó hoạt động
đôi khi. Nhưng bạn có thể tìm ra nguyên nhân gây ra lỗi nhanh hơn nếu bạn chạy chương trình
theo trình gỡ lỗi. Ở đầu cửa sổ shell tương tác, nhấp vào Gỡ lỗi , sau đó
Trình gỡ lỗi (nếu mục menu Trình gỡ lỗi chưa có kiểm tra) để hiển thị Gỡ lỗi
Cửa sổ điều khiển. Trong cửa sổ Kiểm soát gỡ lỗi, đảm bảo cả bốn hộp kiểm (Ngăn xếp,
Nguồn, người dân địa phương, Globals) được kiểm tra. Điều này làm cho cửa sổ Debug Control
cung cấp
Thông tin nhất. Sau đó nhấn F5 trong cửa sổ soạn thảo tệp để chạy chương trình bên dưới
trình gỡ lỗi.
Trình gỡ lỗi bắt đầu tại dòng ngẫu nhiên nhập . Không có gì đặc biệt xảy ra ở đây, vì vậy chỉ cần
nhấn Bước để thực hiện nó. Bạn sẽ thấy mô-đun ngẫu nhiên ở cuối Debug
Cửa sổ điều khiển trong khu vực Globals.
Nhấp vào Bước một lần nữa để chạy dòng 2. Một cửa sổ soạn thảo tệp mới sẽ bật mở. Nhớ lấy
hàm randint () nằm trong mô-đun ngẫu nhiên . Khi bạn bước vào
Hàm, bạn bước vào mô-đun ngẫu nhiên vì đó là nơi hàm randint
Là. Các hàm đi kèm với các mô-đun của Python hầu như không bao giờ có lỗi trong mã của
chúng, vì vậy
bạn có thể chỉ cần nhấp ra để bước ra khỏi hàm randint () và quay lại chương trình của bạn.
Sau khi bạn bước ra ngoài, bạn có thể đóng cửa sổ mô-đun ngẫu nhiên .
Dòng 3 cũng là một lệnh gọi hàm randint () . Chúng ta không cần phải thông qua mã này,
vì vậy chỉ cần nhấp qua để bước qua chức năng này. Các randint () mã chức năng vẫn là
được thực thi, nó chỉ được thực hiện cùng một lúc để chúng ta không phải bước qua nó.
Dòng 4 là lệnh in () để hiển thị cho người chơi các số ngẫu nhiên. Nhưng vì chúng ta đang sử
dụng
trình gỡ lỗi, chúng tôi biết chương trình sẽ in những con số nào ngay cả trước khi nó in
chúng! Chỉ
nhìn vào khu vực Globals của cửa sổ Debug Control. Bạn có thể thấy số1 và
các biến number2 và bên cạnh chúng là các giá trị nguyên được lưu trữ trong các biến đó. Khi
nào
Tôi chạy trình gỡ lỗi, nó trông như thế này:
84

Trang 99
Hình 7-4: Cửa sổ Debug Control.
Biến number1 có giá trị 9 và biến number2 có giá trị 10 .
Khi bạn bấm Bước, chương trình sẽ hiển thị chuỗi trong lệnh gọi print () với các lệnh này
các giá trị. (Tất nhiên, chúng tôi sử dụng hàm str () để chúng tôi có thể nối chuỗi
phiên bản của các số nguyên này.)
Nhấp vào Bước trên dòng 5 sẽ khiến trình gỡ lỗi chờ cho đến khi người chơi vào
phản ứng. Đi trước và nhập câu trả lời đúng (trong trường hợp của tôi, 19) vào vỏ tương tác
cửa sổ. Trình gỡ lỗi sẽ tiếp tục và chuyển xuống dòng 6.
Dòng 6 là một nếu tuyên bố. Điều kiện là giá trị trong câu trả lời phải khớp với
tổng của số1 và số2 . Nếu điều kiện là True , thì trình gỡ lỗi sẽ chuyển sang
dòng 7. Nếu điều kiện là Sai , trình gỡ lỗi sẽ chuyển sang dòng 9. Nhấp vào Bước một nữa
thời gian để tìm ra nơi nó đi
Trình gỡ lỗi hiện đang ở dòng 9! Chuyện gì đã xảy ra? Các điều kiện trong khi tuyên bố bắt buộc
đã sai . Hãy xem các giá trị cho number1 , number2 và trả lời .
Chú ý rằng number1 và number2 là các số nguyên, vì vậy số tiền của họ sẽ cũng là một
số nguyên. Nhưng câu trả lời là một chuỗi. Điều đó có nghĩa là câu trả lời == number1 +
điều kiện số2 sẽ được ước tính là '19' == 19 . Một giá trị chuỗi và một số nguyên
giá trị sẽ luôn không bằng nhau, do đó điều kiện sẽ được ước tính thành Sai .
Đó là lỗi trong chương trình. Lỗi là chúng tôi sử dụng câu trả lời khi chúng tôi nên sử dụng
int (trả lời) . Đi trước và thay đổi dòng 6 để sử dụng int (answer) == number1 +
number2 thay vì answer == number1 + number2 và chạy lại chương trình.
2 + 3 là gì?
5
Chính xác!
Lần này, chương trình hoạt động chính xác. Chạy thêm một lần nữa và nhập câu trả lời sai
nhằm mục đích đảm bảo chương trình không cho chúng tôi biết chúng tôi đã trả lời đúng. Chúng
ta có
bây giờ gỡ lỗi chương trình này. Hãy nhớ rằng, máy tính sẽ chạy các chương trình của bạn chính
xác như
bạn gõ chúng, ngay cả khi những gì bạn gõ không phải là những gì bạn dự định.
85
7 - Sử dụng trình gỡ lỗi

Trang 100
Điểm phá vỡ
Bước qua mã một dòng tại một thời điểm có thể vẫn còn quá chậm. Thường thì bạn sẽ muốn
chương trình chạy ở tốc độ bình thường cho đến khi đạt đến một dòng nhất định. Bạn có thể làm
điều này với
điểm phá vỡ. Điểm dừng được đặt trên một dòng khi bạn muốn trình gỡ lỗi kiểm soát
một khi thực hiện đạt đến dòng đó. Vì vậy, nếu bạn nghĩ rằng có vấn đề với mã của bạn trên, hãy
nói,
dòng 17, chỉ cần đặt điểm dừng trên dòng 17 và khi thực thi đạt đến dòng đó, trình gỡ lỗi
sẽ dừng thực thi. Sau đó, bạn có thể bước qua một vài dòng để xem những gì đang xảy ra. Sau đó
bạn có thể nhấp vào Đi để cho phép chương trình thực thi cho đến khi kết thúc (hoặc nghỉ khác
điểm).
Để đặt điểm dừng, nhấp chuột phải vào dòng bạn muốn điểm dừng và chọn "Đặt
Điểm dừng "từ menu xuất hiện. Dòng sẽ được tô sáng màu vàng thành
chỉ ra một điểm dừng là trên dòng đó. Bạn có thể đặt điểm dừng trên nhiều dòng như bạn
muốn Để xóa điểm dừng, nhấp vào dòng và chọn "Xóa điểm dừng" từ
menu xuất hiện.
Hình 7-5: Trình soạn thảo tệp với hai điểm ngắt được đặt.

Ví dụ về việc sử dụng điểm dừng


Hãy thử gỡ lỗi một chương trình có điểm dừng. Đây là một chương trình mô phỏng tiền xu
lật bằng cách gọi ngẫu nhiên.randint (0, 1) . Mỗi lần gọi hàm này trả về
số nguyên 1 , chúng ta sẽ xem xét rằng "các đầu" và tăng một biến được gọi là các đầu . Chúng
tôi sẽ
cũng tăng một biến được gọi là lật để theo dõi số lần chúng ta thực hiện "đồng xu" này
lật ".
Chương trình sẽ thực hiện "lật đồng xu" một nghìn lần. Điều này sẽ đưa một người qua một
Giờ để làm, nhưng máy tính có thể làm điều đó trong một giây! Nhập mã sau vào
trình chỉnh sửa tệp và lưu nó dưới dạng coinFlips.py . Bạn cũng có thể tải mã này từ
http://inventwithpython.com/coinFlips.py
86
Trang 101
coinFlips.py
Mã này có thể được tải xuống từ http://inventwithpython.com/coinFlips.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. nhập ngẫu nhiên
2. in ('Tôi sẽ lật một đồng xu 1000 lần. Đoán xem có bao nhiêu
lần nó sẽ lên đầu. (Nhấn Enter để bắt đầu) ')
3. đầu vào ()
4. lật = 0
5. đầu = 0
6. trong khi lật <1000:
7. nếu ngẫu nhiên.randint (0, 1) == 1:
số 8.
thủ trưởng = thủ trưởng + 1
9. lật = lật + 1
10.
11. nếu lật == 900:
12.
in ('900 lần lật và đã có' + str
(người đứng đầu) + 'người đứng đầu.')
13. nếu lật == 100:
14.
in ('Ở 100 lần tung, các đầu đã xuất hiện' + str
(người đứng đầu) + 'lần cho đến nay.')
15. nếu lật == 500:
16.
in ('Nửa đường đã hoàn thành và các đầu đã xuất hiện' +
str (người đứng đầu) + 'lần.')
17.
18. in ()
19. in ('Trong số 1000 lần tung đồng xu, các đầu đã xuất hiện' + str
(người đứng đầu) + 'lần!')
20. in ('Bạn đã đóng chưa?')
Chương trình chạy khá nhanh. Có lẽ nó đã dành nhiều thời gian hơn để chờ người dùng nhấn
Phím Enter hơn khi thực hiện lật đồng xu. Hãy nói rằng chúng tôi muốn thấy nó làm đồng xu lật
một
bởi một. Trên cửa sổ của vỏ tương tác, nhấp vào Gỡ lỗi và sau đó Trình gỡ lỗi ở trên cùng
menu để mở cửa sổ Debug Control. Sau đó nhấn F5 để chạy chương trình.
Chương trình bắt đầu trong trình gỡ lỗi trên dòng 1. Nhấn Bước ba lần trong Điều khiển gỡ lỗi
cửa sổ để thực hiện ba dòng đầu tiên. Bạn sẽ nhận thấy các nút bị vô hiệu hóa vì
hàm input () đã được gọi và cửa sổ shell tương tác đang chờ
người chơi để gõ một cái gì đó. Nhấp vào cửa sổ vỏ tương tác và nhấn Enter. (Chắc chắn rằng
nhấp vào bên dưới văn bản trong cửa sổ shell, nếu không IDLE có thể không nhận được
tổ hợp phím.) Sau khi nhập văn bản cho lệnh gọi () , các nút Bước sẽ trở thành
kích hoạt lại.
Bạn có thể nhấp vào Bước một vài lần nữa, nhưng bạn sẽ thấy rằng sẽ mất khá nhiều thời gian để
có được thông qua toàn bộ chương trình. Thay vào đó, hãy đặt điểm dừng trên các dòng 12, 14
và 16 (Hình 7-
6).
87
7 - Sử dụng trình gỡ lỗi
Trang 102
Hình 7-6: Ba điểm ngắt được thiết lập.
Sau khi thiết lập các điểm dừng, bấm Đi trong cửa sổ Kiểm soát gỡ lỗi. Chương trình sẽ
chạy ở tốc độ bình thường của nó cho đến khi nó đạt đến lật 100. Mở lật rằng, điều kiện cho nếu
tuyên bố trên dòng 13 là True . Điều này gây ra dòng 14 (trong đó chúng ta có một điểm ngắt
được đặt) thành
thực thi, thông báo cho trình gỡ lỗi dừng chương trình và tiếp quản. Nhìn vào gỡ lỗi
Cửa sổ điều khiển trong phần Globals để xem giá trị của lật và đầu là gì.
Nhấp vào Đi một lần nữa và chương trình sẽ tiếp tục cho đến khi đến điểm dừng tiếp theo trên
dòng
16. Một lần nữa, xem như thế nào các giá trị trong flips và người đứng đầu đã thay đổi. Bạn có
thể nhấp vào Đi một
thêm thời gian để tiếp tục thực hiện cho đến khi nó đạt đến điểm dừng tiếp theo.
Và nếu bạn bấm Đi lần nữa, việc thực thi sẽ tiếp tục cho đến khi điểm dừng tiếp theo là
đạt tới, nằm trên dòng 12. Bạn có thể nhận thấy rằng hàm print () trên các dòng
12, 14 và 16 được gọi theo thứ tự khác với chúng xuất hiện trong mã nguồn. Đó là
bởi vì chúng được gọi theo thứ tự rằng điều kiện if của câu lệnh trở thành True .
Sử dụng trình gỡ lỗi có thể giúp làm rõ lý do tại sao điều này là.
Tóm lược
Viết chương trình chỉ là một phần của công việc để làm trò chơi. Phần tiếp theo là làm
chắc chắn mã chúng tôi đã viết thực sự hoạt động. Trình gỡ lỗi cho chúng tôi bước qua mã một
dòng tại một
thời gian, trong khi kiểm tra dòng nào thực thi (và theo thứ tự nào) và giá trị nào
các biến chứa. Khi điều này quá chậm, chúng ta có thể đặt điểm dừng và nhấp vào Đi để cho
phép
chương trình chạy bình thường cho đến khi đạt đến điểm dừng.
Sử dụng trình gỡ lỗi là một cách tuyệt vời để hiểu chính xác chương trình đang làm gì. Trong khi
cuốn sách này cung cấp giải thích về tất cả các trò chơi trong đó, trình gỡ lỗi có thể giúp bạn tìm
hiểu
nhiều hơn về chính bạn.
88

Trang 103
Các chủ đề được đề cập trong Chương này:
● Cách chơi Hangman.
● Nghệ thuật ASCII

● Thiết kế trò chơi của chúng tôi bằng cách vẽ biểu đồ dòng chảy trước khi lập trình.

Trong chương này, chúng ta sẽ thực hiện một trò chơi Hangman. Trò chơi này phức tạp hơn
so với trò chơi trước đây của chúng tôi, nhưng nó cũng vui hơn nhiều. Bởi vì trò chơi là tiên tiến,
chúng tôi
trước tiên nên lập kế hoạch cẩn thận bằng cách tạo một sơ đồ gọi là biểu đồ dòng chảy (giải thích
sau).
Trong hai chương tiếp theo, chúng tôi sẽ thực sự viết mã cho Hangman.
Trong trường hợp bạn chưa từng chơi Hangman trước đây, trước tiên hãy tìm hiểu các quy tắc
dành cho Hangman.
Cách chơi "Hangman"
Trong trường hợp bạn không biết, Hangman là trò chơi dành cho hai người thường chơi bằng
cách sử dụng
Giấy và bút chì. Một người chơi nghĩ về một từ và sau đó vẽ một khoảng trống trên trang cho
mỗi từ
chữ trong từ. Sau đó, người chơi thứ hai cố gắng đoán các chữ cái có thể có trong từ. Nếu
Họ đoán đúng, người chơi đầu tiên viết thư vào chỗ trống. Nếu họ đoán không chính xác,
người chơi đầu tiên vẽ một phần cơ thể của người treo cổ. Nếu người chơi thứ hai có thể đoán
tất cả các chữ cái trong từ trước khi người đàn ông đã hoàn toàn rút ra, họ thắng. Nhưng nếu họ
Không thể tìm ra nó kịp thời, người đàn ông bị treo cổ và họ thua trò chơi!
Chạy mẫu của "Hangman"
Dưới đây là một ví dụ về những gì người chơi có thể thấy khi họ chạy chương trình Hangman
89

Trang 104
chúng tôi sẽ viết sau. Các văn bản mà người chơi nhập vào được in đậm.
HÀN QUỐC
+ --- +
| |
|
|
|
|
=========
Chữ cái bị thiếu:
___
Đoán một lá thư.
một
+ --- +
| |
|
|
|
|
=========
Chữ cái bị thiếu:
_a_
Đoán một lá thư.
o
+ --- +
| |
Ôi |
|
|
|
=========
Chữ cái bị thiếu: o
_a_
Đoán một lá thư.
r
+ --- +
| |
Ôi |
| |
|
|
=========
Chữ cái bị thiếu: hoặc
_a_
Đoán một lá thư.
90

Trang 105
t
+ --- +
| |
Ôi |
| |
|
|
=========
Chữ cái bị thiếu: hoặc
_ tại
Đoán một lá thư.
một
Bạn đã đoán được bức thư đó. Chọn lại.
Đoán một lá thư.
c
Đúng! Từ bí mật là "con mèo"! Bạn đã thắng!
Bạn có muốn chơi lại không? (có hay không)
Không

Nghệ thuật ASCII


Một nửa số dòng mã trong chương trình Hangman hoàn toàn không phải là mã, nhưng là đa dòng
chuỗi sử dụng các ký tự bàn phím để vẽ hình ảnh. Loại đồ họa này được gọi là ASCII
nghệ thuật (phát âm là "ask-ee"), bởi vì các ký tự bàn phím (như chữ cái, số và cả
tất cả các dấu hiệu khác trên bàn phím) được gọi là ký tự ASCII. ASCII là viết tắt của
Mã tiêu chuẩn Mỹ để trao đổi thông tin (chúng ta sẽ tìm hiểu thêm về nó trong
Chương mật mã Caesar). Dưới đây là một vài con mèo được thực hiện trong nghệ thuật ASCII:
^ ___ ^
Ngày mai
| )
_____ / xx xxx \ _____
|. . )
_ / xxx xx xxx xxx \ __
(v)
__ / xxx xxx xx xxx \ __
\ ____ |
/ xxxxxxxxx xx xx xx xx xxx \
| \
/
xx / \
xx xx \
| |
/
/\
x xx \
|
\
| / \ | \
xx x \
| | |
| | \ | \ ____
Z
x\
| | | \
| | \ ____ /
\z
xxx |
| | | | -. | |
\z
|
| | | ____ / |
\/
\
\
((((() (______ /
/
____ / |
|
__ |
\ ____
|
xxx |
/|
___ ___-------
__ / x |
/|
| | _______ ____ /
|
| o \ -------- \ _ / _ / ___ /
xx /
| oo \ _____ /
_ / ______ /
xx /
\ \ __
__ /
xx /
\ \ ____________ /
x_ /
\
/
\ ____
_______ /
\
91
8 - Biểu đồ dòng chảy

Trang 106
Thiết kế chương trình với sơ đồ
Trò chơi này phức tạp hơn một chút so với những trò chơi chúng ta đã thấy cho đến nay, vì vậy
hãy tham gia
Khoảnh khắc để suy nghĩ về cách nó kết hợp với nhau. Đầu tiên chúng ta sẽ tạo một biểu đồ
dòng chảy (giống như tại
kết thúc chương Dragon Realm) để giúp chúng tôi hình dung chương trình này sẽ làm gì. Một
biểu đồ dòng chảy là một sơ đồ hiển thị một loạt các bước như một số hộp được kết nối với
mũi tên. Mỗi hộp đại diện cho một bước và mũi tên chỉ ra cách một bước dẫn đến các bước khác.
Bạn có thể theo dõi thông qua biểu đồ luồng bằng cách đặt ngón tay vào hộp "Bắt đầu" của luồng
biểu đồ và theo các mũi tên đến các hộp khác cho đến khi bạn đến hộp "Kết thúc". Bạn chỉ có thể
di chuyển từ hộp này sang hộp khác theo hướng mũi tên. Bạn không bao giờ có thể đi ngược
(trừ khi có một mũi tên thứ hai quay trở lại, như trong "Người chơi đã đoán lá thư này"
ô bên dưới.) Đây là biểu đồ dòng hoàn chỉnh cho trò chơi Hangman (Hình 8-1).
Hình 8-1: Biểu đồ luồng hoàn chỉnh cho những gì xảy ra trong trò chơi Hangman.
Tất nhiên, chúng ta không phải tạo một biểu đồ dòng chảy. Chúng tôi chỉ có thể bắt đầu viết
mã. Nhưng
thông thường, một khi chúng ta bắt đầu lập trình, chúng ta sẽ nghĩ đến những thứ cần được thêm
hoặc thay đổi
92

Trang 107
mà chúng tôi đã không xem xét trước đây. Chúng tôi cuối cùng có thể phải thay đổi hoặc xóa rất
nhiều mã
mà chúng tôi đã viết, sẽ rất lãng phí công sức. Để tránh điều này, nó luôn luôn
tốt nhất nên suy nghĩ cẩn thận và lên kế hoạch chương trình sẽ hoạt động như thế nào trước khi
chúng ta bắt đầu viết nó.
Biểu đồ luồng sau đây được cung cấp như một ví dụ về biểu đồ luồng trông như thế nào và
làm thế nào để làm cho họ. Hiện tại, vì bạn chỉ đang sử dụng mã nguồn từ cuốn sách này, nên
bạn
không cần vẽ biểu đồ luồng trước khi viết mã. Chương trình đã được viết, vì vậy bạn
không phải lên kế hoạch gì cả Nhưng khi bạn tạo trò chơi của riêng mình, biểu đồ dòng chảy có
thể là
rất tiện dụng.
Tạo biểu đồ dòng chảy
Hãy nhớ rằng, biểu đồ dòng chảy của bạn không phải luôn trông giống hệt như biểu đồ
này. Miễn là
khi bạn hiểu biểu đồ dòng chảy bạn đã thực hiện, nó sẽ hữu ích khi bạn bắt đầu viết mã. Tốt
bắt đầu với biểu đồ luồng chỉ có hộp "Bắt đầu" và hộp "Kết thúc", như trong Hình 8-2:
Hình 8-2: Bắt đầu biểu đồ luồng của bạn với hộp Bắt đầu và Kết thúc.
93
8 - Biểu đồ dòng chảy

Trang 108
Bây giờ hãy nghĩ về những gì xảy ra khi chúng ta chơi Hangman. Đầu tiên, một người chơi (
máy tính trong trường hợp này) nghĩ về một từ bí mật. Sau đó, người chơi thứ hai (người đang
chạy
chương trình) sẽ đoán chữ cái. Hãy thêm các hộp cho các sự kiện này, như trong Hình 8-3.
(Các hộp mới đối với mỗi biểu đồ luồng có đường viền nét đứt xung quanh chúng.) Mũi tên
hiển thị thứ tự mà chương trình nên di chuyển. Đó là, đầu tiên chương trình sẽ xuất hiện
với một từ bí mật, và sau đó nó sẽ yêu cầu người chơi đoán một chữ cái.
Hình 8-3: Rút ra hai bước đầu tiên của Hangman dưới dạng các hộp có mô tả.
Nhưng trò chơi không kết thúc sau khi người chơi đoán một chữ cái. Nó cần kiểm tra xem nếu
lá thư đó có trong từ bí mật hay không.
94

Trang 109
Phân nhánh từ Hộp lưu đồ
Có hai khả năng: chữ cái sẽ có trong từ hoặc nó sẽ không có. Điều này có nghĩa là
chúng ta cần thêm hai hộp mới vào sơ đồ của mình. Từ "Yêu cầu người chơi đoán chữ cái"
hộp, chúng tôi chỉ có thể di chuyển đến hộp "Thư nằm trong từ bí mật" hoặc "Thư không bí mật
hộp "từ". Điều này sẽ tạo ra một nhánh (nghĩa là một phần tách) trong biểu đồ luồng, như trong
Hình 8-
4:
Hình 8-4: Có hai điều khác nhau có thể xảy ra sau
Người chơi đoán, do đó, có hai mũi tên đi đến các hộp riêng biệt.
95
8 - Biểu đồ dòng chảy

Trang 110
Nếu bức thư nằm trong từ bí mật, chúng ta cần kiểm tra xem người chơi đã đoán được chưa
các chữ cái, có nghĩa là họ đã thắng trò chơi. Nhưng, nếu bức thư không nằm trong từ bí mật,
một phần cơ thể khác được thêm vào người treo.
Chúng tôi có thể thêm hộp cho những trường hợp quá. Chúng tôi không cần một mũi tên từ "Thư
nằm trong
từ "hộp" đến hộp "Người chơi đã hết bộ phận cơ thể và mất", bởi vì đó là hộp
không thể mất miễn là bạn chỉ đoán đúng chữ cái. Ngoài ra, không thể
chiến thắng miễn là bạn chỉ đoán những chữ cái không chính xác, vì vậy chúng ta không cần
phải vẽ mũi tên đó
hoặc. Biểu đồ dòng chảy của chúng ta bây giờ trông giống như Hình 8-5.
Hình 8-5: Sau nhánh, các bước tiếp tục trên các đường dẫn riêng biệt của chúng.
96

Trang 111
Kết thúc hoặc khởi động lại trò chơi
Khi người chơi đã thắng hoặc thua, chúng tôi sẽ hỏi họ xem họ có muốn chơi lại với người mới
không
từ bí mật. Nếu người chơi không muốn chơi lại, chương trình sẽ kết thúc. Nếu chương trình
không kết thúc, chúng tôi nghĩ ra một từ bí mật mới, như trong Hình 8-6:
Hình 8-6: Trò chơi kết thúc nếu người chơi không muốn chơi lại hoặc trò chơi quay lại từ đầu.
97
8 - Biểu đồ dòng chảy

Trang 112
Đoán lại
Biểu đồ dòng chảy này có thể trông giống như nó đã kết thúc, nhưng có một thứ chúng ta đang
quên:
Người chơi không đoán một lá thư chỉ một lần. Họ phải liên tục đoán chữ cái
cho đến khi họ thắng hoặc thua Chúng ta cần vẽ hai mũi tên mới để biểu đồ luồng cho thấy điều
này,
như trong Hình 8-7.
Hình 8-7: Trò chơi không phải lúc nào cũng kết thúc sau khi đoán. Mũi tên mới (được phác thảo) cho thấy người chơi có thể
đoán
lần nữa.
98

Trang 113
Chúng tôi cũng đang quên một cái gì đó khác. Điều gì xảy ra nếu người chơi đoán một chữ cái mà họ
đã
đoán trước? Thay vì để họ thắng hay thua trong trường hợp này, chúng tôi sẽ cho phép họ đoán
thay vào đó, chữ cái khác, như trong Hình 8-8.
Hình 8-8: Thêm một bước trong trường hợp người chơi đoán một chữ cái mà họ đã đoán.
Cung cấp phản hồi cho người chơi
Chúng tôi cũng cần một số cách để cho người chơi thấy họ đang làm như thế nào. Để làm điều
này, chúng tôi sẽ
cho họ xem bảng treo, cũng như từ bí mật (có khoảng trống cho các chữ cái họ
chưa đoán được). Những hình ảnh này sẽ cho họ thấy mức độ gần với chiến thắng hoặc
thua trò chơi
Chúng tôi sẽ cần cập nhật thông tin này mỗi khi người chơi đoán một chữ cái. Chúng ta có thể
thêm
một "Hiển thị bảng và khoảng trống cho người chơi." ô vào biểu đồ dòng chảy giữa "Đi lên
với hộp từ "bí mật" và hộp "Yêu cầu người chơi đoán chữ cái", như trong Hình 8-9.
Hộp này sẽ nhắc nhở chúng ta rằng chúng ta cần cho người chơi thấy một bảng treo được cập
nhật để họ
có thể xem những chữ cái nào họ đã đoán đúng và những chữ cái nào không nằm trong bí mật
từ.
99
8 - Biểu đồ dòng chảy

Trang 114
Hình 8-9: Thêm "Hiển thị bảng và khoảng trống cho người chơi." để cung cấp cho người chơi phản hồi.
Trông tôt đây! Biểu đồ dòng chảy này hoàn toàn vạch ra mọi thứ có thể
xảy ra ở Hangman, và theo thứ tự nào. Tất nhiên biểu đồ luồng này chỉ là một ví dụ - bạn
sẽ không thực sự cần sử dụng nó, bởi vì bạn chỉ đang sử dụng mã nguồn được đưa ra ở
đây. Nhưng
Khi bạn thiết kế trò chơi của riêng mình, biểu đồ dòng chảy có thể giúp bạn nhớ mọi thứ bạn
cần mã.
Tóm tắt: Tầm quan trọng của việc lên kế hoạch
Trò chơi
Có vẻ như rất nhiều công việc để phác thảo một biểu đồ dòng chảy về chương trình đầu tiên. Sau
tất cả, mọi người muốn chơi trò chơi, không nhìn vào sơ đồ! Nhưng nó dễ làm hơn nhiều
thay đổi và nhận thấy vấn đề bằng cách suy nghĩ về cách chương trình hoạt động trước khi viết
mã cho nó.
Nếu bạn nhảy vào để viết mã trước, bạn có thể phát hiện ra các vấn đề yêu cầu bạn phải
100

Trang 115
thay đổi mã bạn đã viết. Mỗi khi bạn thay đổi mã, bạn đang dùng
một cơ hội mà bạn tạo ra lỗi bằng cách thay đổi quá ít hoặc quá nhiều. Tốt hơn là nên biết
những gì bạn muốn xây dựng trước khi bạn xây dựng nó.
101
8 - Biểu đồ dòng chảy

Trang 116
Các chủ đề được đề cập trong Chương này:
● Phương pháp
● Các append () phương pháp danh sách
● Các phương thức chuỗi dưới () và trên ()

● Các ngược () phương pháp danh sách

● Các split () phương pháp chuỗi

● Các phạm vi () chức năng

● Các danh sách () chức năng

● cho các vòng lặp

● báo cáo elif

● Các phương thức chuỗi startwith () và endswith () .

● Kiểu dữ liệu từ điển.

● cặp khóa-giá trị

● Các phương thức từ điển khóa () và giá trị ()

● Gán nhiều biến, chẳng hạn như a, b, c = [1, 2, 3]

Trò chơi này giới thiệu nhiều khái niệm mới. Nhưng đừng lo lắng; chúng tôi sẽ thử nghiệm với
khái niệm lập trình trong vỏ tương tác đầu tiên. Một số loại dữ liệu như chuỗi và
danh sách có các hàm được liên kết với các giá trị của chúng được gọi là các phương
thức. Chúng ta sẽ học
một số phương pháp khác nhau có thể thao tác chuỗi và danh sách cho chúng tôi. Chúng ta cũng
sẽ học
về một loại vòng lặp mới gọi là vòng lặp for và một loại dữ liệu mới gọi là từ điển.
Một khi bạn hiểu những khái niệm này, sẽ dễ dàng hơn để hiểu trò chơi trong này
chương: Hangman.
Bạn có thể tìm hiểu thêm từ Wikipedia: http://en.wikipedia.org/wiki/Hangman_(game)
102

Trang 117
Mã nguồn của Hangman
Trò chơi của chương này dài hơn một chút so với các trò chơi trước của chúng tôi. Bạn có thể
nhập
mã bên dưới trực tiếp vào trình chỉnh sửa tệp (mà tôi khuyên dùng) hoặc bạn có thể lấy mã
từ trang web của cuốn sách này. Để lấy mã từ web, trong trình duyệt web, hãy truy cập URL
http://inventwithpython.com/ch CHƯƠNG9 và làm theo hướng dẫn để tải xuống
mã nguồn.
hangman.py
Mã này có thể được tải xuống từ http://inventwithpython.com/hangman.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. nhập ngẫu nhiên
2. HÀNG NGÀY = ['' '
3.
4. + --- +
5. | |
6. |
7. |
8. |
9. |
10. ========= '' ',' ''
11.
12. + --- +
13. | |
14. O |
15. |
16. |
17. |
18. ========= '' ',' ''
19.
20. + --- +
21. | |
22. O |
23. | |
24. |
25. |
26. ========= '' ',' ''
27.
28. + --- +
29. | |
30. O |
31. / | |
32. |
33. |
34. ========= '' ',' ''
35.
36. + --- +
37. | |
38. O |
103
9 - Hangman

Trang 118
39. / | \ |
40. |
41. |
42. ========= '' ',' ''
43.
44. + --- +
45. | |
46. O |
47. / | \ |
48. / |
49. |
50. ========= '' ',' ''
51.
52. + --- +
53. | |
54. O |
55. / | \ |
56. / \ |
57. |
58. ========= '' ']
59. words = 'ant baboon badger bat bear hải ly mèo ngao mèo
rắn hổ mang báo sư tử quạ hươu nai lừa lừa vịt đại bàng
cáo ếch dê ngỗng diều hâu sư tử thằn lằn llama nốt ruồi khỉ
chuột nai mule newt rái cá cú gấu trúc vẹt chim bồ câu
thỏ ram chuột quạ tê giác cá hồi con cá mập cừu skunk
con rắn nhện con cò thiên nga hổ con cóc cá hồi
rùa chồn sói wombat ngựa vằn'.split ()
60.
61. def getRandomWord (wordList):
62. # Hàm này trả về một chuỗi ngẫu nhiên từ
thông qua danh sách các chuỗi.
63. wordIndex = Random.randint (0, len (wordList) - 1)
64. return wordList [word Index]
65.
66. def displayBoard (HANGMANPICS, missLetters,
đúngLetters, secretWord):
67. in (HANGMANPICS [len (missLetters)])
68. in ()
69.
70. in ('Chữ cái bị thiếu:', end = '')
71. cho thư trong missLetters:
72.
in (thư, kết thúc = '')
73. in ()
74.
75. khoảng trống = '_' * len (SecretWord)
76.
77. for i trong phạm vi (len (secretWord)): # thay thế khoảng trắng bằng
chữ đoán đúng
78.
if secretWord [i] trong đúngLetters:
79.
khoảng trống = khoảng trống [: i] + secretWord [i] + khoảng trống
[i + 1:]
80.
81. cho chữ cái trong khoảng trống: # hiển thị từ bí mật với
khoảng trắng ở giữa mỗi chữ cái
104

Trang 119
82.
in (thư, kết thúc = '')
83. in ()
84.
85. def getGuess (đã được bảo vệ):
86. # Trả về chữ cái mà người chơi đã nhập. Chức năng này
đảm bảo người chơi nhập một chữ cái duy nhất và không
thứ gì khác.
87. trong khi Đúng:
88.
in ('Đoán một chữ cái.')
89.
đoán = đầu vào ()
90.
đoán = đoán.lower ()
91.
nếu len (đoán)! = 1:
92.
in ('Vui lòng nhập một chữ cái.')
93.
elif đoán trong đã được bảo vệ:
94.
in ('Bạn đã đoán được bức thư đó.
Chọn lại. ')
95.
đoán nếu không trong 'abcdefghijklmnopqrstuvwxyz':
96.
in ('Vui lòng nhập BÀI.')
97.
khác:
98.
đoán lại
99.
100. def playAgain ():
101. # Hàm này trả về True nếu người chơi muốn
chơi lại, nếu không nó trả về Sai.
102. print ('Bạn có muốn chơi lại không? (Có hoặc không)')
103. return return (). Lower (). Startedwith ('y')
104.
105.
106. in ('HANGMA N')
107. missLetters = ''
108. đúngLetters = ''
109. secretWord = getRandomWord (từ)
110. gameIsDone = Sai
111.
112. trong khi Đúng:
113. displayBoard (HANGMANPICS, missLetters,
đúngLetters, secretWord)
114.
115. # Hãy để người chơi gõ một chữ cái.
116. đoán = getGuess (missLetters + đúngLetters)
117.
118. nếu đoán trong secretWord:
119.
đúngLetters = đúngLetters + đoán
120.
121.
# Kiểm tra xem người chơi đã thắng chưa
122.
Found ALLLetters = True
123.
cho tôi trong phạm vi (len (secretWord)):
124.
if secretWord [i] không đúng
125.
Found ALLLetters = Sai
126.
phá vỡ
127.
nếu tìm thấy AllLetters:
128.
in ('Có! Từ bí mật là "' + secretWord
+ '"! Bạn đã thắng!')
129.
gameIsDone = Đúng
105
9 - Hangman

Trang 120
130. khác:
131.
missLetters = missLetters + đoán
132.
133.
# Kiểm tra xem người chơi đã đoán quá nhiều lần chưa và
mất đi
134.
if len (missLetters) == len (HANGMANPICS) - 1:
135.
displayBoard (HANGMANPICS, missLetters,
đúngLetters, secretWord)
136.
in ('Bạn đã hết dự đoán! \ nSau đó' +
str (len (missLetters)) + 'đoán sai và' + str (len
(chính xác)) + 'đoán đúng, từ này là "' +
bí mậtWord + '"')
137.
gameIsDone = Đúng
138.
139. # Hỏi người chơi nếu họ muốn chơi lại (nhưng chỉ
nếu trò chơi được thực hiện).
140. nếu gameIsDone:
141.
nếu chơiAgain ():
142.
missLetters = ''
143.
đúngLetters = ''
144.
gameIsDone = Sai
145.
secretWord = getRandomWord (từ)
146.
khác:
147.
phá vỡ

Mã hoạt động như thế nào


1. nhập ngẫu nhiên
Chương trình Hangman sẽ chọn ngẫu nhiên một từ bí mật từ danh sách bí mật
từ ngữ. Điều này có nghĩa là chúng ta sẽ cần mô-đun ngẫu nhiên nhập khẩu.
2. HÀNG NGÀY = ['' '
3.
4. + --- +
5. | |
6. |
7. |
8. |
9. |
10. ========= '' ',' ''
... phần còn lại của mã quá lớn để hiển thị ở đây ...
"Dòng" mã này là một phép gán biến đơn giản, nhưng nó thực sự trải dài trên một số thực
các dòng trong mã nguồn. "Dòng" thực tế không kết thúc cho đến dòng 58. Để giúp bạn hiểu
106

Trang 121
Mã này có nghĩa là gì, bạn nên tìm hiểu về các chuỗi và danh sách nhiều dòng:
Chuỗi nhiều dòng
Thông thường khi bạn viết các chuỗi trong mã nguồn của mình, chuỗi phải nằm trên một dòng.
Tuy nhiên, nếu bạn sử dụng ba dấu ngoặc đơn thay vì một trích dẫn đơn để bắt đầu và kết thúc
chuỗi, chuỗi có thể trên một số dòng:
>>> fizz = '' 'Alice thân mến,
Tôi sẽ trở về nhà vào cuối tháng. tôi sẽ
gặp bạn sau.
Bạn của bạn,
Bob '' '
>>> in fizz
Alice thân yêu,
Tôi sẽ trở về nhà vào cuối tháng. tôi sẽ
gặp bạn sau.
Bạn của bạn,
Bob
>>>
Nếu chúng ta không có chuỗi nhiều dòng, chúng ta sẽ phải sử dụng ký tự thoát \ n để
đại diện cho các dòng mới. Nhưng điều đó có thể làm cho chuỗi khó đọc trong mã nguồn, như
trong
ví dụ này:
>>> fizz = 'Alice thân mến, \ nTôi sẽ trở về nhà tại
cuối tháng. Tôi sẽ gặp bạn sau đó. \
bạn, \ nBob '
>>> in fizz
Alice thân yêu,
Tôi sẽ trở về nhà vào cuối tháng. tôi sẽ
gặp bạn sau.
Bạn của bạn,
Bob
>>>
Các chuỗi nhiều dòng không phải giữ cùng một vết lõm để duy trì trong cùng một khối.
Trong chuỗi nhiều dòng, Python bỏ qua các quy tắc thụt lề mà nó thường có ở đâu
khối kết thúc.
def writeLetter ():
# bên trong khối def
in '' 'Alice thân mến,
107
9 - Hangman

Trang 122
Bạn khỏe không? Viết lại cho tôi sớm.
Trân trọng,
Bob '' '# cuối chuỗi nhiều dòng và in
tuyên bố
in 'PS tôi nhớ bạn.' # vẫn bên trong
khối chặn
writeLetter () # Đây là dòng đầu tiên bên ngoài
khối def.
Không biến đổi
Bạn có thể nhận thấy rằng tên của HANGMANPICS có trong tất cả các thủ đô. Đây là
quy ước lập trình cho các biến không đổi. Các hằng số là các biến có giá trị làm
không thay đổi trong suốt chương trình. Mặc dù chúng ta có thể thay
đổi HANGMANPICS giống như bất kỳ
biến khác, tất cả các mũ nhắc nhở lập trình viên không viết mã mà làm như vậy.
Các biến không đổi rất hữu ích cho việc cung cấp các mô tả cho các giá trị có đặc biệt
Ý nghĩa. Vì giá trị đa chuỗi không bao giờ thay đổi, không có lý do gì chúng tôi không thể sao
chép
chuỗi nhiều dòng này mỗi khi chúng ta cần giá trị đó. Biến HANGMANPICS không bao giờ
khác nhau Nhưng việc gõ HANGMANPICS ngắn hơn nhiều so với việc gõ nhiều dòng lớn đó
chuỗi.
Ngoài ra, có những trường hợp tự gõ giá trị có thể không rõ ràng. Nếu chúng ta đặt một
biến trứng = 72 , chúng ta có thể quên tại sao chúng ta lại đặt biến đó thành số nguyên 72 .
Nhưng nếu chúng ta xác định một biến không đổi DOZEN = 12 , thì chúng ta có thể đặt trứng =
DOZEN *
6 và chỉ cần nhìn vào mã biết rằng biến trứng đã được đặt thành sáu chục.
Giống như tất cả các quy ước, chúng ta không phải sử dụng các biến liên tục hoặc thậm chí đặt
tên của
biến không đổi trong tất cả các thủ đô. Nhưng làm theo cách này sẽ giúp người khác dễ dàng hơn
lập trình viên để hiểu làm thế nào các biến này được sử dụng. (Nó thậm chí có thể giúp bạn nếu
bạn là
nhìn vào mã bạn đã viết từ lâu.)
Danh sách
Bây giờ tôi sẽ cho bạn biết về một loại dữ liệu mới được gọi là danh sách . Một giá trị danh sách
có thể chứa một số
các giá trị khác trong đó. Hãy thử gõ cái này vào vỏ: ['táo', 'cam', 'HELLO
THẾ GIỚI '] . Đây là một giá trị danh sách có chứa ba giá trị chuỗi. Cũng giống như bất kỳ giá
trị nào khác,
bạn có thể lưu trữ danh sách này trong một biến. Hãy thử gõ spam = ['táo', 'cam',
'HELLO WORLD'] , sau đó nhập thư rác để xem nội dung của thư rác .
>>> spam = ['táo', 'cam', 'HELLO WORLD']
>>> thư rác
108

Trang 123
['Táo', 'cam', 'HELLO WORLD']
>>>
Danh sách là một cách tốt để lưu trữ một số giá trị khác nhau vào một biến. Cá nhân
các giá trị bên trong danh sách cũng được gọi là các mục . Hãy thử gõ: động vật = ['aardvark',
'anteater', 'linh dương', 'albert'] để lưu trữ các chuỗi khác nhau vào biến
động vật . Dấu ngoặc vuông cũng có thể được sử dụng để lấy một mục từ danh sách. Thử gõ
động vật [0] , hoặc động vật [1] , hoặc động vật [2] , hoặc động vật [3] vào vỏ để xem
những gì họ đánh giá.
>>> động vật = ['aardvark', 'anteater', 'linh dương',
'albert']
>>> động vật [0]
'aardvark'
>>> động vật [1]
'Thú ăn kiến'
>>> động vật [2]
'con linh dương'
>>> động vật [3]
'albert'
>>>
Số giữa các dấu ngoặc vuông là chỉ mục . Trong Python, chỉ mục đầu tiên là
số 0 thay vì số 1. Vì vậy, mục đầu tiên trong danh sách nằm ở chỉ số 0, mục thứ hai
là ở chỉ số 1, mục thứ ba là ở chỉ số 2, v.v. Danh sách rất tốt khi chúng ta phải
lưu trữ rất nhiều và rất nhiều giá trị, nhưng chúng tôi không muốn các biến cho từng cái. Nếu
không chúng tôi sẽ
có cái gì đó như thế này:
>>> động vật1 = 'aardvark'
>>> động vật2 = 'thú ăn kiến'
>>> động vật3 = 'linh dương'
>>> động vật4 = 'albert'
>>>
Điều này làm cho việc làm việc với tất cả các chuỗi như một nhóm rất khó khăn, đặc biệt là nếu
bạn có
Hàng trăm hoặc hàng ngàn (thậm chí hàng triệu) chuỗi khác nhau mà bạn muốn được lưu trữ
trong danh sách.
Sử dụng dấu ngoặc vuông, bạn có thể coi các mục trong danh sách giống như bất kỳ giá trị nào
khác. Thử
gõ động vật [0] + động vật [2] vào vỏ:
>>> động vật [0] + động vật [2]
'aardvarkantelope'
>>>
109
9 - Hangman

Trang 124
Bởi vì động vật [0] ước tính theo chuỗi 'aardvark' và động vật [2]
ước tính theo chuỗi 'linh dương' , sau đó các động vật biểu hiện [0] + động vật
[2] giống như 'aardvark' + 'linh dương' . Chuỗi kết nối này đánh giá
để 'aardvarkantelope' .
Điều gì xảy ra nếu chúng ta nhập một chỉ mục lớn hơn chỉ mục lớn nhất của danh sách? Thử gõ
động vật [4] hoặc động vật [99] vào vỏ:
>>> động vật = ['aardvark', 'anteater', 'linh dương',
'albert']
>>> động vật [4]
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "", dòng 1, trong
động vật [4]
IndexError: liệt kê chỉ mục ngoài phạm vi
>>> động vật [99]
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "", dòng 1, trong
động vật [99]
IndexError: liệt kê chỉ mục ngoài phạm vi
>>>
Nếu bạn thử truy cập một chỉ mục quá lớn, bạn sẽ gặp lỗi chỉ mục .
Thay đổi giá trị của các mục danh sách với chỉ mục
Bài tập
Bạn cũng có thể sử dụng dấu ngoặc vuông để thay đổi giá trị của một mục trong danh sách. Thử

động vật [1] = 'ANTEATER' , sau đó nhập động vật để xem danh sách.
>>> động vật = ['aardvark', 'anteater', 'linh dương',
'albert']
>>> động vật [1] = 'ANTEATER'
>>> động vật
['aardvark', 'ANTEATER', 'linh dương', 'albert']
>>>
Mục thứ hai trong danh sách động vật đã được ghi đè bằng một chuỗi mới.
Danh sách kết nối
Bạn có thể nối các danh sách lại với nhau thành một danh sách với toán tử + , giống như bạn có
thể nối các chuỗi.
110

Trang 125
Khi tham gia danh sách, điều này được gọi là nối danh sách . Hãy thử gõ [1, 2, 3, 4]
+ ['táo', 'cam'] + ['Alice', 'Bob'] vào vỏ:
>>> [1, 2, 3, 4] + ['táo', 'cam'] +
['Alice', 'Bob']
[1, 2, 3, 4, 'táo', 'cam', 'Alice', 'Bob']
>>>
Lưu ý rằng danh sách không phải lưu trữ các giá trị của cùng loại dữ liệu. Ví dụ trên
có một danh sách với cả số nguyên và chuỗi trong đó.
Các trong điều hành
Các trong điều hành làm cho nó dễ dàng để xem nếu một giá trị bên trong một danh sách hay
không. Biểu thức sử dụng
các trong điều hành trả về một giá trị boolean: Đúng nếu giá trị là trong danh sách và False nếu
giá trị không có trong danh sách. Hãy thử gõ 'linh dương' ở động vật vào vỏ:
>>> động vật = ['aardvark', 'anteater', 'linh dương',
'albert']
>>> 'linh dương' ở động vật
Thật
>>>
Biểu thức 'linh dương' ở động vật trả về Đúng vì chuỗi
"Linh dương" có thể được tìm thấy trong danh sách, động vật . (Nó nằm ở chỉ số 2.)
Nhưng nếu chúng ta gõ biểu thức 'con kiến' ở động vật , điều này sẽ trả về Sai vì
chuỗi 'ant' không tồn tại trong danh sách. Chúng ta có thể thử biểu thức 'ant' trong
['Bọ cánh cứng', 'wasp', 'ant'] và thấy rằng nó sẽ trả về True .
>>> động vật = ['aardvark', 'anteater', 'linh dương',
'albert']
>>> 'linh dương' ở động vật
Thật
>>> 'kiến' ở động vật
Sai
>>> 'kiến' trong ['bọ cánh cứng', 'ong', 'kiến']
Thật
>>>
Các trong điều hành cũng làm việc cho chuỗi cũng như danh sách. Bạn có thể kiểm tra nếu một
chuỗi tồn tại
theo cách khác giống như cách bạn có thể kiểm tra nếu một giá trị tồn tại trong danh sách. Hãy
thử gõ 'xin chào' vào
111
9 - Hangman

Trang 126
'Alice nói xin chào với Bob.' vào vỏ. Biểu thức này sẽ đánh giá
Đúng .
>>> 'xin chào' trong 'Alice nói xin chào với Bob.'
Thật
>>>
Xóa các mục khỏi danh sách với del Statements
Bạn có thể xóa các mục khỏi danh sách bằng một câu lệnh del . ("del" là viết tắt của "xóa.") Hãy
thử
tạo danh sách các số bằng cách gõ: spam = [2, 4, 6, 8, 10] và sau đó del
thư rác [1] . Nhập thư rác để xem nội dung của danh sách:
>>> thư rác = [2, 4, 6, 8, 10]
>>> del spam [1]
>>> thư rác
[2, 6, 8, 10]
>>>
Lưu ý rằng khi bạn xóa mục ở chỉ mục 1, mục đã từng ở chỉ mục 2
trở thành chỉ mục mới 1. Mục đã từng ở chỉ số 3 được chuyển thành chỉ mục mới 2.
Tất cả mọi thứ ở trên mục mà chúng tôi đã xóa di chuyển xuống một chỉ mục. Chúng ta có thể
gõ del
spam [1] nhiều lần để tiếp tục xóa các mục khỏi danh sách:
>>> thư rác = [2, 4, 6, 8, 10]
>>> del spam [1]
>>> thư rác
[2, 6, 8, 10]
>>> del spam [1]
>>> thư rác
[2, 8, 10]
>>> del spam [1]
>>> thư rác
[2, 10]
>>>
Chỉ cần nhớ rằng del là một câu lệnh, không phải là một hàm hoặc một toán tử. Nó không đánh
giá
cho bất kỳ giá trị trả lại.
112

Trang 127
Danh sách danh sách
Danh sách là loại dữ liệu có thể chứa các giá trị khác dưới dạng các mục trong danh sách. Nhưng
những mặt hàng này có thể
cũng là danh sách khác. Giả sử bạn có một danh sách các cửa hàng tạp hóa, một danh sách các
công việc và một danh sách các
bánh nướng yêu thích. Bạn có thể đặt cả ba danh sách này vào một danh sách khác. Hãy thử gõ
nó vào
vỏ:
>>> hàng tạp hóa = ['trứng', 'sữa', 'súp', 'táo',
'bánh mỳ']
>>> việc vặt = ['dọn dẹp', 'cắt cỏ', 'đi chợ
mua sắm']
>>> FavoritesPies = ['táo', 'frumbleberry']
>>> listOfLists = [tạp hóa, công việc,
yêu thích]
>>> listOfLists
[['Trứng', 'sữa', 'súp', 'táo', 'bánh mì'],
['Dọn dẹp', 'cắt cỏ', 'đi mua sắm hàng tạp hóa'],
['Táo', 'frumbleberry']]
>>>
Bạn cũng có thể nhập các mục sau và nhận cùng một giá trị cho cả bốn biến:
>>> listOfLists = [['trứng', 'sữa', 'súp',
'Táo', 'bánh mì'], ['sạch', 'cắt cỏ', 'đi
mua sắm thực phẩm '], [' táo ',' frumbleberry ']]
>>> tạp hóa = listOfLists [0]
>>> công việc = listOfLists [1]
>>> FavoritesPies = listOfLists [2]
>>> tạp hóa
['Trứng', 'sữa', 'súp', 'táo', 'bánh mì']
>>> việc vặt
['Dọn dẹp', 'cắt cỏ', 'đi mua sắm hàng tạp hóa']
>>> yêu thích
['Táo', 'frumbleberry']
>>>
Để có được một mục trong danh sách các danh sách, bạn sẽ sử dụng hai bộ dấu ngoặc vuông như
thế này:
listOfLists [1] [2] sẽ đánh giá chuỗi 'đi tạp hóa
mua sắm ' . Điều này là do listOfLists [1] ước tính cho danh sách ['sạch',
'cắt cỏ', 'đi mua sắm hàng tạp hóa'] [2] . Điều đó cuối cùng đánh giá
"Đi mua sắm hàng tạp hóa" .
Dưới đây là một ví dụ khác về danh sách các danh sách, cùng với một số chỉ mục trỏ đến
các mục trong danh sách các danh sách có tên x . Mũi tên đỏ chỉ vào các chỉ mục của danh sách
bên trong
113
9 - Hangman

Trang 128
chính họ. Hình ảnh cũng được lật sang một bên để dễ đọc hơn:
Hình 9-1: Các chỉ mục của một danh sách các danh sách.

Phương pháp
Các phương thức cũng giống như các hàm, nhưng chúng luôn được gắn vào một giá trị. Ví dụ,
tất cả các giá trị chuỗi có phương thức () thấp hơn , trả về một bản sao của giá trị chuỗi trong
chữ thường. Bạn không thể tự gọi hàm dưới () và bạn không truyền đối số chuỗi
để hạ () của chính nó (như ở dưới ('Xin chào') ). Bạn phải đính kèm cuộc gọi phương thức vào
một
giá trị chuỗi cụ thể bằng cách sử dụng một khoảng thời gian.
Các phương thức chuỗi dưới () và trên ()
Hãy thử nhập 'Hello world!'. Lower () vào trình vỏ tương tác để xem ví dụ
của phương pháp này:
>>> 'Xin chào world'.lower ()
'Chào thế giới!'
>>>
Ngoài ra còn có một phương thức trên () cho các chuỗi, thay đổi tất cả các ký tự trong một
chuỗi để viết hoa. Hãy thử nhập 'Hello world'.upper () vào trình bao:
114

Trang 129
>>> 'Xin chào world'.upper ()
'CHÀO THẾ GIỚI! '
>>>
Vì phương thức Upper () trả về một chuỗi, bạn có thể gọi một phương thức trên chuỗi đó là
tốt. Hãy thử gõ 'Hello world!'. Upper (). Lower () vào shell:
>>> 'Xin chào world'.upper (). Lower ()
'Chào thế giới!'
>>>
'Xin chào thế giới!'. Upper () đánh giá chuỗi 'HELLO WORLD!' , và sau đó
chúng ta gọi phương thức low () của chuỗi đó . Điều này trả về chuỗi 'xin chào thế giới!' , mà
là giá trị cuối cùng trong đánh giá. Thứ tự là quan trọng. "Xin chào thế giới!". Thấp hơn
() .upper () không giống với 'Xin chào thế giới!'. Upper (). Lower () :
>>> 'Xin chào world'.lower (). Upper ()
'CHÀO THẾ GIỚI!'
>>>
Hãy nhớ rằng, nếu một chuỗi được lưu trữ trong một biến, bạn có thể gọi một phương thức chuỗi
trên đó
Biến đổi. Nhìn vào ví dụ này:
>>> fizz = 'Xin chào thế giới'
>>> fizz.upper ()
'CHÀO THẾ GIỚI'
>>>
Các phương thức liệt kê ngược () và append ()
Kiểu dữ liệu danh sách cũng có phương thức. Phương thức Reverse () sẽ đảo ngược thứ tự của
các mục trong danh sách. Hãy thử nhập spam = [1, 2, 3, 4, 5, 6, 'meo',
'giả mạo'] , rồi spam.reverse () để đảo ngược danh sách. Sau đó nhập thư rác để xem
nội dung của biến.
>>> thư rác = [1, 2, 3, 4, 5, 6, 'meo', 'giả mạo']
>>> spam.reverse ()
>>> thư rác
['giả mạo', 'meo', 6, 5, 4, 3, 2, 1]
115
9 - Hangman

Trang 130
>>>
Phương thức danh sách phổ biến nhất bạn sẽ sử dụng là append () . Phương pháp này sẽ thêm
giá trị bạn vượt qua như một đối số đến cuối danh sách. Hãy thử gõ như sau vào shell:
>>> trứng = []
>>> trứng.append ('thủy phi cơ')
>>> trứng
['thủy phi cơ']
>>> trứng.append ('lươn')
>>> trứng
['thủy phi cơ', 'lươn']
>>> trứng.append (42)
>>> trứng
['thủy phi cơ', 'lươn', 42]
>>>
Mặc dù kiểu dữ liệu chuỗi và danh sách có phương thức, số nguyên không xảy ra
phương pháp.
Sự khác biệt giữa phương thức và chức năng
Bạn có thể tự hỏi tại sao Python có các phương thức, vì chúng dường như hoạt động giống như
chức năng. Một số loại dữ liệu có phương pháp. Phương thức là các hàm được liên kết với các
giá trị của
kiểu dữ liệu đó. Ví dụ, các phương thức chuỗi là các hàm có thể được gọi trên bất kỳ chuỗi
nào. Nếu
bạn có giá trị chuỗi 'Xin chào' , bạn có thể gọi phương thức chuỗi là Upper () như thế này:
'Xin chào'.upper () . Hoặc nếu chuỗi 'Xin chào' được lưu trữ trong một biến có tên là thư rác ,
thì chuỗi đó
sẽ trông như thế này: spam.upper ()
Bạn không thể gọi các phương thức chuỗi trên các giá trị của các loại dữ liệu khác. Ví dụ: [1, 2,
'apple']. Upper () sẽ gây ra lỗi vì [1, 2, 'apple'] là một danh sách và
trên () là một phương thức chuỗi.
Các giá trị của các kiểu dữ liệu có các phương thức cũng được gọi là các đối tượng. Hướng đối
tượng
lập trình là một chút nâng cao cho cuốn sách này và bạn không cần phải hiểu hoàn toàn
nó để làm game Chỉ cần hiểu rằng các đối tượng là một tên khác cho các giá trị của các kiểu dữ
liệu
có phương pháp.
Các split () Danh sách Phương
Dòng 59 là một dòng mã rất dài, nhưng nó thực sự chỉ là một câu lệnh gán đơn giản.
Dòng này cũng sử dụng phương thức split () , đây là một phương thức cho kiểu dữ liệu chuỗi
(chỉ
như các phương thức dưới () và trên () ).
116

Trang 131
59. words = 'ant baboon badger bat bear hải ly mèo ngao mèo
rắn hổ mang báo sư tử quạ
chồn cáo ếch ếch dê ngỗng diều hâu sư tử thằn lằn llama
khỉ moose chuột mule newt rái cá cú vẹt gấu trúc
pigeon python thỏ ram chuột quạ tê giác cá hồi
cá mập cừu chồn hôi con rắn nhện cò thiên nga
cóc cá hồi rùa chồn cá voi sói wombat
ngựa vằn'.split ()
Như bạn có thể thấy, dòng này chỉ là một chuỗi rất dài, chứa đầy các từ được phân tách bằng dấu
cách.
Và ở cuối chuỗi, chúng ta gọi phương thức split () . Phương thức split () thay đổi
chuỗi dài này vào một danh sách, với mỗi từ tạo thành một mục danh sách. "Tách" xảy ra
bất cứ nơi nào một không gian xảy ra trong chuỗi. Lý do chúng tôi làm theo cách này, thay vì chỉ
viết
trong danh sách, là chúng ta dễ dàng nhập một chuỗi dài hơn. Nếu chúng ta tạo nó như một danh
sách
bắt đầu bằng, chúng ta sẽ phải gõ: ['ant', 'baboon', 'badger', ... và cứ thế,
với dấu ngoặc kép và dấu phẩy cho mỗi từ đơn.
Để biết ví dụ về cách hoạt động của phương thức chuỗi split () , hãy thử gõ hàm này vào shell:
>>> 'Người mẹ rất năng động của tôi chỉ phục vụ chúng tôi chín
bánh nướng'.split ()
['Của tôi', 'rất', 'tràn đầy năng lượng', 'mẹ', 'chỉ',
'Phục vụ', 'chúng tôi', 'chín', 'bánh nướng']
>>>
Kết quả là một danh sách gồm chín chuỗi, một chuỗi cho mỗi từ trong chuỗi gốc.
Các khoảng trắng được thả từ các mục trong danh sách. Khi chúng ta đã gọi split () , các từ
danh sách sẽ chứa tất cả các từ bí mật có thể được máy tính chọn cho chúng tôi
Trò chơi Hangman. Bạn cũng có thể thêm các từ của riêng bạn vào chuỗi hoặc xóa bất kỳ từ nào
bạn không
muốn được tham gia trò chơi Chỉ cần chắc chắn rằng các từ được phân cách bằng khoảng trắng.
Mã hoạt động như thế nào
Bắt đầu từ dòng 61, chúng tôi xác định một hàm mới gọi là getRandomWord () , có một
tham số duy nhất có tên là wordList . Chúng tôi sẽ gọi chức năng này khi chúng tôi muốn chọn
một
từ bí mật duy nhất từ một danh sách các từ bí mật.
61. def getRandomWord (wordList):
62. # Hàm này trả về một chuỗi ngẫu nhiên từ
thông qua danh sách các chuỗi.
63. wordIndex = Random.randint (0, len (wordList) - 1)
64. return wordList [word Index]
117
9 - Hangman

Trang 132
Hàm getRandomWord () được truyền một danh sách các chuỗi làm đối số cho
tham số wordList . Trên dòng 63, chúng tôi sẽ lưu trữ một chỉ mục ngẫu nhiên trong danh sách
này trong
biến word Index . Chúng tôi làm điều này bằng cách gọi randint () với hai đối số. Nhớ lại
các đối số trong một lệnh gọi hàm được phân tách bằng dấu phẩy, vì vậy đối số đầu tiên là 0 và
đối số thứ hai là len (wordList) - 1 . Đối số thứ hai là một biểu thức
đó là đánh giá đầu tiên. len (wordList) sẽ trả về kích thước nguyên của danh sách được truyền
cho
getRandomWord () , trừ một.
Lý do chúng tôi cần - 1 là vì các chỉ mục cho danh sách bắt đầu từ 0, không phải 1. Nếu chúng
tôi có
danh sách ba mục, chỉ mục của mục đầu tiên là 0, chỉ mục của mục thứ hai là 1,
chỉ số của mục thứ ba là 2. Độ dài của danh sách này là 3, nhưng chỉ số 3 nằm sau chỉ mục cuối
cùng.
Đây là lý do tại sao chúng tôi trừ 1 từ chiều dài.
Ví dụ: nếu chúng tôi chuyển ['apple', 'cam', nho '] làm đối số cho getRandomWord
() , sau đó len (wordList) sẽ trả về số nguyên 3 và biểu thức 3 - 1 sẽ đánh giá
đến số nguyên 2.
Điều đó có nghĩa là wordIndex sẽ chứa giá trị trả về của randint (0, 2), có nghĩa là
wordIndex sẽ bằng 0, 1 hoặc 2. Trên dòng 64, chúng tôi sẽ trả về phần tử trong wordList tại
chỉ số nguyên được lưu trữ trong word Index.
Hãy giả vờ rằng chúng tôi đã gửi ['táo', 'cam', nho '] làm đối số cho
getRandomWord () và randint (0, 2) đã trả về số nguyên 2 . Mà có
có nghĩa là dòng 64 sẽ trở thành wordList trở lại [2] , sẽ đánh giá thành
trả lại 'nho' . Đây là cách getRandomWord () trả về một chuỗi ngẫu nhiên trong
danh sách wordList . Đoạn mã sau được nhập vào trình bao tương tác thể hiện điều này:
>>> nhập ngẫu nhiên
>>> in (word Index)
2
>>> in (['táo', 'cam', 'nho'] [word Index])
giống nho
>>>
Và hãy nhớ rằng, chúng ta có thể chuyển bất kỳ danh sách các chuỗi mà chúng ta muốn
tới getRandomWord ()
chức năng, đó là những gì làm cho nó rất hữu ích cho trò chơi Hangman của chúng tôi.
Hiển thị bảng cho người chơi
Tiếp theo chúng ta cần tạo một chức năng khác sẽ in bảng hangman trên
màn hình, cùng với bao nhiêu chữ cái mà người chơi đã đoán đúng (và không chính xác).
66. def displayBoard (HANGMANPICS, missLetters,
đúngLetters, secretWord):
118
Trang 133
67. in (HANGMANPICS [len (missLetters)])
68. in ()
Mã này xác định một hàm mới có tên displayBoard () . Hàm này có bốn
thông số. Hàm này sẽ triển khai mã cho "Hiển thị bảng và khoảng trống cho
hộp "người chơi" trong biểu đồ luồng của chúng tôi. Dưới đây là ý nghĩa của từng tham số:
● HANGMANPICS - Đây là danh sách các chuỗi nhiều dòng sẽ hiển thị bảng dưới dạng

Nghệ thuật ASCII. Chúng tôi sẽ luôn vượt qua biến HANGMANPICS toàn cầu làm đối số


cho tham số này.
● missLetters - Đây là một chuỗi được tạo thành từ các chữ cái mà người chơi đã đoán rằng

không có trong từ bí mật.


● đúngLetters - Đây là một chuỗi được tạo thành từ các chữ cái mà người chơi đã đoán

đó là trong từ bí mật.
● secretWord - Chuỗi này là từ bí mật mà người chơi đang cố đoán ..

Cuộc gọi hàm print () đầu tiên sẽ hiển thị bảng. HANGMANPICS sẽ là một danh sách
chuỗi cho mỗi bảng có thể. HANGMANPICS [0] cho thấy một giá treo cổ trống rỗng,
HANGMANPICS [1] hiển thị phần đầu (điều này xảy ra khi người chơi bỏ lỡ một chữ cái),
HANGMANPICS [2] cho thấy một cái đầu và cơ thể (điều này xảy ra khi người chơi bỏ lỡ hai
các chữ cái), và cứ thế cho đến HANGMANPICS [6] khi hiển thị toàn bộ hangman và trình phát
thua cuộc
Số lượng chữ cái trong missLetters sẽ cho chúng ta biết có bao nhiêu lần đoán sai
người chơi đã thực hiện. Chúng ta có thể gọi len (missLetters) để tìm ra số này. Điều này
số cũng có thể được sử dụng làm chỉ mục cho danh sách HANGMANPICS , cho phép chúng tôi
in
bảng đúng cho số lần đoán không chính xác. Vì vậy, nếu missLetters là 'aetr'
sau đó len ('aetr') sẽ trả về 4 và chúng tôi sẽ hiển thị chuỗi HANGMANPICS [4] . Điều này
là những gì HANGMANPICS [len (missLetters)] đánh giá. Dòng này hiển thị
bảng treo chính xác cho người chơi.
70. in ('Chữ cái bị thiếu:', end = '')
71. cho thư trong missLetters:
72.
in (thư, kết thúc = '')
73. in ()
Dòng 71 là một loại vòng lặp mới, được gọi là vòng lặp for . Một cho vòng lặp là loại giống như
một thời gian
vòng. Dòng 72 là toàn bộ thân vòng lặp for . Hàm phạm vi () thường được sử dụng với
cho các vòng lặp. Tôi sẽ giải thích cả hai trong hai phần tiếp theo.
Hãy nhớ rằng đối số từ khóa end = '' chỉ sử dụng một = dấu, không phải hai.
119
9 - Hangman

Trang 134
Các phạm vi () và list () Chức năng
Hàm phạm vi () rất dễ hiểu. Bạn có thể gọi nó bằng một hoặc hai
đối số nguyên. Khi được gọi với một đối số, phạm vi () sẽ trả về một đối tượng phạm vi
số nguyên từ 0 đến (nhưng không bao gồm) đối số. Đối tượng phạm vi này có thể được chuyển
đổi
đến kiểu dữ liệu danh sách quen thuộc hơn với hàm list () . Hãy thử gõ danh sách (phạm vi
(10)) vào vỏ:
>>> danh sách (phạm vi (10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
Hàm list () rất giống với các hàm str () hoặc int () . Nó chỉ chuyển đổi
đối tượng nó được truyền vào một danh sách. Rất dễ dàng để tạo các danh sách lớn với phạm vi
()
chức năng. Hãy thử gõ trong danh sách (phạm vi (10000)) vào trình bao:
>>> danh sách (phạm vi (10000))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, ...
... Văn bản ở đây đã bị bỏ qua cho
ngắn gọn ...
... 9989, 9990, 9991, 9992, 9993, 9994, 9995, 9996,
9997, 9998, 9999]
>>>
Danh sách này rất lớn, thậm chí nó sẽ không vừa với màn hình. Nhưng chúng ta có thể lưu danh
sách vào
biến giống như bất kỳ danh sách khác bằng cách nhập này:
>>> thư rác = danh sách (phạm vi (10000))
>>>
Nếu bạn chuyển hai đối số cho phạm vi () , danh sách các số nguyên mà nó trả về là từ đầu tiên
đối số lên đến (nhưng không bao gồm) đối số thứ hai. Hãy thử gõ danh sách (phạm vi (10,
20)) vào vỏ:
>>> danh sách (phạm vi (10, 20))
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>>
Các phạm vi () là một chức năng rất hữu ích, bởi vì chúng ta thường sử dụng nó trong cho vòng
(là
120

Trang 135
giống như các vòng lặp trong khi chúng ta đã thấy).
cho các vòng lặp
Các cho vòng lặp là rất tốt tại vòng lặp trên một danh sách các giá trị. Điều này khác với
trong khi vòng lặp, mà vòng chừng như một điều kiện nhất định là đúng. Một cho tuyên bố bắt
đầu
với từ khóa for , theo sau là một biến, theo sau là từ khóa in , theo sau là một
chuỗi (chẳng hạn như danh sách hoặc chuỗi) hoặc đối tượng phạm vi (được trả về bởi hàm phạm
vi () ),
và sau đó là một dấu hai chấm. Mỗi lần thực hiện chương trình đi qua vòng lặp (nghĩa là trên mỗi
Lặp lại qua vòng lặp) biến trong câu lệnh for nhận giá trị của lần tiếp theo
mục trong danh sách.
Ví dụ, bạn vừa biết rằng hàm phạm vi () sẽ trả về một danh sách các số nguyên.
Chúng tôi sẽ sử dụng danh sách này làm danh sách cho tuyên bố. Trong shell, gõ cho i trong
phạm vi
(10): và nhấn Enter. Sẽ không có gì xảy ra, nhưng cái vỏ sẽ thụt vào con trỏ, bởi vì nó
đang chờ bạn gõ vào khối for. Loại in (i) và nhấn Enter. Sau đó, để nói
shell tương tác bạn đã nhập xong vào khối for, nhấn Enter lần nữa để nhập khoảng trống
hàng. Shell sau đó sẽ thực thi câu lệnh for và khối của bạn:
>>> cho tôi trong phạm vi (10):
... in (i)
...
0
1
2
3
4
5
6
7
số 8
9
>>>
Chú ý rằng với cho vòng, bạn không cần phải chuyển đổi các đối tượng phạm vi được trả về bởi
các
Hàm phạm vi () vào một danh sách với danh sách () . Đối với các vòng lặp làm điều này cho
chúng tôi tự động.
Các cho vòng lặp thực thi mã bên trong cho khối một lần cho mỗi mục trong danh sách. Mỗi
Thời gian nó thực thi mã trong khối for, biến i được gán giá trị tiếp theo của
mục tiếp theo trong danh sách. Nếu chúng ta sử dụng câu lệnh for với danh sách [0, 1, 2, 3, 4, 5,
6, 7, 8, 9] thay vì phạm vi (10) , nó sẽ giống nhau vì phạm vi ()
giá trị trả về của hàm giống như danh sách đó:
121
9 - Hangman

Trang 136
>>> cho tôi trong phạm vi ([0, 1, 2, 3, 4, 5, 6, 7, 8,
9]):
... in (i)
...
0
1
2
3
4
5
6
7
số 8
9
>>>
Hãy thử nhập cái này vào vỏ: để biết điều gì trong ['mèo', 'mì ống',
'lập trình', 'spam']: và nhấn Enter, sau đó nhập print ('Tôi thực sự thích
'+ điều) và nhấn Enter, sau đó nhấn Enter lần nữa để báo cho shell kết thúc for-
khối. Đầu ra sẽ trông như thế này:
>>> cho điều trong ['mèo', 'mì ống', 'lập trình',
'Thư rác']:
... In ('Tôi thực sự thích' + điều)
...
Tôi rất thích mèo
Tôi rất thích mì ống
Tôi rất thích lập trình
Tôi rất thích thư rác
>>
Và hãy nhớ, vì các chuỗi cũng là một kiểu dữ liệu chuỗi giống như danh sách, bạn có thể sử
dụng
chúng trong cho báo cáo là tốt. Ví dụ này sử dụng một ký tự từ chuỗi trên
mỗi lần lặp:
>>> cho tôi trong 'Xin chào thế giới!':
... in (i)
...
H
e
tôi
tôi
122

Trang 137
o
w
o
r
tôi
d
!
>>>
Một khi vòng tương đương của một cho Vòng
Các cho vòng lặp là rất giống với khi vòng lặp, nhưng khi bạn chỉ cần lặp qua
các mục trong danh sách, sử dụng vòng lặp for sẽ nhập ít mã hơn nhiều. Bạn có thể làm cho
một trong khi vòng lặp
hoạt động tương tự như vòng lặp for bằng cách thêm mã bổ sung:
>>> trình tự = ['mèo', 'mì ống', 'lập trình',
'Thư rác']
>>> chỉ số = 0
>>> while (chỉ mục <len (chuỗi)):
... điều = chuỗi [chỉ mục]
... In ('Tôi thực sự thích' + điều)
... chỉ mục = chỉ số + 1
...
Tôi rất thích mèo
Tôi rất thích mì ống
Tôi rất thích lập trình
Tôi rất thích thư rác
>>>
Nhưng sử dụng câu lệnh for sẽ tự động thực hiện tất cả mã bổ sung này cho chúng tôi và thực
hiện
lập trình dễ dàng hơn vì chúng ta có ít loại. Trò chơi Hangman của chúng tôi sẽ sử dụng cho các
vòng lặp vì vậy
bạn có thể thấy chúng hữu ích như thế nào trong các trò chơi thực sự.
Một điều nữa về cho vòng, là cho tuyên bố có trong từ khóa trong đó. Nhưng
khi bạn sử dụng trong từ khóa trong một cho tuyên bố, Python không đối xử với nó như trong
toán tử bạn sẽ sử dụng trong một cái gì đó như 42 trong [0, 42, 67] . Các trong từ khóa trong
cho các câu lệnh chỉ được sử dụng để phân tách biến và danh sách nó nhận các giá trị của nó.
Phần còn lại của hàm displayBoard () hiển thị các chữ cái bị bỏ lỡ và tạo
chuỗi của từ bí mật với tất cả các chữ cái vô duyên như khoảng trống.
in ('Chữ cái bị thiếu:', end = '')
cho thư trong missLetters:
123
9 - Hangman

Trang 138
in (thư, kết thúc = '')
in()
Điều này cho vòng lặp một dòng 71 sẽ hiển thị tất cả các dự đoán bị bỏ lỡ mà người chơi đã thực
hiện.
Khi bạn chơi Hangman trên giấy, bạn thường viết những chữ cái này sang một bên để
bạn biết không đoán chúng lần nữa Trên mỗi lần lặp của vòng lặp, giá trị của chữ cái sẽ
lần lượt từng chữ cái trong missLetters . Hãy nhớ rằng end = '' sẽ thay thế
ký tự dòng mới được in sau khi chuỗi được thay thế bằng một ký tự khoảng trắng.
Nếu missLetters là 'ajtw' thì vòng lặp for này sẽ hiển thị ajtw .
Hiển thị từ bí mật với khoảng trống
Vì vậy, đến thời điểm này, chúng tôi đã cho người chơi thấy bảng treo và các chữ cái bị bỏ
lỡ. Hiện nay
chúng tôi muốn in từ bí mật, ngoại trừ chúng tôi muốn các dòng trống cho các chữ cái. Chúng ta
có thể sử dụng _
ký tự (được gọi là ký tự gạch dưới) cho điều này. Nhưng chúng ta nên in các chữ cái trong
từ bí mật mà người chơi đã đoán và sử dụng _ ký tự cho các chữ cái mà người chơi không có
đoán chưa Trước tiên chúng ta có thể tạo một chuỗi không có gì ngoài một dấu gạch dưới cho
mỗi chữ cái trong
từ bí mật. Sau đó, chúng ta có thể thay thế các khoảng trống cho mỗi chữ cái trong chính xác . Vì
thế nếu
từ bí mật là 'rái cá' thì chuỗi trống sẽ là '_____' (năm _
nhân vật). Nếu đúngLetters là chuỗi 'rt' thì chúng tôi muốn thay đổi
chuỗi trống thành '_tt_r' . Đây là đoạn mã thực hiện điều đó:
75. khoảng trống = '_' * len (SecretWord)
76.
77. for i trong phạm vi (len (secretWord)): # thay thế khoảng trắng bằng
chữ đoán đúng
78.
if secretWord [i] trong đúngLetters:
79.
khoảng trống = khoảng trống [: i] + secretWord [i] + khoảng trống
[i + 1:]
80.
81. cho chữ cái trong khoảng trống: # hiển thị từ bí mật với
khoảng trắng ở giữa mỗi chữ cái
Dòng 75 tạo biến khoảng trống đầy _ dấu gạch dưới bằng cách sử dụng sao chép chuỗi.
Hãy nhớ rằng toán tử * cũng có thể được sử dụng trên một chuỗi và một số nguyên, vì vậy biểu
thức
'xin chào' * 3 đánh giá thành 'hellohellohello' . Điều này sẽ đảm bảo rằng khoảng trống
có cùng số dấu gạch dưới như secretWord có các chữ cái.
Sau đó, chúng tôi sử dụng một vòng lặp for để đi qua từng chữ cái trong secretWord và thay thế
gạch dưới với chữ cái thực nếu nó tồn tại trong chính xác . Dòng 79 có thể nhìn
gây nhầm lẫn. Có vẻ như chúng ta đang sử dụng dấu ngoặc vuông với khoảng trống và
biến bí mậtWord . Nhưng đợi một chút, khoảng trống và SecretWord là chuỗi, không phải
danh sách. Và hàm len () cũng chỉ lấy danh sách làm tham số, không phải chuỗi. Nhưng trong
Python,
nhiều điều bạn có thể làm để liệt kê bạn cũng có thể làm với chuỗi:
124

Trang 139
Thay thế các dấu gạch dưới bằng các chữ được đánh giá đúng
77. for i trong phạm vi (len (secretWord)): # thay thế khoảng trống
với các chữ cái được đoán chính xác
78.
if secretWord [i] trong đúngLetters:
79.
khoảng trống = khoảng trống [: i] + secretWord [i] + khoảng trống
[i + 1:]
Hãy giả vờ giá trị của secretWord là 'rái cá' và giá trị trong
đúngLetters là 'tr' . Sau đó len (secretWord) sẽ trả về 5 . Sau đó phạm vi
(len (secretWord)) trở thành phạm vi (5) , lần lượt trả về danh sách [0, 1, 2,
3, 4] .
Bởi vì giá trị của i sẽ đảm nhận từng giá trị trong [0, 1, 2, 3, 4] , sau đó cho
mã vòng lặp tương đương với điều này:
if secretWord [0] trong đúngLetters:
khoảng trống = khoảng trống [: 0] + secretWord [0] + khoảng trống [1:]
if secretWord [1] trong đúngLetters:
khoảng trống = khoảng trống [: 1] + secretWord [1] + khoảng trống [2:]
if secretWord [2] trong đúngLetters:
khoảng trống = khoảng trống [: 2] + secretWord [2] + khoảng trống [3:]
if secretWord [3] trong đúngLetters:
khoảng trống = khoảng trống [: 3] + secretWord [3] + khoảng trống [4:]
if secretWord [4] trong đúngLetters:
khoảng trống = khoảng trống [: 4] + secretWord [4] + khoảng trống [5:]
(Nhân tiện, viết ra mã như thế này được gọi là unrolling loop .)
Nếu bạn bối rối không biết giá trị của thứ gì đó như secretWord [0] hay
khoảng trống [3:] là, sau đó nhìn vào bức tranh này. Nó cho thấy giá trị của SecretWord và
khoảng trống biến và chỉ mục cho mỗi chữ cái trong chuỗi.
Hình 9-2: Các chỉ mục của chuỗi trống và chuỗi SecretWord .
125
9 - Hangman

Trang 140
Nếu chúng ta thay thế các lát danh sách và các chỉ mục danh sách bằng các giá trị mà chúng đại diện,
mã vòng lặp không được kiểm soát sẽ giống như thế này:
nếu 'o' trong 'tr': # Điều kiện là Sai, khoảng trống ==
'
blanks = '' + 'o' + '____' # Dòng này bị bỏ qua.
nếu 't' trong 'tr': # Điều kiện là Đúng, khoảng trống ==
'
khoảng trống = '_' + 't' + '___' # Dòng này là
Thực thi.
nếu 't' trong 'tr': # Điều kiện là Đúng, khoảng trống ==
'_T___'
khoảng trống = '_t' + 't' + '__' # Dòng này là
Thực thi.
nếu 'e' trong 'tr': # Điều kiện là Sai, khoảng trống ==
'_tt__'
blanks = '_tt' + 'e' + '_' # Dòng này bị bỏ qua.
nếu 'r' trong 'tr': # Điều kiện là Đúng, khoảng trống ==
'_tt__'
khoảng trống = '_tt_' + 'r' + '' # Dòng này là
Thực thi.
# khoảng trống hiện có giá trị '_tt_r'
Ba ví dụ mã trên đều làm cùng một việc (ít nhất, chúng làm khi
secretWord là 'rái cá' và chính xác là 'tr' . Hộp đầu tiên là thực tế
mã chúng tôi có trong trò chơi của chúng tôi. Hộp thứ hai hiển thị mã thực hiện điều tương tự
ngoại trừ
không có vòng lặp for . Hộp thứ ba giống như hộp thứ hai, ngoại trừ chúng tôi đã đánh giá
nhiều biểu thức trong hộp thứ hai.
Một vài dòng mã tiếp theo hiển thị giá trị mới của khoảng trắng với khoảng trắng ở giữa
Mỗi bức thư.
81. cho chữ cái trong khoảng trống: # hiển thị từ bí mật với
khoảng trắng ở giữa mỗi chữ cái
82.
in (thư, kết thúc = '')
83. in ()
Đây cho vòng lặp sẽ in ra mỗi nhân vật trong chuỗi khoảng trống . Hãy nhớ rằng
bây giờ, khoảng trống có thể có một số dấu gạch dưới của nó được thay thế bằng các chữ cái
trong
secretWord . Đối số từ khóa kết thúc trong lệnh gọi print () của dòng 82 tạo ra bản in
Hàm () đặt một ký tự khoảng trắng ở cuối chuỗi thay vì ký tự dòng mới.
Đây là phần cuối của hàm displayBoard () .
126
Trang 141
Nhận số lượt đoán
Hàm getGuess () mà chúng ta tạo tiếp theo sẽ được gọi bất cứ khi nào chúng ta muốn để
người chơi gõ một chữ cái để đoán. Hàm trả về chữ cái mà người chơi đoán là một chuỗi.
Hơn nữa, getGuess () sẽ đảm bảo rằng người chơi nhập một chữ cái hợp lệ trước khi quay lại
từ chức năng.
85. def getGuess (đã được bảo vệ):
86. # Trả về chữ cái mà người chơi đã nhập. Điều này
Chức năng đảm bảo người chơi nhập một chữ cái duy nhất,
và không phải cái gì khác.
Hàm getGuess () có một tham số chuỗi được gọi là yetGuished mà
chứa các chữ cái mà người chơi đã đoán và sẽ yêu cầu người chơi đoán một chữ cái
lá thư. Chữ cái duy nhất này sẽ là giá trị trả về cho hàm này.
87. trong khi Đúng:
88.
in ('Đoán một chữ cái.')
89.
đoán = đầu vào ()
90.
đoán = đoán.lower ()
Chúng tôi sẽ sử dụng một trong khi vòng lặp vì chúng tôi muốn tiếp tục yêu cầu các cầu thủ cho
một lá thư đến
họ nhập văn bản là một chữ cái mà họ chưa đoán được trước đó. Lưu ý rằng
điều kiện cho trong khi vòng lặp chỉ đơn giản là giá trị Boolean Đúng . Điều đó có nghĩa là cách
duy nhất
thực thi sẽ không bao giờ rời khỏi vòng lặp này bằng cách thực hiện một câu lệnh break (để lại
vòng lặp) hoặc câu lệnh return (để lại toàn bộ hàm). Một vòng lặp như vậy được gọi là một
vòng lặp vô hạn , bởi vì nó sẽ lặp mãi mãi (trừ khi nó đạt đến một câu lệnh break ).
Mã bên trong vòng lặp yêu cầu người chơi nhập một chữ cái, được lưu trữ trong biến
đoán . Nếu người chơi nhập một chữ cái viết hoa, nó sẽ được chuyển thành chữ thường
90.
Tuyên bố elif ("Khác nếu")
Hãy xem đoạn mã sau:
if catName == 'Fuzzball':
in ('Con mèo của bạn mờ.')
khác:
in ('Con mèo của bạn không mờ lắm chút nào.')
Chúng ta đã thấy mã như thế này trước đây và nó khá đơn giản. Nếu biến catName bằng nhau
với chuỗi 'Fuzzball' , thì điều kiện if là đúng và chúng tôi nói với người dùng
127
9 - Hangman

Trang 142
rằng con mèo của cô là mờ. Nếu catName là bất cứ điều gì khác, thì chúng tôi nói với người dùng
rằng con mèo của cô ấy không phải là
mờ.
Nhưng điều gì sẽ xảy ra nếu chúng ta muốn một cái gì đó khác ngoài "mờ" và "không
mờ"? Chúng ta có thể đặt
một câu lệnh if và other khác bên trong khối khác đầu tiên như thế này:
if catName == 'Fuzzball':
in ('Con mèo của bạn mờ.')
khác:
nếu catName == 'Điểm'
in ('Con mèo của bạn được phát hiện.')
khác:
in ('Con mèo của bạn không mờ cũng không
phát hiện. ')
Nhưng nếu chúng ta muốn nhiều thứ hơn, thì mã bắt đầu có nhiều vết lõm:
if catName == 'Fuzzball':
in ('Con mèo của bạn mờ.')
khác:
nếu catName == 'Điểm'
in ('Con mèo của bạn được phát hiện.')
khác:
nếu catName == 'FattyKitty'
in ('Con mèo của bạn béo.')
khác:
nếu catName == 'Puff'
in ('Con mèo của bạn bị sưng húp.')
khác:
in ('Con mèo của bạn không mờ
không phát hiện cũng không béo cũng không phồng. ')
Nhập tất cả các khoảng trống đó có nghĩa là bạn có nhiều cơ hội mắc lỗi với
thụt đầu dòng. Vì vậy, Python có từ khóa elif . Sử dụng elif , đoạn mã trên trông như thế này:
if catName == 'Fuzzball':
in ('Con mèo của bạn mờ.')
elif catName == 'Điểm'
in ('Con mèo của bạn được phát hiện.')
elif catName == 'FattyKitty'
in ('Con mèo của bạn béo.')
elif catName == 'Puff'
in ('Con mèo của bạn bị sưng húp.')
khác:
128

Trang 143
in ('Con mèo của bạn không mờ cũng không bị phát hiện
không béo cũng không phồng. ')
Nếu điều kiện cho nếu tuyên bố là False , sau đó chương trình sẽ kiểm tra
điều kiện cho câu lệnh elif đầu tiên (đó là catName == 'Spots' . Nếu đó
điều kiện là Sai , sau đó chương trình sẽ kiểm tra điều kiện của câu lệnh elif tiếp theo .
Nếu tất cả các điều kiện cho câu lệnh if và elif là Sai , thì mã trong
khối khác thực thi.
Nhưng nếu một trong các điều kiện elif là True , mã khối elif được thực thi và sau đó
thực thi nhảy xuống dòng đầu tiên qua khối khác. Vì vậy, chỉ có một trong các khối trong này
Câu lệnh if-elif-other sẽ được thực thi. Bạn cũng có thể rời khỏi khối khác nếu bạn không
cần một, và chỉ cần có một câu lệnh if-elif.
Đảm bảo người chơi đã nhập đúng
91.
nếu len (đoán)! = 1:
92.
in ('Vui lòng nhập một chữ cái.')
93.
elif đoán trong đã được bảo vệ:
94.
in ('Bạn đã đoán được bức thư đó.
Chọn lại. ')
95.
đoán nếu không trong 'abcdefghijklmnopqrstuvwxyz':
96.
in ('Vui lòng nhập BÀI.')
97.
khác:
98.
đoán lại
Biến đoán chứa văn bản mà người chơi đã nhập để đoán. Chúng ta cần phải
hãy chắc chắn rằng họ đã gõ một chữ cái viết thường. Nếu họ không, chúng ta nên lặp lại và
hỏi lại họ Các nếu kiểm tra điều kiện tuyên bố rằng văn bản là một và chỉ thư. Nếu nó
không phải, sau đó chúng ta thực thi mã if-block, và sau đó thực thi nhảy xuống qua cái khác-
khối. Nhưng vì không còn mã nào sau câu lệnh if-elif-other này, nên vòng lặp thực thi trở lại
đến dòng 87.
Nếu điều kiện cho nếu tuyên bố là False , chúng tôi kiểm tra tình trạng báo cáo kết quả elif của
trên dòng 93. Điều kiện này là Đúng nếu chữ cái tồn tại bên trong đã được bảo vệ
biến (hãy nhớ, đây là một chuỗi có mọi chữ cái mà người chơi đã đoán). Nếu
điều kiện này là True , sau đó chúng tôi hiển thị thông báo lỗi cho người chơi và nhảy xuống
qua khối khác. Nhưng sau đó chúng tôi sẽ ở cuối khối while, vì vậy thực thi nhảy
sao lưu lên dòng 87.
Nếu điều kiện cho nếu tuyên bố và elif tuyên bố đều False , sau đó chúng tôi
kiểm tra điều kiện của câu lệnh elif thứ hai trên dòng 95. Nếu người chơi gõ một số hoặc
một nhân vật hài hước ( đoán là có giá trị như '5' hoặc '!' ), sau đó đoán sẽ không
tồn tại trong chuỗi 'abcdefghijklmnopqrstuvwxyz' . Nếu đây là trường hợp, elif
điều kiện của tuyên bố là đúng .
129
9 - Hangman

Trang 144
Hình 9-3 là một ví dụ về các câu lệnh elif . Trừ khi ba điều kiện này là tất cả
Sai , người chơi sẽ tiếp tục lặp và tiếp tục được yêu cầu viết thư. Nhưng khi cả ba
trong số các điều kiện là Sai , thì câu lệnh return của khối khác sẽ chạy và chúng ta sẽ
thoát khỏi vòng lặp và chức năng này.
Hình 9-3: Câu lệnh elif .
Yêu cầu người chơi chơi lại
100. def playAgain ():
101. # Hàm này trả về True nếu người chơi muốn
chơi lại, nếu không nó trả về Sai.
102. print ('Bạn có muốn chơi lại không? (Có hoặc không)')
103. return return (). Lower (). Startedwith ('y')
Hàm playAgain () chỉ có một lệnh gọi hàm print () và trả về
tuyên bố. Câu lệnh return có một biểu thức có vẻ phức tạp, nhưng chúng ta có thể
phá vỡ nó. Khi chúng tôi đánh giá biểu thức này thành một giá trị, giá trị đó sẽ được trả về từ
Chức năng này.
Biểu thức trên dòng 103 không có bất kỳ toán tử nào, nhưng nó có một lệnh gọi hàm
và hai cuộc gọi phương thức. Cuộc gọi hàm là input () và các cuộc gọi phương thức thấp hơn ()
và bắt đầu ('y') . Hãy nhớ rằng các cuộc gọi phương thức là các cuộc gọi chức năng được đính
kèm
bởi một khoảng thời gian để giá trị bên trái của họ . Lower () được gắn vào giá trị trả về của đầu
vào
() .
input () trả về một chuỗi văn bản mà người dùng đã nhập. Dưới đây là từng bước xem xét
Python đánh giá biểu thức này như thế nào nếu người dùng gõ vào CÓ.
trả về đầu vào (). low (). startedwith ('y')
trả về 'YES'.lower (). startedwith (' y ')
130

Trang 145
trả về 'yes'.startswith (' y ')
trả lại đúng
Điểm quan trọng của hàm playAgain () là cho phép người chơi nhập có hoặc không để cho
chúng tôi biết
nếu họ muốn chơi một vòng Hangman khác. Nếu người chơi gõ bằng CÓ, thì
giá trị trả về của input () là chuỗi 'CÓ' . Và 'YES'.lower () trả về
phiên bản chữ thường của chuỗi đính kèm. Vì vậy, giá trị trả về của 'YES'.lower () là
"Có" .
Nhưng có cuộc gọi phương thức thứ hai, startedwith ('y') . Hàm này trả về True nếu
chuỗi liên kết bắt đầu bằng tham số chuỗi giữa dấu ngoặc đơn và Sai
nếu nó không Giá trị trả về của 'yes'.startswith (' y ') là True .
Bây giờ chúng tôi đã đánh giá biểu hiện này! Chúng ta có thể thấy rằng những gì nó làm là để
cho người chơi
gõ vào một phản hồi, chúng tôi viết thường câu trả lời, kiểm tra xem nó bắt đầu bằng
chữ 'y' hay 'Y' ,
và sau đó trả về True nếu có và Sai nếu không. Phù!
Bên cạnh đó, còn có một phương thức chuỗi endswith (someString) sẽ
return True nếu chuỗi kết thúc bằng chuỗi trong someString và false nếu không có.
Đánh giá các chức năng chúng tôi xác định
Đó là tất cả các chức năng chúng tôi đang tạo cho trò chơi này!
● getRandomWord (wordList) sẽ lấy danh sách các chuỗi được truyền cho nó dưới dạng

tham số và trả về một chuỗi từ nó. Đó là cách chúng tôi sẽ chọn một từ cho
người chơi đoán.
● displayBoard (HANGMANPICS, missLetters, trueLetters,

secretWord) sẽ hiển thị trạng thái hiện tại của bảng, bao gồm bao nhiêu
từ bí mật mà người chơi đã đoán từ trước đến nay và các chữ cái sai của người chơi
đoán. Hàm này cần bốn tham số được truyền để hoạt động chính xác.
HANGMANPICS là một danh sách các chuỗi chứa nghệ thuật ASCII cho mỗi hangman có thể
bảng. đúngLetters và missLetters là các chuỗi được tạo thành từ các chữ cái
mà người chơi đã đoán được trong và không có từ bí mật. Và bí mật
là từ bí mật mà người chơi đang cố đoán. Hàm này không có giá trị trả về.
● getGuess (yetGuished) nhận một chuỗi các chữ cái mà trình phát đã có

đoán và sẽ tiếp tục hỏi người chơi về một lá thư mà anh ta không có
đã đoán ra. (Đó là, một chữ cái chưa có trong GuGuished . Hàm này
trả về chuỗi ký tự được chấp nhận mà người chơi đoán.
● playAgain () là một hàm hỏi xem người chơi có muốn chơi một vòng khác không

Người treo cổ. Hàm này trả về True nếu người chơi thực hiện và Sai nếu người chơi
131
9 - Hangman

Trang 146
không.
Bây giờ chúng ta sẽ bắt đầu mã cho phần chính của trò chơi, sẽ gọi phần trên
chức năng khi cần thiết. Nhìn lại biểu đồ dòng chảy của chúng tôi.
Hình 9-4: Biểu đồ dòng hoàn chỉnh của Hangman.

Mã chính cho Hangman


Chúng ta cần viết mã thực hiện mọi thứ trong biểu đồ luồng này và thực hiện theo thứ tự. Các
phần chính của mã bắt đầu ở dòng 106. Mọi thứ trước đó chỉ là các định nghĩa hàm
và một phép gán biến rất lớn cho HANGMANPICS .
Thiết lập các biến
106. in ('HANGMA N')
132

Trang 147
107. missLetters = ''
108. đúngLetters = ''
109. secretWord = getRandomWord (từ)
110. gameIsDone = Sai
Dòng 106 là dòng thực tế đầu tiên thực thi trong trò chơi của chúng tôi. Chúng tôi bắt đầu bằng
cách chỉ định một khoảng trống
chuỗi cho missLetters và CorrLetters , vì người chơi chưa đoán được
bất kỳ chữ cái bị mất hoặc chính xác nào. Sau đó, chúng tôi gọi getRandomWord (từ) , trong đó
các từ
là một biến với danh sách rất lớn các từ bí mật có thể chúng tôi đã gán trên dòng 59. Sự trở lại
giá trị của getRandomWord (từ) là một trong những từ này và chúng tôi lưu nó vào
biến bí mật . Sau đó, chúng tôi cũng đặt một biến có tên là gameIsDone thành false . Chúng tôi
sẽ đặt gameIsDone thành True khi chúng tôi muốn báo hiệu rằng trò chơi đã kết thúc và
chương trình nên hỏi người chơi nếu họ muốn chơi lại.
Đặt giá trị của các biến này là những gì chúng ta làm trước khi người chơi bắt đầu đoán chữ cái.
Hiển thị bảng cho người chơi
112. trong khi Đúng:
113. displayBoard (HANGMANPICS, missLetters,
đúngLetters, secretWord)
Các trong khi điều kiện vòng lặp luôn là thật , có nghĩa là chúng tôi sẽ luôn luôn lặp mãi mãi
cho đến khi một tuyên bố phá vỡ được gặp phải. Chúng tôi sẽ thực hiện một tuyên bố phá vỡ khi
trò chơi
đã kết thúc (vì người chơi thắng hoặc người chơi thua).
Dòng 113 gọi hàm displayBoard () của chúng tôi , chuyển cho nó danh sách nghệ thuật hangman
ASCII
hình ảnh và ba biến chúng tôi đặt trên các dòng 107, 108 và 109. Di chuyển thực hiện chương
trình
đến khi bắt đầu displayBoard () ở dòng 66. Dựa trên số lượng chữ cái mà người chơi có
đoán đúng và bỏ lỡ, chức năng này hiển thị bảng treo người thích hợp cho
người chơi.
Để người chơi tham gia đoán của họ
115. # Hãy để người chơi gõ một chữ cái.
116. đoán = getGuess (missLetters + đúngLetters)
Nếu bạn nhìn vào biểu đồ dòng chảy của chúng tôi, bạn chỉ thấy một mũi tên đi từ "Hiển thị bảng

ô trống cho người chơi. "vào ô" Yêu cầu người chơi đoán chữ cái. "Vì chúng tôi có
đã viết một hàm để đoán dự đoán từ người chơi, hãy gọi hàm đó. Nhớ lại
rằng hàm cần tất cả các chữ cái trong missLetters và trueLetters
kết hợp lại, vì vậy chúng ta sẽ chuyển qua làm đối số một chuỗi là nối của cả hai chuỗi
dây. Đối số này là cần thiết bởi getGuess () vì hàm này có mã để kiểm tra xem
người chơi gõ vào một chữ cái mà họ đã đoán.
133
9 - Hangman

Trang 148
Kiểm tra xem Thư có trong Từ bí mật không
118. nếu đoán trong secretWord:
119.
đúngLetters = đúngLetters + đoán
Bây giờ hãy xem liệu một chữ cái trong chuỗi đoán có tồn tại trong secretWord không . Nếu nó
không
tồn tại, sau đó chúng ta nên tiếp nhau bức thư trong đoán đến correctLetters chuỗi.
Tiếp theo chúng ta có thể kiểm tra xem chúng ta đã đoán được tất cả các chữ cái chưa và đã
thắng.
Kiểm tra xem Người chơi đã Thắng chưa
121.
# Kiểm tra xem người chơi đã thắng chưa
122.
Found ALLLetters = True
123.
cho tôi trong phạm vi (len (secretWord)):
124.
if secretWord [i] không đúng
125.
Found ALLLetters = Sai
126.
phá vỡ
Làm thế nào để chúng ta biết nếu người chơi đã đoán từng chữ cái trong từ bí mật? Tốt,
đúngLetters có mỗi chữ cái mà người chơi đoán đúng và secretWord là
bản thân từ bí mật. Chúng tôi không thể kiểm tra nếu đúngLetters == secretWord
bởi vì hãy xem xét tình huống này: nếu secretWord là chuỗi 'rái cá' và
đúngLetters là chuỗi 'orte' , sau đó đúngLetters ==
secretWord sẽ sai ngay cả khi người chơi đã đoán từng chữ cái trong
từ bí mật.
Người chơi chỉ cần đoán các chữ cái không theo thứ tự và họ vẫn thắng, nhưng chương trình của
chúng tôi
sẽ không chính xác nghĩ rằng người chơi chưa giành chiến thắng. Ngay cả khi họ đã đoán các
chữ cái theo thứ tự,
đúngLetters sẽ là chuỗi 'oter' vì người chơi không thể đoán chữ t
nhiều hơn một lần. Biểu thức 'rái cá' == 'rái cá' sẽ đánh giá thành Sai thậm chí
mặc dù người chơi đã thắng
Cách duy nhất chúng ta có thể chắc chắn rằng người chơi đã thắng là đi qua từng chữ cái trong
secretWord và xem nếu nó tồn tại trong chính xác . Nếu, và chỉ khi, mỗi chữ cái duy nhất
trong secretWord tồn tại trong chính xác Người chơi sẽ giành chiến thắng.
Lưu ý rằng điều này khác với việc kiểm tra xem mỗi chữ cái trong đúngLetters có trong
secretWord . Nếu đúngLetters là chuỗi 'ot' và secretWord là
'Rái cá' , đúng là mọi chữ cái trong 'ot' đều nằm trong 'rái cá' , nhưng điều đó không có nghĩa
Người chơi đã đoán được từ bí mật và giành chiến thắng.
Vậy làm thế nào chúng ta có thể làm điều này? Chúng ta có thể lặp qua từng chữ cái
trong secretWord và nếu chúng ta tìm thấy
một chữ cái không tồn tại trong chính xác , chúng tôi biết rằng người chơi đã không đoán được
tất cả các chữ cái Đây là lý do tại sao chúng tôi tạo một biến mới có tên FoundAllLetters và đặt

134

Trang 149
đến giá trị Boolean Đúng . Chúng tôi bắt đầu giả định rằng chúng tôi đã tìm thấy tất cả các chữ cái,
nhưng
sẽ thay đổi Found AllLetters thành false khi chúng tôi tìm thấy một chữ cái trong secretWord đó

không correctLetters .
Các cho vòng lặp sẽ đi qua những con số 0 lên đến (nhưng không bao gồm) chiều dài của
từ. Hãy nhớ rằng phạm vi (5) sẽ đánh giá vào danh sách [0, 1, 2, 3, 4] . Sớm
dòng 123, chương trình thực thi tất cả mã bên trong khối for với biến i sẽ là
đặt thành 0 , rồi 1 , rồi 2 , rồi 3 , rồi 4 .
Chúng tôi sử dụng phạm vi (len (secretWord)) để tôi có thể được sử dụng để truy cập từng chữ
cái trong
từ bí mật. Vì vậy, nếu chữ cái đầu tiên trong secretWord (được đặt tại secretWord [0] )
không có trong chính xác , chúng tôi biết rằng chúng tôi có thể
đặt FoundAllLetters thành Sai . Cũng thế,
bởi vì chúng tôi không phải kiểm tra phần còn lại của các chữ cái trong secretWord , chúng tôi
có thể phá vỡ
ra khỏi vòng lặp này Nếu không, chúng tôi lặp lại dòng 123 và kiểm tra chữ cái tiếp theo.
Nếu FoundAllLetters quản lý để tồn tại từng chữ cái mà không bị chuyển sang
Sai , sau đó nó sẽ giữ giá trị True ban đầu mà chúng tôi đã cung cấp cho nó. Dù bằng cách nào,
giá trị trong
FoundAllLetters chính xác theo thời gian chúng tôi vượt qua điều này cho vòng lặp và chạy dòng
127.
129.
nếu tìm thấy AllLetters:
130.
in ('Có! Từ bí mật là "' +
secretWord + '"! Bạn đã thắng!')
131.
gameIsDone = Đúng
Đây là một kiểm tra đơn giản để xem nếu chúng tôi tìm thấy tất cả các chữ cái. Nếu chúng tôi đã
tìm thấy mọi chữ cái trong
từ bí mật, chúng ta nên nói với người chơi rằng họ đã thắng. Chúng tôi cũng sẽ thiết lập
biến gameIsDone thành True . Chúng tôi sẽ kiểm tra biến này để xem chúng tôi có nên để
Người chơi đoán lại hoặc nếu người chơi đã đoán xong.
Khi người chơi đoán không chính xác
130. khác:
Đây là sự khởi đầu của khối khác. Hãy nhớ rằng, mã trong khối này sẽ thực thi nếu
điều kiện là sai . Nhưng điều kiện nào? Để tìm hiểu, chỉ ngón tay của bạn khi bắt đầu
các từ khóa khác và di chuyển nó thẳng lên. Bạn sẽ thấy rằng các từ khóa khác
thụt lề giống như thụt lề của từ khóa if trên dòng 118. Vì vậy, nếu điều kiện trên
dòng 118 là Sai , sau đó chúng tôi sẽ chạy mã trong khối khác này. Nếu không, chúng tôi bỏ qua
vượt qua khối khác đến dòng 140.
131.
missLetters = missLetters + đoán
135
9 - Hangman

Trang 150
Vì thư đoán của người chơi đã sai, chúng tôi sẽ thêm nó vào các Thư bị bỏ lỡ
chuỗi. Điều này giống như những gì chúng tôi đã làm trên dòng 119 khi người chơi đoán đúng.
133.
# Kiểm tra xem người chơi đã đoán quá nhiều lần chưa và
mất đi
134.
if len (missLetters) == len (HANGMANPICS) - 1:
135.
displayBoard (HANGMANPICS, missLetters,
đúngLetters, secretWord)
136.
in ('Bạn đã hết dự đoán! \ nSau đó'
+ str (len (missLetters)) + 'đoán sai và' + str
(len (chính xác)) + 'đoán đúng, từ này là
"'+ SecretWord +'" ')
137.
gameIsDone = Đúng
Hãy suy nghĩ về cách chúng ta biết khi người chơi đã đoán quá nhiều lần. Khi bạn chơi
Hangman trên giấy, đây là khi bản vẽ của hangman kết thúc. Chúng tôi vẽ
hangman trên màn hình với các lệnh in () , dựa trên số lượng chữ cái trong
missedLetters . Hãy nhớ rằng mỗi lần người chơi đoán sai, chúng tôi sẽ thêm (hoặc dưới dạng
lập trình viên sẽ nói, ghép) ký tự sai thành chuỗi trong missLetters .
Vì vậy, độ dài của missLetters (hoặc, trong mã, len (missLetters) ) có thể cho chúng ta biết
số lần đoán sai.
Tại thời điểm nào người chơi hết đoán và thua? Chà, danh sách HANGMANPICS
có 7 hình ảnh (thực sự, chúng là các chuỗi nghệ thuật ASCII). Vì vậy, khi len (missLetters)
bằng 6 , chúng tôi biết người chơi đã mất vì hình ảnh hangman sẽ kết thúc.
(Hãy nhớ rằng HANGMANPICS [0] là mục đầu tiên trong danh sách và HANGMANPICS [6]
là cái cuối cùng Điều này là do chỉ mục của một danh sách có 7 mục đi từ 0 đến 6, không phải từ
1 đến 7.)
Vậy tại sao chúng ta có len (missLetters) == len (HANGMANPICS) - 1 là
điều kiện trên dòng 134, thay vì len (missLetters) == 6 ? Giả vờ rằng chúng ta
thêm một chuỗi khác vào danh sách HANGMANPICS (có thể là hình ảnh của người treo đầy đủ
với một
đuôi, hoặc một cánh tay đột biến thứ ba). Sau đó, hình ảnh cuối cùng trong danh sách sẽ là
tại HANGMANPICS
[7] . Vì vậy, chúng tôi không chỉ phải thay đổi danh sách HANGMANPICS bằng một chuỗi mới,
mà còn
chúng ta cũng phải nhớ thay đổi dòng 134 thành len (missLetters) == 7 .
Đây có thể không phải là vấn đề lớn đối với một chương trình như Hangman, nhưng khi bạn bắt
đầu viết lớn hơn
các chương trình bạn có thể phải thay đổi một số dòng mã khác nhau trên toàn bộ chương trình
của mình
để thay đổi hành vi của chương trình. Bằng cách này, nếu chúng tôi muốn làm cho trò chơi khó
hơn
hoặc dễ dàng hơn, chúng ta chỉ cần thêm hoặc xóa các chuỗi nghệ thuật ASCII
vào HANGMANPICS và thay đổi
không có gì khác
Một lý do thứ hai chúng tôi sử dụng len (HANGMANPICS) - 1 là vì vậy khi chúng tôi đọc mã
trong chương trình này sau, chúng ta biết tại sao chương trình này hành xử theo cách của nó. Nếu
bạn viết
len (missLetters) == 6 và sau đó xem mã hai tuần sau, bạn có thể
tự hỏi có gì đặc biệt về số 6. Bạn có thể đã quên rằng 6 là số cuối cùng
chỉ mục trong danh sách HANGMANPICS . Tất nhiên, bạn có thể viết bình luận để nhắc nhở
bản thân,
giống:
136

Trang 151
134. if len (missLetters) == 6: # 6 là chỉ mục cuối cùng trong
Danh sách HÀNG NGÀY
Nhưng nó dễ dàng hơn khi chỉ sử dụng len (HANGMANPICS) - 1 thay thế.
Vì vậy, khi độ dài của chuỗi missLetters bằng len (HANGMANPICS)
- 1 , chúng tôi biết người chơi đã hết đoán và thua trò chơi. Chúng tôi in dài
chuỗi cho người dùng biết từ bí mật là gì và sau đó đặt giá trị gameIsDone thành
Giá trị Boolean Đúng . Đây là cách chúng tôi sẽ nói với bản thân rằng trò chơi đã hoàn thành và
chúng tôi
nên bắt đầu lại
Hãy nhớ rằng khi chúng ta có \ n trong một chuỗi, nó đại diện cho ký tự dòng mới.
139. # Hỏi người chơi nếu họ muốn chơi lại (nhưng chỉ
nếu trò chơi được thực hiện).
140. nếu gameIsDone:
141.
nếu chơiAgain ():
142.
missLetters = ''
143.
đúngLetters = ''
144.
gameIsDone = Sai
145.
secretWord = getRandomWord (từ)
Nếu người chơi thắng hoặc thua sau khi đoán chữ cái của họ, thì mã của chúng tôi sẽ đặt
biến gameIsDone thành True . Nếu đây là trường hợp, chúng ta nên hỏi người chơi nếu họ muốn
để chơi lại. Chúng tôi đã viết hàm playAgain () để xử lý việc có hay không
từ người chơi. Hàm này trả về giá trị Boolean là True nếu người chơi muốn chơi
một trò chơi khác của Hangman và Sai nếu họ đã có đủ.
Nếu người chơi không muốn chơi lại, chúng tôi sẽ đặt lại các giá trị trong missLetters và
đúngLetters thành chuỗi trống, đặt gameIsDone thành false , sau đó chọn một chuỗi mới
từ bí mật bằng cách gọi lại getRandomWord () , chuyển cho nó danh sách các bí mật có thể
từ ngữ.
Theo cách này, khi chúng ta lặp lại từ đầu vòng lặp (trên dòng 112), bảng sẽ là
trở lại điểm bắt đầu (hãy nhớ rằng chúng tôi quyết định hình ảnh hangman nào sẽ hiển thị dựa
trên độ dài
của missLetters , mà chúng ta chỉ đặt là chuỗi trống) và trò chơi sẽ giống như
lần đầu tiên chúng tôi bước vào vòng lặp. Sự khác biệt duy nhất là chúng ta sẽ có một từ bí mật
mới,
bởi vì chúng tôi đã lập trình getRandomWord () để trả về một từ được chọn ngẫu nhiên mỗi từ
thời gian chúng tôi gọi nó.
Có một cơ hội nhỏ rằng từ bí mật mới sẽ giống với từ bí mật cũ,
nhưng đây chỉ là sự trùng hợp Giả sử bạn đã lật một đồng xu và nó đã xuất hiện, và sau đó
bạn lật đồng xu một lần nữa và nó cũng xuất hiện. Cả hai lần lật đồng xu là ngẫu nhiên, đó là
chỉ là một sự trùng hợp ngẫu nhiên mà họ đã đưa ra giống nhau cả hai lần. Theo đó, bạn có thể nhận
được
137
9 - Hangman

Trang 152
trả lại cùng một từ từ getRandomWord () hai lần liên tiếp, nhưng đây chỉ là một
sự trùng hợp
146.
khác:
147.
phá vỡ
Nếu người chơi gõ 'không' khi được hỏi liệu họ có muốn chơi lại không, thì họ quay lại
giá trị của lệnh gọi hàm playAgain () sẽ là Sai và khối khác
sẽ thực hiện. Khối khác này chỉ có một dòng, một câu lệnh break . Điều này gây ra
việc thực hiện để nhảy đến cuối vòng lặp đã được bắt đầu trên dòng 112. Nhưng bởi vì có
không còn mã sau vòng lặp, chương trình kết thúc.
Thực hiện các thay đổi mới cho chương trình Hangman
Chương trình này lớn hơn nhiều so với chương trình Dragon Realm, nhưng chương trình này
cũng
tinh vi hơn. Nó thực sự giúp tạo ra một biểu đồ dòng chảy hoặc bản phác thảo nhỏ để nhớ làm
thế nào
bạn muốn mọi thứ hoạt động Hãy nhìn vào biểu đồ dòng chảy và cố gắng tìm các dòng mã
đại diện cho mỗi khối.
Tại thời điểm này, bạn có thể chuyển sang chương tiếp theo. Nhưng tôi khuyên bạn nên tiếp tục
đọc
tìm hiểu về một số cách chúng ta có thể cải thiện trò chơi Hangman của mình.
Sau khi bạn chơi Hangman một vài lần, bạn có thể nghĩ rằng sáu lần đoán không
đủ để có được nhiều từ. Chúng tôi có thể dễ dàng cung cấp cho người chơi nhiều dự đoán hơn
bằng cách thêm
nhiều chuỗi hơn vào danh sách HANGMANPICS . Thật dễ dàng, chỉ cần thay đổi] hình vuông
dấu ngoặc trên dòng 58 đến a, '' 'dấu phẩy và ba dấu ngoặc kép (xem dòng 57 bên dưới). Sau đó
thêm
tiếp theo:
58. ========== '' ',' ''
59.
60. + ---- +
61. | |
62. [O |
63. / | \ |
64. / \ |
65.
|
66. ========== '' ',' ''
67.
68. + ---- +
69. | |
70. [O] |
71. / | \ |
72. / \ |
73.
|
74. ========== '' ']
138

Trang 153
Chúng tôi đã thêm hai chuỗi nhiều dòng mới vào danh sách HANGMANPICS, một chuỗi có
tai trái của hangman được vẽ và tai kia có cả hai tai được vẽ. Bởi vì chương trình của chúng tôi
sẽ nói
người chơi họ đã thua khi số lần đoán giống với số chuỗi
trong HANGMANPICS (trừ một), đây là thay đổi duy nhất chúng ta cần thực hiện.
Chúng tôi cũng có thể thay đổi danh sách các từ bằng cách thay đổi các từ trên dòng 59. Thay vì
động vật, chúng ta có thể có màu sắc:
59. words = 'đỏ cam vàng xanh lục chàm tím trắng
màu nâu đen'.split ()
60. Hoặc hình dạng:
61. words = 'hình tam giác vuông hình tròn hình elip hình elip
bẫy hình chevron hình lục giác hình lục giác
octogon'.split ()
62. Hoặc trái cây:
63. words = 'táo chanh chanh chanh lê dưa hấu
bưởi anh đào chuối xoài dâu tây
cà chua'.split ()

Từ điển
Với một số sửa đổi, chúng tôi có thể thay đổi mã của mình để trò chơi Hangman của chúng tôi có
thể sử dụng tất cả
của những từ này như là bộ riêng biệt. Chúng tôi có thể nói cho người chơi biết từ bí mật là từ
(như "động vật", "màu sắc", "hình dạng" hoặc "trái cây"). Bằng cách này, người chơi không đoán
được tất cả động vật
thời gian.
Để thực hiện thay đổi này, chúng tôi sẽ giới thiệu một loại dữ liệu mới gọi là từ điển . Một
dictionary là một tập hợp các giá trị khác giống như một danh sách, nhưng thay vì truy cập vào
các mục
trong từ điển có chỉ mục số nguyên, bạn truy cập chúng bằng chỉ mục thuộc bất kỳ loại dữ liệu
nào (nhưng
chuỗi thường xuyên nhất).
Hãy thử gõ như sau vào shell:
>>> thứ = {'xin chào': 'Xin chào, bạn có khỏe không?',
'Trò chuyện': 'Thời tiết thế nào?', 'Tạm biệt': 'Đó là
rất vui được nói chuyện với bạn! '}
>>>
Đó là những dấu ngoặc nhọn {và}. Trên bàn phím, chúng nằm trên cùng một phím với hình
vuông
niềng răng [và]. Chúng tôi sử dụng dấu ngoặc nhọn để gõ một giá trị từ điển trong Python. Các
giá trị trong
giữa chúng là các cặp khóa-giá trị . Các phím là những thứ bên trái của dấu hai chấm và
các giá trị nằm bên phải dấu hai chấm. Bạn có thể truy cập các giá trị (giống như các mục trong
danh sách)
trong từ điển bằng cách sử dụng khóa (giống như các chỉ mục trong danh sách). Hãy thử gõ vào
vỏ
nội dung ['xin chào'] và nội dung ['trò chuyện'] và nội dung ['tạm biệt'] :
139
9 - Hangman

Trang 154
>>> thứ ['xin chào']
'Xin chào. Bạn khỏe không?'
>>> thứ ['trò chuyện']
'Thời tiết thế nào?'
>>> thứ ['tạm biệt']
'Thật tuyệt khi nói chuyện với bạn!'
>>>
Lấy kích thước của từ điển với len ()
Bạn thấy, thay vì đặt một chỉ số nguyên ở giữa các dấu ngoặc vuông, bạn đặt một
chỉ số chuỗi khóa. Điều này sẽ đánh giá giá trị cho khóa đó. Bạn có thể có được kích thước (đó
là,
có bao nhiêu cặp khóa-giá trị trong từ điển) với hàm len () . Thử gõ len
(thứ) vào vỏ:
>>> len (thứ)
3
>>>
Phiên bản danh sách của từ điển này sẽ chỉ có các giá trị và trông giống như
điều này:
listStuff = ['Xin chào, bạn thế nào?', 'Thế nào là
thời tiết? ',' Thật tuyệt khi nói chuyện với bạn! ']
Danh sách này không có bất kỳ khóa nào, như 'xin chào' và 'trò chuyện' và 'tạm biệt' trong
từ điển. Chúng ta phải sử dụng các chỉ số nguyên 0 , 1 và 2 .
Sự khác biệt giữa từ điển và danh sách
Từ điển khác với danh sách vì chúng không có thứ tự . Mục đầu tiên trong danh sách
listStuff có tên là listStuff [0] . Nhưng không có mục "đầu tiên" trong một
từ điển, bởi vì từ điển không có bất kỳ thứ tự. Hãy thử gõ cái này vào vỏ:
>>> Favorites1 = {'trái cây': 'táo',
'động vật': 'mèo', 'số': 42}
>>> Favorites2 = {'động vật': 'mèo', 'số': 42,
'trái cây': 'táo'}
>>> Favorites1 == Favorites2
Thật
140

Trang 155
>>>
Như bạn có thể thấy, biểu thức Favorites1 == Favorites2 ước tính là True
bởi vì từ điển không có thứ tự, và chúng được coi là giống nhau nếu chúng có
cùng cặp khóa-giá trị trong chúng. Danh sách được sắp xếp, vì vậy một danh sách có cùng giá trị
trong đó nhưng
theo một thứ tự khác nhau không giống nhau. Hãy thử gõ cái này vào vỏ:
>>> listFavs1 = ['táo', 'mèo', 42]
>>> listFavs2 = ['mèo', 42, 'táo']
>>> listFavs1 == listFavs2
Sai
>>>
Như bạn có thể thấy, hai danh sách listFavs1 và listFavs2 không được coi là
giống nhau vì vấn đề thứ tự trong danh sách.
Bạn cũng có thể sử dụng số nguyên làm khóa cho từ điển. Từ điển có thể có khóa của bất kỳ
kiểu dữ liệu, không chỉ chuỗi. Nhưng hãy nhớ, vì 0 và '0' là các giá trị khác nhau, chúng sẽ
là chìa khóa khác nhau. Hãy thử gõ cái này vào vỏ:
>>> myDict = {'0': 'một chuỗi', 0: 'một số nguyên'}
>>> myDict [0]
'một số nguyên'
>>> myDict ['0']
'một chuỗi'
>>>
Bạn có thể nghĩ rằng sử dụng vòng lặp for rất khó với từ điển vì chúng không có
chỉ số nguyên. Nhưng thật ra, nó dễ. Hãy thử gõ như sau vào vỏ. (Đây là một
gợi ý, trong IDLE, bạn không phải nhập khoảng trắng để bắt đầu một khối mới. IDLE làm điều
đó cho bạn. Đến
kết thúc khối, chỉ cần chèn một dòng trống bằng cách nhấn phím Enter. Hoặc bạn có thể bắt đầu
một cái mới
tệp, nhập mã này, sau đó nhấn F5 để chạy chương trình.)
>>> yêu thích = {'trái cây': 'táo', 'động vật': 'mèo',
'số': 42}
>>> cho tôi trong mục yêu thích:
... in (i)
trái cây
con số
thú vật
>>> cho tôi trong mục yêu thích:
... In (yêu thích [i])
141
9 - Hangman

Trang 156
táo
42
những con mèo
>>>
Như bạn có thể thấy, nếu bạn chỉ sử dụng một từ điển trong một vòng lặp for , biến i sẽ đảm
nhận
các giá trị của khóa từ điển, không phải giá trị của nó. Nhưng nếu bạn có từ điển và chìa khóa,
bạn có thể nhận được giá trị như chúng tôi làm ở trên với mục yêu thích [i] . Nhưng hãy nhớ rằng
bởi vì
từ điển không có thứ tự, bạn không thể dự đoán thứ tự vòng lặp for sẽ thực hiện theo thứ tự nào.
Ở trên, chúng tôi đã gõ phím 'động vật' như trước phím 'số' , nhưng cho
vòng lặp in ra 'số' trước 'động vật' .
Từ điển cũng có hai phương thức hữu ích, khóa () và giá trị () . Những thứ này sẽ trở lại
giá trị của một loại được gọi là dict_keys và dict_values , tương ứng. Những kiểu dữ liệu đó
nằm ngoài phạm vi của cuốn sách này, nhưng bạn có thể dễ dàng chuyển đổi chúng thành danh
sách với danh sách ()
function (giống như str () chuyển đổi một giá trị thành một giá trị chuỗi.) Sau đó, bạn sẽ có một
thứ tự
danh sách các giá trị chính và các giá trị giá trị trong giá trị từ điển. Hãy thử gõ như sau
vào vỏ:
>>> yêu thích = {'trái cây': 'táo', 'động vật': 'mèo',
'số': 42}
>>> danh sách (Favorites.keys ())
['trái cây', 'số', 'động vật']
>>> danh sách (yêu thích. giá trị ())
['Táo', 42, 'mèo']
>>>
Sử dụng các phương pháp này để có được danh sách các khóa và giá trị trong từ điển có thể là
rất hữu ích Đừng quên chuyển đổi giá trị trả về của dict_keys và dict_keys
với chức năng dict_keys trước, nếu không bạn có thể gặp lỗi trong chương trình của mình.
Bộ từ cho Hangman
Chúng tôi sẽ thay đổi chương trình Hangman ban đầu của chúng tôi. Những thay đổi này có thể

được tải xuống từ http://inventwithpython.com/hangman2.py
Vậy làm thế nào chúng ta có thể sử dụng từ điển trong trò chơi của chúng tôi? Đầu tiên, hãy thay
đổi danh sách các từ thành một
từ điển có khóa là chuỗi và giá trị là danh sách các chuỗi. (Hãy nhớ rằng chuỗi
phương thức split () ước tính thành một danh sách.
59. words = {'Colors': 'đỏ cam vàng xanh xanh chàm
tím trắng đen nâu'.split (),
142

Trang 157
60. 'Hình dạng': 'hình tam giác hình tam giác hình vuông hình elip
bẫy hình chevron ngũ giác lục giác hình lục giác octogon'.split
(),
61. 'Trái cây': 'táo chanh chanh chanh lê dưa hấu
bưởi anh đào chuối xoài dâu tây
cà chua'.split (),
62. 'Động vật': 'dơi gấu hải ly mèo cougar cua hươu lừa
vịt đại bàng cá ếch dê leech sư tử thằn lằn khỉ khỉ
chuột rái cá cú gấu trúc python thỏ chuột cá mập cừu
mực hổ Thổ Nhĩ Kỳ rùa chồn cá voi sói wombat
ngựa vằn'.split ()}
Mã này được đặt trên nhiều dòng trong tệp, mặc dù trình thông dịch Python nghĩ
của nó chỉ là một "dòng mã." (Dòng mã không kết thúc cho đến cuối cùng} dấu ngoặc nhọn.)
Các random.choice () Chức năng
Bây giờ chúng ta sẽ phải thay đổi hàm getRandomWord () để nó chọn một
từ ngẫu nhiên từ một từ điển danh sách các chuỗi, thay vì từ một danh sách các chuỗi. Đây là
chức năng ban đầu trông như thế nào:
61. def getRandomWord (wordList):
62. # Hàm này trả về một chuỗi ngẫu nhiên từ
thông qua danh sách các chuỗi.
63. wordIndex = Random.randint (0, len (wordList) - 1)
64. return wordList [word Index]
Thay đổi mã trong hàm này để nó trông như thế này:
64. def getRandomWord (wordDict):
65. # Hàm này trả về một chuỗi ngẫu nhiên từ
thông qua từ điển danh sách các chuỗi, và khóa cũng.
66. # Đầu tiên, chọn ngẫu nhiên một khóa từ từ điển:
67. wordKey = Random.choice (danh sách (wordDict.keys ()))
68.
69. # Thứ hai, chọn ngẫu nhiên một từ trong danh sách của khóa
trong từ điển:
70. wordIndex = Random.randint (0, len (wordDict [wordKey]) -
1)
71.
72. return [wordDict [wordKey] [word Index], wordKey]
Dòng 61 chỉ thay đổi tên của tham số thành một cái gì đó mô tả hơn một chút.
Bây giờ thay vì chọn một từ ngẫu nhiên từ danh sách các chuỗi, trước tiên, chúng tôi chọn một
khóa ngẫu nhiên
từ từ điển và sau đó chúng tôi chọn một từ ngẫu nhiên từ danh sách các chuỗi của khóa. Dòng 65
gọi một hàm mới trong mô-đun ngẫu nhiên có tên là sự lựa chọn () . Các lựa chọn () chức năng
có một tham số, một danh sách. Giá trị trả về của sự lựa chọn () là một mục được chọn ngẫu
nhiên từ
143
9 - Hangman

Trang 158
danh sách này mỗi khi nó được gọi.
Hãy nhớ rằng randint (a, b) sẽ trả về một số nguyên ngẫu nhiên giữa (và bao gồm)
hai số nguyên a và b và lựa chọn (a) trả về một mục ngẫu nhiên từ danh sách a . Nhìn vào
hai dòng mã này và tìm hiểu lý do tại sao chúng làm cùng một điều chính xác:
ngẫu nhiên.randint (0, 9)
Random.choice (danh sách (phạm vi (0, 10)))
Dòng 64 (dòng 70 trong mã mới) cũng đã được thay đổi. Bây giờ thay vì trả lại
chuỗi wordList [wordIndex] , chúng tôi sẽ trả về một danh sách có hai mục. Mục đầu tiên là
wordDict [wordKey] [word Index] . Mục thứ hai là wordKey . Chúng tôi trả lại một danh sách
bởi vì chúng tôi thực sự muốn getRandomWord () trả về hai giá trị, vì vậy hãy đặt chúng
hai giá trị trong một danh sách và trả về danh sách là cách dễ nhất để làm điều này.
Đánh giá một từ điển danh sách
wordDict [wordKey] [wordIndex] có thể trông phức tạp, nhưng nó chỉ là một
biểu hiện bạn có thể đánh giá một bước tại một thời điểm như bất kỳ điều gì khác. Đầu tiên, hãy
tưởng tượng rằng
wordKey có giá trị 'Trái cây' (được chọn trên dòng 65) và wordIndex có
giá trị 5 (được chọn trên dòng 68). Đây là cách wordDict [wordKey] [word Index]
sẽ đánh giá:
wordDict [wordKey] [word Index]
wordDict ['Trái cây'] [5]
['Táo', 'cam', 'chanh', 'vôi', 'lê',
'Dưa hấu', 'nho', 'bưởi', 'anh đào',
'chuối', 'dưa đỏ', 'xoài', 'dâu tây',
'cà chua'] [5]
'dưa hấu'
Trong trường hợp trên, mục trong danh sách mà hàm này trả về sẽ là chuỗi
'dưa hấu' . (Hãy nhớ rằng các chỉ mục bắt đầu từ 0, vì vậy [5] đề cập đến mục thứ 6 trong
danh sách.)
Chỉ có ba thay đổi nữa để thực hiện chương trình của chúng tôi. Hai cái đầu tiên trên dòng
mà chúng ta gọi là hàm getRandomWord () . Hàm được gọi trên dòng 109 và 145
trong chương trình gốc:
144

Trang 159
108.
đúngLetters = ''
109.
secretWord = getRandomWord (từ)
110.
gameIsDone = Sai
...
144.
gameIsDone = Sai
145.
secretWord = getRandomWord (từ)
146.
khác:
Bởi vì hàm getRandomWord () hiện trả về danh sách hai mục thay vì một
chuỗi, secretWord sẽ được chỉ định một danh sách, không phải là một chuỗi. Sau đó chúng tôi sẽ
phải thay đổi
mã như sau:
108. đúngLetters = ''
109. secretWord = getRandomWord (từ)
110. secretKey = secretWord [1]
111. secretWord = secretWord [0]
112. gameIsDone = Sai
...
144. gameIsDone = Sai
145. secretWord = getRandomWord (từ)
146. secretKey = secretWord [1]
147. secretWord = secretWord [0]
148. khác:
Với những thay đổi ở trên, secretWord trước tiên là danh sách hai mục. Sau đó, chúng tôi thêm
một cái mới
biến có tên secretKey và đặt nó vào mục thứ hai trong secretWord . Sau đó, chúng tôi thiết lập
secretWord chính nó đến mục đầu tiên trong danh sách secretWord . Đó có nghĩa là
secretWord sau đó sẽ là một chuỗi.
Nhiều bài tập
Nhưng có một cách dễ dàng hơn bằng cách thực hiện một mẹo nhỏ với các câu lệnh gán. Thử gõ
sau đây vào vỏ:
>>> a, b, c = ['táo', 'mèo', 42]
>>> một
'táo'
>>> b
'những con mèo'
>>> c
145
9 - Hangman

Trang 160
42
>>>
Mẹo nhỏ là đặt cùng một số biến (được phân cách bằng dấu phẩy) ở bên trái của
dấu = như trong danh sách ở phía bên phải của dấu =. Python sẽ tự động gán
giá trị của mục thứ nhất trong danh sách cho biến thứ nhất, giá trị của mục thứ hai đến giá trị thứ
hai
biến, và như vậy. Nhưng nếu bạn không có cùng số lượng biến ở bên trái như
có các mục trong danh sách ở phía bên phải, trình thông dịch Python sẽ báo lỗi cho bạn.
>>> a, b, c, d = ['táo', 'mèo', 42]
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "<pyshell # 8>", dòng 1, trong <mô-đun>
a, b, c, d = ['táo', 'mèo', 42]
ValueError: cần nhiều hơn 3 giá trị để giải nén
>>> a, b, c, d = ['táo', 'mèo']
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "<pyshell # 9>", dòng 1, trong <mô-đun>
a, b, c = ['táo', 'mèo']
ValueError: cần nhiều hơn 2 giá trị để giải nén
>>>
Vì vậy, chúng ta nên thay đổi mã của mình trong Hangman để sử dụng thủ thuật này, điều đó có
nghĩa là
chương trình sử dụng ít dòng mã hơn.
118. đúngLetters = ''
119. secretWord, secretKey = getRandomWord (từ)
120. gameIsDone = Sai
...
155. gameIsDone = Sai
156. secretWord, secretKey = getRandomWord (từ)
157. khác:
In danh mục Word cho người chơi
Thay đổi cuối cùng mà chúng tôi sẽ thực hiện là thêm một lệnh gọi () đơn giản để báo cho người
chơi biết
tập hợp các từ họ đang cố gắng đoán. Bằng cách này, khi người chơi chơi trò chơi, họ sẽ
biết nếu từ bí mật là một động vật, màu sắc, hình dạng, hoặc trái cây. Thêm dòng mã này sau
dòng
112. Đây là mã gốc:
146

Trang 161
112. trong khi Đúng:
113. displayBoard (HANGMANPICS, missLetters,
đúngLetters, secretWord)
Thêm dòng để chương trình của bạn trông như thế này:
122. trong khi Đúng:
123. print ('Từ bí mật nằm trong bộ:' + secretKey)
124. displayBoard (HANGMANPICS, missLetters,
đúngLetters, secretWord)
Bây giờ chúng tôi đã thực hiện với những thay đổi của chúng tôi. Thay vì chỉ một danh sách các
từ, từ bí mật
sẽ được chọn từ nhiều danh sách từ khác nhau. Chúng tôi cũng sẽ cho người chơi biết bộ nào
từ từ bí mật là từ. Hãy thử chơi phiên bản mới này. Bạn có thể dễ dàng thay đổi
từ điển trên dòng 59 để bao gồm nhiều bộ từ hơn.
Tóm lược
Chúng ta đã xong với Hangman. Đây là một chương dài và một số khái niệm mới có
đã được giới thiệu. Nhưng Hangman đã là trò chơi tiên tiến nhất của chúng tôi. Khi trò chơi của
bạn nhận được
ngày càng phức tạp hơn, sẽ là một ý tưởng tốt để phác thảo một biểu đồ dòng chảy trên giấy về
những gì
xảy ra trong chương trình của bạn.
Phương thức là các hàm được liên kết với các giá trị. Các giá trị trả về của các phương thức
phụ thuộc vào các giá trị mà phương thức được liên kết với.
cho các vòng lặp lặp qua các mục trong danh sách. Các phạm vi () chức năng thường được sử
dụng với cho
các vòng lặp vì đó là một cách dễ dàng để tạo danh sách các số liên tiếp.
Khác nếu các câu lệnh (sử dụng từ khóa elif ) sẽ thực thi khối của chúng nếu chúng
điều kiện là True và điều kiện if và elif trước đó là false
Từ điển rất giống với danh sách ngoại trừ việc chúng có thể sử dụng bất kỳ giá trị nào cho một
chỉ mục. Các
các chỉ mục trong từ điển được gọi là khóa. Khóa có thể là chuỗi, số nguyên hoặc bất kỳ giá trị
nào
loại dữ liệu.
147
9 - Hangman

Trang 162
Các chủ đề được đề cập trong Chương này:
● Trí tuệ nhân tạo
● Danh sách tài liệu tham khảo

● Đánh giá ngắn mạch

● Các None Giá trị

Bây giờ chúng tôi sẽ tạo ra một trò chơi Tic Tac Toe nơi người chơi chơi với một nhân tạo đơn
giản
Sự thông minh. Một trí tuệ nhân tạo (hoặc AI ) là một chương trình máy tính có thể
phản ứng thông minh với các động thái của người chơi. Trò chơi này không giới thiệu bất kỳ
phức tạp
khái niệm mới. Chúng ta sẽ thấy rằng trí thông minh nhân tạo chơi Tic Tac Toe thực sự chỉ là
một vài dòng mã
Tic Tac Toe là một trò chơi đơn giản để chơi với giấy và bút chì giữa hai người. Một
người chơi là X và người chơi khác là O. Trên một ô vuông chín đơn giản (mà chúng ta gọi là
bảng), người chơi thay phiên nhau đặt X hoặc O o) n bảng của họ. Nếu một người chơi được ba
dấu của họ trên bảng liên tiếp, cột hoặc một trong hai đường chéo, họ giành chiến thắng.
Hầu hết các trò chơi của Tic Tac Toe kết thúc với tỷ số hòa xảy ra khi bảng được lấp đầy
không có người chơi có ba điểm liên tiếp. Thay vì người chơi thứ hai, nhân tạo của chúng tôi
thông minh sẽ thực hiện các động thái chống lại người dùng. Bạn có thể tìm hiểu thêm về Tic
Tac Toe từ
Wikipedia: http://en.wikipedia.org/wiki/Tic-tac-toe
Mặc dù chương này có thể không giới thiệu nhiều khái niệm lập trình mới, nhưng nó thực hiện
sử dụng kiến thức lập trình hiện có của chúng tôi để tạo một trình phát Tic Tac Toe thông minh.
Hãy bắt đầu bằng cách xem xét một chương trình mẫu.
148

Trang 163
Chạy mẫu của Tic Tac Toe
Chào mừng bạn đến với Tic Tac Toe!
Bạn có muốn là X hoặc O?
X
Máy tính sẽ đi trước.
| |
Ôi | |
| |
-----------
| |
| |
| |
-----------
| |
| |
| |
Bước tiếp theo của bạn là gì? (1-9)
3
| |
Ôi | |
| |
-----------
| |
| |
| |
-----------
| |
Ôi | | X
| |
Bước tiếp theo của bạn là gì? (1-9)
4
| |
Ôi | | Ôi
| |
-----------
| |
X | |
| |
-----------
| |
Ôi | | X
| |
Bước tiếp theo của bạn là gì? (1-9)
5
| |
Ôi | Ôi | Ôi
| |
-----------
| |
X | X |
| |
-----------
| |
149
10 - Ngón chân Tic Tac

Trang 164
Ôi | | X
| |
Máy tính đã đánh bại bạn! Bạn đã thua.
Bạn có muốn chơi lại không? (có hay không)
Không

Mã nguồn của Tic Tac Toe


Trong cửa sổ trình chỉnh sửa tệp mới, nhập mã nguồn này và lưu dưới dạng tictactoe.py . Sau đó
chạy
trò chơi bằng cách nhấn F5. Bạn không cần phải gõ chương trình này trước khi đọc nó
chương. Bạn cũng có thể tải xuống mã nguồn bằng cách truy cập trang web tại URL
http://inventwithpython.com/ch CHƯƠNG10 và làm theo các hướng dẫn trên trang web.
tictactoe.py
Mã này có thể được tải xuống từ http://inventwithpython.com/tictactoe.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. # Tic Tac
2.
3. nhập ngẫu nhiên
4.
5. def drawBoard (bảng):
6. # Hàm này in ra bảng mà nó đã
thông qua.
7.
8. # "board" là danh sách 10 chuỗi đại diện cho
bảng (bỏ qua chỉ số 0)
9. in ('| |')
10. in ('' + bảng [7] + '|' + bảng [8] + '|' +
bảng [9])
11. in ('| |')
12. in ('-----------')
13. in ('| |')
14. in ('' + bảng [4] + '|' + bảng [5] + '|' +
bảng [6])
15. in ('| |')
16. in ('-----------')
17. in ('| |')
18. in ('' + bảng [1] + '|' + bảng [2] + '|' +
bảng [3])
19. in ('| |')
20.
21. def inputPlayerLetter ():
22. # Hãy để người chơi gõ chữ cái mà họ muốn trở thành.
23. # Trả về một danh sách với chữ cái của người chơi là
mục đầu tiên và thư của máy tính là mục thứ hai.
24. thư = ''
25. trong khi không (chữ cái == 'X' hoặc chữ cái == 'O'):
26.
in ('Bạn muốn trở thành X hay O?')
150

Trang 165
27.
chữ = đầu vào (). trên ()
28.
29. # yếu tố đầu tiên trong bộ dữ liệu là của người chơi
thư, thứ hai là thư của máy tính.
30. nếu chữ == 'X':
31.
trở lại ['X', 'O']
32. khác:
33.
trở lại ['O', 'X']
34.
35. def whoGoesFirst ():
36. # Chọn ngẫu nhiên người chơi đi trước.
37. nếu ngẫu nhiên.randint (0, 1) == 0:
38.
trả lại 'máy tính'
39. khác:
40.
trả lại 'người chơi'
41.
42. def playAgain ():
43. # Hàm này trả về True nếu người chơi muốn
chơi lại, nếu không nó trả về Sai.
44. in ('Bạn có muốn chơi lại không? (Có hoặc không)')
45. return return (). Lower (). Startedwith ('y')
46.
47. def makeMove (bảng, thư, di chuyển):
48. bảng [di chuyển] = thư
49.
50. def isWinner (bo, le):
51. # Đưa ra một bảng và thư của người chơi, chức năng này
Trả về Đúng nếu người chơi đó đã thắng.
52. # Chúng tôi sử dụng bo thay vì bảng và le thay vì chữ
vì vậy chúng ta không phải gõ nhiều.
53. return ((bo [7] == le và bo [8] == le và bo [9] == le)
hoặc # trên đầu trang
54. (bo [4] == le và bo [5] == le và bo [6] == le) hoặc #
ở giữa
55. (bo [1] == le và bo [2] == le và bo [3] == le) hoặc #
phía dưới
56. (bo [7] == le và bo [4] == le và bo [1] == le) hoặc #
phía bên trái
57. (bo [8] == le và bo [5] == le và bo [2] == le) hoặc #
xuống giữa
58. (bo [9] == le và bo [6] == le và bo [3] == le) hoặc #
phía bên phải
59. (bo [7] == le và bo [5] == le và bo [3] == le) hoặc #
đường chéo
60. (bo [9] == le và bo [5] == le và bo [1] == le)) #
đường chéo
61.
62. def getBoardCopy (bảng):
63. # Tạo một bản sao của danh sách bảng và trả lại
bản sao.
64. dupeBoard = []
65.
66. cho tôi trong hội đồng quản trị:
67.
dupeBoard.append (i)
151
10 - Ngón chân Tic Tac

Trang 166
68.
69. trả lại bản sao
70.
71. def isSpaceFree (bảng, di chuyển):
72. # Trả về true nếu di chuyển đã qua là miễn phí trên pass
bảng.
73. bảng trở lại [di chuyển] == ''
74.
75. def getPlayerMove (bảng):
76. # Hãy để người chơi gõ di chuyển.
77. di chuyển = ''
78. trong khi di chuyển không trong '1 2 3 4 5 6 7 8 9'.split () hay không
isSpaceFree (bảng, int (di chuyển)):
79.
in ('Bước tiếp theo của bạn là gì? (1-9)')
80.
di chuyển = đầu vào ()
81. return int (di chuyển)
82.
83. def selectRandomMoveFromList (bảng, di chuyểnList):
84. # Trả về di chuyển hợp lệ từ danh sách đã thông qua trên
thông qua hội đồng quản trị.
85. # Trả về Không nếu không có di chuyển hợp lệ.
86.ossibleMoves = []
87. cho tôi trong MovList:
88.
nếu isSpaceFree (bảng, i):
89.
khả năngMoves.append (i)
90.
91. if len (couldMoves)! = 0:
92.
trả về Random.choice (couldMoves)
93. khác:
94.
trở về Không
95.
96. def getComputerMove (bảng, máy tínhLetter):
97. # Đưa ra một bảng và thư của máy tính, xác định
nơi để di chuyển và trở lại di chuyển đó.
98. nếu máy tínhLetter == 'X':
99.
người chơiLetter = 'O'
100. khác:
101.
người chơiLetter = 'X'
102.
103. # Đây là thuật toán của chúng tôi cho Tic Tac Toe AI của chúng tôi:
104. # Đầu tiên, hãy kiểm tra xem chúng tôi có thể giành chiến thắng trong bước tiếp theo không
105. cho i trong phạm vi (1, 10):
106.
sao chép = getBoardCopy (bảng)
107.
nếu isSpaceFree (bản sao, i):
108.
makeMove (bản sao, máy tínhLetter, i)
109.
if isWinner (bản sao, computerLetter):
110.
trả lại tôi
111.
112. # Kiểm tra xem người chơi có thể giành chiến thắng trong lần di chuyển tiếp theo không, và
chặn chúng
113. cho i trong phạm vi (1, 10):
114.
sao chép = getBoardCopy (bảng)
115.
nếu isSpaceFree (bản sao, i):
116.
makeMove (sao chép, playerLetter, i)
152

Trang 167
117.
if isWinner (sao chép, playerLetter):
118.
trả lại tôi
119.
120. # Hãy thử lấy một trong các góc, nếu chúng miễn phí.
121. di chuyển = selectRandomMoveFromList (bảng, [1, 3, 7, 9])
122. nếu di chuyển! = Không có:
123.
di chuyển trở lại
124.
125. # Hãy thử lấy trung tâm, nếu nó miễn phí.
126. nếu isSpaceFree (bảng, 5):
127.
trở về 5
128.
129. # Di chuyển trên một trong các bên.
130. return selectRandomMoveFromList (bảng, [2, 4, 6, 8])
131.
132. def isBoardFull (bảng):
133. # Trả về Đúng nếu mọi khoảng trống trên bảng đã được
Lấy. Nếu không thì trả về Sai.
134. cho i trong phạm vi (1, 10):
135.
nếu isSpaceFree (bảng, i):
136.
trả về sai
137. trả lại đúng
138.
139.
140. in ('Chào mừng bạn đến với Tic Tac Toe!')
141.
142. trong khi Đúng:
143. # Đặt lại bảng
144. theBoard = [''] * 10
145. playerLetter, computerLetter = inputPlayerLetter ()
146. lượt = whoGoesFirst ()
147. print ('' 'Turn +' sẽ xuất hiện trước. ')
148. gameIsPlay = True
149.
150. trong khi gameIsPlay:
151.
nếu bật == 'người chơi':
152.
# Biến lần lượt.
153.
drawBoard (theBoard)
154.
di chuyển = getPlayerMove (theBoard)
155.
makeMove (theBoard, playerLetter, di chuyển)
156.
157.
if isWinner (theBoard, playerLetter):
158.
drawBoard (theBoard)
159.
in ('Hoan hô! Bạn đã thắng trò chơi!')
160.
gameIsPlay = Sai
161.
khác:
162.
if isBoardFull (theBoard):
163.
drawBoard (theBoard)
164.
in ('Trò chơi là hòa!')
165.
phá vỡ
166.
khác:
167.
biến = 'máy tính'
168.
169.
khác:
153
10 - Ngón chân Tic Tac

Trang 168
170.
# Đến lượt máy tính.
171.
di chuyển = getComputerMove (theBoard,
máy tính tốt hơn)
172.
makeMove (theBoard, computerLetter, di chuyển)
173.
174.
if isWinner (theBoard, computerLetter):
175.
drawBoard (theBoard)
176.
in ('Máy tính đã đánh bại bạn! Bạn
thua.')
177.
gameIsPlay = Sai
178.
khác:
179.
if isBoardFull (theBoard):
180.
drawBoard (theBoard)
181.
in ('Trò chơi là hòa!')
182.
phá vỡ
183.
khác:
184.
biến = 'người chơi'
185.
186. nếu không chơiAgain ():
187.
phá vỡ

Thiết kế chương trình


Hình 10-1: Biểu đồ luồng cho Tic Tac Toe
154

Trang 169
Tic Tac Toe là một trò chơi rất dễ dàng và ngắn để chơi trên giấy. Trong máy tính Tic Tac Toe của
chúng tôi
trò chơi, chúng tôi sẽ cho phép người chơi chọn nếu họ muốn là X hoặc O, chọn ngẫu nhiên
người đi
đầu tiên, và sau đó cho phép người chơi và máy tính thay phiên nhau di chuyển trên bảng. Đây là
biểu đồ dòng chảy của trò chơi này có thể trông như thế nào:
Bạn có thể thấy rất nhiều ô ở phía bên trái của biểu đồ là những gì xảy ra trong
đến lượt người chơi. Phía bên phải của biểu đồ cho thấy những gì xảy ra trong lượt của máy
tính. Các
Người chơi có thêm một hộp để vẽ bảng vì máy tính không cần bảng
in trên màn hình. Sau khi người chơi hoặc máy tính thực hiện di chuyển, chúng tôi kiểm tra xem
họ đã thắng hay
gây ra một sự ràng buộc, và sau đó các trò chơi chuyển sang. Nếu máy tính hoặc người chơi quan
hệ hoặc thắng
Trò chơi, chúng tôi hỏi người chơi nếu họ muốn chơi lại.
Đại diện cho Hội đồng quản trị là Dữ liệu
Đầu tiên, chúng ta cần tìm hiểu làm thế nào chúng ta sẽ đại diện cho bảng như một biến. Trên
giấy, bảng Tic Tac Toe được vẽ dưới dạng một cặp đường ngang và một cặp dọc
các dòng, với dấu X, O hoặc khoảng trống trong mỗi chín khoảng trắng.
Trong chương trình của chúng tôi, chúng tôi sẽ đại diện cho bảng Tic Tac Toe như một danh
sách các chuỗi. Mỗi
chuỗi sẽ đại diện cho một trong chín vị trí trên bảng. Chúng tôi sẽ cung cấp một số cho mỗi
của các không gian trên bảng. Để dễ nhớ chỉ mục nào trong danh sách dành cho
Phần nào, chúng ta sẽ phản chiếu các số trên bàn phím của bàn phím. Xem hình 10-2.
Hình 10-2: Bảng sẽ được đánh số như bàn phím số của bàn phím.
Các chuỗi sẽ là 'X' cho trình phát X, 'O' cho trình phát O hoặc chuỗi không gian '
' để đánh dấu một vị trí trên bảng nơi chưa có ai đánh dấu. Chỉ mục của chuỗi trong
danh sách cũng sẽ là số lượng không gian trên bảng.
Vì vậy, nếu chúng ta có một danh sách với mười chuỗi có tên bảng , thì bảng [7] sẽ là phía trên
bên trái
hình vuông trên bảng (có dấu X, O hoặc khoảng trống). bảng [5] sẽ là trung tâm.
Khi người chơi nhập vào nơi họ muốn di chuyển, họ sẽ nhập một số từ 1 đến
155
10 - Ngón chân Tic Tac

Trang 170
9. (Vì không có 0 trên bàn phím, chúng tôi sẽ bỏ qua chuỗi ở chỉ số 0 trong
danh sách.)
Trò chơi AI
Khi chúng ta nói về AI của chúng ta như thế nào
cư xử, chúng ta sẽ nói về
loại không gian trên bảng nó sẽ di chuyển
trên. Để rõ ràng, chúng tôi sẽ dán nhãn ba
các loại khoảng trắng trên bảng Tic Tac Toe:
góc, cạnh, và trung tâm. Hình 10-3
là một biểu đồ về mỗi không gian là gì:
AI cho trò chơi này sẽ đi theo
thuật toán đơn giản. Một thuật toán là một
loạt hướng dẫn để tính toán
một cái gì đó Đây là một rất lỏng lẻo trong trường hợp
thuật toán AI Tic Toe Toe của chúng tôi,
chuỗi các bước sẽ xác định đó là
nơi tốt nhất để di chuyển. Không có gì trong
mã có nội dung: "Những dòng này là một thuật toán." giống như có với def-block của
hàm. Chúng tôi
chỉ cần xem xét thuật toán AI là tất cả các mã được sử dụng trong chương trình của chúng tôi để
xác định
Bước tiếp theo của AI.
Thuật toán của chúng tôi sẽ có các bước sau:
1. Trước tiên, hãy xem nếu có một động thái nào đó, máy tính có thể thực hiện điều đó sẽ thắng
trò chơi. Nếu đó là,
di chuyển Nếu không, hãy đến bước 2.
2. Xem nếu có một động thái người chơi có thể thực hiện sẽ làm cho máy tính bị mất
trò chơi. Nếu có, chúng ta nên di chuyển đến đó để chặn người chơi. Nếu không, hãy đến bước 3.
3. Kiểm tra xem có bất kỳ không gian góc nào (không gian 1, 3, 7 hoặc 9) có miễn phí
không. (Chúng tôi luôn muốn
lấy một mảnh góc thay vì trung tâm hoặc một mảnh bên.) Nếu không có mảnh góc nào là miễn
phí,
sau đó chuyển sang bước 4.
4. Kiểm tra xem trung tâm có miễn phí không. Nếu vậy, di chuyển đến đó. Nếu không, hãy đến
bước 5.
5. Di chuyển trên bất kỳ phần bên nào (khoảng trắng 2, 4, 6 hoặc 8). Không còn bước nào nữa,
bởi vì nếu chúng ta đã đạt đến bước 5 thì không gian bên là khoảng trống duy nhất còn lại.
Tất cả điều này diễn ra trong "Nhận di chuyển của máy tính." hộp trên biểu đồ dòng chảy của
chúng tôi. Chúng tôi có thể thêm
thông tin này cho biểu đồ dòng chảy của chúng tôi như thế này:
Hình 10-3: Vị trí của mặt bên, góc và vị trí trung tâm.
156

Trang 171
Hình 10-4: Năm bước của thuật toán "Di chuyển máy tính".
Các mũi tên rời đi vào hộp "Kiểm tra xem máy tính có thắng không".
Chúng tôi sẽ thực hiện thuật toán này dưới dạng mã trong hàm getComputerMove () của chúng
tôi và
các hàm khác mà getComputerMove () gọi.
Cách thức hoạt động của mã: Dòng 1 đến 81
Bây giờ chúng ta đã biết về cách chúng ta muốn chương trình hoạt động, hãy xem từng dòng
làm.
Bắt đầu chương trình
1. # Tic Tac
2.
3. nhập ngẫu nhiên
Một vài dòng đầu tiên là một nhận xét và nhập mô-đun ngẫu nhiên để chúng ta có thể
sử dụng hàm randint () trong trò chơi của chúng tôi.
157
10 - Ngón chân Tic Tac

Trang 172
In bảng trên màn hình
5. def drawBoard (bảng):
6. # Hàm này in ra bảng mà nó đã
thông qua.
7.
8. # "board" là danh sách 10 chuỗi đại diện cho
bảng (bỏ qua chỉ số 0)
9. in ('| |')
10. in ('' + bảng [7] + '|' + bảng [8] + '|' +
bảng [9])
11. in ('| |')
12. in ('-----------')
13. in ('| |')
14. in ('' + bảng [4] + '|' + bảng [5] + '|' +
bảng [6])
15. in ('| |')
16. in ('-----------')
17. in ('| |')
18. in ('' + bảng [1] + '|' + bảng [2] + '|' +
bảng [3])
19. in ('| |')
Chức năng này sẽ in ra bảng trò chơi, được đánh dấu theo chỉ dẫn của tham số bảng .
Hãy nhớ rằng bảng của chúng tôi được biểu diễn dưới dạng danh sách mười chuỗi, trong đó
chuỗi ở chỉ số 1
là dấu trên không gian 1 trên bảng Tic Tac Toe. (Và hãy nhớ rằng chúng ta bỏ qua chuỗi
tại chỉ mục 0 , vì các khoảng trắng được gắn nhãn từ số 1 đến 9.) Nhiều chức năng của chúng tôi
sẽ
làm việc bằng cách chuyển bảng như một danh sách mười chuỗi cho các hàm của chúng ta. Hãy
chắc chắn để có được khoảng cách
ngay trong chuỗi được in, nếu không bảng sẽ trông buồn cười khi được in
trên màn hình.
Chỉ là một ví dụ, đây là một số giá trị mà tham số bảng có thể có (trên
bên trái) và hàm drawBoard () sẽ in ra những gì:
Bảng 10-1: Ví dụ về các giá trị của bảng và đầu ra từ
drawBoard (bảng) cuộc gọi.
cấu trúc dữ liệu bảng
drawBoard (board) đầu ra
['', '', '', '', 'X',
'O', '', 'X', '', 'O']
| |
X | | Ôi
| |
-----------
| |
X | Ôi |
| |
-----------
| |
| |
158

Trang 173
| |
['', 'O', 'O', '', '',
'X', '', '', '', '']
| |
| |
| |
-----------
| |
| X |
| |
-----------
| |
Ôi | Ôi |
| |
['', '', '', '', '',
'', '', '', '', '']
| |
| |
| |
-----------
| |
| |
| |
-----------
| |
| |
| |
['', 'X', 'X', 'X', 'X',
'X', 'X', 'X', 'X', 'X']
| |
X | X | X
| |
-----------
| |
X | X | X
| |
-----------
| |
X | X | X
| |
['0', '1', '2', '3', '4',
'5', '6', '7', '8', '9']
| |
7 | 8 | 9
| |
-----------
| |
4 | 5 | 6
| |
-----------
| |
1 | 2 | 3
| |
159
10 - Ngón chân Tic Tac

Trang 174
Bảng thứ hai đến bảng cuối cùng chứa đầy X không thể xảy ra (trừ khi X
người chơi đã bỏ qua tất cả các lượt của người chơi O!) Và bảng cuối cùng có các chuỗi chữ số
thay vì
X và O, là các chuỗi không hợp lệ cho bảng. Nhưng hàm drawBoard () không
quan tâm. Nó chỉ in tham số bảng mà nó đã được thông qua. Chương trình máy tính chỉ làm
chính xác những gì bạn nói với họ, ngay cả khi bạn nói với họ những điều sai trái để làm. Chúng
tôi sẽ chỉ làm
chắc chắn các chuỗi không hợp lệ này không được đưa vào danh sách đã qua ở vị trí đầu tiên.
Để Người chơi là X hoặc O
21. def inputPlayerLetter ():
22. # Hãy để người chơi gõ chữ cái mà họ muốn trở thành.
23. # Trả về một danh sách với chữ cái của người chơi là
mục đầu tiên và thư của máy tính là mục thứ hai.
24. thư = ''
25. trong khi không (chữ cái == 'X' hoặc chữ cái == 'O'):
26.
in ('Bạn muốn trở thành X hay O?')
27.
chữ = đầu vào (). trên ()
Các inputPlayerLetter () là một hàm đơn giản. Nó hỏi người chơi có muốn trở thành X không
hoặc O, và sẽ tiếp tục yêu cầu các cầu thủ (với khi loop) cho đến các loại máy nghe nhạc trong
một X
hoặc O. Lưu ý trên dòng 26 rằng chúng tôi tự động thay đổi chuỗi được trả về bởi cuộc gọi thành
nhập () vào các chữ cái viết hoa bằng phương thức chuỗi trên () .
Các trong khi điều kiện vòng lặp chứa dấu ngoặc đơn, có nghĩa là sự biểu hiện bên trong
dấu ngoặc đơn được đánh giá đầu tiên. Nếu biến chữ được đặt thành 'X' , biểu thức sẽ
đánh giá như thế này:
trong khi không (chữ cái == 'X' hoặc chữ cái == 'O'):
trong khi không ('X' == 'X' hoặc 'X' == 'O'):
trong khi không (Đúng hay Sai):
trong khi không (Đúng):
trong khi không đúng
trong khi sai:
Như bạn có thể thấy, nếu chữ cái có giá trị 'X' hoặc 'O' , thì điều kiện của vòng lặp sẽ là
160

Trang 175
Sai và cho phép thực hiện chương trình tiếp tục.
29. # yếu tố đầu tiên trong bộ dữ liệu là của người chơi
thư, thứ hai là thư của máy tính.
30. nếu chữ == 'X':
31.
trở lại ['X', 'O']
32. khác:
33.
trở lại ['O', 'X']
Hàm này trả về một danh sách với hai mục. Mục đầu tiên (nghĩa là chuỗi ở chỉ số 0 )
sẽ là chữ cái của người chơi và mục thứ hai (nghĩa là chuỗi ở chỉ số 1 ) sẽ là
thư của máy tính. Câu lệnh if-other này chọn danh sách thích hợp để trả về.
Quyết định ai đi trước
35. def whoGoesFirst ():
36. # Chọn ngẫu nhiên người chơi đi trước.
37. nếu ngẫu nhiên.randint (0, 1) == 0:
38.
trả lại 'máy tính'
39. khác:
40.
trả lại 'người chơi'
Hàm whoGoesFirst () thực hiện lật đồng xu ảo để xác định ai đi trước,
máy tính hoặc máy nghe nhạc. Thay vì lật một đồng tiền thực tế, mã này nhận được một cách
ngẫu nhiên
số 0 hoặc 1 bằng cách gọi hàm Random.randint () . Nếu chức năng này gọi
trả về 0, hàm whoGoesFirst () trả về chuỗi 'máy tính' . Nếu không thì,
hàm trả về chuỗi 'player' . Mã gọi hàm này sẽ sử dụng
Trả về giá trị để biết ai sẽ thực hiện bước đầu tiên của trò chơi.
Yêu cầu người chơi chơi lại
42. def playAgain ():
43. # Hàm này trả về True nếu người chơi muốn
chơi lại, nếu không nó trả về Sai.
44. in ('Bạn có muốn chơi lại không? (Có hoặc không)')
45. return return (). Lower (). Startedwith ('y')
Hàm playAgain () hỏi người chơi nếu họ muốn chơi một trò chơi khác. Các
Hàm trả về Đúng nếu người chơi nhập vào 'có' hoặc 'CÓ' hoặc 'y' hoặc bất cứ điều gì
bắt đầu bằng chữ Y. Đối với bất kỳ phản hồi nào khác, hàm trả về Sai . Lê ̣nh của
phương thức gọi trên dòng 151 là quan trọng. Giá trị trả về từ cuộc gọi đến đầu vào ()
Hàm là một chuỗi có phương thức low () được gọi trên nó. Các thấp () phương pháp
trả về một chuỗi khác (chuỗi chữ thường) và chuỗi đó có startwith ()
phương thức được gọi trên đó, truyền đối số 'y' .
161
10 - Ngón chân Tic Tac

Trang 176
Không có vòng lặp, bởi vì chúng tôi giả sử rằng nếu người dùng nhập bất cứ thứ gì ngoài chuỗi
bắt đầu bằng 'y' , họ muốn dừng chơi. Vì vậy, chúng tôi chỉ yêu cầu người chơi một lần.
Đặt một dấu trên bảng
47. def makeMove (bảng, thư, di chuyển):
48. bảng [di chuyển] = thư
Hàm makeMove () rất đơn giản và chỉ có một dòng. Các tham số là một danh sách
với mười chuỗi được đặt tên bảng , một trong những chữ cái của người chơi (có tên 'X' hoặc 'O' )
chữ cái và một vị trí trên bảng nơi người chơi đó muốn đến (đó là một số nguyên từ
1 đến 9 ) được đặt tên di chuyển .
Nhưng đợi một chút. Bạn có thể nghĩ rằng chức năng này không làm được gì nhiều. Dường như
thay đổi một trong các mục trong danh sách bảng thành giá trị bằng chữ . Nhưng bởi vì mã này là
trong một hàm, biến bảng sẽ bị quên khi chúng ta thoát khỏi hàm này và để lại
phạm vi chức năng.
Trên thực tế, đây không phải là trường hợp. Điều này là do danh sách là đặc biệt khi bạn vượt
qua chúng như
đối số cho các chức năng. Điều này là do bạn chuyển một tham chiếu đến danh sách chứ không
phải chính danh sách đó.
Hãy tìm hiểu về sự khác biệt giữa danh sách và danh sách tài liệu tham khảo.
Danh sách tài liệu tham khảo
Hãy thử nhập các mục sau vào vỏ:
>>> thư rác = 42
>>> phô mai = thư rác
>>> thư rác = 100
>>> thư rác
100
>>> phô mai
42
Điều này có ý nghĩa từ những gì chúng ta biết cho đến nay. Chúng tôi gán 42 cho biến thư rác và
sau đó chúng tôi sao chép giá trị trong thư rác và gán nó cho phô mai biến . Khi chúng ta sau này
thay đổi giá trị trong thư rác thành 100 , điều này không ảnh hưởng đến giá trị trong phô
mai . Đây là vì
thư rác và phô mai là các biến khác nhau lưu trữ các giá trị khác nhau.
Nhưng danh sách không hoạt động theo cách này. Khi bạn gán danh sách cho một biến có dấu = ,
bạn
đang thực sự chỉ định một tài liệu tham khảo cho danh sách. Một tài liệu tham khảo là một giá
trị trỏ đến một số bit
dữ liệu và tham chiếu danh sách là giá trị trỏ đến danh sách. Đây là một số mã sẽ
làm cho điều này dễ hiểu hơn Nhập cái này vào vỏ:
162

Trang 177
>>> thư rác = [0, 1, 2, 3, 4, 5]
>>> phô mai = thư rác
>>> phô mai [1] = 'Xin chào!'
>>> thư rác
[0, 'Xin chào!', 2, 3, 4, 5]
>>> phô mai
[0, 'Xin chào!', 2, 3, 4, 5]
Lưu ý rằng dòng phô mai = thư rác sao chép danh sách tham chiếu trong thư rác thành phô mai ,
thay vì sao chép chính giá trị danh sách . Điều này là do giá trị được lưu trữ trong thư rác
biến là một tham chiếu danh sách , và không phải là giá trị danh sách . Điều này có nghĩa là các
giá trị được lưu trữ trong
cả thư rác và pho mát đều đề cập đến cùng một danh sách. Chỉ có một danh sách vì danh sách là
không được sao chép, tham chiếu đến danh sách đã được sao chép. Vì vậy, khi bạn sửa đổi phô
mai trong
phô mai [1] = 'Xin chào!' dòng, bạn đang sửa đổi cùng một danh sách mà thư rác đề cập đến.
Đây là lý do tại sao thư rác dường như có cùng giá trị danh sách mà phô mai làm.
Hãy nhớ khi bạn lần đầu tiên biết về các biến, tôi đã nói rằng các biến đó giống như các hộp
có chứa các giá trị. Các biến danh sách thực sự không chứa danh sách, chúng chứa các tham
chiếu
vào danh sách. Dưới đây là một số hình ảnh giải thích những gì xảy ra trong mã bạn vừa nhập:
Hình 10-5: Các biến không có danh sách cửa hàng, mà thay vào đó là các tham chiếu đến danh sách.
Trên dòng đầu tiên, danh sách thực tế không có trong biến thư rác mà là một tham chiếu đến
danh sách. Danh sách này không được lưu trữ trong bất kỳ biến nào.
163
10 - Ngón chân Tic Tac

Trang 178
Hình 10-6: Hai biến lưu trữ hai tham chiếu vào cùng một danh sách.
Khi bạn gán tham chiếu trong thư rác cho phô mai , biến phô mai chứa một
bản sao của tài liệu tham khảo trong thư rác . Bây giờ cả phô mai và thư rác đề cập đến cùng một
danh sách.
Hình 10-7: Thay đổi danh sách thay đổi tất cả các biến có tham chiếu đến danh sách đó.
164

Trang 179
Khi bạn thay đổi danh sách mà phô mai đề cập đến, danh sách mà thư rác đề cập đến cũng được thay
đổi
bởi vì chúng là cùng một danh sách Nếu bạn muốn spam và pho mát để lưu trữ hai danh sách
khác nhau,
bạn phải tạo hai danh sách khác nhau thay vì sao chép một tham chiếu:
>>> thư rác = [0, 1, 2, 3, 4, 5]
>>> phô mai = [0, 1, 2, 3, 4, 5]
Trong ví dụ trên, thư rác và phô mai có hai danh sách khác nhau được lưu trữ trong đó (thậm chí
mặc dù các danh sách này là giống hệt nhau trong nội dung). Bây giờ nếu bạn sửa đổi một trong
các danh sách, nó sẽ không
ảnh hưởng đến cái khác vì thư rác và phô mai có tham chiếu đến hai danh sách khác nhau:
>>> thư rác = [0, 1, 2, 3, 4, 5]
>>> phô mai = [0, 1, 2, 3, 4, 5]
>>> phô mai [1] = 'Xin chào!'
>>> thư rác
[0, 'Xin chào!', 2, 3, 4, 5]
>>> phô mai
[0, 1, 2, 3, 4, 5]
Hình 10-8 cho thấy hai tham chiếu trỏ đến hai danh sách khác nhau như thế nào:
Hình 10-8: Hai biến mỗi lưu trữ tham chiếu đến hai danh sách khác nhau.
165
10 - Ngón chân Tic Tac

Trang 180
Sử dụng danh sách tài liệu tham khảo trong makeMove ()
Chúng ta hãy quay lại hàm makeMove () :
47. def makeMove (bảng, thư, di chuyển):
48. bảng [di chuyển] = thư
Khi chúng ta truyền một giá trị danh sách làm đối số cho tham số bảng , hàm là cục bộ
biến là một bản sao của tài liệu tham khảo, không phải là bản sao của danh sách. Bức thư và di
chuyển
tham số là bản sao của chuỗi và giá trị nguyên mà chúng ta vượt qua. Vì chúng là bản sao, nếu
chúng tôi sửa đổi chữ cái hoặc di chuyển trong chức năng này, các biến ban đầu chúng tôi sử
dụng khi chúng tôi
được gọi là makeMove () sẽ không được sửa đổi. Chỉ các bản sao sẽ được sửa đổi.
Nhưng một bản sao của tài liệu tham khảo vẫn đề cập đến cùng một danh sách mà tài liệu tham
khảo ban đầu đề cập đến.
Vì vậy, nếu chúng ta thay đổi bảng trong chức năng này, danh sách ban đầu được sửa đổi. Khi
nào chúng ta
thoát khỏi hàm makeMove () , bản sao của tham chiếu bị quên cùng với cái khác
thông số. Nhưng vì chúng tôi thực sự đã thay đổi danh sách ban đầu, những thay đổi đó vẫn còn
sau khi chúng ta thoát khỏi hàm Đây là cách hàm makeMove () sửa đổi danh sách mà một
tài liệu tham khảo được thông qua.
Kiểm tra xem người chơi đã thắng chưa
50. def isWinner (bo, le):
51. # Đưa ra một bảng và thư của người chơi, chức năng này
Trả về Đúng nếu người chơi đó đã thắng.
52. # Chúng tôi sử dụng bo thay vì bảng và le thay vì chữ
vì vậy chúng ta không phải gõ nhiều.
53. return ((bo [7] == le và bo [8] == le và bo [9] == le)
hoặc # trên đầu trang
54. (bo [4] == le và bo [5] == le và bo [6] == le) hoặc #
ở giữa
55. (bo [1] == le và bo [2] == le và bo [3] == le) hoặc #
phía dưới
56. (bo [7] == le và bo [4] == le và bo [1] == le) hoặc #
phía bên trái
57. (bo [8] == le và bo [5] == le và bo [2] == le) hoặc #
xuống giữa
58. (bo [9] == le và bo [6] == le và bo [3] == le) hoặc #
phía bên phải
59. (bo [7] == le và bo [5] == le và bo [3] == le) hoặc #
đường chéo
60. (bo [9] == le và bo [5] == le và bo [1] == le)) #
đường chéo
Các dòng 53 đến 60 trong hàm isWinner () thực sự là một câu lệnh if rất dài. Chúng tôi
sử dụng bo và le cho các tham số bảng và chữ cái để chúng ta có ít để gõ vào đây
166

Trang 181
chức năng. (Đây là một mẹo lập trình viên đôi khi sử dụng để giảm số lượng họ cần
kiểu. Hãy chắc chắn để thêm một bình luận, nếu không bạn có thể quên bo và le là gì
có nghĩa là gì.)
Có tám cách có thể để giành chiến thắng tại Tic Tac Toe. Đầu tiên, có một dòng trên đầu trang,
giữa và dưới cùng. Thứ hai, có một dòng xuống bên trái, giữa hoặc phải. Và cuối cùng, có
một trong hai đường chéo. Lưu ý rằng mỗi dòng của điều kiện kiểm tra nếu ba khoảng trắng
bằng với chữ cái được cung cấp (kết hợp với toán tử và toán tử) và chúng tôi sử dụng hoặc
toán tử để kết hợp tám cách khác nhau để giành chiến thắng. Điều này có nghĩa là chỉ một trong
tám cách
phải đúng để chúng tôi nói rằng người chơi sở hữu chữ cái trong le là người chiến thắng.
Hãy giả vờ rằng le là 'O' và bảng trông như thế này:
| |
X | |
| |
-----------
| |
| X |
| |
-----------
| |
Ôi | Ôi | Ôi
| |
Nếu bảng trông như thế, thì bo phải bằng ['', 'O', 'O', 'O', '
',' X ',' ',' X ',' ',' '] . Đây là cách biểu hiện sau khi trở về
từ khóa trên dòng 53 sẽ đánh giá:
Đây là biểu thức như trong mã:
53. return ((bo [7] == le và bo [8] == le và bo [9] == le) hoặc
54. (bo [4] == le và bo [5] == le và bo [6] == le) hoặc
55. (bo [1] == le và bo [2] == le và bo [3] == le) hoặc
56. (bo [7] == le và bo [4] == le và bo [1] == le) hoặc
57. (bo [8] == le và bo [5] == le và bo [2] == le) hoặc
58. (bo [9] == le và bo [6] == le và bo [3] == le) hoặc
59. (bo [7] == le và bo [5] == le và bo [3] == le) hoặc
60. (bo [9] == le và bo [5] == le và bo [1] == le))
Python đầu tiên sẽ thay thế biến bo bằng giá trị bên trong nó:
53. return (('X' == 'O' và '' == 'O' và '' == 'O') hoặc
54. ('' == 'O' và 'X' == 'O' và '' == 'O') hoặc
55. ('O' == 'O' và 'O' == 'O' và 'O' == 'O') hoặc
167
10 - Ngón chân Tic Tac

Trang 182
56. ('X' == 'O' và '' == 'O' và 'O' == 'O') hoặc
57. ('' == 'O' và 'X' == 'O' và 'O' == 'O') hoặc
58. ('' == 'O' và '' == 'O' và 'O' == 'O') hoặc
59. ('X' == 'O' và 'X' == 'O' và 'O' == 'O') hoặc
60. ('' == 'O' và 'X' == 'O' và 'O' == 'O'))
Tiếp theo, Python sẽ đánh giá tất cả những == so sánh bên trong dấu ngoặc đơn với giá trị boolean:
53. return ((Sai và Sai và Sai) hoặc
54. (Sai và Sai và Sai) hoặc
55. (Đúng và Đúng và Đúng) hoặc
56. (Sai và Sai và Đúng) hoặc
57. (Sai và Sai và Đúng) hoặc
58. (Sai và Sai và Đúng) hoặc
59. (Sai và Sai và Đúng) hoặc
60. (Sai và Sai và Đúng))
Sau đó, trình thông dịch Python sẽ đánh giá tất cả các biểu thức bên trong dấu ngoặc đơn:
53. trả lại ((Sai) hoặc
54. (Sai) hoặc
55. (Đúng) hoặc
56. (Sai) hoặc
57. (Sai) hoặc
58. (Sai) hoặc
59. (Sai) hoặc
60. (Sai))
Vì bây giờ chỉ có một giá trị trong dấu ngoặc đơn, chúng ta có thể loại bỏ chúng:
53. trả lại (Sai hoặc
54. Sai hoặc
55. Đúng hay
56. Sai hoặc
57. Sai hoặc
58. Sai hoặc
59. Sai hoặc
60. Sai)
Bây giờ chúng tôi đánh giá biểu thức được kết nối bởi tất cả những người hoặc các nhà khai thác:
53. trả lại (Đúng)
168
Trang 184
Một lần nữa, chúng ta thoát khỏi dấu ngoặc đơn và chúng ta chỉ còn lại một giá trị:
53. trả lại đúng
Vì vậy, với các giá trị cho bo và le , biểu thức sẽ đánh giá là True . Nhớ lại
rằng giá trị của le quan trọng. Nếu le là 'O' và X đã thắng trò chơi, thì isWinner ()
sẽ trả về Sai .
Sao chép dữ liệu bảng
62. def getBoardCopy (bảng):
63. # Tạo một bản sao của danh sách bảng và trả lại
sự trùng lặp.
64. dupeBoard = []
65.
66. cho tôi trong hội đồng quản trị:
67.
dupeBoard.append (i)
68.
69. trả lại bản sao
Hàm getBoardCopy () ở đây để chúng ta có thể dễ dàng tạo một bản sao của một cái đã cho
Danh sách 10 chuỗi đại diện cho một bảng Tic Tac Toe trong trò chơi của chúng tôi. Có những
lúc chúng ta sẽ
muốn thuật toán AI của chúng tôi thực hiện các sửa đổi tạm thời thành bản sao tạm thời của bảng
mà không thay đổi bảng gốc. Trong trường hợp đó, chúng tôi gọi hàm này để tạo một bản sao
của
danh sách của hội đồng quản trị. Danh sách mới thực tế được tạo trên dòng 64, với dấu ngoặc
danh sách trống [] .
Dòng 64 thực sự tạo ra một danh sách hoàn toàn mới và lưu trữ một tham chiếu đến nó
trong dupeBoard . Nhưng
danh sách được lưu trữ trong dupeBoard chỉ là một danh sách trống. Các cho vòng lặp sẽ đi qua
hội đồng quản trị
tham số, nối thêm một bản sao của các giá trị chuỗi trong bảng gốc vào bản sao của chúng tôi
bảng. Cuối cùng, sau vòng lặp, chúng ta sẽ trả về tham chiếu của biến dupeBoard cho
bảng trùng lặp. Vì vậy, bạn có thể thấy hàm getBoardCopy () đang xây dựng một
bản sao của bảng gốc và trả lại một tham chiếu đến bảng mới này, chứ không phải bản gốc
một.
Kiểm tra nếu một không gian trên bảng là miễn phí
71. def isSpaceFree (bảng, di chuyển):
72. # Trả về true nếu di chuyển qua là miễn phí trên
thông qua hội đồng quản trị.
73. bảng trở lại [di chuyển] == ''
Đây là một chức năng đơn giản, được cung cấp một bảng Tic Tac Toe và di chuyển có thể, sẽ trở
lại
nếu di chuyển có sẵn hay không. Hãy nhớ rằng không gian trống trên danh sách bảng của chúng
tôi được đánh dấu là
một chuỗi không gian duy nhất.
169
10 - Ngón chân Tic Tac

Trang 184
Để người chơi nhập di chuyển
75. def getPlayerMove (bảng):
76. # Hãy để người chơi gõ di chuyển.
77. di chuyển = ''
78. trong khi di chuyển không trong '1 2 3 4 5 6 7 8 9'.split () hay không
isSpaceFree (bảng, int (di chuyển)):
79.
in ('Bước tiếp theo của bạn là gì? (1-9)')
80.
di chuyển = đầu vào ()
81. return int (di chuyển)
Hàm getPlayerMove () yêu cầu người chơi nhập số cho khoảng trắng
họ muốn di chuyển. Hàm đảm bảo rằng họ nhập vào một không gian là một không gian hợp lệ
(một
số nguyên 1 đến 9). Nó cũng kiểm tra xem không gian chưa được lấy, đã cho Tic
Bảng Tac Toe chuyển đến chức năng trong tham số bảng .
Hai dòng mã bên trong vòng lặp while chỉ cần yêu cầu người chơi nhập số
từ 1 đến 9. Điều kiện của vòng lặp sẽ tiếp tục lặp, nghĩa là nó sẽ tiếp tục hỏi người chơi
cho một không gian, miễn là điều kiện là True . Điều kiện là Đúng nếu một trong hai
biểu thức ở bên trái hoặc bên phải của hoặc từ khóa là True .
Các biểu hiện trên trái phiếu phụ nếu việc dọn nhà mà người chơi tham gia bằng '1' ,
'2' , '3' , v.v. lên đến '9' bằng cách tạo danh sách với các chuỗi này (với dấu tách ()
phương pháp) và kiểm tra nếu di chuyển trong danh sách này. '1 2 3 4 5 6 7 8 9'.split ()
đánh giá giống như ['1', '2', '3', '4', '5', '6', '7', '8',
'9'] , nhưng dễ gõ hơn.
Các biểu hiện trên đúng kiểm tra bên nếu việc dọn nhà mà người chơi vào là một không gian
trống
trên bảng. Nó kiểm tra điều này bằng cách gọi hàm isSpaceFree () mà chúng ta vừa viết.
Hãy nhớ rằng isSpaceFree () sẽ trả về True nếu di chuyển chúng tôi vượt qua có sẵn trên
bảng. Lưu ý rằng isSpaceFree () mong đợi một số nguyên để di chuyển , vì vậy chúng tôi sử
dụng int ()
chức năng để đánh giá một hình thức di chuyển số nguyên .
Chúng tôi thêm các toán tử không vào cả hai phía để điều kiện sẽ là True khi cả hai
những yêu cầu này không được thực hiện. Điều này sẽ khiến vòng lặp hỏi lại người chơi và
một lần nữa cho đến khi họ bước vào một động thái thích hợp.
Cuối cùng, trên dòng 81, chúng tôi sẽ trả về dạng số nguyên của bất kỳ di chuyển nào mà người
chơi đã nhập.
Hãy nhớ rằng input () trả về một chuỗi, vì vậy chúng ta sẽ muốn sử dụng hàm int () để
đánh giá chuỗi dưới dạng một số nguyên.
Đánh giá ngắn mạch
Bạn có thể nhận thấy có một vấn đề có thể xảy ra trong hàm getPlayerMove () của chúng tôi .
Điều gì xảy ra nếu người chơi nhập vào 'X' hoặc một số chuỗi không nguyên khác? Các động
thái không
170

Trang 185
Biểu thức '1 2 3 4 5 6 7 8 9'.split () ở bên trái hoặc sẽ trả về
Sai như mong đợi, và sau đó chúng tôi sẽ đánh giá biểu thức ở phía bên phải của hoặc
nhà điều hành. Nhưng khi chúng ta chuyển 'X' (sẽ là giá trị di chuyển ) cho int ()
hàm, int ('X') sẽ cho chúng ta một lỗi. Nó cho chúng ta lỗi này vì int ()
Hàm chỉ có thể nhận các chuỗi ký tự số, như '9' hoặc '42' , không phải các chuỗi như 'X'
Để làm ví dụ cho loại lỗi này, hãy thử nhập lỗi này vào trình bao:
>>> int ('42 ')
42
>>> int ('X')
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "<pyshell # 3>", dòng 1, trong <mô-đun>
int ('X')
ValueError: chữ không hợp lệ cho int () với cơ sở
10: 'X'
Nhưng khi bạn chơi trò chơi Tic Tac Toe của chúng tôi và thử nhập 'X' cho di chuyển của bạn,
điều này
lỗi không xảy ra. Lý do là vì trong khi điều kiện vòng lặp là bị ngắn
bị cắt xén.
Điều ngắn mạch có nghĩa là bởi vì biểu thức ở phía bên trái của hoặc
từ khóa (di chuyển không trong '1 2 3 4 5 6 7 8 9'.split () ) ước tính thành True ,
Trình thông dịch Python biết rằng toàn bộ biểu thức sẽ đánh giá là True . Nó không quan trọng
nếu biểu thức ở phía bên phải của từ khóa hoặc đánh giá là Đúng hoặc Sai , bởi vì
chỉ một giá trị ở phía của toán tử hoặc toán tử cần phải là True .
Hãy suy nghĩ về nó: Biểu thức Đúng hoặc Sai đánh giá thành Đúng và biểu thức
Đúng hay Đúng cũng đánh giá là Đúng . Nếu giá trị ở phía bên trái là True , thì không
vấn đề giá trị là gì ở phía bên phải. Vì vậy, Python dừng kiểm tra phần còn lại của
biểu hiện và thậm chí không bận tâm đến việc đánh giá không phải isSpaceFree (bảng, int
(di chuyển)) một phần. Điều này có nghĩa là các hàm int () và isSpaceFree () không bao giờ
gọi là.
Điều này hoạt động tốt với chúng tôi, bởi vì nếu biểu thức ở phía bên phải là True thì
di chuyển không phải là một chuỗi ở dạng số. Điều đó sẽ khiến int () cung cấp cho chúng tôi một
lỗi. Các
chỉ lần di chuyển không trong '1 2 3 4 5 6 7 8 9'.split () ước tính thành Sai
là khi di chuyển không phải là một chuỗi một chữ số. Trong trường hợp đó, lệnh gọi int () sẽ
không cung cấp
chúng tôi một lỗi
Một ví dụ về đánh giá ngắn mạch
Đây là một chương trình ngắn cung cấp một ví dụ tốt về ngắn mạch. Mở một tập tin mới trong
trình chỉnh sửa IDLE và nhập vào chương trình này, lưu nó dưới dạng truefalsefizz.py , sau đó nhấn
F5 để chạy nó.
171
10 - Ngón chân Tic Tac

Trang 186
Đừng thêm các số ở phía bên trái của chương trình, những số chỉ xuất hiện trong cuốn sách này
để làm cho lời giải thích của chương trình dễ hiểu hơn. Các hàm gọi in đậm là
chức năng gọi được đánh giá.
truefalsefizz.py
Mã này có thể được tải xuống từ http://inventwithpython.com/truefalsefizz.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. def TrueFizz (tin nhắn):
2. in (tin nhắn)
3. trả về đúng
4.
5. def falseFizz (tin nhắn):
6. in (tin nhắn)
7. Trả về Sai
số 8.
9. nếu falseFizz ('Mèo') hoặc TrueFizz ('Chó') :
10. in ('Bước 1')
11.
12. nếu TrueFizz ('Xin chào') hoặc TrueFizz ('Tạm biệt'):
13. in ('Bước 2')
14.
15. nếu TrueFizz ('Spam') và TrueFizz ('Cheese') :
16. in ('Bước 3')
17.
18. nếu falseFizz ('Đỏ') và TrueFizz ('Xanh'):
19. in ('Bước 4')
Khi bạn chạy chương trình này, bạn có thể thấy đầu ra (các chữ cái ở bên trái đã được
được thêm vào để làm cho lời giải thích của đầu ra dễ hiểu hơn):
A. Mèo
B. Chó
C. Bước 1
D. Xin chào
E. Bước 2
F. Thư rác
G. Phô mai
H. Bước 3
I màu đỏ
172

Trang 187
Chương trình nhỏ này có hai chức năng: TrueFizz () và falseFizz () . TrueFizz
() sẽ hiển thị thông báo và trả về giá trị True , trong khi falseFizz () sẽ hiển thị
thông báo và trả về giá trị Sai . Điều này sẽ giúp chúng tôi xác định khi nào các chức năng này là
được gọi hoặc khi các chức năng này bị bỏ qua do đoản mạch.
Câu lệnh if đầu tiên (Mèo và Chó)
Câu lệnh if đầu tiên trên dòng 9 trong chương trình nhỏ của chúng tôi trước tiên sẽ đánh
giá TrueFizz () .
Chúng tôi biết điều này xảy ra vì Mèo được in ra màn hình (trên dòng A ở đầu ra). Các
toàn bộ biểu thức vẫn có thể đúng nếu biểu thức ở bên phải của từ khóa là
Đúng . Vì vậy, cuộc gọi TrueFizz ('Chó') một dòng 9 được đánh giá, Chó được in ra
màn hình (trên dòng B ở đầu ra) và True được trả về. On line 9, nếu tuyên bố của
điều kiện đánh giá thành Sai hoặc Đúng , lần lượt đánh giá thành Đúng . Bước 1 là sau đó
In ra màn hình. Không có đoản mạch diễn ra cho đánh giá của biểu thức này.
Tuyên bố thứ hai nếu (Xin chào và tạm biệt)
Câu lệnh if thứ hai trên dòng 12 cũng bị đoản mạch. Đó là bởi vì khi chúng ta gọi
TrueFizz ('Hello') trên dòng 12, nó in Hello (xem dòng D trong đầu ra) và trả về
Đúng . Bởi vì nó không quan trọng ở phía bên phải của từ khóa hoặc Python, Python
thông dịch viên không gọi TrueFizz ('Tạm biệt') . Bạn có thể nói nó không được gọi bởi vì
Tạm biệt không được in ra màn hình. Điều kiện if của câu lệnh là True , vì vậy Bước 2 là
in ra màn hình trên dòng E.
Tuyên bố thứ ba nếu (Spam và Cheese)
Câu lệnh if thứ ba trên dòng 15 không bị đoản mạch. Cuộc gọi đến TrueFizz
('Spam') trả về Đúng , nhưng chúng tôi không biết toàn bộ điều kiện là Đúng hay Sai
bởi vì và nhà điều hành. Vì vậy, Python sẽ gọi TrueFizz ('Cheese') , in ra
Pho mát và trả về True . Điều kiện if của câu lệnh được ước tính là True và
Đúng , lần lượt đánh giá là Đúng . Vì điều kiện là True , Bước 3 được in
đến màn hình trên dòng H.
Thứ tư nếu Tuyên bố (Đỏ và Xanh)
Câu lệnh if thứ tư trên dòng 18 không bị đoản mạch. các FalseFizz
('Red') in cuộc gọi Màu đỏ trên dòng I ở đầu ra và trả về Sai . Bởi vì bên trái
của và từ khóa là Sai , không có vấn đề gì nếu phía bên phải là Đúng hoặc Sai ,
điều kiện sẽ đánh giá thành Sai nào. Vì vậy, TrueFizz ('Blue') không được gọi và
Màu xanh không xuất hiện trên màn hình. Bởi vì điều kiện if của câu lệnh được ước tính là
Sai , Bước 4 cũng không được in ra màn hình.
Đoản mạch có thể xảy ra đối với bất kỳ biểu thức nào bao gồm các toán tử Boolean và
và hoặc . Điều quan trọng cần nhớ là điều này có thể xảy ra; nếu không bạn có thể thấy rằng một
số
các hàm gọi trong biểu thức không bao giờ được gọi và bạn sẽ không hiểu tại sao.
173
10 - Ngón chân Tic Tac

Trang 188
Cách thức hoạt động của mã: Dòng 83 đến 94
Chọn Di chuyển từ Danh sách Di chuyển
83. def selectRandomMoveFromList (bảng, di chuyểnList):
84. # Trả về di chuyển hợp lệ từ danh sách đã thông qua trên
thông qua hội đồng quản trị.
85. # Trả về Không nếu không có di chuyển hợp lệ.
86.ossibleMoves = []
87. cho tôi trong MovList:
88.
nếu isSpaceFree (bảng, i):
89.
khả năngMoves.append (i)
Hàm selectRandomMoveFromList () sẽ được sử dụng cho chúng ta khi chúng ta
triển khai mã cho AI của chúng tôi. Tham số đầu tiên bảng là 10 chuỗi danh sách đó
đại diện cho một bảng Tic Tac Toe. Tham số di chuyển thứ hai là một danh sách các số nguyên
đại diện cho các động thái có thể. Ví dụ: nếu moveList là [1, 3, 7, 9] , điều đó có nghĩa là
chúng ta nên trả lại số cho một trong các không gian góc trên bảng.
Hàm selectRandomMoveFromList () sau đó sẽ chọn một trong những di chuyển đó
từ danh sách có thể . Nó cũng đảm bảo rằng di chuyển mà nó chọn không phải là
đã thực hiện. Để làm điều này, chúng tôi tạo một danh sách trống và gán nó cho khả năng . Các
Vòng lặp for sẽ đi qua danh sách các bước di chuyển đến hàm này trong MovList . Nếu đó
di chuyển có sẵn (mà chúng tôi tìm ra với một cuộc gọi đến isSpaceFree () ), sau đó chúng tôi
thêm nó vào
couldMoves với phương thức append () .
91. if len (couldMoves)! = 0:
92.
trả về Random.choice (couldMoves)
93. khác:
94.
trở về Không
Tại thời điểm này, các possibleMoves danh sách có tất cả các di chuyển được trong movesList
đó cũng là không gian trống trên bảng đại diện bởi bảng . Nếu danh sách không trống, thì
có ít nhất một động thái có thể có thể được thực hiện trên bảng.
Danh sách này có thể trống. Ví dụ: nếu moveList là [1, 3, 7, 9] nhưng
bảng đại diện bởi tham số bảng đã có tất cả các không gian góc đã được thực hiện,
possibleMoves danh sách có thể đã được sản phẩm nào.
Nếu khả năngMoves trống, thì len (couldMoves) sẽ ước tính thành 0 và
mã trong khối khác sẽ thực thi. Lưu ý rằng nó trả về một cái gì đó gọi là Không có .
174

Trang 189
Các None Giá trị
Không có giá trị đặc biệt nào mà bạn có thể gán cho một biến. Các None giá trị đại diện cho
thiếu một giá trị. Không có giá trị duy nhất của kiểu dữ liệu noneType. (Giống như boolean
kiểu dữ liệu chỉ có hai giá trị, kiểu dữ liệu noneType chỉ có một giá trị, Không có .) Nó có thể là
rất hữu ích để sử dụng giá trị Không khi bạn chưa đặt giá trị biến. Dành cho
ví dụ: giả sử bạn có một biến có tên quizAnswer chứa câu trả lời của người dùng
một số câu hỏi trắc nghiệm pop-True. Bạn có thể đặt quizAnswer thành Không nếu người dùng
bỏ qua câu hỏi hoặc không trả lời nó Sử dụng Không có gì sẽ tốt hơn vì nếu bạn đặt nó
thành Đúng hoặc Sai trước khi gán giá trị câu trả lời của người dùng, nó có thể trông giống như
người dùng
đã trả lời câu hỏi mặc dù họ không làm thế.
Gọi đến các chức năng không trả lại bất cứ điều gì (có nghĩa là chúng thoát ra bằng cách đến
cuối của
chức năng và không từ một tuyên bố trở lại) sẽ đánh giá thành Không có . Các None giá trị được
viết
không có dấu ngoặc kép và có chữ "N" và chữ thường "một".
Cách thức hoạt động của mã: Dòng 96 đến 187
Tạo trí tuệ nhân tạo của máy tính
96. def getComputerMove (bảng, máy tínhLetter):
97. # Đưa ra một bảng và thư của máy tính, xác định
nơi để di chuyển và trở lại di chuyển đó.
98. nếu máy tínhLetter == 'X':
99.
người chơiLetter = 'O'
100. khác:
101.
người chơiLetter = 'X'
Hàm getComputerMove () là nơi AI của chúng ta sẽ được mã hóa. Những thông số
là một bảng Tic Tac Toe (trong tham số bảng ) và chữ cái của máy tính là gì
'X' hoặc 'O' ). Một vài dòng đầu tiên chỉ cần gán chữ cái khác cho một biến có tên
người chơi tốt hơn . Điều này cho phép chúng tôi sử dụng cùng một mã, bất kể ai là X và ai là O.
Điều này
Hàm sẽ trả về số nguyên biểu thị không gian mà máy tính sẽ di chuyển.
Hãy nhớ cách thuật toán của chúng tôi hoạt động:
Đầu tiên, hãy xem liệu có một động thái nào mà máy tính có thể thực hiện sẽ giúp trò chơi chiến
thắng. Nếu đó là,
di chuyển Nếu không, đi đến bước thứ hai.
Thứ hai, hãy xem nếu có một động thái người chơi có thể thực hiện sẽ khiến máy tính bị mất
tro choi. Nếu có, chúng ta nên di chuyển đến đó để chặn người chơi. Nếu không, đi đến thứ ba
bậc thang.
Thứ ba, kiểm tra xem có bất kỳ không gian góc nào (không gian 1, 3, 7 hoặc 9) là miễn phí
không. (Chúng tôi luôn muốn
để lấy một mảnh góc thay vì trung tâm hoặc một mảnh bên.) Nếu không có mảnh góc nào là miễn
phí, thì
175
10 - Ngón chân Tic Tac

Trang 190
đi đến bước thứ tư
Thứ tư, kiểm tra nếu trung tâm là miễn phí. Nếu vậy, di chuyển đến đó. Nếu không, hãy đến
bước thứ năm.
Thứ năm, di chuyển trên bất kỳ phần bên nào (khoảng trắng 2, 4, 6 hoặc 8). Không còn bước nào
nữa,
bởi vì nếu chúng ta đã đạt đến bước này thì các không gian bên là khoảng trống duy nhất còn lại.
Máy tính kiểm tra nếu nó có thể thắng trong một lần di chuyển
103. # Đây là thuật toán của chúng tôi cho Tic Tac Toe AI của chúng tôi:
104. # Đầu tiên, hãy kiểm tra xem chúng tôi có thể giành chiến thắng trong bước tiếp theo không
105. cho i trong phạm vi (1, 10):
106.
sao chép = getBoardCopy (bảng)
107.
nếu isSpaceFree (bản sao, i):
108.
makeMove (bản sao, máy tínhLetter, i)
109.
if isWinner (bản sao, computerLetter):
110.
trả lại tôi
Hơn bất cứ điều gì, nếu máy tính có thể giành chiến thắng trong lần di chuyển tiếp theo, máy tính
nên
ngay lập tức thực hiện chiến thắng đó. Chúng tôi sẽ làm điều này bằng cách thử từng trong chín
khoảng trống trên
bảng với một vòng lặp for . Dòng đầu tiên trong vòng lặp tạo một bản sao của danh
sách bảng . Chúng tôi
muốn thực hiện một di chuyển trên bản sao của bảng, và sau đó xem nếu di chuyển đó có kết quả
trong
máy tính chiến thắng. Chúng tôi không muốn sửa đổi bảng gốc Tic Tac Toe, đó là lý do tại sao
chúng tôi thực hiện cuộc gọi đến getBoardCopy () . Chúng tôi kiểm tra xem không gian chúng
tôi sẽ di chuyển có miễn phí không, và nếu
Vì vậy, chúng tôi di chuyển trên không gian đó và xem nếu điều này dẫn đến chiến thắng. Nếu
có, chúng tôi trả lại
số nguyên của không gian.
Nếu di chuyển vào không có khoảng trống nào dẫn đến chiến thắng, thì vòng lặp cuối cùng sẽ kết
thúc và chúng tôi
chuyển sang dòng 112.
Máy tính kiểm tra xem người chơi có thể thắng trong một lần di chuyển không
112. # Kiểm tra xem người chơi có thể giành chiến thắng trong lần di chuyển tiếp theo không, và
chặn chúng
113. cho i trong phạm vi (1, 10):
114.
sao chép = getBoardCopy (bảng)
115.
nếu isSpaceFree (bản sao, i):
116.
makeMove (sao chép, playerLetter, i)
117.
if isWinner (sao chép, playerLetter):
118.
trả lại tôi
Tại thời điểm này, chúng tôi biết chúng tôi không thể giành chiến thắng trong một lần di
chuyển. Vì vậy, chúng tôi muốn đảm bảo con người
Người chơi không thể thắng trong một lần di chuyển nữa. Mã này rất giống nhau, ngoại trừ trên
bản sao của
bảng, chúng tôi đặt thư của người chơi trước khi gọi hàm isWinner () . Nếu có một
vị trí người chơi có thể di chuyển sẽ cho phép họ chiến thắng, máy tính sẽ di chuyển đến đó
chặn mà di chuyển.
176

Trang 191
Nếu người chơi không thể thắng trong một lần di chuyển nữa, vòng lặp for cuối cùng sẽ dừng lại và
thực hiện tiếp tục trên dòng 120.
Kiểm tra các góc, trung tâm và không gian bên (theo thứ tự đó)
120. # Hãy thử lấy một trong các góc, nếu chúng miễn phí.
121. di chuyển = selectRandomMoveFromList (bảng, [1, 3, 7, 9])
122. nếu di chuyển! = Không có:
123.
di chuyển trở lại
Cuộc gọi của chúng tôi để chọnRandomMoveFromList () với danh sách [1, 3, 7, 9] sẽ
đảm bảo rằng nó trả về số nguyên cho một trong các không gian góc. (Hãy nhớ rằng, các không
gian góc
được biểu thị bằng các số nguyên 1 , 3 , 7 và 9. ) Nếu tất cả các không gian góc được lấy, chúng
tôi
Hàm selectRandomMoveFromList () sẽ trả về giá trị Không có . Trong trường hợp đó,
chúng ta sẽ chuyển sang dòng 125.
125. # Hãy thử lấy trung tâm, nếu nó miễn phí.
126. nếu isSpaceFree (bảng, 5):
127.
trở về 5
Nếu không có góc nào có sẵn, chúng tôi sẽ cố gắng di chuyển trên không gian trung tâm nếu nó
miễn phí. Nếu
không gian trung tâm không miễn phí, việc thực thi chuyển sang dòng 129.
129. # Di chuyển trên một trong các bên.
130. return selectRandomMoveFromList (bảng, [2, 4, 6, 8])
Mã này cũng thực hiện cuộc gọi tới selectRandomMoveFromList () , ngoại trừ chúng tôi chuyển

một danh sách các không gian bên ( [2, 4, 6, 8] ). Chúng tôi biết rằng chức năng này sẽ không trở
lại
Không , bởi vì các không gian bên là không gian duy nhất chúng tôi chưa kiểm tra. Đây là kết
thúc
của hàm getComputerMove () và thuật toán AI của chúng tôi.
Kiểm tra xem Hội đồng quản trị có đầy đủ không
132. def isBoardFull (bảng):
133. # Trả về Đúng nếu mọi khoảng trống trên bảng đã được
Lấy. Nếu không thì trả về Sai.
134. cho i trong phạm vi (1, 10):
135.
nếu isSpaceFree (bảng, i):
136.
trả về sai
137. trả lại đúng
Hàm cuối cùng chúng ta sẽ viết là isBoardFull () , trả về True nếu 10-
đối số bảng danh sách chuỗi được thông qua có 'X' hoặc 'O' trong mọi chỉ mục (ngoại trừ
177
10 - Ngón chân Tic Tac

Trang 192
chỉ số 0 , chỉ là một giữ chỗ mà chúng tôi bỏ qua). Nếu có ít nhất một không gian trong
bảng được đặt thành một khoảng trắng '' thì nó sẽ trả về Sai .
Các cho vòng lặp sẽ cho phép chúng tôi kiểm tra không gian từ 1 đến 9 trên bảng Toe Tic
Tac. (Nhớ lại
phạm vi đó (1, 10) sẽ tạo vòng lặp for lặp trên các số nguyên 1 , 2 , 3 , 4 , 5 , 6 , 7 ,
8 và 9. ) Ngay sau khi tìm thấy một không gian trống trong bảng (nghĩa là khi isSpaceFree
(bảng, i) trả về True ), hàm isBoardFull () sẽ trả về Sai .
Nếu thực thi quản lý để đi qua mỗi lần lặp của vòng lặp, chúng ta sẽ biết rằng không ai trong số
các không gian là miễn phí. Vì vậy, tại thời điểm đó, chúng tôi sẽ thực hiện trả về True .
Bắt đầu trò chơi
140. in ('Chào mừng bạn đến với Tic Tac Toe!')
Dòng 140 là dòng đầu tiên không nằm trong hàm, vì vậy đây là dòng mã đầu tiên
thực hiện khi chúng tôi chạy chương trình này.
142. trong khi Đúng:
143. # Đặt lại bảng
144. theBoard = [''] * 10
Đây khi vòng lặp có thật cho tình trạng này, vì vậy điều đó có nghĩa chúng tôi sẽ tiếp tục vòng
lặp trong này
lặp cho đến khi chúng ta gặp một tuyên bố phá vỡ . Dòng 144 thiết lập bảng chính Tic Tac Toe
mà chúng ta sẽ sử dụng, được đặt tên là theBoard . Đó là danh sách 10 chuỗi, trong đó mỗi chuỗi
là một chuỗi
không gian ' '. Ghi nhớ mẹo nhỏ bằng cách sử dụng toán tử nhân với danh sách để nhân rộng
nó: [''] * 10 . Điều đó ước tính thành ['', '', '', '', '', '', '',
'', '', ''] , nhưng ngắn hơn để chúng tôi nhập [''] * 10 .
Quyết định Mark Mark và Ai đi trước
145. playerLetter, computerLetter = inputPlayerLetter ()
Hàm inputPlayerLetter () cho phép người chơi nhập liệu họ có muốn
X hoặc O. Hàm trả về danh sách 2 chuỗi, ['X', 'O'] hoặc ['O', 'X'] . Chúng tôi sử dụng
nhiều mẹo chuyển nhượng ở đây mà chúng ta đã học trong chương Hangman. Nếu
inputPlayerLetter () trả về ['X', 'O'] , sau đó playerLetter được đặt thành 'X' và
computerLetter được đặt thành 'O'. Nếu inputPlayerLetter () trả về ['O', 'X'] ,
sau đó playerLetter được đặt thành 'O' và computerLetter được đặt thành 'X'.
146. lượt = whoGoesFirst ()
147. print ('' 'Turn +' sẽ xuất hiện trước. ')
178

Trang 193
148. gameIsPlay = True
Hàm whoGoesFirst () quyết định ngẫu nhiên ai đi trước và trả về
chuỗi 'người chơi' hoặc chuỗi 'máy tính' . Trên dòng 147, chúng tôi nói với người chơi sẽ đi
Đầu tiên. Biến gameIsPlayer là những gì chúng ta sẽ sử dụng để theo dõi xem trò chơi có
đã được thắng, thua, bị ràng buộc hoặc nếu đến lượt của người chơi khác.
Chạy Turn Turn
150. trong khi gameIsPlay:
Đây là một vòng lặp sẽ liên tục qua lại giữa lượt của người chơi và
đến lượt máy tính, miễn là gameIsPlay được đặt thành True .
151.
nếu bật == 'người chơi':
152.
# Biến lần lượt.
153.
drawBoard (theBoard)
154.
di chuyển = getPlayerMove (theBoard)
155.
makeMove (theBoard, playerLetter, di chuyển)
Biến số ban đầu được đặt bởi whoGoesFirst () . Nó được đặt thành 'người chơi'
hoặc 'máy tính' . Nếu lần lượt chứa chuỗi 'máy tính' , thì điều kiện là Sai
và thực thi sẽ nhảy xuống dòng 169.
Điều đầu tiên chúng ta làm khi đến lượt của người chơi (theo biểu đồ dòng chảy mà chúng ta đã
vẽ
phần đầu của chương này) được hiển thị bảng cho người chơi. Gọi drawBoard ()
và chuyển biếnBoard sẽ in bảng trên màn hình. Chúng tôi sau đó để
người chơi nhập di chuyển của mình bằng cách gọi hàm getPlayerMove () của chúng tôi và đặt
di chuyển lên
bảng bằng cách gọi hàm makeMove () của chúng tôi .
157.
if isWinner (theBoard, playerLetter):
158.
drawBoard (theBoard)
159.
in ('Hoan hô! Bạn đã thắng trò chơi!')
160.
gameIsPlay = Sai
Bây giờ người chơi đã di chuyển, chương trình của chúng tôi sẽ kiểm tra xem họ có chiến thắng
không
trò chơi với động thái này. Nếu hàm isWinner () trả về True , chúng ta sẽ hiển thị cho chúng
bảng chiến thắng (cuộc gọi trước tới drawBoard () hiển thị bảng trước khi họ thực hiện
di chuyển chiến thắng) và in một thông báo cho họ biết họ đã thắng.
Sau đó, chúng tôi đặt gameIsPlay thành Sai để việc thực thi không tiếp tục với
đến lượt máy tính.
179
10 - Ngón chân Tic Tac

Trang 194
161.
khác:
162.
if isBoardFull (theBoard):
163.
drawBoard (theBoard)
164.
in ('Trò chơi là hòa!')
165.
phá vỡ
Nếu người chơi không giành chiến thắng với chiêu cuối của mình, thì có lẽ chiêu cuối của anh ta
đã lấp đầy toàn bộ
hội đồng quản trị và bây giờ chúng ta có một cà vạt. Trong khối khác này, chúng tôi kiểm tra
xem bảng có đầy đủ với một cuộc gọi đến
hàm isBoardFull () . Nếu nó trả về True , thì chúng ta nên vẽ bảng bằng cách
gọi drawBoard () và nói với người chơi một sự ràng buộc đã xảy ra. Các phá vỡ tuyên bố di chúc
phá vỡ chúng ra khỏi trong khi vòng lặp chúng ta đang ở và nhảy xuống dòng 186.
Chạy máy tính
166.
khác:
167.
biến = 'máy tính'
Nếu người chơi không thắng hoặc buộc trò chơi, thì chúng ta chỉ nên đặt biến lần lượt thành
'máy tính' để khi này khi vòng lặp sao cho bắt đầu nó sẽ thực hiện
mã cho lượt của máy tính.
169.
khác:
Nếu biến số lần lượt không được đặt thành 'người chơi' cho điều kiện trên dòng 151, thì chúng
tôi
biết đó là lượt của máy tính và mã trong khối khác này sẽ thực thi. Mã này rất
tương tự như mã cho lượt của người chơi, ngoại trừ máy tính không cần bảng
được in trên màn hình để chúng ta bỏ qua việc gọi hàm drawBoard () .
170.
# Đến lượt máy tính.
171.
di chuyển = getComputerMove (theBoard,
máy tính tốt hơn)
172.
makeMove (theBoard, computerLetter, di chuyển)
Mã này gần giống với mã cho lượt của người chơi trên dòng 154 và 155.
174.
if isWinner (theBoard, computerLetter):
175.
drawBoard (theBoard)
176.
in ('Máy tính đã đánh bại bạn! Bạn
thua.')
177.
gameIsPlay = Sai
180

Trang 195
Chúng tôi muốn kiểm tra xem máy tính có chiến thắng với lần di chuyển cuối cùng không. Lý do
chúng tôi gọi
drawBoard () ở đây là vì người chơi sẽ muốn xem những gì máy tính đã di chuyển
để giành chiến thắng trong trò chơi. Sau đó, chúng tôi đặt gameIsPlay thành Sai để trò chơi
không
tiếp tục. Lưu ý rằng các dòng từ 174 đến 177 gần như giống hệt với các dòng 157 đến 160.
178.
khác:
179.
if isBoardFull (theBoard):
180.
drawBoard (theBoard)
181.
in ('Trò chơi là hòa!')
182.
phá vỡ
Các dòng mã này giống hệt với mã trên các dòng từ 162 đến 165. Sự khác biệt duy nhất là
Đây là một kiểm tra cho một trò chơi gắn liền sau khi máy tính đã di chuyển.
183.
khác:
184.
biến = 'người chơi'
Nếu trò chơi không thắng cũng không bị ràng buộc, thì nó sẽ trở thành lượt của người
chơi. Không có nhiều
các dòng mã bên trong vòng lặp while, do đó, việc thực thi sẽ quay trở lại while
tuyên bố trên dòng 150.
186. nếu không chơiAgain ():
187.
phá vỡ
Các dòng mã này được đặt ngay sau khi khối while bắt đầu bởi while
tuyên bố trên dòng 150. Hãy nhớ rằng, chúng ta sẽ chỉ thoát ra khỏi đó trong khi vòng lặp nếu nó
điều kiện ( biến gameIsPlay ) là Sai . gameIsPlay được đặt thành
Sai khi trò chơi kết thúc, vì vậy tại thời điểm này, chúng tôi sẽ hỏi người chơi nếu họ
muốn chơi lại.
Hãy nhớ rằng, khi chúng ta đánh giá điều kiện trong câu lệnh if này , chúng ta gọi
Hàm playAgain () sẽ cho phép người dùng nhập nếu họ muốn chơi hay không.
playAgain () sẽ trả về Đúng nếu người chơi gõ cái gì đó bắt đầu với một 'y' như
"Có" hoặc "y" . Nếu không thì playAgain () sẽ trả về Sai .
Nếu playAgain () trả về Sai , thì điều kiện if của câu lệnh là True (vì
các không điều hành mà đảo ngược giá trị Boolean) và chúng tôi thực hiện các câu lệnh
break. Cái đó
phá vỡ chúng ra khỏi trong khi vòng lặp được bắt đầu trên đường 142. Nhưng không có nhiều
dòng
mã sau đó trong khi chặn, vì vậy chương trình kết thúc.
181
10 - Ngón chân Tic Tac

Trang 196
Tóm tắt: Tạo nhân tạo chơi trò chơi
Trí tuệ
Tạo một chương trình có thể chơi trò chơi để xem xét cẩn thận tất cả
những tình huống có thể xảy ra với AI và cách nó sẽ phản ứng trong từng tình huống đó.
Tic Tac Toe AI của chúng tôi khá đơn giản vì không có nhiều động thái khả thi trong Tic Tac
Toe so với một trò chơi như cờ vua hoặc cờ đam.
AI của chúng tôi chỉ đơn giản là chặn người chơi di chuyển nếu người chơi sắp thắng. Nếu người
chơi không
sắp thắng, nó kiểm tra xem có bất kỳ động thái nào có thể cho phép bản thân chiến thắng
không. Sau đó, AI đơn giản
chọn bất kỳ không gian góc có sẵn, sau đó là không gian trung tâm, sau đó là không gian
bên. Đây là một
thuật toán đơn giản cho máy tính để làm theo.
Chìa khóa để thực hiện AI của chúng tôi là tạo các bản sao của dữ liệu bảng và mô phỏng
di chuyển trên bản sao. Bằng cách đó, mã AI có thể xem liệu một động thái sẽ dẫn đến thắng hay
thua.
Sau đó, AI có thể làm cho di chuyển trên bảng thực sự. Kiểu mô phỏng này rất hiệu quả
lúc dự đoán thế nào là một động thái tốt hay không.
182

Trang 197
Các chủ đề được đề cập trong Chương này:
● Mã hóa cứng
● Toán tử phân công tăng cường, + = , - = , * = , / =
● Các random.shuffle () Chức năng

● Các loại () Danh sách Phương

● Các join () Danh sách Phương

● Nội suy chuỗi (còn gọi là Định dạng chuỗi)

● Công cụ xác định chuyển đổi % s

● Vòng lặp lồng nhau

Trong chương này, bạn sẽ tìm hiểu một vài phương thức và hàm mới đi kèm với Python.
Bạn cũng sẽ tìm hiểu về các toán tử gán tăng cường và nội suy chuỗi. Những
khái niệm không cho phép bạn làm bất cứ điều gì bạn không thể làm trước đây, nhưng chúng là
những phím tắt đẹp
làm cho việc nhập mã của bạn dễ dàng hơn.
Bagels là một trò chơi đơn giản mà bạn có thể chơi với một người bạn. Bạn của bạn nghĩ lên một
cách ngẫu nhiên 3-
số chữ số không có chữ số lặp lại, và bạn cố gắng đoán số đó là gì. Sau mỗi cái
đoán xem, bạn của bạn cho bạn manh mối về mức độ đoán của bạn. Nếu người bạn nói với bạn
"Bánh mì tròn", điều đó có nghĩa là không có chữ số nào trong ba chữ số bạn đoán được nằm
trong số bí mật. Nếu
bạn của bạn nói với bạn "pico", sau đó một trong những chữ số nằm trong số bí mật, nhưng bạn
đoán
có chữ số ở sai vị trí. Nếu bạn của bạn nói với bạn "fermi", thì dự đoán của bạn có
chữ số đúng ở vị trí chính xác. Tất nhiên, ngay cả khi bạn có được đầu mối pico hoặc fermi, bạn
vẫn
không biết chữ số nào trong dự đoán của bạn là số chính xác.
Bạn cũng có thể nhận được nhiều manh mối sau mỗi lần đoán. Nói số bí mật là 456, và của bạn
đoán là 546. Manh mối bạn nhận được từ bạn của bạn sẽ là "fermi pico pico" bởi vì một
183

Trang 198
chữ số là đúng và ở đúng vị trí (chữ số 6) và hai chữ số nằm trong bí mật
số nhưng ở sai vị trí (các chữ số 4 và 5).
Chạy mẫu
Tôi đang nghĩ về một số có 3 chữ số. Hãy thử đoán nó là gì.
Dưới đây là một số manh mối:
Khi tôi nói: Điều đó có nghĩa là:
Pico
Một chữ số là đúng nhưng ở vị trí sai.
Fermi
Một chữ số là chính xác và ở đúng vị trí.
Bánh mì tròn
Không có chữ số là chính xác.
Tôi đã nghĩ ra một con số. Bạn có 10 lần đoán để có được nó.
Đoán # 1:
123
Fermi
Đoán # 2:
453
Pico
Đoán # 3:
425
Fermi
Đoán số 4:
327
Bánh mì tròn
Đoán số 5:
489
Bánh mì tròn
Đoán số 6:
075
Fermi Fermi
Đoán số 7:
015
Fermi Pico
Đoán số 8:
175
Bạn đã nhận nó!
Bạn có muốn chơi lại không? (có hay không)
Không

Mã nguồn của Bagel


bagels.py
Mã này có thể được tải xuống từ http://inventwithpython.com/bagels.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. nhập ngẫu nhiên
2. def getSecretNum (numDigits):
3. # Trả về một chuỗi dài numDigits, được tạo thành từ
184
Trang 199
chữ số ngẫu nhiên duy nhất.
4. số = danh sách (phạm vi (10))
5. Random.shuffle (số)
6. secretNum = ''
7. cho i trong phạm vi (numDigits):
số 8.
secretNum + = str (số [i])
9. bí mật trở lại
10.
11. def getClues (đoán, secretNum):
12. # Trả về một chuỗi với các đầu mối pico, fermi, bagels
cho người dùng.
13. nếu đoán == secretNum:
14.
trở lại 'Bạn đã nhận nó!'
15.
16. đầu mối = []
17.
18. cho i trong phạm vi (len (đoán)):
19.
nếu đoán [i] == secretNum [i]:
20.
clue.append ('Fermi')
21.
elif đoán [i] trong secretNum:
22.
đầu mối.append ('Pico')
23. nếu len (đầu mối) == 0:
24.
trả lại 'Bánh mì tròn'
25.
26. clue.sort ()
27. return '' .join (đầu mối)
28.
29. def isOnlyDigits (num):
30. # Trả về Đúng nếu num là một chuỗi chỉ được tạo thành từ
chữ số. Nếu không thì trả về Sai.
31. nếu num == '':
32.
trả về sai
33.
34. cho tôi bằng num:
35.
nếu tôi không ở '0 1 2 3 4 5 6 7 8 9'.split ():
36.
trả về sai
37.
38. trả lại đúng
39.
40. def playAgain ():
41. # Hàm này trả về True nếu người chơi muốn
chơi lại, nếu không nó trả về Sai.
42. in ('Bạn có muốn chơi lại không? (Có hoặc không)')
43. return return (). Lower (). Startedwith ('y')
44.
45. SỐ LƯỢNG = 3
46. TỐI THIỂU = 10
47.
48. print ('Tôi đang nghĩ về một số% s chữ số. Hãy thử đoán
nó là gì % (SỐ TIỀN)
49. in ('Đây là một số manh mối:')
50. print ('Khi tôi nói: Điều đó có nghĩa là:')
51. in ('Pico
Một chữ số là đúng nhưng trong
sai vị trí.')
185
11 - Bánh mì tròn

Trang 200
52. in ('Fermi
Một chữ số là chính xác và trong
Đúng vị trí.')
53. in ('Bánh mì tròn
Không có chữ số nào là đúng. ')
54.
55. trong khi Đúng:
56. secretNum = getSecretNum (NUMDIGITS)
57. print ('Tôi đã nghĩ ra một con số. Bạn có% s đoán
để có được nó. ' % (TỐI ĐA))
58.
59. numGuesses = 1
60. trong khi numGuesses <= MAXGUESS:
61.
đoán = ''
62.
trong khi len (đoán)! = NUMDIGITS hay không isOnlyDigits
(phỏng đoán):
63.
in ('Đoán #% s:'% (numGuesses))
64.
đoán = đầu vào ()
65.
66.
clue = getClues (đoán, secretNum)
67.
in (đầu mối)
68.
numGuesses + = 1
69.
70.
nếu đoán == secretNum:
71.
phá vỡ
72.
nếu numGuesses> MAXGUESS:
73.
print ('Bạn đã hết dự đoán. Câu trả lời là
%S.' % (bí mật))
74.
75. nếu không chơiAgain ():
76.
phá vỡ

Thiết kế chương trình


Dưới đây là một biểu đồ cho chương trình này. Biểu đồ mô tả các sự kiện cơ bản của những gì
xảy ra trong trò chơi này và theo thứ tự chúng có thể xảy ra:
186

Trang 201
Hình 11-1: Biểu đồ dòng chảy cho trò chơi Bagels.
Và đây là mã nguồn cho trò chơi của chúng tôi. Bắt đầu một tệp mới và nhập mã vào, và sau đó
lưu tập tin dưới dạng bagels.py . Chúng tôi sẽ thiết kế trò chơi của chúng tôi để rất dễ dàng thay
đổi kích thước
của số bí mật. Nó có thể là 3 chữ số hoặc 5 chữ số hoặc 30 chữ số. Chúng tôi sẽ làm điều này
bằng cách sử dụng một
biến không đổi có tên là NUMDIGITS thay vì mã hóa số nguyên 3 vào nguồn của chúng tôi
mã.
Mã hóa cứng có nghĩa là viết một chương trình theo cách nó thay đổi hành vi của
chương trình yêu cầu thay đổi rất nhiều mã nguồn. Ví dụ, chúng ta có thể mã cứng
tên vào một hàm print () gọi như: print ('Hello, Albert') . Hoặc chúng ta có thể
sử dụng dòng này: print ('Hello,' + name) cho phép chúng tôi thay đổi tên
được in bằng cách thay đổi biến tên trong khi chương trình đang chạy.
Cách thức hoạt động của mã: Dòng 1 đến 9
Khi bắt đầu chương trình, chúng tôi nhập mô-đun ngẫu nhiên và cũng tạo một hàm cho
tạo ra một số bí mật ngẫu nhiên để người chơi đoán. Quá trình tạo ra điều này
số không khó và cũng đảm bảo rằng nó chỉ có các chữ số duy nhất trong đó.
187
11 - Bánh mì tròn

Trang 202
1. nhập ngẫu nhiên
Trò chơi này nhập mô-đun ngẫu nhiên để chúng tôi có thể sử dụng số ngẫu nhiên của mô-đun
chức năng.
Xáo trộn một bộ chữ số duy nhất
2. def getSecretNum (numDigits):
3. # Trả về một chuỗi dài numDigits, được tạo thành từ
chữ số ngẫu nhiên duy nhất.
4. số = danh sách (phạm vi (10))
5. Random.shuffle (số)
Hàm đầu tiên của chúng ta được đặt tên là getSecretNum () , sẽ tạo ra bí mật ngẫu nhiên
con số. Thay vì có mã chỉ tạo ra các số có 3 chữ số, chúng tôi sử dụng một tham số
có tên numDigits để cho chúng tôi biết số bí mật nên có bao nhiêu chữ số. (Cách này,
chúng ta có thể làm cho trò chơi tạo ra các số bí mật với bốn hoặc sáu chữ số, ví dụ, chỉ bằng
đi qua 4 hoặc 6 như numDigits .)
Bạn có thể nhận thấy rằng giá trị trả về của lệnh gọi đến phạm vi () của chúng tôi lần lượt được
chuyển đến
một hàm gọi là list () . Hàm list () trả về giá trị danh sách của giá trị được truyền cho
nó, giống như hàm str () trả về dạng chuỗi hoặc hàm int () trả về
dạng nguyên. Lý do chúng tôi làm điều này là do hàm Range () về mặt kỹ thuật không
trả về một danh sách nhưng một cái gì đó gọi là một đối tượng lặp. Lặp đi lặp lại là một chủ đề
mà bạn không cần
để biết tại thời điểm này, vì vậy chúng không được đề cập trong cuốn sách này.
Cứ sau mỗi lần chúng ta sử dụng hàm phạm vi () thì nó lại nằm trong một vòng lặp for . Lặp đi
lặp lại là tốt
để sử dụng cho các vòng lặp (giống như danh sách), nhưng nếu chúng ta muốn lưu trữ một danh
sách các số nguyên trong một
biến, hãy chắc chắn chuyển đổi giá trị trả về của phạm vi () thành một danh sách với danh sách ()
chức năng đầu tiên. (Giống như chúng ta làm trên dòng 4.)
Các random.shuffle () Chức năng
Đầu tiên, chúng tôi tạo một danh sách các số nguyên từ 0 đến 9 bằng cách gọi danh sách (phạm
vi (10)) và lưu trữ một
tham chiếu đến danh sách này bằng số. Sau đó, chúng ta gọi một hàm trong mô-đun ngẫu nhiên
có tên
xáo trộn () . Tham số duy nhất cho Random.shuffle () là một tham chiếu đến danh sách. Các
Hàm shuffle () sẽ thay đổi ngẫu nhiên thứ tự của tất cả các mục trong danh sách.
Lưu ý rằng Random.shuffle () không trả về giá trị. Nó thay đổi danh sách bạn vượt qua nó
"tại chỗ" (giống như hàm makeMove () của chúng tôi trong chương Tic Tac Toe đã sửa đổi danh
sách
nó đã được thông qua tại chỗ, thay vì trả về một danh sách mới với sự thay đổi). Nó thực sự sẽ là
không chính xác để viết số = Random.shuffle (số) .
188

Trang 203
Hãy thử trải nghiệm với hàm Random.shuffle () bằng cách nhập thông tin sau
mã vào vỏ tương tác:
>>> nhập ngẫu nhiên
>>> thư rác = phạm vi (danh sách (10))
>>> in (spam)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> Random.shuffle (spam)
>>> in (spam)
[1, 2, 5, 9, 4, 7, 0, 3, 6, 8]
>>> Random.shuffle (spam)
>>> in (spam)
[3, 0, 5, 9, 6, 8, 2, 4, 1, 7]
>>> Random.shuffle (spam)
>>> in (spam)
[9, 8, 3, 5, 4, 7, 1, 2, 0, 6]
>>>
Mỗi khi bạn chuyển một tham chiếu danh sách tới Random.shuffle () , danh sách mà nó tham
chiếu có
tất cả các mặt hàng giống nhau nhưng theo một thứ tự khác nhau. Lý do chúng tôi làm điều này
là vì chúng tôi muốn
số bí mật để có giá trị duy nhất. Trò chơi Bagels thú vị hơn nhiều nếu bạn không có
số trùng lặp trong số bí mật, chẳng hạn như '244' hoặc '333' .
Lấy số bí mật từ các chữ số bị xáo trộn
6. secretNum = ''
7. cho i trong phạm vi (numDigits):
số 8.
secretNum + = str (số [i])
9. bí mật trở lại
Số bí mật sẽ là một chuỗi gồm ba chữ số đầu tiên (vì chúng tôi sẽ vượt qua 3 cho
tham số numDigits ) của danh sách các số nguyên được xáo trộn. Ví dụ: nếu danh sách xáo trộn

[9, 8, 3, 5, 4, 7, 1, 2, 0, 6] sau đó chúng tôi muốn chuỗi trả về bởi getSecretNum () là
'983' .
Biến secretNum bắt đầu dưới dạng một chuỗi trống. Sau đó chúng tôi lặp một số lần
bằng giá trị số nguyên tính bằng numDigits . Trên mỗi lần lặp qua vòng lặp, một lần lặp mới
số nguyên được kéo từ danh sách được xáo trộn, được chuyển đổi thành một chuỗi và được nối
vào cuối của
bí mật . Vì vậy, nếu numDigits là 3 , vòng lặp sẽ lặp lại ba lần và ba lần ngẫu nhiên
chữ số sẽ được nối thành chuỗi.
Ví dụ: nếu các số đề cập đến danh sách [9, 8, 3, 5, 4, 7, 1, 2, 0, 6] ,
sau đó ở lần lặp đầu tiên, các số [0] (nghĩa là 9 ) sẽ được chuyển đến str () , trong đó
quay trở lại '9' được nối đến cuối SecretNum . Ở lần lặp thứ hai,
189
11 - Bánh mì tròn

Trang 204
điều tương tự xảy ra với các số [1] (nghĩa là 8 ) và ở lần lặp thứ ba giống nhau
xảy ra với các số [2] (nghĩa là 3 ). Giá trị cuối cùng của secretNum được trả về là
'983' .
Bạn có thể nhận thấy rằng secretNum trong hàm này là một chuỗi, không phải là số
nguyên. Điều này có thể
có vẻ kỳ lạ, nhưng hãy nhớ rằng số bí mật của chúng tôi có thể là một cái gì đó như '012' . Nếu
chúng ta
lưu trữ này dưới dạng một số nguyên, nó sẽ là 12 (không có số 0 đứng đầu) sẽ làm cho nó
khó hơn để làm việc với trong chương trình của chúng tôi.
Toán tử chuyển nhượng tăng cường
Các + = điều hành trên dòng 8 là mới. Đây là một trong những toán tử gán tăng cường.
Thông thường, nếu bạn muốn thêm hoặc nối một giá trị vào một biến, bạn sẽ sử dụng mã
Trông như thế này:
thư rác = 42
thư rác = thư rác + 10
phô mai = 'Xin chào'
phô mai = phô mai + 'thế giới!'
Sau khi chạy đoạn mã trên, thư rác sẽ có giá trị 52 và phô mai sẽ có
giá trị 'Xin chào thế giới!' . Các toán tử gán tăng cường là một phím tắt
giải phóng bạn khỏi việc gõ lại tên biến. Đoạn mã sau thực hiện chính xác như
đoạn mã trên:
thư rác = 42
thư rác + = 10
# Giống như thư rác = thư rác + 10
phô mai = 'Xin chào'
phô mai + = 'thế giới!' # Giống như phô mai = phô mai +
'thế giới!'
Có các toán tử gán tăng cường khác. - = sẽ trừ đi một giá trị từ một
số nguyên. * = sẽ nhân biến với một giá trị. / = sẽ chia một biến cho một giá trị.
Lưu ý rằng các toán tử gán gán tăng này thực hiện các phép toán tương tự như - , * ,
và / toán tử. Toán tử gán gán là một phím tắt gọn gàng.
Cách thức hoạt động của mã: Dòng 11 đến 24
Chúng tôi cũng cần một cách để tìm ra manh mối nào để hiển thị cho người chơi.
11. def getClues (đoán, secretNum):
12. # Trả về một chuỗi với các đầu mối pico, fermi, bagels
190

Trang 205
cho người dùng.
13. nếu đoán == secretNum:
14.
trở lại 'Bạn đã nhận nó!'
Hàm getClues () sẽ trả về một chuỗi với các đầu mối fermi, pico và bagels,
tùy thuộc vào những gì nó được thông qua cho các tham số đoán và secretNum . Nhất
Bước rõ ràng và dễ nhất là kiểm tra xem dự đoán có chính xác như số bí mật không. Trong
trường hợp đó, chúng tôi có thể trả lại 'Bạn hiểu rồi!' .
16. đầu mối = []
17.
18. cho i trong phạm vi (len (đoán)):
19.
nếu đoán [i] == secretNum [i]:
20.
clue.append ('Fermi')
21.
elif đoán [i] trong secretNum:
22.
đầu mối.append ('Pico')
Nếu dự đoán không chính xác như số bí mật, chúng ta cần tìm ra manh mối nào
đưa cho người chơi. Trước tiên, chúng tôi sẽ thiết lập một danh sách có tên đầu mối , chúng tôi
sẽ thêm các chuỗi
'Fermi' và 'Pico' khi cần thiết. Chúng tôi sẽ kết hợp các chuỗi trong danh sách này thành một
chuỗi
chuỗi để trả về.
Chúng tôi thực hiện điều này bằng cách lặp qua từng chỉ số có thể có
trong đoán và secretNum (cả hai
có cùng kích thước). Chúng tôi sẽ cho rằng đoán và secretNum có cùng kích thước. Như
giá trị của tôi thay đổi từ 0 để 1 để 2 , và như vậy, những nếu kiểm tra tuyên bố nếu là người đầu
tiên, thứ hai,
thứ ba, v.v ... thư đoán giống như số ở cùng vị trí trong secretNum . Nếu
vì vậy, chúng tôi sẽ thêm một chuỗi 'Fermi' vào đầu mối.
Nếu điều kiện đó là Sai, chúng tôi sẽ kiểm tra xem số ở vị trí thứ i có đoán được không
tồn tại bất cứ nơi nào trong secretNum . Nếu điều kiện này là True, chúng ta biết rằng số đó là
ở đâu đó trong số bí mật nhưng không ở cùng một vị trí. Đây là lý do tại sao chúng tôi thêm
'Pico' để đầu mối .
23. nếu len (đầu mối) == 0:
24.
trả lại 'Bánh mì tròn'
Nếu chúng ta đi qua toàn bộ vòng lặp for ở trên và không bao giờ thêm bất cứ điều gì vào danh
sách đầu mối , thì
chúng ta đều biết rằng không có chữ số chính xác tại tất cả trong đoán . Trong trường hợp này,
chúng ta chỉ nên quay lại
chuỗi 'Bagels' là đầu mối duy nhất của chúng tôi.
191
11 - Bánh mì tròn
Trang 206
Các loại () Danh sách Phương
26. clue.sort ()
Danh sách có một phương thức có tên sort () sắp xếp lại các mục trong danh sách sẽ nằm trong
thứ tự ABC. Hãy thử nhập nội dung sau vào vỏ tương tác:
>>> thư rác = [5, 3, 4, 1, 2]
>>> spam.sort ()
>>> thư rác
[1, 2, 3, 4, 5]
Lưu ý rằng phương thức sort () không trả về danh sách đã sắp xếp mà chỉ sắp xếp danh sách
nó được gọi là "tại chỗ". Điều này giống như cách phương thức Reverse () hoạt động. Bạn
sẽ không bao giờ muốn sử dụng dòng mã này: return spam.sort () vì điều đó sẽ
trả về giá trị Không (đó là những gì sort () trả về). Thay vào đó bạn sẽ muốn một riêng
dòng spam.sort () và sau đó dòng trả về thư rác .
Lý do chúng tôi muốn sắp xếp danh sách đầu mối là vì chúng tôi có thể trả lại thêm manh mối
mà chúng tôi
không có ý định dựa trên thứ tự của các đầu mối. Nếu đầu mối tham chiếu danh sách ['Pico',
'Fermi', 'Pico'] , sau đó điều đó sẽ cho chúng ta biết rằng chữ số trung tâm của dự đoán của chúng
ta nằm trong
đúng vị trí. Vì hai đầu mối khác đều là Pico, nên chúng tôi biết rằng tất cả chúng tôi phải
làm là hoán đổi chữ số thứ nhất và thứ ba và chúng ta có số bí mật. Nhưng nếu manh mối là
luôn được sắp xếp theo thứ tự bảng chữ cái, người chơi không thể chắc chắn số đầu mối Fermi
đề cập đến.
Các join () Chuỗi Phương pháp
27. return '' .join (đầu mối)
Các join () phương pháp chuỗi trả về một chuỗi của từng hạng mục trong đối số danh sách tham
gia
cùng với nhau. Chuỗi mà phương thức được gọi trên (trên dòng 27, đây là một khoảng trắng, '' )
xuất hiện ở giữa mỗi mục trong danh sách. Vì vậy, chuỗi được trả về trên dòng 27 là mỗi chuỗi
chuỗi trong đầu mối kết hợp với nhau bằng một khoảng trắng ở giữa mỗi chuỗi.
Ví dụ: nhập thông tin sau vào vỏ tương tác:
>>> 'x'.join ([' xin chào ',' thế giới '])
'Thế giới xin chào'
>>> 'ABCDEF'.join ([' x ',' y ',' z '])
'xABCDEFyABCDEFz'
192

Trang 207
>>> '' .join (['Của tôi', 'tên', 'là', 'Zophie'])
'Tên tôi là Zophie'
Cách thức hoạt động của mã: Dòng 29 đến 53
Chúng tôi cần thêm một vài chức năng để trò chơi của chúng tôi sử dụng. Đầu tiên là một chức
năng sẽ cho biết
chúng tôi nếu đoán rằng người chơi đã nhập là một số nguyên hợp lệ. Hãy nhớ rằng đầu vào ()
Hàm trả về một chuỗi bất cứ thứ gì người chơi nhập vào. Nếu người chơi nhập vào bất cứ thứ gì
ngoại trừ
số cho dự đoán của họ, chúng tôi muốn hỏi lại người chơi để đoán đúng.
Chức năng thứ hai là thứ chúng ta đã thấy trước đây trong các trò chơi trước. Chúng tôi muốn
một
Chức năng sẽ hỏi người chơi nếu họ muốn chơi lại trò chơi và từ người chơi
trả lời, tìm hiểu xem đó là câu trả lời Có hay Không.
Kiểm tra nếu một chuỗi chỉ có số
29. def isOnlyDigits (num):
30. # Trả về Đúng nếu num là một chuỗi chỉ được tạo thành từ
chữ số. Nếu không thì trả về Sai.
31. nếu num == '':
32.
trả về sai
Các isOnlyDigits () là một hàm nhỏ sẽ giúp chúng tôi xác định nếu người chơi
đã nhập một dự đoán chỉ được tạo thành từ các số. Để làm điều này, chúng tôi sẽ kiểm tra từng
chữ cái riêng lẻ trong chuỗi có tên num và đảm bảo đó là một số.
Dòng 31 kiểm tra nhanh để xem chúng tôi có được gửi chuỗi trống không và nếu có, chúng tôi sẽ
trả lại
Sai .
34. cho tôi bằng num:
35.
nếu tôi không ở '0 1 2 3 4 5 6 7 8 9'.split ():
36.
trả về sai
37.
38. trả lại đúng
Chúng tôi sử dụng một vòng lặp for trên chuỗi num . Giá trị của tôi sẽ có một ký tự từ
các num chuỗi trên mỗi lần lặp. Bên trong khối for, chúng tôi kiểm tra xem tôi không tồn tại
trong danh sách
được trả về bởi '0 1 2 3 4 5 6 7 8 9'.split () . Nếu không, chúng tôi biết rằng có
một ký tự trong num đó là một cái gì đó bên cạnh một số. Trong trường hợp đó, chúng ta nên trả
lại
giá trị Sai .
Nếu việc thực thi tiếp tục qua vòng lặp for , thì chúng ta biết rằng mỗi ký tự trong num là một
số vì chúng tôi đã không trả lại chức năng. Trong trường hợp đó, chúng tôi trả lại giá trị
Đúng .
193
11 - Bánh mì tròn

Trang 208
Tìm hiểu xem Người chơi có muốn chơi lại không
40. def playAgain ():
41. # Hàm này trả về True nếu người chơi muốn
chơi lại, nếu không nó trả về Sai.
42. in ('Bạn có muốn chơi lại không? (Có hoặc không)')
43. return return (). Lower (). Startedwith ('y')
Hàm playAgain () giống như hàm chúng ta đã sử dụng trong Hangman và Tic Tac Toe.
Biểu thức dài trên dòng 43 sẽ đánh giá là Đúng hoặc Sai . Giá trị trả về
từ lệnh gọi đến hàm input () là một chuỗi có phương thức low () được gọi trên
nó Các thấp () phương thức trả về một chuỗi (chuỗi chữ thường) và chuỗi có của nó
Phương thức startedwith () được gọi trên nó, truyền đối số 'y' .
Bắt đầu trò chơi
45. SỐ LƯỢNG = 3
46. TỐI THIỂU = 10
47.
48. print ('Tôi đang nghĩ về một số% s chữ số. Hãy thử đoán
nó là gì % (SỐ TIỀN)
49. in ('Đây là một số manh mối:')
50. print ('Khi tôi nói: Điều đó có nghĩa là:')
51. in ('Pico
Một chữ số là đúng nhưng trong
sai vị trí.')
52. in ('Fermi
Một chữ số là chính xác và trong
Đúng vị trí.')
53. in ('Bánh mì tròn
Không có chữ số nào là đúng. ')
Đây là sự khởi đầu thực tế của chương trình. Thay vì mã hóa cứng ba chữ số như kích thước của
số bí mật, chúng tôi sẽ sử dụng biến số NUMDIGITS không đổi . Và thay vì khó khăn-
mã hóa tối đa mười lần đoán mà người chơi có thể thực hiện, chúng tôi sẽ sử dụng hằng số
biến tối đa . (Điều này là do nếu chúng ta tăng số chữ số thì số bí mật
có, chúng tôi cũng có thể muốn cung cấp cho người chơi nhiều dự đoán hơn. Chúng tôi đặt tên
biến trong tất cả
thủ đô để cho thấy chúng có nghĩa là không đổi.)
Lệnh gọi hàm print () sẽ cho người chơi biết luật chơi và Pico,
Fermi và Bagels có nghĩa là. Cuộc gọi in () của Dòng 48 có % (NUMDIGITS) được thêm vào
kết thúc và % s bên trong chuỗi. Đây là một kỹ thuật được gọi là nội suy chuỗi.
Nội suy chuỗi
Nội suy chuỗi là một lối tắt khác, như các toán tử gán gán tăng. Thông thường,
nếu bạn muốn sử dụng các giá trị chuỗi bên trong các biến trong một chuỗi khác, bạn phải sử
dụng dấu +
điều hành nối:
194

Trang 209
>>> tên = 'Alice'
>>> sự kiện = 'bữa tiệc'
>>> ở đâu = 'hồ bơi'
>>> khi nào = 'Thứ bảy'
>>> thời gian = '6:00 tối'
>>> in ('Xin chào,' + tên + '. Bạn sẽ đi đến
'+ sự kiện +' tại '+ trong đó +' này '+ khi +'
tại '+ thời gian +'? ')
Xin chào, Alice. Bạn sẽ đi dự tiệc ở bể bơi chứ
Thứ bảy này lúc 6:00 tối?
>>>
Như bạn có thể thấy, rất khó để nhập một dòng nối nhiều chuỗi
cùng với nhau. Thay vào đó, bạn có thể sử dụng phép nội suy chuỗi , cho phép bạn đặt các trình
giữ chỗ như
% s (các trình giữ chỗ này được gọi là chỉ định chuyển đổi ), sau đó đặt tất cả biến
tên ở cuối Mỗi % s được thay thế bằng giá trị trong biến ở cuối dòng.
Ví dụ, đoạn mã sau thực hiện tương tự như đoạn mã trên:
tên = 'Alice'
sự kiện = 'bữa tiệc'
trong đó = 'hồ bơi'
khi = 'thứ bảy'
thời gian = '6:00 tối'
in ('Xin chào,% s. Bạn có đi đến% s ở% s này không
% s ở% s? ' % (tên, sự kiện, ở đâu, khi nào, thời gian))
Xin chào, Alice. Bạn sẽ đi dự tiệc ở bể bơi chứ
Thứ bảy này lúc 6:00 tối?
>>>
Nội suy chuỗi có thể làm cho mã của bạn dễ dàng nhập và đọc hơn là sử dụng
một số toán tử + nối.
Dòng cuối cùng có lệnh gọi print () với một chuỗi với các chỉ định chuyển đổi, theo sau
bằng dấu%, theo sau là một dấu ngoặc đơn với các biến trong đó. Đầu tiên
tên biến sẽ được sử dụng cho % s đầu tiên , biến thứ hai với % s thứ hai và do đó
trên. Trình thông dịch Python sẽ báo lỗi cho bạn nếu bạn không có cùng số % s
chỉ định chuyển đổi khi bạn có các biến.
Một lợi ích khác của việc sử dụng phép nội suy chuỗi thay vì nối chuỗi là
phép nội suy hoạt động với bất kỳ kiểu dữ liệu nào, không chỉ các chuỗi. Tất cả các giá trị được
tự động
chuyển đổi thành kiểu dữ liệu chuỗi. (Đây là những gì s trong % s là viết tắt của.) Nếu bạn nhập
cái này
mã vào trình bao, bạn sẽ gặp lỗi:
195
11 - Bánh mì tròn

Trang 210
>>> thư rác = 42
>>> in ('Spam ==' + spam)
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "<stdin>", dòng 1, trong <mô-đun>
TypeError: Không thể chuyển đổi đối tượng 'int' thành str
ngầm
>>>
Bạn gặp lỗi này vì nối chuỗi chỉ có thể kết hợp hai chuỗi và spam
là một số nguyên. Thay vào đó, bạn sẽ phải nhớ đặt str (spam) vào đó. Nhưng vơi
nội suy chuỗi, bạn có thể có bất kỳ loại dữ liệu. Hãy thử nhập cái này vào vỏ:
>>> thư rác = 42
>>> in ('Spam ==% s'% (spam))
Thư rác == 42
>>>
Như bạn có thể thấy, sử dụng phép nội suy chuỗi thay vì nối chuỗi dễ dàng hơn nhiều
bởi vì bạn không phải lo lắng về kiểu dữ liệu của biến. Ngoài ra, chuỗi
phép nội suy có thể được thực hiện trên bất kỳ chuỗi nào, không chỉ các chuỗi được sử dụng
trong các lệnh gọi hàm print () .
Nội suy chuỗi còn được gọi là định dạng chuỗi .
Cách thức hoạt động của mã: Dòng 55 đến 76
Bây giờ chương trình đã hiển thị các quy tắc cho Bagels cho người chơi, chương trình sẽ
tạo ngẫu nhiên một số bí mật và sau đó nhập một vòng lặp trong đó nó liên tục yêu cầu
người chơi đoán cho đến khi cô ta đoán đúng số bí mật hoặc đã hết
đoán Sau đó, chúng tôi sẽ hỏi người chơi nếu cô ấy muốn chơi lại.
Tạo số bí mật
55. trong khi Đúng:
56. secretNum = getSecretNum (NUMDIGITS)
57. in ('Tôi đã nghĩ ra một số. Bạn có% s
đoán để có được nó. ' % (TỐI ĐA))
58.
59. numGuesses = 1
60. trong khi numGuesses <= MAXGUESS:
Chúng tôi bắt đầu với một trong khi vòng lặp mà có một điều kiện của việc thật , có nghĩa là nó
sẽ lặp mãi mãi
cho đến khi chúng tôi thực hiện một tuyên bố phá vỡ . Trong vòng lặp vô hạn, chúng tôi nhận
được một số bí mật từ
196

Trang 211
Hàm getSecretNum () của chúng tôi (truyền cho nó NUMDIGITS để cho biết chúng tôi có bao
nhiêu chữ số
muốn số bí mật phải có) và gán nó cho secretNum . Nhớ lấy
secretNum là một chuỗi, không phải là số nguyên.
Chúng tôi cho người chơi biết có bao nhiêu chữ số trong số bí mật của chúng tôi bằng cách sử
dụng phép nội suy chuỗi
thay vì nối chuỗi. Chúng tôi đặt một biến numGuesses thành 1 , để biểu thị rằng đây là
đoán đầu tiên. Sau đó chúng tôi nhập mới trong khi vòng lặp đó sẽ tiếp tục lặp chừng
numGuesses nhỏ hơn hoặc bằng MAXGUESS .
Bắt Guess
Chú ý rằng thứ hai này trong khi vòng lặp trên dòng 60 là bên trong khác trong khi vòng lặp mà
bắt đầu
trên dòng 55. Bất cứ khi nào chúng ta có các vòng lặp bên trong, chúng ta gọi chúng là các vòng
lặp lồng nhau . Bạn
nên biết rằng bất kỳ tuyên bố phá vỡ hoặc tiếp tục sẽ chỉ phá vỡ hoặc tiếp tục
ra khỏi vòng lặp trong cùng, và không phải bất kỳ vòng lặp bên ngoài.
61.
đoán = ''
62.
trong khi len (đoán)! = NUMDIGITS hay không isOnlyDigits
(phỏng đoán):
63.
in ('Đoán #% s:'% (numGuesses))
64.
đoán = đầu vào ()
Biến đoán sẽ giữ dự đoán của người chơi. Chúng tôi sẽ tiếp tục lặp lại và yêu cầu
Người chơi đoán cho đến khi người chơi nhập một đoán có cùng số chữ số với
số bí mật và chỉ được tạo thành các chữ số. Đây là những gì trong khi vòng lặp mà bắt đầu trên
đường dây
62 là cho. Chúng tôi đặt đoán như chuỗi trống trên đường 61 để các trong khi điều kiện vòng lặp

Sai lần đầu tiên, đảm bảo rằng chúng tôi vào vòng lặp ít nhất một lần.
Lấy manh mối cho trò chơi Guess
66.
clue = getClues (đoán, secretNum)
67.
in (đầu mối)
68.
numGuesses + = 1
Sau khi thực hiện được quá khứ trong khi vòng lặp trên dòng 62, chúng ta biết rằng đoán chứa
một
đoán hợp lệ. Chúng tôi chuyển số này và số bí mật trong secretNum cho getClues () của chúng
tôi
chức năng. Nó trả về một chuỗi chứa các manh mối của chúng ta, mà chúng ta sẽ hiển thị cho
người chơi. Chúng tôi
sau đó tăng numGuesses lên 1 bằng cách sử dụng toán tử gán tăng cho phép cộng.
Kiểm tra xem người chơi thắng hay thua
70.
nếu đoán == secretNum:
71.
phá vỡ
72.
nếu numGuesses> MAXGUESS:
197
11 - Bánh mì tròn

Trang 212
73.
print ('Bạn đã hết dự đoán. Câu trả lời là
%S.' % (bí mật))
Nếu đoán là giá trị tương tự như secretNum , thì chúng tôi biết người chơi đã chính xác
đoán số bí mật và chúng ta có thể thoát ra khỏi vòng lặp này ( trong khi vòng lặp đó là
bắt đầu trên dòng 60). Nếu không, thì thực thi tiếp tục đến dòng 72, nơi chúng tôi kiểm tra xem
nếu
Người chơi hết đoán. Nếu vậy, chúng tôi nói với người chơi rằng họ đã thua và bí mật là gì
số là Chúng ta biết rằng điều kiện cho trong khi vòng lặp trên dòng 55 sẽ là False , vì vậy
không cần cho một tuyên bố phá vỡ .
Tại thời điểm này, thực hiện nhảy trở lại trong khi vòng lặp trên đường dây 60 nơi chúng ta để
cho các cầu thủ
có một dự đoán khác. Nếu người chơi hết dự đoán (hoặc chúng tôi đã thoát ra khỏi vòng lặp với
ngắt câu lệnh trên dòng 71), sau đó thực thi sẽ tiến hành đến dòng 75.
Yêu cầu người chơi chơi lại
75. nếu không chơiAgain ():
76.
phá vỡ
Sau khi rời khỏi trong khi vòng lặp trên dòng 60, chúng tôi yêu cầu các cầu thủ nếu muốn chơi
lại bằng cách
gọi hàm playAgain () của chúng tôi . Nếu playAgain () trả về Sai , thì chúng ta nên
thoát ra khỏi trong khi vòng lặp được bắt đầu trên đường 55. Kể từ khi có mã không còn nữa sau
vòng lặp này, chương trình chấm dứt.
Nếu playAgain () trả về True , thì chúng ta sẽ không thực thi lệnh break và
thực thi sẽ quay trở lại dòng 55. Một số bí mật mới sẽ được tạo để
Người chơi có thể chơi một trò chơi mới.
Tóm tắt: Làm tốt ở Bagels
Bagels là một trò chơi khá đơn giản để lập trình nhưng có thể khó giành chiến thắng. Nhưng nếu
bạn giữ
chơi, cuối cùng bạn sẽ khám phá ra những cách tốt hơn để đoán và sử dụng các manh mối
Trò chơi mang đến cho bạn.
Chương này đã giới thiệu một vài hàm và phương thức mới ( Random.shuffle () , sort
() và tham gia () ), cùng với một vài phím tắt tiện dụng. Sử dụng bài tập tăng
toán tử liên quan đến việc gõ ít hơn khi bạn muốn thay đổi giá trị tương đối của một biến (chẳng
hạn như trong
spam = spam + 1 , có thể rút ngắn thành spam + = 1 ). Nội suy chuỗi có thể
làm cho mã của bạn dễ đọc hơn nhiều bằng cách đặt % s (được gọi là công cụ xác định chuyển
đổi) bên trong
chuỗi thay vì sử dụng nhiều hoạt động nối chuỗi.
Các join () phương pháp chuỗi được thông qua một danh sách các chuỗi đó sẽ được nối với nhau,
với chuỗi liên kết ban đầu ở giữa chúng. Ví dụ: 'X'.join
(['xin chào', 'thế giới', 'yay']) sẽ đánh giá chuỗi,
198

Trang 213
'Xin chào thế giới' .
Các loại () phương pháp danh sách sẽ sắp xếp lại các mục trong danh sách để được theo thứ tự
chữ cái.
Các append () phương pháp danh sách sẽ thêm một giá trị đến cuối danh sách có liên
quan. Nếu thư rác
chứa danh sách ['a', 'b', 'c'] , sau đó gọi spam.append ('d') sẽ thay đổi
danh sách thư rác là ['a', 'b', 'c', 'd'] .
Chương tiếp theo không phải là về lập trình trực tiếp, nhưng sẽ cần thiết cho các trò chơi
chúng tôi muốn tạo ra trong các chương sau của cuốn sách này. Chúng ta sẽ tìm hiểu về các khái
niệm toán học
tọa độ Descartes và số âm. Chúng sẽ được sử dụng trong Sonar, Reversi,
và trò chơi Dodger, nhưng tọa độ và số âm của Cartesian được sử dụng trong hầu hết tất cả
trò chơi (đặc biệt là trò chơi đồ họa). Nếu bạn đã biết về những khái niệm này, hãy đưa ra
chương tiếp theo một cách đọc ngắn gọn dù sao chỉ để làm mới. Hãy lặn xuống!
199
11 - Bánh mì tròn

Trang 214
Các chủ đề được đề cập trong Chương này:
tọa độ Descartes.
● Hệ 

● Trục X và trục Y.

● Tính chất giao hoán của phép cộng.

● Giá trị tuyệt đối và hàm abs () .

Chương này không giới thiệu một trò chơi mới, mà thay vào đó là một số đơn giản
khái niệm toán học mà chúng ta sẽ sử dụng trong phần còn lại của các trò chơi trong cuốn sách
này.
Khi bạn xem các trò chơi 2D (như Tetris hoặc Super Nintendo hoặc Sega Genesis cũ
trò chơi) bạn có thể thấy rằng hầu hết đồ họa trên màn hình có thể di chuyển sang trái hoặc phải
(lần đầu tiên
kích thước) và lên hoặc xuống (chiều thứ hai, do đó 2D). Để chúng tôi tạo ra
trò chơi có các vật thể chuyển động xung quanh hai chiều (chẳng hạn như hai chiều
màn hình máy tính), chúng tôi cần một hệ thống có thể dịch một vị trí trên màn hình sang các số
nguyên
chương trình của chúng tôi có thể đối phó với.
Đây là nơi các hệ tọa độ của Cartesian xuất hiện. Các tọa độ có thể trỏ đến rất
điểm cụ thể trên màn hình để chương trình của chúng tôi có thể theo dõi các khu vực khác nhau
trên
màn.
Số âm thường được sử dụng với các hệ tọa độ Descartes. Thư hai
một nửa chương này sẽ giải thích cách chúng ta có thể làm toán với các số âm.
Bạn có thể đã biết về hệ tọa độ Descartes và số âm từ
lop toan. Trong trường hợp đó, dù sao bạn cũng có thể đọc chương này để đọc nhanh
bản thân bạn.
200

Trang 215
Lưới và tọa độ Descartes
Một vấn đề trong nhiều trò chơi là làm thế nào để nói chuyện
về điểm chính xác trên bảng. Điểm chung
cách giải quyết này là bằng cách đánh dấu từng
hàng và cột riêng lẻ trên một bảng với một
thư và một số. Hình 12-1 là một cờ vua
bảng có mỗi hàng và mỗi cột
đánh dấu.
Trong cờ vua, quân đoàn trông giống như một
đầu ngựa. Các hiệp sĩ trắng được đặt tại
điểm e, 6 và hiệp sĩ đen nằm ở
điểm a, 4. Chúng ta cũng có thể thấy rằng mọi không gian
trên hàng 7 và mọi khoảng trống trong cột c là
trống.
Một lưới với các hàng và cột được dán nhãn như
bàn cờ là tọa độ của Cartesian
hệ thống. Bằng cách sử dụng nhãn hàng và cột
nhãn, chúng ta có thể đưa ra tọa độ dành cho một
và chỉ có một không gian trên bảng. Điều này thực sự có thể giúp chúng tôi mô tả chính xác cho
máy tính
vị trí chúng tôi muốn. Nếu bạn đã học về hệ tọa độ Descartes trong lớp toán,
bạn có thể biết rằng thông thường chúng ta có số cho cả hàng và cột. Cái này rất tiện
bởi vì nếu không sau cột thứ 26 chúng ta sẽ hết chữ. Hội đồng đó sẽ
trông giống như hình 12-2.
Những con số sang trái và bên phải mô tả các cột là một phần của trục X . Các
số đi lên và xuống mà mô tả các hàng là một phần của trục Y . Khi nào chúng ta
mô tả tọa độ, chúng ta luôn nói tọa độ X trước, sau đó là tọa độ Y.
Điều đó có nghĩa là hiệp sĩ trắng trong hình trên nằm ở tọa độ 5, 6 (và không
6, 5). Hiệp sĩ đen nằm ở tọa độ 1, 4 (không nên nhầm với 4, 1).
Lưu ý rằng để hiệp sĩ đen di chuyển đến vị trí của hiệp sĩ trắng, hiệp sĩ đen
phải di chuyển lên hai không gian, và sau đó sang phải bởi bốn không gian. (Hoặc di chuyển
đúng bốn khoảng trắng
và sau đó di chuyển lên hai khoảng trống.) Nhưng chúng ta không cần nhìn vào bảng để tìm ra
điều này. Nếu
chúng ta biết hiệp sĩ trắng nằm ở 5, 6 và hiệp sĩ đen nằm ở 1, 4, sau đó chúng ta
chỉ có thể sử dụng phép trừ để tìm ra thông tin này.
Trừ tọa độ X của hiệp sĩ đen và tọa độ X của hiệp sĩ trắng: 5 - 1 = 4. Điều đó
có nghĩa là hiệp sĩ đen phải di chuyển dọc theo trục X bằng bốn khoảng trống.
Trừ tọa độ Y của hiệp sĩ đen và tọa độ Y của hiệp sĩ trắng: 6 - 4 = 2. Điều đó
có nghĩa là hiệp sĩ đen phải di chuyển dọc theo trục Y bằng hai khoảng trống.
Hình 12-1: Một bàn cờ mẫu có một
hiệp sĩ đen ở a, 4 và một hiệp sĩ trắng ở e, 6.
201
12 - Tọa độ Descartes

Trang 216
Số âm
Một khái niệm khác mà Cartesian
tọa độ sử dụng là số âm.
Số âm là số
nhỏ hơn không. Chúng tôi đặt một dấu trừ vào
phía trước một số cho thấy rằng nó là một
số âm. -1 nhỏ hơn 0. Và -2
nhỏ hơn -1. Và -3 nhỏ hơn -2.
Nếu bạn nghĩ về số thường xuyên (được gọi là
số dương ) khi bắt đầu từ 1 và
ngày càng tăng, bạn có thể nghĩ về tiêu cực
số bắt đầu từ -1 và giảm dần.
Bản thân 0 không tích cực hay tiêu cực. Trong này
hình ảnh, bạn có thể thấy những con số tích cực
tăng sang phải và tiêu cực
số giảm dần về bên trái:
Hình 12-3: Một dòng số.
Dòng số thực sự hữu ích để thực hiện phép trừ và cộng với âm
số. Biểu thức 4 + 3 có thể được coi là hiệp sĩ trắng bắt đầu từ vị trí 4
và di chuyển 3 khoảng trắng sang phải (bổ sung có nghĩa là tăng, ở bên phải
phương hướng).
Hình 12-4: Di chuyển hiệp sĩ trắng sang phải thêm vào tọa độ.
Như bạn có thể thấy, hiệp sĩ trắng kết thúc ở vị trí 7. Điều này có ý nghĩa, bởi vì 4 + 3
là 7.
Phép trừ có thể được thực hiện bằng cách di chuyển hiệp sĩ trắng sang trái. Phép trừ
giảm, đó là theo hướng trái. 4 - 6 sẽ là hiệp sĩ trắng bắt đầu từ vị trí
4 và di chuyển 6 khoảng trắng sang trái:
Hình 12-2: Bàn cờ giống nhau nhưng có
tọa độ số cho cả hàng và cột.
202

Trang 217
Hình 12-5: Di chuyển hiệp sĩ trắng sang trái trừ khỏi tọa độ.
Hiệp sĩ trắng kết thúc ở vị trí -2. Điều đó có nghĩa là 4 - 6 bằng -2.
Nếu chúng ta cộng hoặc trừ một số âm, hiệp sĩ trắng sẽ di chuyển ngược lại
phương hướng. Nếu bạn thêm một số âm, hiệp sĩ di chuyển sang trái . Nếu bạn trừ đi một
số âm, hiệp sĩ di chuyển sang phải . Biểu thức -6 - -4 sẽ bằng -
2. Hiệp sĩ bắt đầu từ -6 và di chuyển sang phải 4 khoảng trống. Lưu ý rằng -6 - -4 có
cùng một câu trả lời là -6 + 4.
Hình 12-6: Ngay cả khi hiệp sĩ trắng bắt đầu ở tọa độ âm, di chuyển sang phải vẫn thêm vào tọa độ.
Hình 12-7: Đặt hai dòng số với nhau tạo ra một hệ tọa độ Descartes.
Dòng số giống như trục X. Nếu chúng ta thực hiện dòng số lên và xuống
203
12 - Tọa độ Descartes

Trang 218
thay vì trái và phải, nó sẽ mô hình trục Y. Thêm một số dương (hoặc
trừ một số âm) sẽ di chuyển hiệp sĩ lên dòng số và trừ đi
một số dương (hoặc thêm một số âm) sẽ di chuyển hiệp sĩ xuống. Khi nào chúng ta
Đặt hai dòng số này lại với nhau, chúng ta có một hệ tọa độ Descartes như trong Hình
12-7.
Tọa độ 0, 0 có một tên đặc biệt: gốc .
Thủ thuật toán học
Trừ các số âm hoặc thêm số âm có vẻ dễ dàng khi bạn có một
số dòng trước mặt bạn, nhưng nó có thể dễ dàng khi bạn chỉ có số. Đây
là ba thủ thuật bạn có thể làm để tự đánh giá các biểu thức này dễ dàng hơn.
Thủ thuật 1: "Một điểm trừ ăn dấu cộng bên trái"
Đầu tiên là nếu bạn đang thêm một số âm, ví dụ; 4 + -2. Bí quyết đầu tiên là "a
dấu trừ ăn dấu cộng bên trái ". Khi bạn thấy dấu trừ có dấu cộng bên trái,
bạn có thể thay thế dấu cộng bằng dấu trừ. Câu trả lời vẫn vậy, bởi vì
thêm một giá trị âm cũng giống như trừ đi một giá trị dương. 4 + -2 và 4 - 2 cả hai
đánh giá đến 2.
Hình 12-8: Thủ thuật 1 - Thêm số dương và số âm.
Thủ thuật 2: "Hai nhược điểm kết hợp thành một phép cộng"
Thủ thuật thứ hai là nếu bạn trừ đi một số âm, ví dụ: 4 - -2. Các
mẹo thứ hai là "hai điểm trừ kết hợp thành một điểm cộng". Khi bạn nhìn thấy hai dấu trừ tiếp
theo
với nhau mà không có một số ở giữa chúng, chúng có thể kết hợp thành một dấu cộng. Các
câu trả lời vẫn như vậy, vì trừ đi một giá trị âm giống như thêm một
giá trị dương.
204

Trang 219
Hình 12-9: Thủ thuật 2 - Trừ một số dương và âm.
Thủ thuật 3: Tính chất giao hoán của phép cộng
Bí quyết thứ ba là hãy nhớ rằng khi bạn thêm hai số như 6 và 4, thì không
cho dù họ đang ở thứ tự nào (Đây được gọi là tính chất giao hoán của phép cộng.)
Điều đó có nghĩa là cả 6 + 4 và 4 + 6 đều có cùng giá trị, 10. Nếu bạn đếm các ô trong
hình dưới đây, bạn có thể thấy rằng bạn không có vấn đề gì với số thứ tự
thêm vào.
Hình 12-10: Thủ thuật 3 - Tính chất giao hoán của phép cộng.
Giả sử bạn đang thêm số âm và số dương, như -6 + 8. Bởi vì bạn
đang thêm số, bạn có thể trao đổi thứ tự của các số mà không thay đổi câu trả lời. -
6 + 8 giống với 8 + -6. Nhưng khi bạn nhìn vào 8 + -6, bạn sẽ thấy rằng dấu trừ có thể ăn
dấu cộng ở bên trái và vấn đề trở thành 8 - 6 = 2. Nhưng điều này có nghĩa là -6 + 8 là
còn 2! Chúng tôi đã sắp xếp lại vấn đề để có cùng một câu trả lời, nhưng giúp chúng tôi dễ dàng
hơn
giải quyết mà không cần sử dụng máy tính hoặc máy tính.
205
12 - Tọa độ Descartes

Trang 220
Hình 12-11: Sử dụng các thủ thuật toán học của chúng ta với nhau.
Tất nhiên, bạn luôn có thể sử dụng trình vỏ tương tác làm máy tính để đánh giá những thứ này
biểu thức. Vẫn rất hữu ích khi biết ba thủ thuật trên khi cộng hoặc trừ
số âm. Rốt cuộc, bạn sẽ không luôn ở trước máy tính với Python
thời gian!
>>> 4 + -2
2
>>> -4 + 2
-2
>>> -4 + -2
-6
>>> 4 - -2
6
>>> -4 - 2
-6
>>> -4 - -2
-2
>>>
Giá trị tuyệt đối và abs () Chức năng
Các giá trị tuyệt đối của một số là số mà không có dấu hiệu tiêu cực ở phía trước nó.
Điều này có nghĩa là số dương không thay đổi, nhưng số âm trở thành dương.
Ví dụ: giá trị tuyệt đối của -4 là 4. Giá trị tuyệt đối của -7 là 7. Giá trị tuyệt đối
của 5 (đó là tích cực) chỉ là 5.
Chúng ta có thể tìm thấy cách xa hai thứ trên một dòng số cách nhau bằng cách lấy
giá trị tuyệt đối của sự khác biệt của họ. Hãy tưởng tượng rằng hiệp sĩ trắng ở vị trí 4 và
206

Trang 221
hiệp sĩ đen ở vị trí -2. Để tìm ra khoảng cách giữa chúng, bạn sẽ tìm thấy
khác biệt bằng cách trừ đi vị trí của chúng và lấy giá trị tuyệt đối của số đó.
Nó hoạt động bất kể thứ tự của các con số là gì. -2 - 4 (nghĩa là trừ hai âm
bốn) là -6 và giá trị tuyệt đối của -6 là 6. Tuy nhiên, 4 - -2 (nghĩa là bốn âm
hai) là 6 và giá trị tuyệt đối của 6 là 6. Sử dụng giá trị tuyệt đối của chênh lệch là một
cách tốt để tìm khoảng cách giữa hai điểm trên một dòng số (hoặc trục).
Hàm abs () có thể được sử dụng để trả về giá trị tuyệt đối của một số nguyên. Cơ bụng ()
Hàm là một hàm dựng sẵn, vì vậy bạn không cần nhập bất kỳ mô-đun nào để sử dụng nó. Vượt
qua nó
số nguyên hoặc giá trị float và nó sẽ trả về giá trị tuyệt đối:
>>> abs (-5)
5
>>> abs (42)
42
>>> abs (-10,5)
10,5
Hệ thống tọa độ của màn hình máy tính
Thông thường là máy tính
màn hình sử dụng hệ tọa độ
có gốc (0, 0) ở trên cùng
góc trái của màn hình, trong đó
tăng đi xuống và đến
đúng. Không có tiêu cực
tọa độ. Điều này là do văn bản
được in bắt đầu ở phía trên bên trái,
và được in ở bên phải
và hướng xuống dưới. Hầu hết máy tính
đồ họa sử dụng tọa độ này
và chúng tôi sẽ sử dụng nó trong
Trò chơi. Ngoài ra nó là phổ biến để
giả sử rằng màn hình có thể hiển thị
80 ký tự văn bản trên mỗi hàng và 25
ký tự văn bản trên mỗi cột (nhìn
trong hình 12-12). Điều này đã từng
kích thước màn hình tối đa
màn hình có thể hỗ trợ. Trong khi
màn hình ngày nay thường có thể hiển thị nhiều văn bản hơn, chúng tôi sẽ không cho rằng người
dùng
màn hình lớn hơn 80 đến 25.
Hình 12-12: Hệ tọa độ Descartes trên màn hình máy tính.
207
12 - Tọa độ Descartes

Trang 222
Tóm tắt: Sử dụng Toán này trong Trò chơi
Đây không phải là quá nhiều toán học để lập trình. Trong thực tế, hầu hết các chương trình
không đòi hỏi phải hiểu nhiều về toán học. Cho đến chương này, chúng tôi đã nhận được bằng
về phép cộng và phép nhân đơn giản.
Hệ thống tọa độ Descartes là cần thiết để mô tả chính xác nơi trong hai chiều
diện tích một vị trí nhất định là. Các tọa độ được tạo thành từ hai số: tọa độ X và
tọa độ Y. Trục X chạy trái và phải và trục Y chạy lên và xuống. Trên một
màn hình máy tính (và trong hầu hết các chương trình máy tính), trục X bắt đầu từ 0 ở bên trái
và tăng trên đường bên phải. Trục Y bắt đầu từ 0 trên đỉnh màn hình và
tăng trên đường xuống.
Đối với phần còn lại của cuốn sách, chúng tôi sẽ sử dụng các khái niệm chúng ta đã học trong
chương này trong các trò chơi của mình
bởi vì chúng có hai chiều trong đó. Tất cả các trò chơi đồ họa yêu cầu
hiểu làm thế nào tọa độ Cartesian làm việc.
208

Trang 223
Các chủ đề được đề cập trong Chương này:
● Cấu trúc dữ liệu.
● Các remove () phương pháp danh sách.
● Các isdigit () phương pháp chuỗi.

● Hàm sys.exit () .

Trò chơi trong chương này chỉ giới thiệu một vài phương pháp hữu ích mới đi kèm
Python, phương thức danh sách remove () và phương thức chuỗi isdigit () . Nhưng đây là
chương trình đầu tiên sẽ sử dụng tọa độ Descartes và các khái niệm toán học
chúng ta đã học ở chương 11. Chương trình này cũng sẽ sử dụng việc sử dụng các cấu trúc dữ
liệu (đó là
thực sự chỉ là một cách thú vị để nói các biến có chứa danh sách các danh sách.) Khi các trò chơi
của chúng tôi trở thành
phức tạp hơn, chúng ta sẽ cần lưu trữ dữ liệu của mình theo những cách được tổ chức tốt.
Sonar là một công nghệ mà tàu sử dụng để xác định vị trí các vật thể dưới biển. Trong chương
này
Trò chơi, người chơi đặt các thiết bị sonar ở nhiều nơi trên đại dương để xác định vị trí chìm
rương kho báu. Các thiết bị sonar (trong trò chơi của chúng tôi) có thể cho người chơi biết kho
báu cách đó bao xa
ngực là từ thiết bị sonar, nhưng không theo hướng nào. Nhưng bằng cách đặt nhiều sonar
Thiết bị ngừng hoạt động, người chơi có thể tìm ra chính xác nơi rương kho báu.
Có ba rương để thu thập, nhưng người chơi chỉ có mười sáu thiết bị sonar để sử dụng
tìm họ. Hãy tưởng tượng rằng chúng ta không thể nhìn thấy rương kho báu trong bức ảnh sau
đây.
Bởi vì mỗi thiết bị sonar chỉ có thể tìm thấy khoảng cách chứ không thể định hướng, những nơi
có thể
kho báu có thể là bất cứ nơi nào trong một vòng quanh thiết bị sonar (xem Hình 13-1).
209

Trang 224
Hình 13-1: Thiết bị sonar đầu tiên hiển thị một chiếc nhẫn
nơi có thể kho báu có thể được đặt.
Hình 13-2: Kết hợp các vòng của cả ba sonar
thiết bị chỉ hiển thị một nơi có thể cho kho báu.
Nhưng nếu chúng ta có nhiều thiết bị sonar hoạt động cùng nhau, chúng ta có thể thu hẹp nó
xuống một
nơi chính xác nơi tất cả các vòng giao nhau. (Xem hình 13-2)
Chạy mẫu
SỚM!
Bạn có muốn xem hướng dẫn? (có không)
Không
1
2
3
4
5
01234567890123456789012345678901234567890123456789012345678989
0 `~~~` ~~~ `~` ~ ~ `~~~~~` ~ `` ~~~ ~ `~` ~~~ `~` `~` `~ ~` `` `~` ~ `` `~` ~~~ ~ `` 0
1 ~ `~~~` `` ~~~ ~ `~` ~ ~ `~` `~` ~ ~ `` `~` ~ `~ ~` ~ `~~~~~ ~` ~ `` `` `~` ~ ~ `~~~ ~` 1
2 `~` `~` `~~~` ~ `` ~ `~` ~ `` ~ `` `` `~~~~~~~~~` ~ `~ ~` ~ `` ~~~ ~ ~ `` `~ ~` ~ `` `2
210

Trang 225
3 `` ~ `~ ~` `~` ~ `` ~ `~` ~ `~ ~` ~ `~ ~` ~ `~` `~~~` ~ `` ~ `` `` `` `` ` `` ~~~ ~ `` ~ `` 3
4 `` ~ ~ `~~~` `~` `~ ~` `` `~` ~ `~` ~ `` ~~~ `` ~ ~ `` `~` ~ ~ `~ ~` ~ `~ `~ ~` ~~~ ~ `` `4
5 ~ ~ `` `~~~` ~ `~ ~` `~` ~ `` ~ `` `~` ~ ~ `~~~~~` ~ ~ `` ~ `~` ~~~ `~ ~ `~` ~ `~` ~~~ `5
6 `` ~ ~ `` `` `~~~ ~` ~ `~~~` `` ~~~ ~ `~ ~` ~ ~ `~ ~` `` ~ ~ `~~~` ~~~ ` `~` ~~~ `` ~~~ 6
7 `~` `` `` `` `` ~ `` `~` `~` `~ ~` ~~~ ~ `~ ~` `~` `~~~` `` ~ `~ ~` ~ ` `~` `~ ~` `` ~ ~ 7
8 `~` `` `~` `` ~ `~ ~` ~~~ `~ ~` `~~~` `~` ~ `` ~~~ `` ~ `~` `` `` ~ `~ ~ `` `~` ~~~ ~ `8
9 ~ `` `~ ~` ~ `~` `~` `~ ~` `~` `~` `` ~ `~` `~~~ ~` ~ `~` ~~~ `~` ~ `~ `~~~` `~ ~` `` 9
10 `` `~` ~ `` `~` `~` `~` ~ ~ `~` `~` `` `~` `~~~` ~ ~ `~ ~` `~~~ ~` ~ ~ ~ `~` ~ ~ `` `` ~ 10
11 `` `~` `` ~~~ `~` `` ~ ~ `~~~` ~ `` `` `~` ~ ~ `~` ~ ~ `~ ~` ~ `~ ~` ~~~ `` `` ~ `` `` ~ `11
12 ~~~ `~` ~~~ `` ~~~~~ ~ `~~~` `~` ~ `~ ~` ~ `~ ~` ~ `` `~~~` `` ~ ~ `~ ~ `~` `~` `~` ~ 12
13 `~ ~` `` `~ ~` `~` `` ~~~ `~` `` ~ `~~~~~~~~~` ~ ~ `` ~~~~~ `` `` ` ~ `~` ~ `` ~~~ ~ 13
14 `~ ~` ~ `~` `` `~` `` ~ `~` ~ `` `~ ~` ~~~ ~ `~` `` ~ `` ~ `` ~ `` ~~~ `` `` ~ ~ `` `` `` ~ 14
01234567890123456789012345678901234567890123456789012345678989
1
2
3
4
5
Bạn có 16 thiết bị sonar còn lại. 3 rương kho báu còn lại.
Nơi nào bạn muốn thả thiết bị sonar tiếp theo? (0-59 0-14) (hoặc
loại bỏ)
10 10
1
2
3
4
5
01234567890123456789012345678901234567890123456789012345678989
0 `~~~` ~~~ `~` ~ ~ `~~~~~` ~ `` ~~~ ~ `~` ~~~ `~` `~` `~ ~` `` `~` ~ `` `~` ~~~ ~ `` 0
1 ~ `~~~` `` ~~~ ~ `~` ~ ~ `~` `~` ~ ~ `` `~` ~ `~ ~` ~ `~~~~~ ~` ~ `` `` `~` ~ ~ `~~~ ~` 1
2 `~` `~` `~~~` ~ `` ~ `~` ~ `` ~ `` `` `~~~~~~~~~` ~ `~ ~` ~ `` ~~~ ~ ~ `` `~ ~` ~ `` `2
3 `` ~ `~ ~` `~` ~ `` ~ `~` ~ `~ ~` ~ `~ ~` ~ `~` `~~~` ~ `` ~ `` `` `` `` ` `` ~~~ ~ `` ~ `` 3
4 `` ~ ~ `~~~` `~` `~ ~` `` `~` ~ `~` ~ `` ~~~ `` ~ ~ `` `~` ~ ~ `~ ~` ~ `~ `~ ~` ~~~ ~ `` `4
5 ~ ~ `` `~~~` ~ `~ ~` `~` ~ `` ~ `` `~` ~ ~ `~~~~~` ~ ~ `` ~ `~` ~~~ `~ ~ `~` ~ `~` ~~~ `5
6 `` ~ ~ `` `` `~~~ ~` ~ `~~~` `` ~~~ ~ `~ ~` ~ ~ `~ ~` `` ~ ~ `~~~` ~~~ ` `~` ~~~ `` ~~~ 6
7 `~` `` `` `` `` ~ `` `~` `~` `~ ~` ~~~ ~ `~ ~` `~` `~~~` `` ~ `~ ~` ~ ` `~` `~ ~` `` ~ ~ 7
8 `~` `` `~` `` ~ `~ ~` ~~~ `~ ~` `~~~` `~` ~ `` ~~~ `` ~ `~` `` `` ~ `~ ~ `` `~` ~~~ ~ `8
9 ~ `` `~ ~` ~ `~` `~` `~ ~` `~` `~` `` ~ `~` `~~~ ~` ~ `~` ~~~ `~` ~ `~ `~~~` `~ ~` `` 9
10 `` `~` ~ `` `~ 5` ~` `~` ~ ~ `~` `~` `` `~` `~~~` ~ ~ `~ ~` `~~~ ~` ~ ~ ~ `~` ~ ~ `` `` ~ 10
11 `` `~` `` ~~~ `~` `` ~ ~ `~~~` ~ `` `` `~` ~ ~ `~` ~ ~ `~ ~` ~ `~ ~` ~~~ `` `` ~ `` `` ~ `11
12 ~~~ `~` ~~~ `` ~~~~~ ~ `~~~` `~` ~ `~ ~` ~ `~ ~` ~ `` `~~~` `` ~ ~ `~ ~ `~` `~` `~` ~ 12
13 `~ ~` `` `~ ~` `~` `` ~~~ `~` `` ~ `~~~~~~~~~` ~ ~ `` ~~~~~ `` `` ` ~ `~` ~ `` ~~~ ~ 13
14 `~ ~` ~ `~` `` `~` `` ~ `~` ~ `` `~ ~` ~~~ ~ `~` `` ~ `` ~ `` ~ `` ~~~ `` `` ~ ~ `` `` `` ~ 14
01234567890123456789012345678901234567890123456789012345678989
1
2
3
4
5
Kho báu được phát hiện ở khoảng cách 5 từ thiết bị sonar.
Bạn có 15 thiết bị sonar còn lại. 3 rương kho báu còn lại.
Nơi nào bạn muốn thả thiết bị sonar tiếp theo? (0-59 0-14) (hoặc
loại bỏ)
15 6
1
2
3
4
5
01234567890123456789012345678901234567890123456789012345678989
0 `~~~` ~~~ `~` ~ ~ `~~~~~` ~ `` ~~~ ~ `~` ~~~ `~` `~` `~ ~` `` `~` ~ `` `~` ~~~ ~ `` 0
1 ~ `~~~` `` ~~~ ~ `~` ~ ~ `~` `~` ~ ~ `` `~` ~ `~ ~` ~ `~~~~~ ~` ~ `` `` `~` ~ ~ `~~~ ~` 1
2 `~` `~` `~~~` ~ `` ~ `~` ~ `` ~ `` `` `~~~~~~~~~` ~ `~ ~` ~ `` ~~~ ~ ~ `` `~ ~` ~ `` `2
3 `` ~ `~ ~` `~` ~ `` ~ `~` ~ `~ ~` ~ `~ ~` ~ `~` `~~~` ~ `` ~ `` `` `` `` ` `` ~~~ ~ `` ~ `` 3
4 `` ~ ~ `~~~` `~` `~ ~` `` `~` ~ `~` ~ `` ~~~ `` ~ ~ `` `~` ~ ~ `~ ~` ~ `~ `~ ~` ~~~ ~ `` `4
5 ~ ~ `` `~~~` ~ `~ ~` `~` ~ `` ~ `` `~` ~ ~ `~~~~~` ~ ~ `` ~ `~` ~~~ `~ ~ `~` ~ `~` ~~~ `5
6 `` ~ ~ `` `` `~~~ ~` ~ 4 ~~~ `` `~~~ ~` ~ ~ `~ ~` ~ ~ `` `~ ~` ~~~ `~~~` `~` ~~~ `` ~~~ 6
7 `~` `` `` `` `` ~ `` `~` `~` `~ ~` ~~~ ~ `~ ~` `~` `~~~` `` ~ `~ ~` ~ ` `~` `~ ~` `` ~ ~ 7
8 `~` `` `~` `` ~ `~ ~` ~~~ `~ ~` `~~~` `~` ~ `` ~~~ `` ~ `~` `` `` ~ `~ ~ `` `~` ~~~ ~ `8
9 ~ `` `~ ~` ~ `~` `~` `~ ~` `~` `~` `` ~ `~` `~~~ ~` ~ `~` ~~~ `~` ~ `~ `~~~` `~ ~` `` 9
10 `` `~` ~ `` `~ 5` ~` `~` ~ ~ `~` `~` `` `~` `~~~` ~ ~ `~ ~` `~~~ ~` ~ ~ ~ `~` ~ ~ `` `` ~ 10
11 `` `~` `` ~~~ `~` `` ~ ~ `~~~` ~ `` `` `~` ~ ~ `~` ~ ~ `~ ~` ~ `~ ~` ~~~ `` `` ~ `` `` ~ `11
12 ~~~ `~` ~~~ `` ~~~~~ ~ `~~~` `~` ~ `~ ~` ~ `~ ~` ~ `` `~~~` `` ~ ~ `~ ~ `~` `~` `~` ~ 12
13 `~ ~` `` `~ ~` `~` `` ~~~ `~` `` ~ `~~~~~~~~~` ~ ~ `` ~~~~~ `` `` ` ~ `~` ~ `` ~~~ ~ 13
14 `~ ~` ~ `~` `` `~` `` ~ `~` ~ `` `~ ~` ~~~ ~ `~` `` ~ `` ~ `` ~ `` ~~~ `` `` ~ ~ `` `` `` ~ 14
211
13 - Truy tìm kho báu Sonar

Trang 226
01234567890123456789012345678901234567890123456789012345678989
1
2
3
4
5
Kho báu được phát hiện ở khoảng cách 4 từ thiết bị sonar.
Bạn có 14 thiết bị sonar còn lại. 3 rương kho báu còn lại.
Nơi nào bạn muốn thả thiết bị sonar tiếp theo? (0-59 0-14) (hoặc
loại bỏ)
15 10
1
2
3
4
5
01234567890123456789012345678901234567890123456789012345678989
0 `~~~` ~~~ `~` ~ ~ `~~~~~` ~ `` ~~~ ~ `~` ~~~ `~` `~` `~ ~` `` `~` ~ `` `~` ~~~ ~ `` 0
1 ~ `~~~` `` ~~~ ~ `~` ~ ~ `~` `~` ~ ~ `` `~` ~ `~ ~` ~ `~~~~~ ~` ~ `` `` `~` ~ ~ `~~~ ~` 1
2 `~` `~` `~~~` ~ `` ~ `~` ~ `` ~ `` `` `~~~~~~~~~` ~ `~ ~` ~ `` ~~~ ~ ~ `` `~ ~` ~ `` `2
3 `` ~ `~ ~` `~` ~ `` ~ `~` ~ `~ ~` ~ `~ ~` ~ `~` `~~~` ~ `` ~ `` `` `` `` ` `` ~~~ ~ `` ~ `` 3
4 `` ~ ~ `~~~` `~` `~ ~` `` `~` ~ `~` ~ `` ~~~ `` ~ ~ `` `~` ~ ~ `~ ~` ~ `~ `~ ~` ~~~ ~ `` `4
5 ~ ~ `` `~~~` ~ `~ ~` `~` ~ `` ~ `` `~` ~ ~ `~~~~~` ~ ~ `` ~ `~` ~~~ `~ ~ `~` ~ `~` ~~~ `5
6 `` ~ ~ `` `` `~~~ ~` ~ O ~~~ `` `~~~ ~` ~ ~ `~ ~` ~ ~ `` `~ ~` ~~~ `~~~` `~` ~~~ `` ~~~ 6
7 `~` `` `` `` `` ~ `` `~` `~` `~ ~` ~~~ ~ `~ ~` `~` `~~~` `` ~ `~ ~` ~ ` `~` `~ ~` `` ~ ~ 7
8 `~` `` `~` `` ~ `~ ~` ~~~ `~ ~` `~~~` `~` ~ `` ~~~ `` ~ `~` `` `` ~ `~ ~ `` `~` ~~~ ~ `8
9 ~ `` `~ ~` ~ `~` `~` `~ ~` `~` `~` `` ~ `~` `~~~ ~` ~ `~` ~~~ `~` ~ `~ `~~~` `~ ~` `` 9
10 `` `~` ~ `` `~ O` ~` `O` ~ ~` ~ `` ~ `` `` ~ `` ~~~ `~ ~` ~ ~ `` ~~~ ~ `~ ~ ~ `~` ~ ~ `` `` ~ 10
11 `` `~` `` ~~~ `~` `` ~ ~ `~~~` ~ `` `` `~` ~ ~ `~` ~ ~ `~ ~` ~ `~ ~` ~~~ `` `` ~ `` `` ~ `11
12 ~~~ `~` ~~~ `` ~~~~~ ~ `~~~` `~` ~ `~ ~` ~ `~ ~` ~ `` `~~~` `` ~ ~ `~ ~ `~` `~` `~` ~ 12
13 `~ ~` `` `~ ~` `~` `` ~~~ `~` `` ~ `~~~~~~~~~` ~ ~ `` ~~~~~ `` `` ` ~ `~` ~ `` ~~~ ~ 13
14 `~ ~` ~ `~` `` `~` `` ~ `~` ~ `` `~ ~` ~~~ ~ `~` `` ~ `` ~ `` ~ `` ~~~ `` `` ~ ~ `` `` `` ~ 14
01234567890123456789012345678901234567890123456789012345678989
1
2
3
4
5
Bạn đã tìm thấy một rương kho báu bị chìm!
Bạn có 13 thiết bị sonar còn lại. 2 rương kho báu còn lại.
Nơi nào bạn muốn thả thiết bị sonar tiếp theo? (0-59 0-14) (hoặc
loại bỏ)
... bỏ qua cho ngắn gọn ....
1
2
3
4
5
01234567890123456789012345678901234567890123456789012345678989
0 `~~~` ~~~ `~` ~ ~ `~~~~~` ~ `` ~~~ ~ `~` ~~~ `~` `~` `~ ~` `` `~` ~ `` `~` ~~~ ~ `` 0
1 ~ `~~~` `` ~~~ ~ `~` ~ ~ `~` `~` ~ ~ `` `~ O ~` ~ ~ `~` ~~~~~ ~ `~` `` ` `~` ~ ~ `~~~ ~` 1
2 `~` `~` `~~~` ~ `` ~ `~` ~ `` ~ `` `` `~ ~ O ~~~ O ~ ~` ~ `~ ~` ~ `` ~~~ ~ ~ `` `~ ~` ~ `` `2
3 `` ~ 3 ~ ~ `` 8` ~ `` ~ `~` ~ `~ ~` ~ `~ ~` ~ `~` `~~~` ~ `O ~` `` `~` `` ` `` ~~~ ~ `` ~ `` 3
4 `` ~ ~ `~~~` `~` `~ ~` `` `~` ~ `~` ~ O` ~ ~ O`` ~ ~ `` `~` ~ ~ `~ ~` ~ `~ `~ ~` ~~~ ~ `` `4
5 ~ ~ `` `~~~` ~ `~ ~` `~` ~ `` ~ `` `~` ~ ~ `~~~~~` ~ ~ `` ~ `~` ~~~ `~ ~ `~` ~ `~` ~~~ `5
6 `` ~ ~ `` `` `~~~ ~` ~ O ~~~ `` `~~~ ~` ~ ~ `~ ~` ~ ~ `` `~ ~` ~~~ `~~~` `O` ~~~` `~~~ 6
7 `~` `` `` `` `` ~ `` `~` `~` `~ ~` ~~~ ~ `~ ~` `~` `~~~` `` ~ `~ ~` ~ ` `~` `~ ~` `` ~ ~ 7
8 `~` `` `~` `` ~ `~ ~` ~~~ `~ ~` `~~~` `~` ~ `` ~~~ `` ~ `O```0` ~` ~ ~ `` `~` ~~~ ~ `8
9 ~ `` `~ ~` ~ `~` `~` `~ ~` `~` `~` `` ~ O ~ `` ~~~ ~ `~` ~ `~~~` ~ `~` ~ `~~~` `~ ~` `` 9
10 `` `~` ~ `` `~ O` ~` `O` ~ ~` ~ `` ~ `` `` ~ `` ~~~ `~ ~` ~ ~ `` ~~~ ~ `~ ~ ~ `~` ~ ~ `` `` ~ 10
11 `` `~` `` ~~~ `~` `` ~ ~ `~~~` ~ `` `` `~` ~ ~ `~` ~ ~ `~ ~` ~ `~ ~` ~~~ `` `` ~ `` `` ~ `11
12 ~~~ `~` ~~~ `` ~~~~~ ~ `~~~` `~` ~ `~ ~` ~ `~ ~` ~ `` `~~~` `` ~ ~ `~ ~ `~` `~` `~` ~ 12
13 `~ ~` `` `~ ~` `~` `` ~~~ `~` `` ~ `~~~~~~~~~` ~ ~ `` ~~~~~ `` `` ` ~ `~` ~ `` ~~~ ~ 13
14 `~ ~` ~ `~` `` `~` `` ~ `~` ~ `` `~ ~` ~~~ ~ `~` `` ~ `` ~ `` ~ `` ~~~ `` `` ~ ~ `` `` `` ~ 14
01234567890123456789012345678901234567890123456789012345678989
1
2
3
4
5
Kho báu được phát hiện ở khoảng cách 4 từ thiết bị sonar.
Chúng tôi đã hết thiết bị sonar! Bây giờ chúng ta phải quay tàu
và đầu
cho nhà với rương kho báu vẫn còn đó! Trò chơi kết thúc.
212

Trang 227
Các rương còn lại đã ở đây:
0, 4
Bạn có muốn chơi lại không? (có hay không)
Không

Mã nguồn của Sonar


Biết về tọa độ Descartes, các dòng số, số âm và tuyệt đối
giá trị sẽ giúp chúng tôi ra với trò chơi Sonar của chúng tôi. Nếu bạn không nghĩ rằng bạn hiểu
những
khái niệm, quay trở lại chương 11 để đánh lên. Dưới đây là mã nguồn của trò chơi. Gõ nó
vào một tệp mới, sau đó lưu tệp dưới dạng sonar.py và chạy nó bằng cách nhấn phím F5. Bạn
không
cần hiểu mã để nhập hoặc chơi trò chơi, mã nguồn sẽ được giải thích
một lát sau.
Ngoài ra, bạn có thể tải xuống mã nguồn từ trang web của cuốn sách tại URL
http://inventwithpython.com/ch CHƯƠNG13.
sonar.py
Mã này có thể được tải xuống từ http://inventwithpython.com/sonar.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. # Sonar
2.
3. nhập ngẫu nhiên
4. hệ thống nhập khẩu
5.
6. def drawBoard (bảng):
7. # Vẽ cấu trúc dữ liệu bảng.
số 8.
9. hline = '' # không gian ban đầu cho các số xuống
phía bên trái của bảng
10. cho i trong phạm vi (1, 6):
11.
hline + = ('' * 9) + str (i)
12.
13. # in các số trên đầu trang
14. in (hline)
15. in ('' + ('0123456789' * 6))
16. in ()
17.
18. # in mỗi trong số 15 hàng
19. cho tôi trong phạm vi (15):
20.
# số một chữ số cần được đệm bằng một
không gian thêm
21.
nếu tôi <10:
22.
ExtraSpace = ''
23.
khác:
24.
ExtraSpace = ''
25.
in ('% s% s% s% s'% (ExtraSpace, i, getRow
(bảng, tôi), tôi))
26.
213
13 - Truy tìm kho báu Sonar

Trang 228
27. # in các số ở phía dưới
28. in ()
29. in ('' + ('0123456789' * 6))
30. in (hline)
31.
32.
33. def getRow (bảng, hàng):
34. # Trả về một chuỗi từ cấu trúc dữ liệu bảng tại
hàng nhất định.
35. boardRow = ''
36. cho i trong phạm vi (60):
37.
boardRow + = board [i] [hàng]
38. bảng hoàn trả
39.
40. def getNewBoard ():
41. # Tạo cấu trúc dữ liệu bảng 60x15 mới.
42. bảng = []
43. cho x trong phạm vi (60): # danh sách chính là danh sách 60
danh sách
44.
board.append ([])
45.
cho y trong phạm vi (15): # mỗi danh sách trong danh sách chính
có 15 chuỗi ký tự đơn
46.
# sử dụng các ký tự khác nhau cho đại dương để
làm cho nó dễ đọc hơn
47.
nếu ngẫu nhiên.randint (0, 1) == 0:
48.
bảng [x] .append ('~')
49.
khác:
50.
bảng [x] .append ('`')
51. bảng hoàn trả
52.
53. def getRandomChests (numChests):
54. # Tạo danh sách cấu trúc dữ liệu ngực (hai mục
danh sách tọa độ x, y int)
55. rương = []
56. cho i trong phạm vi (numChests):
57.
chests.append ([Random.randint (0, 59),
ngẫu nhiên.randint (0, 14)])
58. rương trở lại
59.
60. def isValidMove (x, y):
61. # Trả về Đúng nếu tọa độ nằm trên bảng,
Nếu không thì Sai.
62. trả về x> = 0 và x <= 59 và y> = 0 và y <= 14
63.
64. def makeMove (bảng, rương, x, y):
65. # Thay đổi cấu trúc dữ liệu bảng bằng thiết bị sonar
tính cách. Hủy bỏ rương kho báu
66. # từ danh sách rương khi chúng được tìm thấy. Trả về sai
nếu đây là một động thái không hợp lệ.
67. # Nếu không, hãy trả về chuỗi kết quả của việc này
di chuyển.
68. nếu không phải làValidMove (x, y):
69.
trả về sai
70.
214

Trang 229
71. tinyDistance = 100 # bất kỳ rương nào sẽ gần hơn
100.
72. cho cx, cy trong rương:
73.
nếu abs (cx - x)> abs (cy - y):
74.
khoảng cách = abs (cx - x)
75.
khác:
76.
khoảng cách = abs (cy - y)
77.
78.
if distance <tinyDistance: # chúng tôi muốn
rương kho báu gần nhất.
79.
smallDistance = khoảng cách
80.
81. nếu smallDistance == 0:
82.
# xy là trực tiếp trên một rương kho báu!
83.
rương.remove ([x, y])
84.
trở lại 'Bạn đã tìm thấy một rương kho báu bị chìm!'
85. khác:
86.
nếu nhỏ nhất Độ bền <10:
87.
board [x] [y] = str (smallDistance)
88.
return 'Kho báu được phát hiện ở khoảng cách% s
từ thiết bị sonar. ' % (nhỏ nhất)
89.
khác:
90.
bảng [x] [y] = 'O'
91.
trở về 'Sonar đã không phát hiện bất cứ điều gì. Tất cả
rương kho báu ngoài tầm. '
92.
93.
94. def enterPlayerMove ():
95. # Hãy để người chơi gõ di chuyển. Trả lại một mục hai
danh sách tọa độ int xy.
96. print ('Nơi nào bạn muốn thả sonar tiếp theo
thiết bị? (0-59 0-14) (hoặc loại bỏ) ')
97. trong khi Đúng:
98.
di chuyển = đầu vào ()
99.
if move.lower () == 'thoát':
100.
in ('Cảm ơn vì đã chơi!')
101.
sys.exit ()
102.
103.
di chuyển = di chuyển.split ()
104.
if len (di chuyển) == 2 và di chuyển [0]. thẩm quyền () và di chuyển
[1]. Thẩm quyền () và isValidMove (int (di chuyển [0]), int (di chuyển [1])):
105.
return [int (di chuyển [0]), int (di chuyển [1])]
106.
in ('Nhập số từ 0 đến 59, dấu cách, sau đó
một số từ 0 đến 14. ')
107.
108.
109. def playAgain ():
110. # Hàm này trả về True nếu người chơi muốn
chơi lại, nếu không nó trả về Sai.
111. in ('Bạn có muốn chơi lại không? (Có hoặc không)')
112. return return (). Lower (). Startedwith ('y')
113.
114.
115. def showInXD ():
215
13 - Truy tìm kho báu Sonar

Trang 230
116. in ('' 'Hướng dẫn:
117. Bạn là thuyền trưởng của Simon, một con tàu săn tìm kho báu.
Nhiệm vụ hiện tại của bạn
118. là tìm thấy ba rương kho báu bị chìm
ẩn nấp trong một phần của
119. đại dương bạn đang ở và thu thập chúng.
120.
121. Để chơi, nhập tọa độ của điểm trong đại dương
bạn muốn thả một
122. thiết bị sonar. Sonar có thể tìm ra cách xa
ngực gần nhất là nó.
123. Ví dụ: d bên dưới đánh dấu nơi thiết bị được
đánh rơi, và 2
124. đại diện cho khoảng cách 2 từ thiết bị. 4
đại diện
125. khoảng cách 4 từ thiết bị.
126.
127. 444444444
128. 4
4
129. 4 22222 4
130. 4 2 2 4
131. 4 2 d 2 4
132. 4 2 2 4
133. 4 22222 4
134. 4
4
135. 444444444
136. Nhấn enter để tiếp tục ... '' ')
137. đầu vào ()
138.
139. print ('' 'Ví dụ, đây là một rương kho báu (c)
nằm cách xa 2
140. từ thiết bị sonar (d):
141.
142. 222222
143. c 2
144. 2 ngày 2
145. 2 2
146. 222222
147.
148. Điểm mà thiết bị bị rơi sẽ được đánh dấu bằng
một 2.
149.
150. Rương kho báu không di chuyển xung quanh. Thiết bị Sonar có thể
phát hiện kho báu
151. rương lên đến khoảng cách 9. Nếu tất cả các rương đều ra khỏi
phạm vi, điểm
152. sẽ được đánh dấu bằng O
153.
154. Nếu một thiết bị được thả trực tiếp vào rương kho báu, bạn
đã được khám phá
155. vị trí của rương, và nó sẽ được thu thập. Các
thiết bị sonar sẽ
156. vẫn ở đó.
157.
216

Trang 232
158. Khi bạn thu thập rương, tất cả các thiết bị sonar sẽ cập nhật lên
xác định vị trí tiếp theo
159. Rương kho báu chìm gần nhất.
160. Nhấn enter để tiếp tục ... '' ')
161. đầu vào ()
162. in ()
163.
164.
165. in ('SONAR!')
166. in ()
167. in ('Bạn có muốn xem hướng dẫn không? (Có / không)')
168. nếu đầu vào (). Thấp hơn (). Startedwith ('y'):
169. showInemony ()
170.
171. trong khi Đúng:
172. # thiết lập trò chơi
173. sonarDevices = 16
174. theBoard = getNewBoard ()
175. theChests = getRandomChests (3)
176. drawBoard (theBoard)
177. trướcMoves = []
178.
179. trong khi sonarDevices> 0:
180.
# Bắt đầu một lượt:
181.
182.
# hiển thị thiết bị sonar / trạng thái ngực
183.
nếu sonarDevices> 1: ExtraSsonar = 's'
184.
khác: ExtraSsonar = ''
185.
if len (theChests)> 1: ExtraSchest = 's'
186.
khác: ExtraSchest = ''
187.
in ('Bạn có% s thiết bị sonar% s còn lại.% s
kho báu% s còn lại. ' % (sonarDevices, ExtraSsonar,
len (theChests), ExtraSchest))
188.
189.
x, y = enterPlayerMove ()
190.
trướcMoves.append ([x, y]) # chúng tôi phải theo dõi tất cả
di chuyển để các thiết bị sonar có thể được cập nhật.
191.
192.
moveResult = makeMove (theBoard, theChests, x, y)
193.
nếu moveResult == Sai:
194.
tiếp tục
195.
khác:
196.
if moveResult == 'Bạn đã tìm thấy một chìm
rương kho báu!':
197.
# cập nhật tất cả các thiết bị sonar hiện tại
trên bản đồ.
198.
cho x, y trong trướcMoves:
199.
makeMove (theBoard, theChests, x, y)
200.
drawBoard (theBoard)
201.
in (moveResult)
202.
203.
if len (theChests) == 0:
204.
in ('Bạn đã tìm thấy tất cả các kho báu bị chìm
rương! Xin chúc mừng và trò chơi hay! ')
217
13 - Truy tìm kho báu Sonar

Trang 232
205.
phá vỡ
206.
207.
sonarDevices - = 1
208.
209. nếu sonarDevices == 0:
210.
in ('Chúng tôi đã hết thiết bị sonar! Bây giờ chúng tôi
phải quay tàu lại và đi ')
211.
in ('cho nhà với rương kho báu vẫn chưa ra
đó Trò chơi kết thúc. ')
212.
in ('Các rương còn lại đã ở đây:')
213.
cho x, y trong các mục:
214.
in ('% s,% s'% (x, y))
215.
216. nếu không chơiAgain ():
217.
sys.exit ()

Thiết kế chương trình


Sonar là một loại phức tạp, vì vậy tốt hơn là nhập mã của trò chơi và chơi nó
vài lần đầu tiên để hiểu chuyện gì đang xảy ra Sau khi bạn chơi game một vài lần,
bạn có thể có được một ý tưởng về chuỗi các sự kiện trong trò chơi này.
Trò chơi Sonar sử dụng danh sách các danh sách và các biến phức tạp khác. Những phức tạp
các biến được gọi là cấu trúc dữ liệu . Cấu trúc dữ liệu sẽ cho phép chúng tôi lưu trữ không cần
thiết
sắp xếp các giá trị trong một biến duy nhất. Chúng tôi sẽ sử dụng cấu trúc dữ liệu cho bảng
Sonar
và các vị trí của các rương kho báu. Một ví dụ về cấu trúc dữ liệu là bảng
biến trong chương Tic Tac Toe.
Nó cũng hữu ích để viết ra những điều chúng ta cần chương trình của chúng tôi để làm, và đưa ra
một số tên hàm sẽ xử lý các hành động này. Nhớ đặt tên hàm sau cái gì
họ đặc biệt làm. Nếu không, chúng ta có thể quên một chức năng, hoặc gõ hai
chức năng khác nhau mà làm cùng một điều.
Bảng 13-1: Danh sách từng chức năng mà trò chơi Sonar cần.
Những gì các mã nên làm.
Chức năng sẽ làm

In bảng trò chơi trên màn hình dựa trên bảng
cấu trúc dữ liệu được truyền, bao gồm cả tọa độ dọc
phía trên, dưới và bên trái và bên phải.
vẽBoard ()
Tạo một cấu trúc dữ liệu bảng mới .
getNewBoard ()
Tạo cấu trúc dữ liệu rương mới có số
rương ngẫu nhiên rải rác trên bảng trò chơi.
getRandomChests ()
Kiểm tra xem tọa độ XY được truyền vào đây
Chức năng có nằm trên bảng trò chơi hay không.
isValidMove ()
Để người chơi nhập vào tọa độ XY tiếp theo của anh ta
218

Trang 233
Đây có thể không phải là tất cả các chức năng chúng ta cần, nhưng một danh sách như thế này là
một ý tưởng tốt để giúp đỡ
bạn bắt đầu với việc lập trình các trò chơi của riêng bạn. Ví dụ, khi chúng ta viết
Hàm drawBoard () trong trò chơi Sonar, chúng tôi nhận ra rằng chúng tôi cũng cần một getRow
()
chức năng. Viết ra một chức năng một lần và sau đó gọi nó hai lần thì tốt hơn là viết ra
mã hai lần. Toàn bộ điểm của các chức năng là giảm mã trùng lặp xuống một nơi,
Vì vậy, nếu chúng ta cần thay đổi mã đó, chúng ta chỉ cần thay đổi một nơi trong
chương trình.
Cách thức hoạt động của mã: Dòng 1 đến 38
1. # Sonar
2.
3. nhập ngẫu nhiên
4. hệ thống nhập khẩu
Ở đây chúng tôi nhập hai mô-đun, ngẫu nhiên và sys . Các sys mô-đun chứa exit ()
chức năng, khiến chương trình chấm dứt ngay lập tức. Chúng tôi sẽ gọi chức năng này
sau đó trong chương trình của chúng tôi.
Vẽ bảng trò chơi
6. def drawBoard (bảng):
Các ký tự đánh dấu lại (`) và dấu ngã (~) được đặt bên cạnh phím 1 trên bàn phím của bạn.
Chúng giống như những con sóng của đại dương. Đâu đó trong đại dương này là ba rương kho
báu,
nhưng bạn không biết ở đâu. Bạn có thể tìm ra nó bằng cách trồng các thiết bị sonar, và nói với
chương trình trò chơi trong đó bằng cách nhập vào tọa độ X và Y (được in trên bốn
các cạnh của màn hình.)
Hàm drawBoard () là hàm đầu tiên chúng ta sẽ xác định cho chương trình của mình. Các
Hội đồng của trò chơi sonar là một đại dương nghệ thuật ASCII với tọa độ đi dọc theo trục X và
Y,
và trông như thế này:
di chuyển và tiếp tục hỏi cho đến khi họ gõ vào tọa độ
chính xác
enterPlayerMove ()
Đặt một thiết bị sonar trên bảng trò chơi và cập nhật
cấu trúc dữ liệu bảng sau đó trả về một chuỗi mô tả
Chuyện gì đã xảy ra.
di chuyển()
Hỏi người chơi nếu họ muốn chơi một trò chơi khác
Sonar.
chơi lại()
In ra hướng dẫn cho trò chơi.
chương trình hướng dẫn ()
219
13 - Truy tìm kho báu Sonar

Trang 233
1
2
3
4
5
01234567890123456789012345678901234567890123456789012345678989
0 ~~~ `~` `~~~` `~~~ ~` `~` ~ `~` ~ `~ ~` ~~~ `~ ~` ~ `` `` `` ~ ~ `~` ` ~ `~ ~` `` ~ `~` 0
1 `~` ~ `` `` ~ ~ `` ~ `~` `` ~ `` `~` `` ~ `~~~` `~ ~` ~~~ `` `` `` ~ `~` `~ ~` `~ ~` ~ ~ `1
2 `` `~~~ ~` ~ `~ ~` `` ~~~ `` ~ `` `` ~ ~ `~` ~ ~ `~` ~ `~` `` ~ ~ `~` `~ ~ `~` ~~~~~ ~ `~ 2
3 ~~~ ~ `~~~` `~` `` ~ `` ~ ~ `~` ~ ~ `~` ~ ~ `` ~ `` `` ~ `~` `` `` `` `` ` ~ `~` ~ `` `` `~ 3
4 ~ `` `~~~~~` ~ ~ `` `` ~~~ ~ `` `~~~` ~ `~` ~ `` `` ~ `~ ~` ~ `~ ~` `~ ~ `~` `~` ~ `` ~ ~ 4
5 `~` `` ~ `~` ~ ~ `~~~` `` ~ ~ `` ~ `` `` `` ~ ~ `` ~ `~` ~~~ ~ `~ ~` `~~~ ~~~ `~` `` ~ ~ `5
6 `` ~ ~ `~ ~` ~ `` ~ `` `` `~` `` `~ ~` `~` ~~~ ~ `~ ~` `` ~~~ `` ~ `~` ~ ~ `` ~~~ `` `~~~ 6
7 `` ~ `` ~~~~~ ~ `` `~` ~ `` `~~~` `~` ~ `` ~ `~~~~~ ~` `` `` `` ~~~ ` ~ ~ `~ ~` ~ ~ `~ ~ 7
8 ~ ~ `~` ~ ~ `` `~` `~ ~` `~~~` `~ ~` ~ `~ ~` ~ `~` `` ~ `` `~~~` `` ~~~ ~~~ `~` ~~~ ~ `8
9 `` `~` `~` ~~~ `~ ~` `` ~ `` ~ `` ~~~ `` `~` `` `~` `` ~ `~ ~` ~~~~~ ` ~ `` ~~~~~ `` `9
10 `~~~ ~` `` ~ `~` `` `~` ~ `~ ~` `~` ~~~ ~ `~` `~` `~` `` ~ ~ `` `` `` ` ~ `~` `~` `` `` 10
11 ~ ~ `~` ~ ~ `~` `~` ~~~ `` `` `` `` `` `` `` `` ~ ~ `` `` `~` ~ ~ `` ~ `~ ~ ~ ~ `~~~` ~ ~ `~ 11
12 ~ ~ `~~~ ~` `` ~~~ `` `` `~ ~` `~` ~ `~ ~` `` `` `~` ~ ~ `` ~ `` `` `` ` ~ `` ~~~ `~ ~` ~ 12
13 `~` `` `` `~ ~` `~` ~~~ `` `~~~ ~` `` ~ ~ `~` ~~~ `~` `` `` `` ~ ~ `~` `` ~ `` ~ `~~~ ~ 13
14 ~~~ `` ~ `` `~` `` `` ~ ~ `~` ~ `` ~ ~ `~` `~` ~ ~ `~` ~ `` ~ `~` `~ ~` `~ `~` `~` `` ~~~ 14
01234567890123456789012345678901234567890123456789012345678989
1
2
3
4
5
Chúng tôi sẽ chia bản vẽ trong hàm drawBoard () thành bốn bước. Đầu tiên, chúng tôi
tạo một biến chuỗi của dòng với 1, 2, 3, 4 và 5 cách nhau với các khoảng trống rộng. Thứ hai,
chúng tôi sử dụng chuỗi đó để hiển thị tọa độ trục X dọc theo đỉnh màn hình. Thứ ba, chúng tôi
in từng hàng của đại dương cùng với tọa độ trục Y ở cả hai bên của màn hình.
Và thứ tư, chúng tôi in ra trục X một lần nữa ở phía dưới. Có tọa độ ở tất cả các phía
giúp người chơi dễ dàng di chuyển ngón tay của mình dọc theo các khoảng trống để xem chính
xác vị trí
họ muốn lên kế hoạch cho một thiết bị sonar.
Vẽ tọa độ X dọc theo đỉnh
7. # Vẽ cấu trúc dữ liệu bảng.
số 8.
9. hline = '' # không gian ban đầu cho các số xuống
phía bên trái của bảng
10. cho i trong phạm vi (1, 6):
11.
hline + = ('' * 9) + str (i)
Hãy nhìn lại phần trên cùng của bảng, lần này có dấu cộng thay vì để trống
không gian để chúng ta có thể đếm các không gian dễ dàng hơn:
Hình 13-3: Khoảng cách chúng tôi sử dụng để in đầu bảng trò chơi.
220

Trang 235
Các số trên dòng đầu tiên đánh dấu vị trí hàng chục đều có chín khoảng trắng trong
giữa chúng và có mười ba khoảng trống ở phía trước 1. Chúng ta sẽ tạo một chuỗi
với dòng này và lưu nó trong một biến có tên hline .
13. # in các số trên đầu trang
14. in (hline)
15. in ('' + ('0123456789' * 6))
16. in ()
Để in các số trên đầu bảng sonar, trước tiên chúng tôi in nội dung của
biến hline . Sau đó, trên dòng tiếp theo, chúng tôi in ba khoảng trắng (để hàng này thẳng hàng
chính xác), và sau đó in chuỗi
'012345678901234567890123456789012345678901234567890123456789'
Nhưng điều này thật tẻ nhạt khi nhập vào nguồn, vì vậy thay vào đó chúng tôi nhập
( '0123456789' * 6 )
mà ước tính cho cùng một chuỗi.
Vẽ hàng của đại dương
18. # in mỗi trong số 15 hàng
19. cho tôi trong phạm vi (15):
20.
# số một chữ số cần được đệm bằng một
không gian thêm
21.
nếu tôi <10:
22.
ExtraSpace = ''
23.
khác:
24.
ExtraSpace = ''
25.
in ('% s% s% s% s'% (ExtraSpace, i, getRow
(bảng, tôi), tôi))
Bây giờ chúng tôi in từng hàng của bảng, bao gồm các số ở bên cạnh để dán nhãn
trục Y. Chúng tôi sử dụng vòng lặp for để in các hàng từ 0 đến 14 trên bảng, cùng với
số hàng ở hai bên của bảng.
Chúng tôi có một vấn đề nhỏ. Các số chỉ có một chữ số (như 0, 1, 2, v.v.) chỉ lấy
tăng một khoảng trống khi chúng tôi in chúng ra, nhưng các số có hai chữ số (như 10, 11 và 12)
chiếm hai không gian. Điều này có nghĩa là các hàng có thể không xếp hàng và sẽ trông như thế
này:
8 ~ ~ `~` ~ ~ `` `~` `~ ~` `~~~` `~ ~` ~ `~ ~` ~ `~` `` ~ `` `~~~` `` ~~~ ~~~ `~` ~~~ ~ `8
9 `` `~` `~` ~~~ `~ ~` `` ~ `` ~ `` ~~~ `` `~` `` `~` `` ~ `~ ~` ~~~~~ ` ~ `` ~~~~~ `` `9
10 `~~~ ~` `` ~ `~` `` `~` ~ `~ ~` `~` ~~~ ~ `~` `~` `~` `` ~ ~ `` `` `` ` ~ `~` `~` `` `` 10
11 ~ ~ `~` ~ ~ `~` `~` ~~~ `` `` `` `` `` `` `` `` ~ ~ `` `` `~` ~ ~ `` ~ `~ ~ ~ ~ `~~~` ~ ~ `~ 11
Giải pháp rất dễ dàng. Chúng tôi chỉ cần thêm một khoảng trắng ở phía trước của tất cả các số có
một chữ số. Nếu-
câu lệnh khác bắt đầu trên dòng 21 thực hiện điều này. Chúng tôi sẽ in biến ExtraSpace
khi chúng tôi in hàng và nếu tôi nhỏ hơn 10 (có nghĩa là nó sẽ chỉ có một chữ số),
221
13 - Truy tìm kho báu Sonar

Trang 236
chúng ta gán một chuỗi không gian duy nhất cho ExtraSpace . Mặt khác, chúng tôi
đặt ExtraSpace thành
một chuỗi trống. Bằng cách này, tất cả các hàng của chúng tôi sẽ xếp hàng khi chúng tôi in
chúng.
Hàm getRow () sẽ trả về một chuỗi đại diện cho số hàng chúng ta truyền vào. Nó là
hai tham số là cấu trúc dữ liệu bảng được lưu trữ trong biến bảng và một hàng
con số. Chúng ta sẽ xem xét chức năng này tiếp theo.
Vẽ tọa độ X dọc theo đáy
27. # in các số ở phía dưới
28. in ()
29. in ('' + ('0123456789' * 6))
30. in (hline)
Mã này tương tự như dòng 14 đến 17. Điều này sẽ in tọa độ trục X dọc theo
phía dưới màn hình.
Lấy trạng thái của một hàng trong đại dương
33. def getRow (bảng, hàng):
34. # Trả về một chuỗi từ cấu trúc dữ liệu bảng tại
hàng nhất định.
35. boardRow = ''
36. cho i trong phạm vi (60):
37.
boardRow + = board [i] [hàng]
38. bảng hoàn trả
Hàm này xây dựng một chuỗi gọi là boardRow từ các ký tự được lưu trong bảng .
Đầu tiên chúng ta đặt boardRow thành chuỗi trống. Số hàng (là tọa độ Y) là
thông qua như một tham số. Chuỗi chúng tôi muốn được tạo bằng cách ghép bảng [0] [hàng] ,
bảng [1] [hàng] , bảng [2] [hàng] , v.v. lên bảng [59] [hàng] . (Đây là
bởi vì hàng được tạo thành từ 60 ký tự, từ chỉ số 0 đến chỉ số 59. )
Các cho vòng lặp từ số nguyên 0 để 59 . Trên mỗi lần lặp, ký tự tiếp theo trong
cấu trúc dữ liệu bảng được sao chép vào cuối boardRow . Vào thời điểm vòng lặp được thực
hiện,
ExtraSpace được hình thành đầy đủ, vì vậy chúng tôi trả lại nó.
Cách thức hoạt động của mã: Dòng 40 đến 62
Bây giờ chúng ta có một chức năng để in một cấu trúc dữ liệu của bảng trò chơi đã cho vào
chuỗi, hãy
chuyển sang các chức năng khác mà chúng ta sẽ cần. Khi bắt đầu trò chơi, chúng ta sẽ cần phải
tạo
một cấu trúc dữ liệu bảng trò chơi mới và cũng đặt các rương kho báu ngẫu nhiên xung quanh
bảng.
Chúng ta cũng nên tạo một hàm có thể cho biết các tọa độ được người chơi nhập vào có phải là
một
di chuyển hợp lệ hay không.
222

Trang 237
Tạo một bảng trò chơi mới
40. def getNewBoard ():
41. # Tạo cấu trúc dữ liệu bảng 60x15 mới.
42. bảng = []
43. cho x trong phạm vi (60): # danh sách chính là danh sách 60
danh sách
44.
board.append ([])
Khi bắt đầu mỗi trò chơi mới, chúng tôi sẽ cần một cấu trúc dữ liệu bảng mới . các hội đồng
quản trị
cấu trúc dữ liệu là một danh sách các danh sách các chuỗi. Danh sách đầu tiên đại diện cho tọa
độ X. Kể từ khi chúng tôi
bảng của trò chơi có 60 ký tự, danh sách đầu tiên này cần chứa 60 danh sách. Vì vậy, chúng tôi
tạo ra một
cho vòng lặp sẽ nối thêm 60 danh sách trống vào nó.
45.
cho y trong phạm vi (15): # mỗi danh sách trong danh sách chính
có 15 chuỗi ký tự đơn
46.
# sử dụng các ký tự khác nhau cho đại dương để
làm cho nó dễ đọc hơn
47.
nếu ngẫu nhiên.randint (0, 1) == 0:
48.
bảng [x] .append ('~')
49.
khác:
50.
bảng [x] .append ('`')
Nhưng bảng không chỉ là một danh sách 60 danh sách trống. Mỗi trong số 60 danh sách đại diện
cho Y
phối hợp của hội đồng quản trị trò chơi của chúng tôi. Có 15 hàng trong bảng, vì vậy mỗi 60
danh sách này phải
có 15 ký tự trong đó. Chúng tôi có một cho vòng lặp để thêm 15 chuỗi ký tự duy nhất
đại diện cho đại dương. "Đại dương" sẽ chỉ là một chuỗi các chuỗi '~' và '`' , vì vậy chúng tôi
sẽ chọn ngẫu nhiên giữa hai. Chúng ta có thể làm điều này bằng cách tạo ra một số ngẫu nhiên
trong khoảng từ 0 đến 1 với một cuộc gọi đến Random.randint () . Nếu giá trị trả về của
Random.randint () là 0 , chúng tôi thêm chuỗi '~' . Nếu không, chúng tôi sẽ thêm '`'
chuỗi.
Điều này giống như quyết định sử dụng nhân vật nào bằng cách tung đồng xu. Và kể từ khi giá
trị trả về
từ Random.randint () sẽ là 0 khoảng một nửa thời gian, một nửa số ký tự đại dương sẽ
được '~' và nửa kia sẽ là '`' . Điều này sẽ mang lại cho đại dương của chúng ta một cái nhìn ngẫu
nhiên, sặc sỡ

Hãy nhớ rằng biến bảng là danh sách 60 danh sách có 15 chuỗi. Điều đó có nghĩa là
để có được chuỗi ở tọa độ 26, 12, chúng tôi sẽ truy cập bảng [26] [12] chứ không phải bảng
[12] [26] . Tọa độ X là đầu tiên, sau đó là tọa độ Y.
Dưới đây là hình ảnh để chứng minh các chỉ mục của một danh sách các danh sách có
tên x . Mũi tên đỏ
chỉ vào các chỉ mục của danh sách bên trong chính họ. Hình ảnh cũng được lật về phía của nó để
thực hiện
nó dễ đọc hơn:
223
13 - Truy tìm kho báu Sonar

Trang 238
Hình 13-4: Các chỉ mục của một danh sách các danh sách.
51. bảng hoàn trả
Cuối cùng, chúng tôi trả về biến bảng . Hãy nhớ rằng trong trường hợp này, chúng tôi đang trả
lại một
tham chiếu đến danh sách mà chúng tôi thực hiện. Mọi thay đổi chúng tôi đã thực hiện cho danh
sách (hoặc danh sách bên trong
danh sách) trong chức năng của chúng tôi sẽ vẫn ở đó bên ngoài chức năng.
Tạo Rương kho báu ngẫu nhiên
53. def getRandomChests (numChests):
54. # Tạo danh sách cấu trúc dữ liệu ngực (hai mục
danh sách tọa độ x, y int)
55. rương = []
56. cho i trong phạm vi (numChests):
57.
chests.append ([Random.randint (0, 59),
ngẫu nhiên.randint (0, 14)])
58. rương trở lại
Một nhiệm vụ khác chúng ta cần làm khi bắt đầu trò chơi là quyết định nơi cất giấu kho báu
rương được. Chúng tôi sẽ đại diện cho các rương kho báu trong trò chơi của chúng tôi dưới dạng
danh sách hai danh sách
số nguyên. Hai số nguyên này sẽ là tọa độ X và Y. Ví dụ: nếu dữ liệu ngực
cấu trúc là [[2, 2], [2, 4], [10, 0]] , thì điều này có nghĩa là có ba
rương kho báu, một ở 2, 2, một rương khác ở 2, 4 và một rương thứ ba ở 10, 0.
224

Trang 239
Chúng ta sẽ truyền tham số numChests để cho biết chức năng có bao nhiêu rương kho báu
chúng tôi muốn nó tạo ra. Chúng tôi thiết lập một vòng lặp for để lặp lại số lần này và trên mỗi
lần
lặp đi lặp lại chúng tôi nối một danh sách hai số nguyên ngẫu nhiên. Tọa độ X có thể ở bất cứ
đâu
0 đến 59 và tọa độ Y có thể từ bất kỳ đâu trong khoảng từ 0 đến 14. Biểu thức
[Random.randint (0, 59), Random.randint (0, 14)] được truyền cho
phương pháp chắp thêm sẽ đánh giá một cái gì đó như [2, 2] hoặc [2, 4] hoặc [10, 0] . Điều này
cấu trúc dữ liệu sau đó được trả lại.
Xác định xem Di chuyển có hợp lệ không
60. def isValidMove (x, y):
61. # Trả về Đúng nếu tọa độ nằm trên bảng,
Nếu không thì Sai.
62. trả về x> = 0 và x <= 59 và y> = 0 và y <= 14
Người chơi sẽ nhập tọa độ X và Y nơi họ muốn thả thiết bị sonar.
Nhưng họ có thể không gõ các tọa độ không tồn tại trên bảng trò chơi. Chữ X
tọa độ phải nằm trong khoảng từ 0 đến 59 và tọa độ Y phải nằm trong khoảng từ 0 đến 14.
Hàm này sử dụng một biểu thức đơn giản sử dụng và toán tử để đảm bảo rằng mỗi
điều kiện là Đúng . Nếu chỉ một là Sai , thì toàn bộ biểu thức ước tính thành Sai .
Giá trị Boolean này được trả về bởi hàm.
Cách thức hoạt động của mã: Dòng 64 đến 91
Đặt một di chuyển trên bảng
64. def makeMove (bảng, rương, x, y):
65. # Thay đổi cấu trúc dữ liệu bảng bằng thiết bị sonar
tính cách. Hủy bỏ rương kho báu
66. # từ danh sách rương khi chúng được tìm thấy. Trở về
Sai nếu đây là một động thái không hợp lệ.
67. # Nếu không, hãy trả về chuỗi kết quả của việc này
di chuyển.
68. nếu không phải làValidMove (x, y):
69.
trả về sai
Trong trò chơi Sonar của chúng tôi, bảng trò chơi được cập nhật để hiển thị số cho mỗi thiết bị
sonar
giảm. Con số cho thấy cách xa kho báu gần nhất. Vì vậy, khi người chơi
thực hiện một động thái bằng cách cho chương trình tọa độ X và Y, chúng ta sẽ thay đổi bảng
dựa trên các vị trí của các rương kho báu. Đây là lý do tại sao hàm makeMove () của chúng
ta mất
Bốn tham số: cấu trúc dữ liệu của bảng trò chơi, cấu trúc dữ liệu của rương kho báu và
Tọa độ X và Y.
Hàm này sẽ trả về giá trị Boolean sai nếu tọa độ X và Y nếu là
thông qua không tồn tại trên bảng trò chơi. Nếu isValidMove () trả về Sai , thì
225
13 - Truy tìm kho báu Sonar

Trang 240
makeMove () sẽ trả về Sai .
Nếu tọa độ hạ cánh trực tiếp vào kho báu, makeMove () sẽ trả về chuỗi
'Bạn đã tìm thấy một rương kho báu bị chìm!' . Nếu tọa độ XY là
trong khoảng cách 9 hoặc ít hơn một rương kho báu, chúng tôi trả lại chuỗi 'Kho báu
được phát hiện ở khoảng cách% s từ thiết bị sonar. ' (trong đó % s là
khoảng cách). Nếu không, makeMove () sẽ trả về chuỗi 'Sonar đã không
phát hiện bất cứ điều gì. Tất cả các rương kho báu nằm ngoài phạm vi. ' .
71. tinyDistance = 100 # bất kỳ rương nào sẽ gần hơn
hơn 100
72. cho cx, cy trong rương:
73.
nếu abs (cx - x)> abs (cy - y):
74.
khoảng cách = abs (cx - x)
75.
khác:
76.
khoảng cách = abs (cy - y)
77.
78.
if distance <tinyDistance: # chúng tôi muốn
rương kho báu gần nhất.
79.
smallDistance = khoảng cách
Đưa ra tọa độ XY nơi người chơi muốn thả thiết bị sonar và danh sách
về tọa độ XY cho các rương kho báu (trong danh sách các rương danh sách), làm thế nào để
chúng ta tìm ra
Rương kho báu nào gần nhất?
Một thuật toán để tìm kho báu gần nhất
Trong khi các biến x và y chỉ là các số nguyên (giả sử 5 và 0 ), cùng nhau chúng đại diện cho
vị trí trên bảng trò chơi (là hệ tọa độ của Cartesian) nơi người chơi
đoán. Biến rương có thể có một giá trị như [[5, 0], [0, 2], [4,
2]] , giá trị đó đại diện cho vị trí của ba rương kho báu. Mặc dù những
các biến là một loạt các số, chúng ta có thể hình dung nó như thế này:
226

Trang 241
Hình 13-5: Các vị trí trên bảng mà [[5, 0], [0, 2], [4, 2]] đại diện.
Chúng tôi tìm ra khoảng cách từ thiết bị sonar ở 0, 2 với "nhẫn" và
khoảng cách xung quanh nó:
Hình 13-6: Bảng được đánh dấu với khoảng cách từ vị trí 0, 2.
Nhưng làm thế nào để chúng tôi dịch điều này thành mã cho trò chơi của chúng tôi? Chúng ta
cần một cách để đại diện
khoảng cách như một biểu thức. Lưu ý rằng khoảng cách từ tọa độ XY luôn là
lớn hơn của hai giá trị: giá trị tuyệt đối của chênh lệch của hai tọa độ X và
giá trị tuyệt đối của sự khác biệt của hai tọa độ Y.
Điều đó có nghĩa là chúng ta nên trừ tọa độ X của thiết bị sonar và X của kho báu
phối hợp, và sau đó lấy giá trị tuyệt đối của số này. Chúng tôi làm tương tự cho sonar
tọa độ Y của thiết bị và tọa độ Y của rương kho báu. Giá trị lớn hơn của hai giá trị này là
khoảng cách. Hãy nhìn vào bảng ví dụ của chúng tôi với các vòng ở trên để xem thuật toán này

chính xác.
227
13 - Truy tìm kho báu Sonar

Trang 242
Tọa độ X và Y của sonar là 3 và 2. Kho báu đầu tiên của X và Y
tọa độ (đầu tiên trong danh sách [[5, 0], [0, 2], [4, 2]] nghĩa là) là 5 và 0.
Đối với tọa độ X, 3 - 5 ước tính thành -2 và giá trị tuyệt đối của -2 là 2 .
Đối với tọa độ Y, 2 - 1 ước tính thành 1 và giá trị tuyệt đối của 1 là 1 .
So sánh hai giá trị tuyệt đối 2 và 1 , 2 là giá trị lớn hơn và phải là
khoảng cách từ thiết bị sonar và rương kho báu ở tọa độ 5, 1. Chúng ta có thể nhìn vào
hội đồng quản trị và thấy rằng thuật toán này hoạt động, bởi vì rương kho báu ở mức 5,1 nằm
trong sonar
vòng 2 của thiết bị. Hãy nhanh chóng so sánh hai cái rương kia để xem khoảng cách của anh ấy
có hoạt động không
ra cũng đúng.
Hãy tìm khoảng cách từ thiết bị sonar ở mức 3,2 và rương kho báu ở mức 0,2. cơ bụng (3
- 0) ước tính thành 3 . Hàm abs () trả về giá trị tuyệt đối của số chúng ta
vượt qua nó abs (2 - 2) ước tính thành 0 . 3 lớn hơn 0 , vì vậy khoảng cách từ sonar
thiết bị ở 3,2 và rương kho báu ở 0,2 là 3 . Chúng tôi nhìn vào bảng và thấy điều này là đúng.
Chúng ta hãy tìm khoảng cách từ thiết bị sonar ở mức 3,2 và rương kho báu cuối cùng ở mức
4.2. cơ bụng
(3 - 4) ước tính thành 1. abs (2 - 2) ước tính thành 0 . 1 lớn hơn 0 , nên khoảng cách
từ thiết bị sonar ở 3,2 và rương kho báu ở 4.2 là 1 . Chúng tôi nhìn vào bảng và thấy
điều này cũng đúng
Bởi vì cả ba khoảng cách đều hoạt động chính xác, thuật toán của chúng tôi hoạt động. Khoảng
cách
từ thiết bị sonar đến ba rương kho báu bị chìm là 2 , 3 và 1 . Trên mỗi lần đoán, chúng tôi
muốn biết khoảng cách từ thiết bị sonar đến gần nhất trong ba rương kho báu
khoảng cách. Để làm điều này, chúng tôi sử dụng một biến được gọi là smallDistance . Hãy nhìn
vào
mã lại:
71. tinyDistance = 100 # bất kỳ rương nào sẽ gần hơn
hơn 100
72. cho cx, cy trong rương:
73.
nếu abs (cx - x)> abs (cy - y):
74.
khoảng cách = abs (cx - x)
75.
khác:
76.
khoảng cách = abs (cy - y)
77.
78.
if distance <tinyDistance: # chúng tôi muốn
rương kho báu gần nhất.
79.
smallDistance = khoảng cách
Bạn cũng có thể sử dụng nhiều phân trong cho vòng lặp. Ví dụ: bài tập
câu lệnh a, b = [5, 10] sẽ gán 5 cho a và 10 cho b . Ngoài ra, đối với vòng lặp for i
trong [0, 1, 2, 3, 4] sẽ gán biến i cho các giá trị 0 và 1 , v.v.
lặp đi lặp lại
228

Trang 243
Các cho vòng lặp cho cx, cy trong ngực: liên hợp gặt đập cả hai nguyên tắc này.
Bởi vì rương là một danh sách trong đó mỗi mục trong danh sách tự nó là một danh sách gồm hai
số nguyên, đầu tiên
trong số các số nguyên này được gán cho cx và số nguyên thứ hai được gán cho cy . Vì vậy,
nếu rương
có giá trị [[5, 0], [0, 2], [4, 2]] , trên lần lặp đầu tiên thông qua vòng lặp,
cx sẽ có giá trị 5 và cy sẽ có giá trị 0 .
Dòng 73 xác định giá trị nào lớn hơn: giá trị tuyệt đối của chênh lệch của X
tọa độ, hoặc giá trị tuyệt đối của sự khác biệt của tọa độ Y. ( abs (cx - x)
<abs (cy - y) có vẻ như là cách dễ dàng hơn để nói điều đó, phải không?). Nếu-khác
câu lệnh gán giá trị lớn hơn của biến cho khoảng cách .
Vì vậy, trên mỗi lần lặp của vòng lặp for , biến khoảng cách giữ khoảng cách của a
khoảng cách rương kho báu từ thiết bị sonar. Nhưng chúng tôi muốn ngắn nhất (nghĩa là nhỏ
nhất)
khoảng cách của tất cả các rương kho báu. Đây là nơi xuất hiện biến nhỏ nhất
trong. Bất cứ khi nào biến khoảng cách nhỏ hơn giá trị nhỏ nhất , thì
giá trị trong khoảng cách trở thành giá trị mới của smallDistance .
Chúng tôi cung cấp cho giá trị nhỏ nhất Giá trị cao không tưởng của 100 vào đầu
vòng lặp để ít nhất một trong các rương kho báu mà chúng ta tìm thấy sẽ được đưa vào
nhỏ nhất . Đến khi vòng lặp for kết thúc, chúng ta biết rằng
smallDistance giữ khoảng cách ngắn nhất giữa thiết bị sonar và tất cả
rương kho báu trong game.
81. nếu smallDistance == 0:
82.
# xy là trực tiếp trên một rương kho báu!
83.
rương.remove ([x, y])
84.
trở lại 'Bạn đã tìm thấy một rương kho báu bị chìm!'
Thời gian duy nhất mà giá trị nhỏ nhất bằng 0 là khi XY của thiết bị sonar
tọa độ giống như tọa độ XY của rương kho báu. Điều này có nghĩa là người chơi có
đoán chính xác vị trí của một rương kho báu. Chúng ta nên loại bỏ cái rương này
danh sách số nguyên từ cấu trúc dữ liệu rương với phương thức danh sách remove () .
Các remove () Danh sách Phương
Các remove () phương pháp danh sách sẽ loại bỏ sự xuất hiện đầu tiên của giá trị thông qua như
là một
tham số từ danh sách. Ví dụ: thử gõ nội dung sau vào vỏ tương tác:
>>> x = [42, 5, 10, 42, 15, 42]
>>> x.remove (10)
>>> x
[42, 5, 42, 15, 42]
229
13 - Truy tìm kho báu Sonar

Trang 244
Bạn có thể thấy rằng giá trị 10 đã bị xóa khỏi danh sách x . Các remove () phương pháp
loại bỏ sự xuất hiện đầu tiên của giá trị bạn vượt qua và chỉ lần đầu tiên. Ví dụ: gõ
sau đây vào vỏ:
>>> x = [42, 5, 10, 42, 15, 42]
>>> x.remove (42)
>>> x
[5, 10, 42, 15, 42]
Lưu ý rằng chỉ có 42 giá trị đầu tiên bị xóa, nhưng giá trị thứ hai và thứ ba vẫn còn
ở đó Phương thức remove () sẽ gây ra lỗi nếu bạn cố xóa một giá trị không có trong
danh sách:
>>> x = [5, 42]
>>> x.remove (10)
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "<stdin>", dòng 1, trong <mô-đun>
ValueError: list.remove (x): x không có trong danh sách
>>>
Sau khi loại bỏ rương kho báu được tìm thấy khỏi danh sách rương , chúng tôi trả lại chuỗi
'Bạn đã tìm thấy một rương kho báu bị chìm!' để nói với người gọi rằng đoán
đã đúng Hãy nhớ rằng mọi thay đổi được thực hiện cho danh sách trong hàm sẽ tồn tại bên ngoài
chức năng là tốt.
85. khác:
86.
nếu nhỏ nhất Độ bền <10:
87.
board [x] [y] = str (smallDistance)
88.
return 'Kho báu được phát hiện ở khoảng cách% s
từ thiết bị sonar. ' % (nhỏ nhất)
89.
khác:
90.
bảng [x] [y] = 'O'
91.
trở về 'Sonar đã không phát hiện bất cứ điều gì. Tất cả
rương kho báu ngoài tầm. '
Khối khác thực thi nếu smallDistance không bằng 0 , có nghĩa là người chơi
không đoán được vị trí chính xác của rương kho báu. Chúng tôi trả lại hai chuỗi khác nhau,
tùy thuộc vào việc thiết bị sonar được đặt trong phạm vi của bất kỳ rương kho báu nào. Nếu nó
là, chúng tôi đánh dấu bảng bằng phiên bản chuỗi của smallDistance . Nếu không, chúng tôi
đánh dấu
bảng có '0' .
230

Trang 245
Cách thức hoạt động của mã: Dòng 94 đến 162
Một số chức năng cuối cùng chúng ta cần là cho phép người chơi di chuyển trên bảng trò chơi,
Hỏi người chơi nếu anh ta muốn chơi lại (điều này sẽ được gọi vào cuối trò chơi) và in
các hướng dẫn cho trò chơi trên màn hình (cái này sẽ được gọi ở đầu
trò chơi).
Bắt di chuyển
94. def enterPlayerMove ():
95. # Hãy để người chơi gõ di chuyển. Trả lại một mục hai
danh sách tọa độ int xy.
96. print ('Nơi nào bạn muốn thả sonar tiếp theo
thiết bị? (0-59 0-14) (hoặc loại bỏ) ')
97. trong khi Đúng:
98.
di chuyển = đầu vào ()
99.
if move.lower () == 'thoát':
100.
in ('Cảm ơn vì đã chơi!')
101.
sys.exit ()
Hàm này thu thập tọa độ XY của bước di chuyển tiếp theo của người chơi. Nó có một trong
khi vòng lặp
để nó tiếp tục yêu cầu người chơi di chuyển tiếp theo. Người chơi cũng có thể gõ thoát
để thoát khỏi trò chơi. Trong trường hợp đó, chúng ta gọi là sys.exit () chức năng mà
chấm dứt ngay chương trình.
103.
di chuyển = di chuyển.split ()
104.
if len (di chuyển) == 2 và di chuyển [0]. thẩm quyền () và di chuyển
[1]. Thẩm quyền () và isValidMove (int (di chuyển [0]), int (di chuyển
[1])):
105.
return [int (di chuyển [0]), int (di chuyển [1])]
106.
in ('Nhập số từ 0 đến 59, dấu cách, sau đó
một số từ 0 đến 14. ')
Giả sử người chơi chưa gõ 'thoát' , chúng tôi gọi phương thức split () khi di chuyển
và thiết lập danh sách nó trả về làm giá trị di chuyển mới . Những gì chúng tôi mong đợi di
chuyển là một danh sách
hai số. Các số này sẽ là các chuỗi, bởi vì phương thức split () trả về một danh sách
dây. Nhưng chúng ta có thể chuyển đổi chúng thành số nguyên với hàm int () .
Nếu người chơi nhập vào một cái gì đó như '1 2 3' , thì danh sách được trả về bằng split ()
sẽ là ['1', '2', '3'] . Trong trường hợp đó, biểu thức len (di chuyển) == 2 sẽ
là Sai và toàn bộ biểu thức ngay lập tức đánh giá thành Sai (vì
biểu hiện ngắn mạch.)
Nếu danh sách được trả về bởi split () có độ dài bằng 2 , thì nó sẽ có một bước di chuyển [0] và
di chuyển [1] . Chúng ta gọi phương thức chuỗi isdigit () trên các chuỗi đó. isdigit () sẽ
231
13 - Truy tìm kho báu Sonar

Trang 246
Trả về Đúng nếu chuỗi chỉ bao gồm các số. Nếu không, nó trả về Sai . Thử
gõ nội dung sau vào shell tương tác:
>>> '42 '. Kỹ thuật số ()
Thật
>>> 'bốn mươi' thẩm quyền ()
Sai
>>> ''. thẩm quyền ()
Sai
>>> 'xin chào'.
Sai
>>> x = '10 '
>>> x. thẩm quyền ()
Thật
>>>
Như bạn có thể thấy, cả di chuyển [0] .itorigit () và di chuyển [1] .itorigit () phải là
Đúng . Phần cuối cùng của biểu thức này gọi hàm move [1] của chúng ta để kiểm tra xem XY
tọa độ tồn tại trên bảng. Nếu tất cả các biểu thức này là True , thì hàm này trả về
một danh sách hai số nguyên của tọa độ XY. Nếu không, người chơi sẽ được yêu cầu nhập
tọa độ một lần nữa.
Yêu cầu người chơi chơi lại
109. def playAgain ():
110. # Hàm này trả về True nếu người chơi muốn
chơi lại, nếu không nó trả về Sai.
111. in ('Bạn có muốn chơi lại không? (Có hoặc không)')
112. return return (). Lower (). Startedwith ('y')
Hàm playAgain () sẽ hỏi người chơi nếu họ muốn chơi lại và sẽ
tiếp tục hỏi cho đến khi người chơi gõ một chuỗi bắt đầu bằng 'y' . Hàm này trả về một
Giá trị boolean.
In Hướng dẫn trò chơi cho người chơi
115. def showInXD ():
116. in ('' 'Hướng dẫn:
117. Bạn là đội trưởng của Simon, một người săn tìm kho báu
tàu. Nhiệm vụ hiện tại của bạn
118. là tìm thấy ba rương kho báu bị chìm
ẩn nấp trong một phần của
119. đại dương bạn đang ở và thu thập chúng.
120.
121. Để chơi, nhập tọa độ của điểm trong đại dương
232

Trang 247
bạn muốn thả một
122. thiết bị sonar. Sonar có thể tìm ra cách xa
ngực gần nhất là nó.
123. Ví dụ: d bên dưới đánh dấu nơi thiết bị được
đánh rơi, và 2
124. đại diện cho khoảng cách 2 từ thiết bị. 4
đại diện
125. khoảng cách 4 từ thiết bị.
126.
127. 444444444
128. 4
4
129. 4 22222 4
130. 4 2 2 4
131. 4 2 d 2 4
132. 4 2 2 4
133. 4 22222 4
134. 4
4
135. 444444444
136. Nhấn enter để tiếp tục ... '' ')
137. đầu vào ()
Các showInstructions () chỉ là một vài print () gọi là in nhiều dòng
dây. Hàm input () chỉ cho người chơi cơ hội nhấn Enter trước khi in
chuỗi tiếp theo. Điều này là do màn hình chỉ có thể hiển thị 25 dòng văn bản tại một thời điểm.
139. print ('' 'Ví dụ, đây là một rương kho báu (c)
nằm cách xa 2
140. từ thiết bị sonar (d):
141.
142. 222222
143. c 2
144. 2 ngày 2
145. 2 2
146. 222222
147.
148. Điểm mà thiết bị bị rơi sẽ được đánh dấu bằng
một 2.
149.
150. Rương kho báu không di chuyển xung quanh. Thiết bị Sonar có thể
phát hiện kho báu
151. rương lên đến khoảng cách 9. Nếu tất cả các rương đều ra khỏi
phạm vi, điểm
152. sẽ được đánh dấu bằng O
153.
154. Nếu một thiết bị được thả trực tiếp vào rương kho báu, bạn
đã được khám phá
155. vị trí của rương, và nó sẽ được thu thập. Các
thiết bị sonar sẽ
156. vẫn ở đó.
157.
158. Khi bạn thu thập rương, tất cả các thiết bị sonar sẽ cập nhật lên
233
13 - Truy tìm kho báu Sonar

Trang 248
xác định vị trí tiếp theo
159. Rương kho báu chìm gần nhất.
160. Nhấn enter để tiếp tục ... '' ')
161. đầu vào ()
162. in ()
Đây là phần còn lại của các hướng dẫn trong một chuỗi nhiều dòng. Sau khi người chơi nhấn
Enter,
hàm trả về. Đây là tất cả các chức năng chúng tôi sẽ xác định cho trò chơi của chúng tôi. Phần
còn lại của
chương trình là phần chính của trò chơi của chúng tôi.
Cách thức hoạt động của mã: Dòng 165 đến 217
Bây giờ chúng ta đã viết xong tất cả các chức năng mà trò chơi của chúng ta sẽ cần, hãy bắt đầu
chính
một phần của chương trình.
Bắt đầu trò chơi
165. in ('SONAR!')
166. in ()
167. in ('Bạn có muốn xem hướng dẫn không? (Có / không)')
168. nếu đầu vào (). Thấp hơn (). Startedwith ('y'):
169. showInemony ()
Đầu vào biểu thức (). Lower (). Startedwith ('y') hỏi người chơi nếu họ muốn
để xem hướng dẫn và đánh giá là Đúng nếu người chơi nhập chuỗi bắt đầu bằng
'Y' hoặc 'Y' . Nếu vậy, showInXD () được gọi.
171. trong khi Đúng:
172. # thiết lập trò chơi
173. sonarDevices = 16
174. theBoard = getNewBoard ()
175. theChests = getRandomChests (3)
176. drawBoard (theBoard)
177. trướcMoves = []
Đây khi vòng lặp là trò chơi vòng lặp chính. Dưới đây là những gì các biến là cho:
Bảng 13-2: Các biến được sử dụng trong vòng lặp trò chơi chính.
Biến đổi
Sự miêu tả
sonarDevices Số lượng thiết bị sonar (và lượt) mà người chơi đã rời đi.
theBoard
Cấu trúc dữ liệu bảng chúng tôi sẽ sử dụng cho trò chơi này. nhận được NewBoard
() sẽ thiết lập chúng tôi với một bảng mới.
Danh sách các cấu trúc dữ liệu ngực. getRandomChests () sẽ trở lại
234

Trang 249
Hiển thị trạng thái trò chơi cho người chơi
179. trong khi sonarDevices> 0:
180.
# Bắt đầu một lượt:
181.
182.
# hiển thị thiết bị sonar / trạng thái ngực
183.
nếu sonarDevices> 1: ExtraSsonar = 's'
184.
khác: ExtraSsonar = ''
185.
if len (theChests)> 1: ExtraSchest = 's'
186.
khác: ExtraSchest = ''
187.
in ('Bạn có% s thiết bị sonar% s còn lại.% s
kho báu% s còn lại. ' % (sonarDevices,
ExtraSsonar, len (theChests), ExtraSchest))
Đây khi vòng lặp thực thi chừng nào người chơi có các thiết bị sonar còn lại. Chúng tôi muốn
để in một thông báo cho người dùng biết có bao nhiêu thiết bị sonar và rương kho báu. Nhưng
có một vấn đề. Nếu còn hai hoặc nhiều thiết bị sonar, chúng tôi muốn in '2 sonar
thiết bị ' . Nhưng nếu chỉ còn một thiết bị sonar, chúng tôi muốn in '1 sonar
thiết bị ' trái. Chúng tôi chỉ muốn hình thức số nhiều của thiết bị nếu có nhiều thiết bị sonar.
Điều tương tự cũng xảy ra với '2 rương kho báu' và '1 rương kho báu' .
Lưu ý trên các dòng từ 183 đến 186 rằng chúng ta có mã sau các câu lệnh if và khác '
Đại tràng. Đây là Python hoàn toàn hợp lệ. Thay vì có một khối mã sau câu lệnh,
thay vào đó bạn chỉ có thể sử dụng phần còn lại của cùng một dòng để làm cho mã của bạn ngắn
gọn hơn. (Của
tất nhiên, điều này có nghĩa là bạn chỉ có thể có một dòng mã cho khối if và khối khác.) Điều này
áp dụng cho bất kỳ câu lệnh nào sử dụng dấu hai chấm, kể cả trong khi và cho các vòng lặp.
Vì vậy, chúng ta có hai biến chuỗi có tên là ExtraSsonar và ExtraSchest , đó là
được đặt thành '' (dấu cách) nếu có nhiều thiết bị sonar hoặc rương báu vật. Nếu không, họ
đang trống Chúng tôi sử dụng chúng trong câu lệnh while trên dòng 187.
Bắt di chuyển
189.
x, y = enterPlayerMove ()
190.
trướcMoves.append ([x, y]) # chúng tôi phải theo dõi tất cả
di chuyển để các thiết bị sonar có thể được cập nhật.
191.
192.
moveResult = makeMove (theBoard, theChests, x, y)
193.
nếu moveResult == Sai:
194.
tiếp tục
Dòng 189 sử dụng thủ thuật nhiều bài tập. enterPlayerMove () trả về hai mục
các cuộc thi
một danh sách ba rương kho báu ở những nơi ngẫu nhiên trên bảng.
trướcMoves Một danh sách tất cả các động tác XY mà người chơi đã thực hiện trong trò chơi.
235
13 - Truy tìm kho báu Sonar

Trang 250
danh sách. Mục đầu tiên sẽ được lưu trữ trong biến x và mục thứ hai sẽ được lưu trữ trong y
Biến đổi. Sau đó chúng tôi đặt hai biến này vào một danh sách hai mục khác, chúng tôi lưu trữ
trong
previousMoves liệt kê với append () phương pháp. Điều này có nghĩa là trướcMoves là một
danh sách tọa độ XY của mỗi lần di chuyển mà người chơi thực hiện trong trò chơi này.
Các x và y biến, cùng với theBoard và theChests (mà đại diện cho
trạng thái hiện tại của bảng trò chơi) đều được gửi đến hàm makeMove () . Như những gì chúng
ta có
đã thấy, chức năng này sẽ thực hiện các sửa đổi cần thiết cho bảng trò chơi. Nếu
makeMove () trả về giá trị Sai , sau đó xảy ra sự cố với các giá trị x và y
chúng tôi đã vượt qua nó Câu lệnh continue sẽ gửi lệnh thực thi trở lại bắt đầu
trong khi vòng lặp bắt đầu trên đường dây 179 để yêu cầu người chơi cho tọa độ XY nữa.
Tìm rương kho báu bị chìm
195.
khác:
196.
if moveResult == 'Bạn đã tìm thấy một chìm
rương kho báu!':
197.
# cập nhật tất cả các thiết bị sonar hiện tại
trên bản đồ.
198.
cho x, y trong trướcMoves:
199.
makeMove (theBoard, theChests, x, y)
200.
drawBoard (theBoard)
201.
in (moveResult)
Nếu makeMove () không trả về giá trị Sai , nó sẽ trả về một chuỗi
cho chúng tôi biết kết quả của động thái đó là gì Nếu chuỗi này là 'Bạn đã tìm thấy một
rương kho báu bị chìm! ' , điều đó có nghĩa là chúng ta nên cập nhật tất cả các thiết bị sonar
trên bảng để họ phát hiện rương kho báu gần thứ hai trên bảng. Chúng tôi có XY
tọa độ của tất cả các thiết bị sonar hiện trên bảng được lưu trữ trong cácMov trước đó . Vì thế
chúng ta chỉ có thể truyền lại tất cả các tọa độ XY này cho hàm makeMove () để có nó
vẽ lại các giá trị trên bảng.
Chúng tôi không phải lo lắng về cuộc gọi này để makeMove () có lỗi, bởi vì chúng tôi
đã biết tất cả các tọa độ XY trong các trước đó là hợp lệ. Chúng tôi cũng biết rằng
cuộc gọi này để makeMove () sẽ không tìm thấy bất kỳ rương kho báu mới nào, bởi vì họ sẽ có
đã bị xóa khỏi bảng khi di chuyển đó lần đầu tiên được thực hiện.
Các cho vòng lặp trên đường dây 198 cũng sử dụng các trick chuyển nhượng nhiều tương tự
cho x và y
vì các mục trong previousMoves danh sách là bản thân danh sách hai mục. Bởi vì chúng tôi
không in bất cứ thứ gì ở đây, người chơi không nhận ra chúng tôi đang làm lại tất cả những thứ
trước đó
di chuyển. Nó chỉ xuất hiện rằng bảng đã được cập nhật hoàn toàn.
Kiểm tra xem Người chơi đã Thắng chưa
203.
if len (theChests) == 0:
236

Trang 251
204.
in ('Bạn đã tìm thấy tất cả các kho báu bị chìm
rương! Xin chúc mừng và trò chơi hay! ')
205.
phá vỡ
Hãy nhớ rằng makeMove () Sửa chức năng theChests liệt kê chúng tôi gửi nó.
Bởi vì TheChests là một danh sách, mọi thay đổi được thực hiện bên trong hàm sẽ tồn tại sau
thực hiện trả về từ hàm. makeMove () xóa các mục khỏi mục Các khi
Rương kho báu được tìm thấy, vì vậy cuối cùng (nếu người chơi đoán đúng) tất cả các kho báu
rương sẽ được gỡ bỏ. (Hãy nhớ rằng, "kho báu", chúng tôi muốn nói đến danh sách hai mục
của tọa độ XY bên trong danh sách Theests .)
Khi tất cả các rương kho báu đã được tìm thấy trên bảng và bị xóa khỏi
theChests , danh sách Theests sẽ có độ dài bằng 0 . Khi điều đó xảy ra, chúng tôi hiển thị một
lời chúc mừng đến các cầu thủ, và sau đó thực hiện một phá vỡ tuyên bố để thoát ra khỏi đây
trong khi vòng lặp. Thực thi sau đó sẽ chuyển xuống dòng 209 (dòng đầu tiên sau khi-
khối.)
Kiểm tra xem người chơi có bị mất không
207.
sonarDevices - = 1
Đây là dòng cuối cùng của thời gian vòng lặp mà bắt đầu trên đường dây 179. Chúng tôi giảm
các
Biến sonarDevices vì người chơi đã sử dụng một. Nếu người chơi tiếp tục bỏ lỡ
rương kho báu, cuối cùng sonarDevices sẽ giảm xuống 0 . Sau dòng này, thực hiện
nhảy ngược lên đến dòng 179 để chúng ta có thể đánh giá lại tình trạng của câu lệnh while (đó là
sonarDevices> 0 ). Nếu sonarDevices là 0 , sau đó tình trạng này sẽ là False và
thực thi sẽ tiếp tục bên ngoài khối while trên dòng 209.
Nhưng cho đến lúc đó, điều kiện sẽ vẫn là True và người chơi có thể tiếp tục đoán.
209. nếu sonarDevices == 0:
210.
in ('Chúng tôi đã hết thiết bị sonar! Bây giờ chúng tôi
phải quay tàu lại và đi ')
211.
in ('cho nhà với rương kho báu vẫn chưa ra
đó Trò chơi kết thúc. ')
212.
in ('Các rương còn lại đã ở đây:')
213.
cho x, y trong các mục:
214.
in ('% s,% s'% (x, y))
Dòng 209 là dòng đầu tiên bên ngoài trong khi vòng lặp. Đến lúc này trò chơi kết thúc. Nhưng
bằng cách nào
chúng ta có nói người chơi thắng hay không? Hai nơi duy nhất thực hiện chương trình
đã rời khỏi trong khi vòng lặp là trên đường dây 179 nếu tình trạng thất bại. Trong trường hợp
đó,
sonarDevices sẽ là 0 và người chơi sẽ thua.
237
13 - Truy tìm kho báu Sonar

Trang 252
Vị trí thứ hai là câu lệnh break trên dòng 205. Câu lệnh đó được thực thi nếu
Người chơi đã tìm thấy tất cả các rương kho báu trước khi hết các thiết bị sonar. Trong trường
hợp đó,
sonarDevices sẽ có giá trị lớn hơn 0 .
Các dòng 210 đến 212 sẽ cho người chơi biết họ đã thua. Các cho vòng lặp trên dòng 213 sẽ đi
thông qua các rương kho báu còn lại trong các cuộc thi và hiển thị vị trí của họ cho người chơi
để họ biết nơi các rương kho báu đang ẩn nấp.
Yêu cầu Người chơi chơi lại và Hàm sys.exit ()
216. nếu không chơiAgain ():
217.
sys.exit ()
Dù thắng hay thua, chúng ta gọi hàm playAgain () để cho phép người chơi nhập liệu họ có
Muốn tiếp tục chơi hay không. Nếu không, thì playAgain () trả về Sai . Nhà điều hành không
thay đổi này để Đúng , làm cho nếu điều kiện tuyên bố của Đúng và sys.exit ()
chức năng được thực thi. Điều này sẽ khiến chương trình chấm dứt.
Nếu không, thực hiện nhảy trở lại vào đầu trong khi vòng lặp trên dòng 171.
Tóm tắt: Đánh giá về trò chơi Sonar của chúng tôi
Hãy nhớ cách trò chơi Tic Tac Toe của chúng tôi đánh số các khoảng trống trên bảng Tic Tac
Toe 1
qua 9? Kiểu hệ tọa độ này có thể ổn đối với một bảng có ít hơn
mười không gian. Nhưng bảng Sonar có chín trăm không gian! Hệ tọa độ Descartes
chúng ta đã học trong chương cuối thực sự làm cho tất cả các không gian này có thể quản lý
được, đặc biệt là khi
trò chơi của chúng tôi cần tìm khoảng cách giữa hai điểm trên bảng.
Bảng trò chơi trong các trò chơi sử dụng hệ tọa độ Descartes thường được lưu trữ trong danh
sách
liệt kê sao cho chỉ số đầu tiên là tọa độ x và chỉ số thứ hai là tọa độ y. Điều này
làm cho việc truy cập tọa độ trông giống như bảng [x] [y] .
Những cấu trúc dữ liệu này (chẳng hạn như những cấu trúc được sử dụng cho đại dương và vị trí
của kho báu
rương) làm cho có thể có các khái niệm phức tạp được biểu diễn dưới dạng dữ liệu trong chương
trình của chúng tôi,
và các chương trình trò chơi của chúng tôi chủ yếu trở thành việc sửa đổi các cấu trúc dữ liệu
này.
Trong chương tiếp theo, chúng ta sẽ biểu diễn các chữ cái dưới dạng số bằng ASCII của chúng
số. (Đây là thuật ngữ ASCII tương tự mà chúng tôi đã sử dụng trong "nghệ thuật ASCII" trước
đây.) Bởi
biểu diễn văn bản dưới dạng số, chúng ta có thể thực hiện các phép toán trên chúng
sẽ mã hóa hoặc giải mã tin nhắn bí mật.
238

Trang 253
Các chủ đề được đề cập trong Chương này:
● Mật mã và mật mã
● Mã hóa và giải mã

● Bản mã, bản rõ, khóa và ký hiệu

● Mật mã Caesar

● Giá trị thứ tự ASCII

● Các chr () và ord () chức năng

● Các isalpha () phương pháp chuỗi

● Các phương thức chuỗi isupper () và islower ()

● Phân tích mật mã

● Kỹ thuật vũ phu

Chương trình trong chương này không thực sự là một trò chơi, nhưng thật vui khi chơi với dù
sao.
Chương trình của chúng tôi sẽ chuyển đổi tiếng Anh thông thường thành mã bí mật và cũng
chuyển đổi mã bí mật
trở lại tiếng Anh thông thường một lần nữa. Chỉ có người am hiểu về mã bí mật
sẽ có thể hiểu các tin nhắn bí mật của chúng tôi.
Vì chương trình này thao túng văn bản để chuyển đổi nó thành tin nhắn bí mật, chúng tôi
sẽ tìm hiểu một số hàm và phương thức mới đi kèm với Python để thao tác
dây. Chúng ta cũng sẽ tìm hiểu làm thế nào các chương trình có thể làm toán với các chuỗi văn
bản giống như có thể với
số.
Về mật mã
Khoa học viết mã bí mật được gọi là mật mã . Mật mã đã được
239

Trang 254
được sử dụng trong hàng ngàn năm để gửi tin nhắn bí mật mà chỉ người nhận mới có thể
hiểu, ngay cả khi ai đó bắt được tin nhắn và đọc tin nhắn được mã hóa. Một bí mật
hệ thống mã được gọi là mật mã . Có hàng ngàn mật mã khác nhau đã được
được sử dụng, mỗi cách sử dụng các kỹ thuật khác nhau để giữ bí mật các tin nhắn.
Trong mật mã học, chúng tôi gọi thông điệp rằng chúng tôi muốn bí mật bản rõ . Các
văn bản có thể trông giống như thế này:
Xin chào! Chìa khóa nhà được giấu dưới
chậu hoa màu đỏ.
Khi chúng tôi chuyển đổi bản rõ vào thông điệp được mã hóa, chúng tôi gọi đây là mã hóa các
văn bản thô. Bản rõ được mã hóa vào bản mã . Bản mã trông giống như ngẫu nhiên
các chữ cái (còn được gọi là dữ liệu rác ) và chúng tôi không thể hiểu bản rõ gốc là gì
chỉ bằng cách nhìn vào bản mã. Dưới đây là một ví dụ về một số bản mã:
Ckkz fkx kj becqnejc kqp pdeo oaynap iaoowca!
Nhưng nếu chúng ta biết về các thuật toán mã hóa dùng để mã hóa thông điệp, chúng ta có
thể giải mã các
bản mã trở lại bản rõ. (Giải mã ngược lại với mã hóa.)
Nhiều mật mã cũng sử dụng khóa. Khóa là các giá trị bí mật cho phép bạn giải mã bản mã
đã được mã hóa bằng một mật mã cụ thể. Hãy nghĩ về mật mã giống như một khóa cửa.
Mặc dù tất cả các khóa cửa cùng loại được xây dựng giống nhau, nhưng một khóa cụ thể sẽ
Chỉ mở khóa nếu bạn có chìa khóa được làm cho khóa đó.
Mật mã Caesar
Khi chúng tôi mã hóa một
tin nhắn bằng mật mã,
chúng tôi sẽ chọn chìa khóa
được sử dụng để mã hóa và
giải mã tin nhắn này. Các
chìa khóa cho mật mã Caesar của chúng tôi
sẽ là một số từ 1 đến
26. Trừ khi bạn biết
chính (đó là, biết
số), bạn sẽ không
có thể giải mã
tin nhắn được mã hóa.
Các Mật Mã Caesar là một trong những thuật toán mã hóa sớm nhất từng được phát
minh. Trong mật mã này, bạn
mã hóa tin nhắn bằng cách lấy từng chữ cái trong tin nhắn (trong mật mã, những chữ cái này là
được gọi là biểu tượng bởi vì chúng có thể là chữ cái, số hoặc bất kỳ dấu hiệu nào khác) và thay
thế nó
với một lá thư "thay đổi". Nếu bạn thay đổi chữ A theo một khoảng trắng, bạn sẽ nhận được chữ
B. Nếu bạn thay đổi
chữ A bằng hai khoảng trắng, bạn nhận được chữ C. Hình 14-1 là hình ảnh của một số chữ cái
thay đổi bởi 3 không gian.
Hình 14-1: Chuyển qua các chữ cái theo ba khoảng trắng. Tại đây, B trở thành E.
240

Trang 255
Để có được mỗi chữ cái được dịch chuyển, hãy vẽ ra một hàng hộp với mỗi chữ cái của bảng chữ
cái. Sau đó
vẽ một hàng thứ hai của hộp bên dưới nó, nhưng bắt đầu một số khoảng trống nhất định. Khi bạn
nhận được các chữ cái còn lại ở cuối, quấn quanh trở lại đầu hộp. Đây là một
ví dụ với các chữ cái được dịch chuyển bởi ba khoảng trắng:
Hình 14-2: Toàn bộ bảng chữ cái được dịch chuyển bởi ba khoảng trắng.
Số lượng không gian chúng tôi thay đổi là chìa khóa trong Mật mã Caesar. Ví dụ trên
hiển thị các bản dịch thư cho khóa 3.
Sử dụng khóa 3, nếu chúng tôi mã hóa bản rõ "Howdy", thì "H" trở thành "E". Các
chữ "o" trở thành "l". Chữ "w" trở thành "t". Chữ "d" trở thành "a". Và
chữ "y" trở thành "v". Bản mã của "Xin chào" với khóa 3 trở thành "Eltav".
Chúng tôi sẽ giữ bất kỳ ký tự không phải chữ cái giống nhau. Để giải mã "Eltav" bằng khóa
3, chúng tôi chỉ cần đi từ các hộp dưới cùng trở lại đầu trang. Chữ "E" trở thành "H", chữ cái
"l" trở thành "o", chữ "t" trở thành "w", chữ "a" trở thành "d" và chữ "v"
trở thành "y" để tạo thành "Howdy".
Bạn có thể tìm hiểu thêm về Mật mã Caesar từ Wikipedia tại
http://en.wikipedia.org/wiki/Caesar_codes
ASCII và sử dụng số cho chữ cái
Làm thế nào để chúng tôi thực hiện việc dịch chuyển các chữ cái trong chương trình của chúng
tôi? Chúng ta có thể làm điều này bằng cách
đại diện cho mỗi chữ cái dưới dạng một số (được gọi là số thứ tự ), sau đó cộng hoặc trừ
từ số này để tạo thành một số mới (và một chữ cái mới). ASCII (phát âm là "hỏi-ee"
và là viết tắt của Mã tiêu chuẩn Mỹ để trao đổi thông tin) là một mã
kết nối mỗi ký tự với một số trong khoảng từ 32 đến 127. Các số nhỏ hơn 32 tham chiếu đến
Các ký tự "không thể in", vì vậy chúng tôi sẽ không sử dụng chúng.
Chữ in hoa "A" đến "Z" có các số ASCII từ 65 đến 90.
chữ thường "a" đến "z" có các số ASCII 97 đến 122. Số
chữ số "0" đến "9" có các số ASCII 48 đến 57.
241
14 - Mật mã Caesar

Trang 256
Vì vậy, nếu chúng tôi muốn thay đổi "A" theo ba khoảng trắng, trước tiên chúng tôi chuyển đổi
nó thành một số (65). Sau đó
chúng tôi thêm 3 đến 65, để có được 68. Sau đó, chúng tôi chuyển đổi số 68 trở lại thành một
chữ cái ("D"). Chúng tôi sẽ
sử dụng các hàm chr () và ord () để chuyển đổi giữa các chữ cái và số.
Ví dụ: chữ "A" được biểu thị bằng số 65. Chữ "m" là
được đại diện bởi số 109. Một bảng gồm tất cả các ký tự ASCII từ 32 đến 12 nằm trong
Bảng 14-1.
Các chr () và ord () Chức năng
Hàm chr () (phát âm là "char", viết tắt của "character") lấy một số nguyên ASCII
số cho tham số và trả về chuỗi ký tự đơn. Các ord () chức năng
(viết tắt của "ordinal") lấy một chuỗi ký tự đơn cho tham số và trả về số nguyên
Giá trị ASCII cho ký tự đó. Hãy thử nhập nội dung sau vào vỏ tương tác:
>>> chr (65)
'A'
>>> ord ('A')
65
Bảng 14-1: Bảng ASCII
32
(không gian)
48
0
64
@
80
P
96
`
112
p
33
!
49
1
65
Một
81
Q
97
một
113
q
34
"
50
2
66
B
82
R
98
b
114
r
35
#
51
3
67
C
83
S
99
c
115
S
36
$
52
4
68
D
84
T
100
d
116
t
37
%
53
5
69
E
85
Bạn
101
e
117
bạn
38
&
54
6
70
F
86
V
102
f
118
v
39
'
55
7
71
G
87
W
103
g
119
w
40
(
56
số 8
72
H
88
X
104
h
120
x
41
)
57
9
73
Tôi
89
Y
105
Tôi
121
y
42
*
58
:
74
J
90
Z
106
j
122
z
43
+
59
;
75
K
91
[
107
k
123
{
44
,
60
<
76
L
92
\
108
tôi
124
|
45
-
61
==
77
M
93
]
109
m
125
}
46
.
62
>
78
N
94
^
110
n
126
~
47
/
63
?
79
Ôi
95
_
111
o
242

Trang 256
>>> chr (65 + 8)
'TÔI'
>>> chr (52)
'4'
>>> chr (ord ('F'))
'F'
>>> ord (chr (68))
68
>>>
Trên dòng thứ ba, chr (65 + 8) ước tính thành chr (73) . Nếu bạn nhìn vào bảng ASCII,
bạn có thể thấy 73 là số thứ tự của chữ in hoa "I". Trên dòng thứ năm, chr (ord
('F')) ước tính cho chr (70) ước tính thành 'F' . Cho kết quả của ord () vào
chr () sẽ trả lại cho bạn đối số ban đầu. Điều tương tự cũng xảy ra khi cho ăn kết quả của
chr () đến ord () , như được hiển thị bởi dòng thứ sáu.
Sử dụng chr () và ord () sẽ có ích cho chương trình Mật mã Caesar của chúng tôi. họ đang
cũng hữu ích khi chúng ta cần chuyển đổi chuỗi thành số và số thành chuỗi.
Chạy mẫu của Mật mã Caesar
Dưới đây là bản chạy mẫu của chương trình Mật mã Caesar, mã hóa tin nhắn:
Bạn có muốn mã hóa hoặc giải mã một tin nhắn?
mã hóa
Nhập tin nhắn của bạn:
Bầu trời phía trên cổng là màu của tivi, được điều chỉnh theo
kênh chết.
Nhập số khóa (1-26)
13
Văn bản dịch của bạn là:
Gur fxl nobir gur cbeg jnf gur pbybe bs gryrivfvba, gharq gb n
qrnq punaary.
Bây giờ chúng tôi sẽ chạy chương trình và giải mã văn bản mà chúng tôi vừa
được mã hóa.
Bạn có muốn mã hóa hoặc giải mã một tin nhắn?
giải mã
Nhập tin nhắn của bạn:
Gur fxl nobir gur cbeg jnf gur pbybe bs gryrivfvba, gharq gb n
qrnq punaary.
Nhập số khóa (1-26)
13
Văn bản dịch của bạn là:
Bầu trời phía trên cổng là màu của tivi, được điều chỉnh theo
kênh chết.
Trong lần chạy này, chúng tôi sẽ cố gắng giải mã văn bản đã được mã hóa, nhưng chúng tôi sẽ sử
dụng sai
243
14 - Mật mã Caesar

Trang 256
Chìa khóa. Hãy nhớ rằng nếu bạn không biết khóa chính xác, văn bản được giải mã sẽ chỉ là
dữ liệu rác.
Bạn có muốn mã hóa hoặc giải mã một tin nhắn?
giải mã
Nhập tin nhắn của bạn:
Gur fxl nobir gur cbeg jnf gur pbybe bs gryrivfvba, gharq gb n
qrnq punaary.
Nhập số khóa (1-26)
15
Văn bản dịch của bạn là:
Rfc qiw yzmtc rfc nmpr uyq rfc amjmp md RCjctgqgml, rslcb rm y
bcyb afyllcj.

Mã nguồn của Caesar


Đây là mã nguồn cho chương trình Mật mã Caesar. Nếu bạn không muốn gõ tất cả
mã này, bạn có thể truy cập trang web của cuốn sách này tại URL
http://inventwithpython.com/ch CHƯƠNG14 và làm theo hướng dẫn để tải xuống nguồn
mã. Sau khi bạn nhập mã này vào, hãy lưu tệp dưới dạng mật mã
mật mã
Mã này có thể được tải xuống từ http://inventwithpython.com/codes.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. # Mật mã Caesar
2.
3. MAX_KEY_SIZE = 26
4.
5. def getMode ():
6. trong khi Đúng:
7.
in ('Bạn có muốn mã hóa hoặc giải mã
thông điệp?')
số 8.
chế độ = đầu vào (). thấp hơn ()
9.
if mode trong 'mã hóa e decrypt d'.split ():
10.
chế độ trở lại
11.
khác:
12.
in ('Nhập "mã hóa" hoặc "e" hoặc
"Giải mã" hoặc "d". ')
13.
14. def getMessage ():
15. in ('Nhập tin nhắn của bạn:')
16. trả về đầu vào ()
17.
18. def getKey ():
19. khóa = 0
20. trong khi Đúng:
21.
in ('Nhập số khóa (1-% s)'%
244

Trang 259
(MAX_KEY_SIZE))
22.
key = int (đầu vào ())
23.
if (key> = 1 và key <= MAX_KEY_SIZE):
24.
chìa khóa trở lại
25.
26. def getTranslatedMessage (chế độ, tin nhắn, khóa):
27. nếu chế độ [0] == 'd':
28.
khóa = -key
29. dịch = ''
30.
31. cho biểu tượng trong tin nhắn:
32.
nếu biểu tượng.alupha ():
33.
num = ord (ký hiệu)
34.
num + = khóa
35.
36.
if Symbol.isupper ():
37.
nếu num> ord ('Z'):
38.
số - = 26
39.
elif num <ord ('A'):
40.
số + = 26
41.
biểu tượng elif.islower ():
42.
nếu num> ord ('z'):
43.
số - = 26
44.
elif num <ord ('a'):
45.
số + = 26
46.
47.
đã dịch + = chr (num)
48.
khác:
49.
dịch + = ký hiệu
50. trả lại dịch
51.
52. chế độ = getMode ()
53. tin nhắn = getMessage ()
54. key = getKey ()
55.
56. print ('Văn bản dịch của bạn là:')
57. in (getTranslatedMessage (chế độ, tin nhắn, khóa))

Cách thức hoạt động của mã: Dòng 1 đến 34


Mã này ngắn hơn nhiều so với các trò chơi khác của chúng tôi. Mã hóa và giải mã
các quá trình chỉ là mặt trái của các quá trình khác, và thậm chí sau đó chúng vẫn chia sẻ nhiều
cùng mã. Hãy xem cách mỗi dòng hoạt động.
1. # Mật mã Caesar
2.
3. MAX_KEY_SIZE = 26
Dòng đầu tiên chỉ đơn giản là một bình luận. Mật mã Caesar là một loại mật mã của một loại mật

gọi là mật mã thay thế đơn giản. Mật mã thay thế đơn giản là mật mã thay thế
245
14 - Mật mã Caesar

Trang 260
một ký hiệu trong bản rõ với một (và chỉ một) ký hiệu trong bản mã. Vì vậy, nếu một "G"
đã được thay thế bằng "Z" trong mật mã, mỗi chữ "G" trong bản rõ sẽ được thay thế
với (và chỉ với) một "Z".
MAX_KEY_SIZE là một biến lưu trữ số nguyên 26 trong đó. MAX_KEY_SIZE nhắc nhở
chúng tôi rằng trong chương trình này, khóa được sử dụng trong mật mã của chúng tôi phải nằm
trong khoảng từ 1 đến 26.
Quyết định mã hóa hoặc giải mã
5. def getMode ():
6. trong khi Đúng:
7.
in ('Bạn có muốn mã hóa hoặc giải mã
thông điệp?')
số 8.
chế độ = đầu vào (). thấp hơn ()
9.
if mode trong 'mã hóa e decrypt d'.split ():
10.
chế độ trở lại
11.
khác:
12.
in ('Nhập "mã hóa" hoặc "e" hoặc
"Giải mã" hoặc "d". ')
Hàm getMode () sẽ cho phép người dùng nhập vào nếu họ muốn mã hóa hoặc giải mã
thông điệp. Giá trị trả về của input () (sau đó có phương thức low () được gọi trên nó,
trong đó trả về phiên bản chữ thường của chuỗi) được lưu trữ trong chế độ . Các nếu tuyên bố
của
điều kiện kiểm tra nếu chuỗi được lưu trữ trong chế độ tồn tại trong danh sách được trả về
bởi 'mã hóa e
giải mã d'.split () . Danh sách này là ['mã hóa', 'e', 'giải mã', 'd'] , nhưng
lập trình viên sẽ dễ dàng hơn khi chỉ cần nhập 'mã hóa e decrypt d'.split ()
và không gõ vào tất cả những trích dẫn và dấu phẩy. Nhưng bạn có thể sử dụng bất cứ thứ gì dễ
nhất cho bạn;
cả hai đều đánh giá cùng một giá trị danh sách.
Hàm này sẽ trả về ký tự đầu tiên trong chế độ miễn là chế độ bằng
'mã hóa' , 'e' , 'giải mã' hoặc 'd' . Điều này có nghĩa là getMode () sẽ trả về
chuỗi 'e' hoặc chuỗi 'd' .
Nhận tin nhắn từ người chơi
14. def getMessage ():
15. in ('Nhập tin nhắn của bạn:')
16. trả về đầu vào ()
Hàm getMessage () chỉ đơn giản là nhận thông điệp để mã hóa hoặc giải mã từ
người dùng và sử dụng chuỗi này làm giá trị trả về của nó.
246

Trang 261
Lấy chìa khóa từ người chơi
18. def getKey ():
19. khóa = 0
20. trong khi Đúng:
21.
in ('Nhập số khóa (1-% s)'%
(MAX_KEY_SIZE))
22.
key = int (đầu vào ())
23.
if (key> = 1 và key <= MAX_KEY_SIZE):
24.
chìa khóa trở lại
Hàm getKey () cho phép người chơi nhập khóa họ sẽ sử dụng để mã hóa hoặc giải mã
thông điệp. Các trong khi vòng đảm bảo rằng các chức năng chỉ trả lại một chìa khóa hợp lệ. Một
hợp lệ
khóa ở đây là một giá trị nằm giữa các giá trị nguyên 1 và 26 (hãy nhớ rằng
MAX_KEY_SIZE sẽ chỉ có giá trị 26 vì nó không đổi). Sau đó nó trả về
Chìa khóa. Hãy nhớ rằng trên dòng 22, khóa đó được đặt thành phiên bản số nguyên của những
gì người dùng
được nhập vào và do đó getKey () trả về một số nguyên.
Mã hóa hoặc giải mã tin nhắn bằng khóa đã cho
26. def getTranslatedMessage (chế độ, tin nhắn, khóa):
27. nếu chế độ [0] == 'd':
28.
khóa = -key
29. dịch = ''
getTranslatedMessage () là chức năng mã hóa và giải mã
trong chương trình của chúng tôi. Nó có ba tham số. chế độ đặt chức năng thành chế độ mã hóa
hoặc
chế độ giải mã. tin nhắn là bản rõ (hoặc bản mã) được mã hóa (hoặc giải mã).
khóa là khóa được sử dụng trong mật mã này.
Dòng đầu tiên trong hàm getTranslatedMessage () xác định xem chúng ta có ở trong không
chế độ mã hóa hoặc chế độ giải mã. Nếu chữ cái đầu tiên trong biến chế độ là chuỗi
'D' , sau đó chúng tôi đang ở chế độ giải mã. Sự khác biệt duy nhất giữa hai chế độ là trong
Chế độ giải mã, khóa được đặt thành phiên bản phủ định của chính nó. Nếu khóa là số
nguyên 22 ,
sau đó trong chế độ giải mã, chúng tôi đặt nó thành -22 . Lý do cho điều này sẽ được giải thích
sau.
dịch là chuỗi sẽ giữ kết quả cuối cùng: hoặc là bản mã (nếu chúng ta là
mã hóa) hoặc bản rõ (nếu chúng ta đang giải mã). Chúng tôi sẽ chỉ nối các chuỗi thành
biến này, vì vậy trước tiên chúng ta lưu trữ chuỗi trống trong bản dịch . (Một biến phải là
được xác định với một số giá trị chuỗi trước tiên trước khi một chuỗi có thể được nối với nó.)
Các isalpha () Chuỗi Phương pháp
Các isalpha () phương pháp chuỗi sẽ trở lại Đúng nếu chuỗi là một chữ hoa hoặc
247
14 - Mật mã Caesar

Trang 262
chữ thường từ A đến Z. Nếu chuỗi chứa bất kỳ ký tự không phải chữ cái nào, thì
isalpha () sẽ trả về Sai . Hãy thử nhập nội dung sau vào vỏ tương tác:
>>> 'Xin chào'.alupha ()
Thật
>>> 'Bốn mươi hai'.alupha ()
Sai
>>> 'Fortytwo'.alupha ()
Thật
>>> '42'.alupha ()
Sai
>>> '' .alupha ()
Sai
>>>
Như bạn có thể thấy, 'Bốn mươi hai'.alupha () sẽ trả về Sai vì ' Bốn mươi
hai ' có một khoảng trắng trong đó, đó là một ký tự không phải chữ cái. 'Fortytwo'.alupha ()
trả về True vì nó không có không gian này. '42'.alupha () trả về Sai
bởi vì cả '4' và '2' đều là các ký tự không phải chữ cái. Và '' .alupha () là Sai
bởi vì isalpha () chỉ trả về True nếu chuỗi chỉ có các ký tự chữ cái và không
chỗ trống.
Chúng tôi sẽ sử dụng phương thức isalpha () trong chương trình của chúng tôi tiếp theo.
31. cho biểu tượng trong tin nhắn:
32.
nếu biểu tượng.alupha ():
33.
num = ord (ký hiệu)
34.
num + = khóa
Chúng tôi sẽ chạy một vòng lặp for trên mỗi chữ cái (hãy nhớ trong mật mã chúng được gọi là
ký hiệu) trong chuỗi tin nhắn . Chuỗi được xử lý giống như danh sách các ký tự đơn
dây. Nếu tin nhắn có chuỗi 'Xin chào' , thì biểu tượng trong 'Xin chào' sẽ
giống như biểu tượng trong ['H', 'e', 'l', 'l', 'o'] . Trên mỗi
Lặp lại qua vòng lặp này, biểu tượng sẽ có giá trị của một chữ cái trong tin nhắn .
Lý do chúng tôi có nếu tuyên bố trên dòng 32 là bởi vì chúng tôi sẽ chỉ mã hóa / giải mã
chữ cái trong tin nhắn. Số, dấu hiệu, dấu chấm câu, và mọi thứ khác sẽ ở lại
hình thức chưa được dịch của họ. Biến num sẽ giữ giá trị thứ tự nguyên của chữ cái
lưu trữ trong biểu tượng . Dòng 34 sau đó "dịch chuyển" giá trị bằng num theo giá trị
trong khóa .
Các phương thức chuỗi isupper () và islower ()
Các phương thức chuỗi isupper () và islower () (nằm trên dòng 36 và 41) hoạt động
248

Trang 263
theo cách rất giống với các phương thức isdigit () và isalpha () . isupper
() sẽ trả về True nếu chuỗi được gọi trên chứa ít nhất một chữ cái viết hoa và không
chữ viết thường. islower () trả về True nếu chuỗi được gọi trên chứa ít nhất
một chữ cái viết thường và không có chữ cái viết hoa. Nếu không, các phương thức này trả
về Sai . Các
sự tồn tại của các ký tự không phải chữ cái như số và dấu cách không ảnh hưởng đến kết quả.
Mặc dù các chuỗi không có bất kỳ chữ cái nào, kể cả các chuỗi trống, cũng sẽ trả về
Sai . Hãy thử nhập nội dung sau vào vỏ tương tác:
>>> 'HELLO'.isupper ()
Thật
>>> 'xin chào'.isupper ()
Sai
>>> 'xin chào'.islower ()
Thật
>>> 'Xin chào'.islower ()
Sai
>>> 'TÌM HIỂU BẠN!'. Isupper ()
Thật
>>> '42'.isupper ()
Sai
>>> '42'.lower ()
Sai
>>> '' .isupper ()
Sai
>>> '' .islower ()
Sai
>>>
Cách thức hoạt động của mã: Dòng 36 đến 57
Quá trình mã hóa (hoặc giải mã) mỗi chữ cái khá đơn giản. Chúng tôi muốn áp dụng
mã Python giống nhau cho mỗi ký tự chữ cái trong chuỗi, đó là những gì tiếp theo
dòng mã làm.
Mã hóa hoặc giải mã từng chữ cái
36.
if Symbol.isupper ():
37.
nếu num> ord ('Z'):
38.
số - = 26
39.
elif num <ord ('A'):
40.
số + = 26
Mã này kiểm tra nếu ký hiệu là một chữ cái viết hoa. Nếu vậy, có hai trường hợp đặc biệt
chúng ta cần phải lo lắng về Nếu ký hiệu là 'Z' và khóa là 4 thì sao? Nếu đó là trường hợp,
249
14 - Mật mã Caesar

Trang 264
giá trị của num ở đây sẽ là ký tự '^' (Số thứ tự của '^' là 94 ). Nhưng ^ không
một lá thư ở tất cả. Chúng tôi muốn bản mã để "bọc xung quanh" đến đầu bảng chữ cái.
Cách chúng ta có thể làm là kiểm tra xem khóa có giá trị lớn hơn giá trị lớn nhất có thể không
giá trị ASCII của chữ cái (vốn là chữ "Z"). Nếu vậy, chúng tôi muốn trừ 26 (vì
có tổng cộng 26 chữ cái) từ num . Sau khi làm điều này, giá trị của num là 68 , đó là
giá trị ASCII cho 'D' .
41.
biểu tượng elif.islower ():
42.
nếu num> ord ('z'):
43.
số - = 26
44.
elif num <ord ('a'):
45.
số + = 26
Nếu ký hiệu là một chữ cái viết thường, chương trình chạy mã rất giống với dòng 36
đến 40. Sự khác biệt duy nhất là chúng tôi sử dụng ord ('z') và ord ('a') thay vì ord
('Z') và ord ('A') .
Nếu chúng ta ở chế độ giải mã, thì khóa sẽ âm. Sau đó, chúng tôi sẽ có
trường hợp đặc biệt trong đó num - = 26 có thể nhỏ hơn giá trị nhỏ nhất có thể (đó là
ord ('A') , nghĩa là, 65 ). Nếu đây là trường hợp, chúng tôi muốn thêm 26 vào num để có nó "bọc
xung quanh".
47.
đã dịch + = chr (num)
48.
khác:
49.
dịch + = ký hiệu
Chuỗi dịch sẽ được nối với ký tự được mã hóa / giải mã. Nếu
biểu tượng không phải là chữ hoa hoặc chữ thường, sau đó khối khác trên dòng 48 sẽ
đã thực hiện thay thế. Tất cả các mã trong khối khác thực hiện là nối biểu tượng gốc vào
các dịch chuỗi. Điều này có nghĩa là dấu cách, số, dấu chấm câu và khác
các ký tự sẽ không được mã hóa (hoặc giải mã).
50. trả lại dịch
Dòng cuối cùng trong hàm getTranslatedMessage () trả về bản dịch
chuỗi.
Bắt đầu chương trình
52. chế độ = getMode ()
53. tin nhắn = getMessage ()
54. key = getKey ()
250

Trang 265
55.
56. print ('Văn bản dịch của bạn là:')
57. in (getTranslatedMessage (chế độ, tin nhắn, khóa))
Đây là phần chính của chương trình của chúng tôi. Chúng tôi gọi mỗi trong số ba chức năng
chúng tôi đã xác định
ở trên lần lượt để có được chế độ, tin nhắn và khóa mà người dùng muốn sử dụng. Chúng tôi sau
đó vượt qua
ba giá trị này làm đối số cho getTranslatedMessage () , có giá trị trả về (
chuỗi dịch ) được in cho người dùng.
Lực lượng vũ phu
Đó là toàn bộ Mật mã Caesar. Tuy nhiên, trong khi mật mã này có thể đánh lừa một số người
không hiểu mật mã, nó sẽ không giữ bí mật tin nhắn từ một người biết
phân tích mật mã. Trong khi mã hóa là khoa học của mã làm, giải mã là
khoa học phá mã.
Bạn có muốn mã hóa hoặc giải mã một tin nhắn?
mã hóa
Nhập tin nhắn của bạn:
Nghi ngờ có thể không dễ chịu, nhưng chắc chắn là vô lý.
Nhập số khóa (1-26)
số 8
Văn bản dịch của bạn là:
Lwcjba uig vwb jm xtmiaivb, jcb kmzbiqvbg qa ijaczl.
Toàn bộ quan điểm của mật mã là như vậy nếu người khác chạm tay vào
tin nhắn được mã hóa, họ không thể tìm ra tin nhắn không được mã hóa ban đầu từ nó. Hãy
giả vờ chúng tôi là người phá mã và tất cả những gì chúng tôi có là văn bản được mã hóa:
Lwcjba uig vwb jm xtmiaivb, jcb kmzbiqvbg qa ijaczl.
Một phương pháp của tiền điện tử được gọi là lực lượng vũ phu. Lực lượng vũ phu là kỹ thuật
cố gắng
mỗi khóa có thể. Nếu nhà mật mã biết mật mã mà tin nhắn sử dụng (hoặc tại
ít đoán nó nhất), họ chỉ có thể đi qua mọi khóa có thể. Bởi vì chỉ có 26
các khóa có thể, sẽ dễ dàng cho một nhà mật mã viết chương trình hơn là in
mật mã được giải mã của mọi khóa có thể và xem liệu bất kỳ đầu ra nào có ý nghĩa. Hãy
thêm một tính năng vũ phu cho chương trình của chúng tôi.
Thêm Chế độ Brute Force vào Chương trình của chúng tôi
Đầu tiên, thay đổi các dòng 7, 9 và 12 (nằm trong hàm getMode () ) để trông giống như
sau đây (những thay đổi được in đậm):
5. def getMode ():
251
14 - Mật mã Caesar

Trang 266
6. trong khi Đúng:
7.
in ('Bạn có muốn mã hóa hoặc giải mã hoặc vũ phu
ép buộc một tin nhắn? ')
số 8.
chế độ = đầu vào (). thấp hơn ()
9.
if mode trong 'mã hóa e giải mã d brute b ' .split ():
10.
chế độ trở lại [0]
11.
khác:
12.
in ('Nhập "mã hóa" hoặc "e" hoặc
"Giải mã" hoặc "d" hoặc "vũ phu" hoặc "b" . ')
Điều này sẽ cho phép chúng tôi chọn "sức mạnh vũ phu" làm chế độ cho chương trình của chúng
tôi. Sau đó sửa đổi và thêm
những thay đổi sau đây cho phần chính của chương trình:
52. chế độ = getMode ()
53. tin nhắn = getMessage ()
54. nếu chế độ [0]! = 'B':
55. key = getKey ()
56.
57. in ('Văn bản dịch của bạn là:')
58. nếu chế độ [0]! = 'B':
59. in (getTranslatedMessage (chế độ, tin nhắn, khóa))
60. khác:
61. cho khóa trong phạm vi (1, MAX_KEY_SIZE + 1):
62.
in (khóa, getTranslatedMessage ('giải mã',
tin nhắn, chìa khóa))
Những thay đổi này làm cho chương trình của chúng tôi yêu cầu người dùng nhập khóa nếu họ
không ở trong "lực lượng vũ phu"
chế độ. Nếu chúng không ở chế độ "brute force", thì getTranslatedMessage ban đầu
() cuộc gọi được thực hiện và chuỗi dịch được in.
Tuy nhiên, nếu không, chúng tôi đang ở chế độ "vũ phu" và chúng tôi chạy
Vòng lặp getTranslatedMessage () lặp từ 1 đến hết
MAX_KEY_SIZE ( 26 ). Hãy nhớ rằng khi hàm phạm vi () trả về một danh sách
số nguyên lên đến nhưng không bao gồm tham số thứ hai, đó là lý do tại sao chúng ta có +
1 . Điều này
chương trình sẽ in ra mọi bản dịch có thể của tin nhắn (bao gồm số khóa
được sử dụng trong bản dịch). Đây là một mẫu chạy của chương trình sửa đổi này:
252

Trang 267
Bạn có muốn mã hóa hoặc giải mã hoặc vũ phu
thông điệp?
vũ phu
Nhập tin nhắn của bạn:
Lwcjba uig vwb jm xtmiaivb, jcb kmzbiqvbg qa ijaczl.
Văn bản dịch của bạn là:
1 Kvbiaz thf uva il wslhzhua, iba jlyahpuaf pz hizbyk.
2 Juahzy sge tuz hk vrkgygtz, haz ikxzgotze oy ghyaxj.
3 Itzgyx rfd sty gj uqjfxfsy, gzy hjwyfnsyd nx fgxzwi.
4 Hsyfxw qec rsx fi tpiewerx, fyx givxemrxc mw efwyvh.
5 Grxewv pdb qrw eh sohvdqw, exw fhuwdlqwb lv devxug.
6 Fqwvu oca pqv dg rngcucpv, dwv egtvckpva ku cduwtf.
7 Epvcut nbz opu cf qmfbtbou, cvu dfsubjouz jt bctvse.
8 Nghi ngờ có thể không dễ chịu, nhưng chắc chắn là vô lý.
9 Cntasr lzx mns quảng cáo okdzrzms, ats bdqszhmsx hr zartqc.
10 Bmszrq kyw lmr zc njcyqylr, zsr acpryglrw gq yzqspb.
11 Alryqp jxv klq yb mibxpxkq, yrq zboqxfkqv fp xyproa.
12 Zkqxpo iwu jkp xa lhawowjp, xqp yanpwejpu eo wxoqnz.
13 Yjpwon hvt ijo wz kgzvnvio, wpo xzmovdiot dn vwnpmy.
14 Xiovnm gus hin vy jfyumuhn, von wylnuchns cm uvm.
15 Whnuml ftr ghm ux iextltgm, unm vxkmtbgmr bl tulnkw.
16 Vgmtlk esq fgl tw hdwsksfl, tml uwjlsaflq ak stkmjv.
17 Uflskj drp efk sv gcvrjrek, slk tvikrzekp zj rsjliu.
18 Tekrji cqo dej ru fbuqiqdj, rkj suhjqydjo yi qrikht.
19 Sdjqih bpn cdi qt eatphpci, qji rtgipxcin xh pqhjss.
20 Rciphg aom bch ps dzsogobh, pih qsfhowbhm wg opgifr.
21 Qboosef znl abg hoặc cyrnfnag, ohg mangvagl vf nofheq.
22 Pagnfe ymk zaf nq bxqmemzf, ngf oqdfmuzfk ue mnegdp.
23 Ozfmed xlj yze mp awpldlye, mfe npceltyej td lmdfco.
24 Nyeldc wki xyd lo zvokckxd, đã dẫn mobdksxdi sc klcebn.
25 Mxdkcb vjh wxc kn yunjbjwc, kdc lnacjrwch rb jkbdam.
26 Lwcjba uig vwb jm xtmiaivb, jcb kmzbiqvbg qa ijaczl.
Sau khi nhìn qua từng hàng, bạn có thể thấy rằng thông điệp thứ 8 không phải là rác, mà là đơn
giản
Tiếng Anh! Nhà mật mã có thể suy luận rằng khóa gốc cho văn bản được mã hóa này phải có
được 8 . Lực lượng vũ phu này sẽ khó có thể quay trở lại vào thời của Caesar và
Đế chế La Mã, nhưng ngày nay chúng ta có máy tính có thể nhanh chóng vượt qua hàng triệu
hoặc thậm chí
hàng tỷ chìa khóa trong một thời gian ngắn. Bạn thậm chí có thể viết một chương trình có thể
nhận ra khi nó
đã tìm thấy một tin nhắn bằng tiếng Anh, vì vậy bạn không đọc qua tất cả các văn bản rác.
Tóm tắt: Xem xét Chương trình Mật mã Caesar của
chúng tôi
Máy tính rất giỏi làm toán. Khi chúng tôi tạo ra một hệ thống để dịch
một số thông tin thành số (chẳng hạn như chúng tôi làm với văn bản và ASCII hoặc với không
gian
và hệ thống tọa độ), các chương trình máy tính có thể xử lý những con số này rất nhanh và
hiệu quả
253
14 - Mật mã Caesar

Trang 268
Nhưng trong khi chương trình mật mã Caesar của chúng tôi ở đây có thể mã hóa các tin nhắn sẽ giữ
chúng
bí mật từ những người phải tìm ra nó bằng bút chì và giấy, nó sẽ không giữ bí mật
từ những người biết cách lấy máy tính để xử lý thông tin cho họ. (Kẻ vũ phu của chúng ta
chế độ lực lượng chứng minh điều này.) Và có những mật mã mã hóa khác rất tiên tiến đến mức
không ai biết cách giải mã những tin nhắn bí mật mà họ tạo ra. (Trừ những người có
chìa khóa của khóa học!)
Một phần lớn của việc tìm ra cách viết một chương trình là tìm ra cách thể hiện
thông tin bạn muốn thao tác như số. Tôi hy vọng chương này đã được đặc biệt thể hiện
bạn làm thế nào điều này có thể được thực hiện. Chương tiếp theo sẽ giới thiệu trò chơi cuối
cùng của chúng tôi, Reversi (cũng
được gọi là Othello). AI chơi trò chơi này sẽ tiến bộ hơn nhiều so với AI
đã chơi Tic Tac Toe trong chương 9. Trên thực tế, AI rất tốt, đến nỗi bạn sẽ thấy rằng hầu hết
thời gian bạn sẽ không thể đánh bại nó!
254

Trang 269
Các chủ đề được đề cập trong Chương này:
● Các bool () Chức năng
● Đánh giá các giá trị không Boolean là Booleans

Cách chơi Reversi


Trong chương này, chúng tôi sẽ thực hiện một trò chơi có tên Reversi. Reversi (còn được gọi là
Othello) là một
bảng trò chơi được chơi trên lưới (vì vậy chúng tôi sẽ sử dụng hệ tọa độ Descartes với XY
tọa độ, giống như chúng ta đã làm với Sonar.) Đây là một trò chơi với hai người chơi. Phiên bản
của chúng tôi
Trò chơi sẽ có AI máy tính tiên tiến hơn AI chúng tôi tạo ra cho Tic Tac
Ngón chân. Trên thực tế, AI này tốt đến mức có thể nó sẽ đánh bại bạn gần như mỗi khi bạn
chơi. (TÔI
biết tôi thua bất cứ khi nào tôi chống lại nó!)
Nếu bạn muốn xem video Reversi đang được phát, có một minh chứng về điều này
trang web của cuốn sách. Truy cập URL http://inventwithpython.com/ideo và tìm "Reversi
Video giới thiệu "video.
Reversi có một bảng 8 x 8 với gạch có màu đen ở một bên và mặt kia màu trắng
(trò chơi của chúng tôi sẽ sử dụng chữ O và chữ X). Bảng khởi đầu trông giống như Hình 15-
1. Mỗi
Người chơi lần lượt đặt xuống một ô màu mới. Bất kỳ gạch nào của đối thủ là
giữa gạch mới và gạch khác có màu đó được lật. Mục tiêu của trò chơi là
có càng nhiều gạch với màu của bạn càng tốt. Ví dụ, Hình 15-2 là gì
trông giống như nếu người chơi trắng đặt một lát trắng mới trên không gian 5, 6.
255

Trang 270
Ngói đen ở 5, 5 nằm giữa gạch trắng mới và ngói trắng hiện có ở 5, 4.
Gạch đen đó được lật lên và trở thành một gạch trắng mới, làm cho bảng trông giống như
Hình 15-3. Màu đen tạo ra một bước di chuyển tương tự tiếp theo, đặt một ô màu đen lên 4, 6, lật
gạch trắng ở 4, 5. Điều này dẫn đến một bảng trông giống như Hình 15-4.
Các ô theo mọi hướng được lật miễn là chúng ở giữa ô mới của người chơi và
gạch hiện có. Dưới đây trong Hình 15-5, trình phát màu trắng đặt một ô ở 3, 6 và lật các ô màu
đen
theo cả hai hướng (được đánh dấu bằng các dòng.) Kết quả là trong Hình 15-6.
Hình 15-1: Bảng Reversi bắt đầu
có hai gạch trắng và hai gạch đen.
Hình 15-2: Màu trắng đặt một lát mới.
Hình 15-3: Di chuyển của White sẽ
lật một trong những viên gạch màu đen.
Hình 15-4: Đen đặt một ô mới,
mà lật qua một trong những viên gạch trắng.
256

Trang 271
Như bạn có thể thấy, mỗi người chơi có thể nhanh chóng lấy phần lớn các ô trên bảng chỉ trong
một hoặc hai động tác. Người chơi phải luôn luôn thực hiện một động tác bắt được ít nhất một
ô. Các
Trò chơi kết thúc khi người chơi không thể di chuyển hoặc bảng hoàn toàn đầy đủ. Các
người chơi có nhiều gạch màu nhất sẽ thắng.
Chiến lược cơ bản của Reversi là xem xét động thái nào sẽ chuyển qua các ô nhiều nhất.
Nhưng bạn cũng nên cân nhắc việc di chuyển sẽ không để đối thủ của bạn chiếm lại nhiều
gạch sau khi di chuyển của bạn. Đặt một lát ở hai bên hoặc, thậm chí tốt hơn, các góc là tốt bởi

ít có khả năng những ô đó sẽ kết thúc giữa các ô của đối thủ. AI chúng ta
làm cho trò chơi này sẽ chỉ đơn giản là tìm kiếm bất kỳ di chuyển góc mà họ có thể thực
hiện. Nếu không có
di chuyển góc có sẵn, sau đó máy tính sẽ chọn di chuyển yêu cầu nhiều gạch nhất.
Bạn có thể tìm hiểu thêm về Reversi từ Wikipedia: http://en.wikipedia.org/wiki/Reversi
Chạy mẫu
Lưu ý rằng phiên bản Reversi của chúng tôi không sử dụng gạch đen trắng vì văn bản đó
chương trình của chúng tôi tạo ra sẽ luôn luôn có cùng màu. Thay vào đó, chúng tôi sẽ sử dụng
X's và O's để
đại diện cho người chơi và người chơi máy tính.
Chào mừng đến với Reversi!
Bạn có muốn là X hoặc O?
x
Người chơi sẽ đi trước.
12345678
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
Hình 15-5: Bước thứ hai của White
tại 3, 6 sẽ lật hai viên gạch màu đen.
Hình 15-6: Bảng sau bước di chuyển thứ hai của màu trắng.
257
15 - Reversi

Trang 272
1 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
2 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
3 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
4 | | | | X | Ôi | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
5 | | | | Ôi | X | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
6 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
7 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
8 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
Bạn có 2 điểm. Máy tính có 2 điểm.
Nhập di chuyển của bạn hoặc nhập thoát để kết thúc trò chơi hoặc gợi ý để biến
tắt / trên gợi ý.
53
12345678
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
1 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
2 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
3 | | | | | X | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
4 | | | | X | X | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
5 | | | | Ôi | X | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
258

Trang 273
| | | | | | | | |
6 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
7 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
8 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
Bạn có 4 điểm. Máy tính có 1 điểm.
Nhấn Enter để xem di chuyển của máy tính.
... bỏ qua cho ngắn gọn ...
12345678
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
1 | Ôi | Ôi | Ôi | Ôi | Ôi | Ôi | Ôi | Ôi |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
2 | Ôi | Ôi | Ôi | Ôi | Ôi | Ôi | Ôi | Ôi |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
3 | Ôi | Ôi | Ôi | Ôi | Ôi | Ôi | Ôi | Ôi |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
4 | Ôi | Ôi | X | Ôi | Ôi | Ôi | Ôi | Ôi |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
5 | Ôi | Ôi | Ôi | X | Ôi | X | Ôi | X |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
6 | Ôi | X | Ôi | X | X | Ôi | Ôi | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
7 | Ôi | X | X | Ôi | Ôi | Ôi | Ôi | Ôi |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
8 | Ôi | X | X | Ôi | | | X | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
Bạn có 12 điểm. Máy tính có 48 điểm.
Nhập di chuyển của bạn hoặc nhập thoát để kết thúc trò chơi hoặc gợi ý để biến
tắt / trên gợi ý.
86
259
15 - Reversi

Trang 274
X ghi được 15 điểm. O ghi được 46 điểm.
Bạn thua rồi. Máy tính đánh bại bạn 31 điểm.
Bạn có muốn chơi lại không? (có hay không)
Không
Như bạn có thể thấy, AI khá giỏi trong việc đánh bại tôi. Để giúp người chơi ra ngoài, chúng tôi
sẽ
lập trình trò chơi của chúng tôi để cung cấp gợi ý. Nếu người chơi gõ 'gợi ý' khi họ di chuyển, họ
có thể
bật và tắt chế độ gợi ý. Khi chế độ gợi ý được bật, tất cả các khả năng di chuyển của người chơi
có thể thực hiện sẽ hiển thị trên bảng dưới dạng '.' các nhân vật, như thế này:
12345678
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
1 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
2 | | | | . | | . | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
3 | | | | Ôi | Ôi | Ôi | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
4 | | | . | Ôi | Ôi | X | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
5 | | | . | Ôi | Ôi | Ôi | X | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
6 | | | | . | | . | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
7 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
8 | | | | | | | | |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +

Mã nguồn của Reversi


Reversi là một chương trình voi ma mút so với các trò chơi trước đây của chúng tôi. Nó có hơn
300
dòng dài! (Nhưng đừng lo lắng, nhiều dòng trong số này chỉ là nhận xét hoặc dòng trống vào
không gian
ra mã và làm cho nó dễ đọc hơn.) Như mọi khi, bạn không cần phải nhập chương trình
260
Trang 275
trước khi đọc chương này. Và bạn cũng có thể tải chương trình bằng cách vào đây
trang web của cuốn sách tại URL, http://inventwithpython.com/ch CHƯƠNG15 và theo dõi
hướng dẫn trực tuyến.
Cũng như các chương trình khác của chúng tôi, trước tiên chúng tôi sẽ tạo một số chức năng để
thực hiện Reversi-
nhiệm vụ liên quan mà phần chính của chương trình của chúng tôi sẽ gọi. Khoảng 250 dòng đầu
tiên
mã dành cho các hàm trợ giúp này và 50 dòng mã cuối cùng thực hiện Reversi
trò chơi chính nó.
Reversi.py
Mã này có thể được tải xuống từ http://inventwithpython.com/reversi.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. # Reversi
2.
3. nhập ngẫu nhiên
4. hệ thống nhập khẩu
5.
6. def drawBoard (bảng):
7. # Chức năng này in ra bảng mà nó đã
thông qua. Trả về Không có.
8. HÓA = '+ --- + --- + --- + --- + --- + --- + --- + --- +'
9. VLINE = '| | | | | | | | | '
10.
11. in ('1 2 3 4 5 6 7 8')
12. in (HLINE)
13. cho y trong phạm vi (8):
14.
in (VLINE)
15.
in (y + 1, kết thúc = '')
16.
cho x trong phạm vi (8):
17.
in ('|% s'% (bảng [x] [y]), end = '')
18.
in ('|')
19.
in (VLINE)
20.
in (HLINE)
21.
22.
23. def resetBoard (bảng):
24. # Làm trống bảng nó được thông qua, ngoại trừ
vị trí bắt đầu ban đầu.
25. cho x trong phạm vi (8):
26.
cho y trong phạm vi (8):
27.
bảng [x] [y] = ''
28.
29. # Phần bắt đầu:
30. bảng [3] [3] = 'X'
31. bảng [3] [4] = 'O'
32. bảng [4] [3] = 'O'
33. bảng [4] [4] = 'X'
34.
35.
36. def getNewBoard ():
261
15 - Reversi

Trang 276
37. # Tạo một cấu trúc dữ liệu bảng hoàn toàn mới.
38. bảng = []
39. cho i trong phạm vi (8):
40.
board.append ([''] * 8)
41.
42. bảng hoàn trả
43.
44.
45. def isValidMove (bảng, gạch, xstart, ystart):
46. # Trả về Sai nếu người chơi di chuyển trên không gian xstart,
ystart không hợp lệ.
47. # Nếu đó là một nước đi hợp lệ, trả về một danh sách các khoảng trắng
sẽ trở thành người chơi nếu họ di chuyển đến đây.
48. if board [xstart] [ystart]! = '' Hoặc không isOnBoard
(xstart, ystart):
49.
trả về sai
50.
51. board [xstart] [ystart] = gạch # tạm thời đặt
gạch trên bảng.
52.
53. nếu gạch == 'X':
54.
otherTile = 'O'
55. khác:
56.
otherTile = 'X'
57.
58. gạchToFlip = []
59. cho xdirection, ydirection trong [[0, 1], [1, 1], [1, 0],
[1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]:
60.
x, y = xstart, ystart
61.
x + = xdirection # bước đầu tiên theo hướng
62.
y + = ydirection # bước đầu tiên theo hướng
63.
if isOnBoard (x, y) và bảng [x] [y] == otherTile:
64.
# Có một mảnh thuộc về người khác
người chơi bên cạnh mảnh của chúng tôi.
65.
x + = xdirection
66.
y + = hướng dẫn
67.
nếu không phải là OnBoard (x, y):
68.
tiếp tục
69.
while board [x] [y] == otherTile:
70.
x + = xdirection
71.
y + = hướng dẫn
72.
nếu không phải làOnBoard (x, y): # thoát ra khỏi
Vòng lặp while, sau đó tiếp tục vòng lặp for
73.
phá vỡ
74.
nếu không phải là OnBoard (x, y):
75.
tiếp tục
76.
if board [x] [y] == gạch:
77.
# Có những mảnh để lật. Đi vào
hướng ngược lại cho đến khi chúng ta đạt đến không gian ban đầu,
lưu ý tất cả các gạch trên đường đi
78.
trong khi Đúng:
79.
x - = x hướng
80.
y - = hướng dẫn
81.
nếu x == xstart và y == ystart:
262

Trang 277
82.
phá vỡ
83.
gạchToFlip.append ([x, y])
84.
85. board [xstart] [ystart] = '' # khôi phục không gian trống
86. if len (gạchToFlip) == 0: # Nếu không có gạch nào được lật,
đây không phải là một động thái hợp lệ
87.
trả về sai
88. gạch trả lại
89.
90.
91. def isOnBoard (x, y):
92. # Trả về Đúng nếu tọa độ nằm trên
bảng.
93. trả về x> = 0 và x <= 7 và y> = 0 và y <= 7
94.
95.
96. def getBoardWithValidMoves (bảng, gạch):
97. # Trả về một bảng mới với. đánh dấu các bước đi hợp lệ
người chơi nhất định có thể thực hiện.
98. dupeBoard = getBoardCopy (bảng)
99.
100. cho x, y trong getValidMoves (dupeBoard, ô vuông):
101.
dupeBoard [x] [y] = '.'
102. trả lại bản sao
103.
104.
105. def getValidMoves (bảng, gạch):
106. # Trả về danh sách [x, y] danh sách các di chuyển hợp lệ cho
cho người chơi trên bảng cho trước.
107. validMoves = []
108.
109. cho x trong phạm vi (8):
110.
cho y trong phạm vi (8):
111.
if isValidMove (bảng, gạch, x, y)! = Sai:
112.
hợp lệMoves.append ([x, y])
113. trả về các giá trị hợp lệ
114.
115.
116. def getScoreOfBoard (bảng):
117. # Xác định điểm số bằng cách đếm các ô. Trả về một
từ điển với các phím 'X' và 'O'.
118. xscore = 0
119. oscore = 0
120. cho x trong phạm vi (8):
121.
cho y trong phạm vi (8):
122.
if board [x] [y] == 'X':
123.
xscore + = 1
124.
if board [x] [y] == 'O':
125.
oscore + = 1
126. return {'X': xscore, 'O': oscore}
127.
128.
129. def enterPlayerTile ():
130. # Hãy là người chơi loại gạch mà họ muốn trở thành.
263
15 - Reversi

Trang 278
131. # Trả về danh sách với ô của người chơi làm đầu tiên
mục và gạch của máy tính là thứ hai.
132. gạch = ''
133. while not (gạch == 'X' hoặc gạch == 'O'):
134.
in ('Bạn muốn trở thành X hay O?')
135.
gạch = đầu vào (). trên ()
136.
137. # phần tử đầu tiên trong bộ dữ liệu là ô của người chơi,
thứ hai là gạch của máy tính.
138. nếu gạch == 'X':
139.
trở lại ['X', 'O']
140. khác:
141.
trở lại ['O', 'X']
142.
143.
144. def whoGoesFirst ():
145. # Chọn ngẫu nhiên người chơi đi trước.
146. nếu ngẫu nhiên.randint (0, 1) == 0:
147.
trả lại 'máy tính'
148. khác:
149.
trả lại 'người chơi'
150.
151.
152. def playAgain ():
153. # Hàm này trả về True nếu người chơi muốn
chơi lại, nếu không nó trả về Sai.
154. in ('Bạn có muốn chơi lại không? (Có hoặc không)')
155. return return (). Lower (). Startedwith ('y')
156.
157.
158. def makeMove (bảng, gạch, xstart, ystart):
159. # Đặt gạch lên bảng tại xstart, ystart và
lật bất kỳ quân cờ nào của đối thủ.
160. # Trả về Sai nếu đây là một nước đi không hợp lệ, Đúng nếu nó di chuyển
có giá trị
161. gạchToFlip = isValidMove (bảng, gạch, xstart, ystart)
162.
163. nếu gạchToFlip == Sai:
164.
trả về sai
165.
166. bảng [xstart] [ystart] = gạch
167. cho x, y trong gạchToFlip:
168.
bảng [x] [y] = gạch
169. trả lại đúng
170.
171.
172. def getBoardCopy (bảng):
173. # Tạo một bản sao của danh sách bảng và trả lại
bản sao.
174. dupeBoard = getNewBoard ()
175.
176. cho x trong phạm vi (8):
177.
cho y trong phạm vi (8):
178.
dupeBoard [x] [y] = board [x] [y]
264

Trang 279
179.
180. trả lại bản sao
181.
182.
183. def isOnC Corner (x, y):
184. # Trả về Đúng nếu vị trí nằm ở một trong bốn
các góc.
185. return (x == 0 và y == 0) hoặc (x == 7 và y == 0) hoặc
(x == 0 và y == 7) hoặc (x == 7 và y == 7)
186.
187.
188. def getPlayerMove (bảng, playerTile):
189. # Hãy để người chơi gõ di chuyển.
190. # Trả về di chuyển là [x, y] (hoặc trả về chuỗi
'gợi ý' hoặc 'bỏ')
191. DIGITS1TO8 = '1 2 3 4 5 6 7 8'.split ()
192. trong khi Đúng:
193.
in ('Nhập di chuyển của bạn hoặc nhập thoát để kết thúc
trò chơi hoặc gợi ý để tắt / bật gợi ý. ')
194.
di chuyển = đầu vào (). thấp hơn ()
195.
nếu di chuyển == 'thoát':
196.
trở lại 'bỏ'
197.
nếu di chuyển == 'gợi ý':
198.
trở lại 'gợi ý'
199.
200.
if len (di chuyển) == 2 và di chuyển [0] trong DIGITS1TO8 và
di chuyển [1] trong DIGITS1TO8:
201.
x = int (di chuyển [0]) - 1
202.
y = int (di chuyển [1]) - 1
203.
if isValidMove (bảng, playerTile, x, y) ==
Sai:
204.
tiếp tục
205.
khác:
206.
phá vỡ
207.
khác:
208.
print ('Đó không phải là một động thái hợp lệ. Nhập x
chữ số (1-8), sau đó là chữ số y (1-8). ')
209.
in ('Ví dụ: 81 sẽ ở trên cùng bên phải
góc.')
210.
211. trả lại [x, y]
212.
213.
214. def getComputerMove (bảng, computerTile):
215. # Đưa ra một bảng và gạch của máy tính, xác định
ở đâu
216. # di chuyển và trả lại di chuyển đó dưới dạng danh sách [x, y].
217.ossibleMoves = getValidMoves (bảng, computerTile)
218.
219. # chọn ngẫu nhiên thứ tự di chuyển có thể
220. Random.shuffle (có thểMoves)
221.
222. # luôn luôn đi cho một góc nếu có sẵn.
223. cho x, y trong khả năngMoves:
265
15 - Reversi

Trang 280
224.
if isOnC Corner (x, y):
225.
trả lại [x, y]
226.
227. # Đi qua tất cả các động thái có thể và ghi nhớ
di chuyển điểm tốt nhất
228. bestScore = -1
229. cho x, y trong khả năngMoves:
230.
dupeBoard = getBoardCopy (bảng)
231.
makeMove (dupeBoard, computerTile, x, y)
232.
điểm = getScoreOfBoard (dupeBoard) [computerTile]
233.
nếu điểm> bestScore:
234.
bestMove = [x, y]
235.
bestScore = điểm
236. trả lại tốt nhất
237.
238.
239. def showPoints (playerTile, computerTile):
240. # In ra số điểm hiện tại.
241. điểm = getScoreOfBoard (mainBoard)
242. in ('Bạn có% s điểm. Máy tính có% s
điểm. ' % (điểm [playerTile], điểm [computerTile]))
243.
244.
245.
246. in ('Chào mừng đến với Reversi!')
247.
248. trong khi Đúng:
249. # Đặt lại bảng và trò chơi.
250. mainBoard = getNewBoard ()
251. resetBoard (mainBoard)
252. playerTile, computerTile = enterPlayerTile ()
253. showH gợi ý = Sai
254. lượt = whoGoesFirst ()
255. in ('' 'Turn +' sẽ đi trước. ')
256.
257. trong khi Đúng:
258.
nếu bật == 'người chơi':
259.
# Biến lần lượt.
260.
nếu hiển thị:
261.
hợp lệMovesBoard = getBoardWithValidMoves
(mainBoard, playerTile)
262.
drawBoard (hợp lệMovesBoard)
263.
khác:
264.
drawBoard (mainBoard)
265.
showPoints (playerTile, computerTile)
266.
di chuyển = getPlayerMove (mainBoard, playerTile)
267.
nếu di chuyển == 'thoát':
268.
in ('Cảm ơn vì đã chơi!')
269.
sys.exit () # chấm dứt chương trình
270.
di chuyển elif == 'gợi ý':
271.
showH gợi ý = không hiển thị
272.
tiếp tục
273.
khác:
274.
makeMove (mainBoard, playerTile, di chuyển [0],
266

Trang 281
di chuyển [1])
275.
276.
if getValidMoves (mainBoard, computerTile) ==
[]:
277.
phá vỡ
278.
khác:
279.
biến = 'máy tính'
280.
281.
khác:
282.
# Đến lượt máy tính.
283.
drawBoard (mainBoard)
284.
showPoints (playerTile, computerTile)
285.
đầu vào ('Nhấn Enter để xem máy tính
di chuyển. ')
286.
x, y = getComputerMove (mainBoard,
máy tínhTile)
287.
makeMove (mainBoard, computerTile, x, y)
288.
289.
if getValidMoves (mainBoard, playerTile) == []:
290.
phá vỡ
291.
khác:
292.
biến = 'người chơi'
293.
294. # Hiển thị điểm số cuối cùng.
295. drawBoard (mainBoard)
296. điểm = getScoreOfBoard (mainBoard)
297. in ('X ghi% s điểm. O ghi% s điểm.'%
(điểm ['X'], điểm ['O']))
298. nếu điểm [playerTile]> điểm [computerTile]:
299.
in ('Bạn đánh bại máy tính bằng% s điểm!
Xin chúc mừng!' % (điểm [playerTile] - điểm số
[computerTile]))
300. elif points [playerTile] <points [computerTile]:
301.
print ('Bạn thua. Máy tính đánh bại bạn bằng% s
điểm. ' % (điểm [computerTile] - điểm [playerTile]))
302. khác:
303.
in ('Trò chơi là một sự ràng buộc!')
304.
305. nếu không chơiAgain ():
306.
phá vỡ

Mã hoạt động như thế nào


Cấu trúc dữ liệu của Game Board
Trước khi chúng ta nhập mã, chúng ta nên nói về cấu trúc dữ liệu bảng. Dữ liệu này
cấu trúc là một danh sách các danh sách, giống như danh sách trong trò chơi Sonar trước đây của
chúng tôi. Danh sách này được tạo ra
bảng đó [x] [y] sẽ đại diện cho ký tự trên không gian nằm ở vị trí x trên trục X
(đi sang trái / phải) và vị trí y trên trục Y (đi lên / xuống). Nhân vật này có thể là
ký tự dấu cách '' (để biểu thị khoảng trắng), dấu '.' ký tự dấu chấm (để thể hiện một
có thể di chuyển trong chế độ gợi ý) hoặc 'X' hoặc 'O' (để thể hiện ô của người chơi). Bất cứ khi
nào bạn
267
15 - Reversi

Trang 282
xem một tham số có tên bảng, biến tham số đó có nghĩa là danh sách danh sách này
cấu trúc dữ liệu bảng .
Nhập khẩu các mô-đun khác
1. # Reversi
2.
3. nhập ngẫu nhiên
4. hệ thống nhập khẩu
Chúng tôi nhập mô-đun ngẫu nhiên cho các hàm randint () và sự lựa chọn () của nó và
mô-đun sys cho hàm exit () của nó.
Vẽ cấu trúc dữ liệu bảng trên màn hình
6. def drawBoard (bảng):
7. # Chức năng này in ra bảng mà nó đã
thông qua. Trả về Không có.
8. HÓA = '+ --- + --- + --- + --- + --- + --- + --- + --- +'
9. VLINE = '| | | | | | | | | '
10.
11. in ('1 2 3 4 5 6 7 8')
12. in (HLINE)
Hàm drawBoard () sẽ in ra bảng trò chơi hiện tại dựa trên dữ liệu
cấu trúc trong hội đồng quản trị. Lưu ý rằng mỗi ô vuông của bảng trông như thế này:
+ --- +
| |
| X |
| |
+ --- +
Vì chúng ta sẽ in chuỗi bằng đường ngang (và dấu cộng tại
giao điểm) lặp đi lặp lại, chúng ta sẽ lưu trữ nó trong một biến không đổi có tên là HLINE .
Ngoài ra còn có các dòng trên và dưới chính giữa của ô X hoặc O không có gì ngoài '|'
các ký tự (được gọi là các ký tự "ống") với ba khoảng trắng ở giữa. Chúng tôi sẽ lưu trữ chuỗi
này
trong một hằng số có tên là VLINE .
Dòng 11 là lệnh gọi hàm print () đầu tiên được thực hiện và nó in ra các nhãn cho
Trục X dọc theo đỉnh của bảng. Dòng 12 in dòng ngang trên cùng của bảng.
13. cho y trong phạm vi (8):
268

Trang 283
14.
in (VLINE)
15.
in (y + 1, kết thúc = '')
16.
cho x trong phạm vi (8):
17.
in ('|% s'% (bảng [x] [y]), end = '')
18.
in ('|')
19.
in (VLINE)
20.
in (HLINE)
In từng hàng không gian trên bảng khá lặp đi lặp lại, vì vậy chúng ta có thể sử dụng một vòng lặp ở
đây. Chúng tôi
sẽ lặp tám lần, một lần cho mỗi hàng. Dòng 15 in nhãn cho trục Y ở bên trái
bên của bảng, và có một dấu phẩy ở cuối của bảng để ngăn chặn một dòng mới. Điều này là để
chúng ta có thể
có một vòng lặp khác (một lần nữa lặp lại tám lần, một lần cho mỗi không gian) in ra mỗi không
gian
(cùng với ký tự 'X' , 'O' hoặc '' cho không gian đó tùy thuộc vào nội dung được lưu trữ trong
bảng.
Lệnh gọi hàm print () bên trong vòng lặp bên trong cũng có dấu phẩy ở cuối của nó,
có nghĩa là một ký tự không gian được in thay vì một ký tự dòng mới. Điều này tạo ra
không gian thứ hai trong chuỗi không gian-không gian-gạch-không gian mà chúng tôi in ra, lặp
đi lặp lại trong tám lần
lần Điều đó sẽ tạo ra một dòng duy nhất trên màn hình giống như '| X | X | X | X
| X | X | X | X ' (nghĩa là, nếu mỗi giá trị của bảng [x] [y] là ' X ' ). Sau
vòng lặp bên trong được thực hiện, hàm print () gọi trên dòng 18 in ra '|' cuối cùng tính cách
cùng với một dòng mới (vì nó không kết thúc bằng dấu phẩy).
(Cuộc gọi in () buộc chúng ta phải luôn in một ký tự dòng mới hoặc khoảng trắng ở cuối
tất cả mọi thứ chúng tôi in. Nếu chúng ta không muốn nhân vật cuối cùng này, thì chúng ta luôn
có thể sử dụng
Hàm sys.stdout.write () , có một tham số chuỗi đơn mà nó in ra. Là
chắc chắn nhập sys trước khi gọi hàm này.)
Mã bên trong vòng lặp for bên ngoài bắt đầu trên dòng 13 in ra toàn bộ một hàng của
hội đồng quản trị như thế này:
| | | | | | | | |
| X | X | X | X | X | X | X | X |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
Khi được in ra tám lần, nó tạo thành toàn bộ bảng (tất nhiên, một số khoảng trắng trên
bảng sẽ có 'O' hoặc '' thay vì 'X' .:
| | | | | | | | |
| X | X | X | X | X | X | X | X |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
| X | X | X | X | X | X | X | X |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
269
15 - Reversi

Trang 284
| | | | | | | | |
| X | X | X | X | X | X | X | X |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
| X | X | X | X | X | X | X | X |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
| X | X | X | X | X | X | X | X |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
| X | X | X | X | X | X | X | X |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
| X | X | X | X | X | X | X | X |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
| | | | | | | | |
| X | X | X | X | X | X | X | X |
| | | | | | | | |
+ --- + --- + --- + --- + --- + --- + --- + --- +
Đặt lại bảng trò chơi
Một điều quan trọng cần nhớ là tọa độ mà chúng tôi in ra cho người chơi là
từ 1 đến 8, nhưng các chỉ mục trong cấu trúc dữ liệu bảng là từ 0 đến 7.
23. def resetBoard (bảng):
24. # Làm trống bảng nó được thông qua, ngoại trừ
vị trí bắt đầu ban đầu.
25. cho x trong phạm vi (8):
26.
cho y trong phạm vi (8):
27.
bảng [x] [y] = ''
Ở đây chúng tôi sử dụng một vòng lặp bên trong một vòng lặp để đặt cấu trúc dữ liệu bảng thành
tất cả các khoảng trống. Chúng tôi sẽ
gọi hàm resetBoard () bất cứ khi nào chúng tôi bắt đầu một trò chơi mới và muốn xóa
gạch từ một trò chơi trước.
Thiết lập các mảnh bắt đầu
29. # Phần bắt đầu:
30. bảng [3] [3] = 'X'
31. bảng [3] [4] = 'O'
32. bảng [4] [3] = 'O'
33. bảng [4] [4] = 'X'
270

Trang 285
Khi chúng tôi bắt đầu một trò chơi Reversi mới, sẽ không đủ để có một bảng hoàn toàn trống.
Lúc đầu, mỗi người chơi có hai ô được đặt ở chính giữa, vì vậy chúng tôi
cũng sẽ phải thiết lập những cái đó
Chúng tôi không phải trả về biến bảng , vì bảng là tham chiếu đến danh sách.
Ngay cả khi chúng tôi thực hiện các thay đổi bên trong phạm vi của chức năng cục bộ, những
thay đổi này vẫn xảy ra trong
phạm vi toàn cầu vào danh sách đã được thông qua như là một đối số. (Hãy nhớ rằng, đây là
danh sách một cách
các biến khác với các biến không có trong danh sách.)
Tạo cấu trúc dữ liệu bảng trò chơi mới
36. def getNewBoard ():
37. # Tạo một cấu trúc dữ liệu bảng hoàn toàn mới.
38. bảng = []
39. cho i trong phạm vi (8):
40.
board.append ([''] * 8)
41.
42. bảng hoàn trả
Hàm getNewBoard () tạo cấu trúc dữ liệu bảng mới và trả về nó. Hàng
38 tạo danh sách bên ngoài và gán tham chiếu cho danh sách này lên bảng . Dòng 40 tạo
danh sách bên trong sử dụng nhân rộng danh sách. ( [''] * 8 giống với ['', '', '', '',
'', '', '', ''] nhưng ít gõ hơn.) Vòng lặp for ở đây chạy dòng 40 tám
lần để tạo tám danh sách bên trong. Các không gian đại diện cho một bảng trò chơi hoàn toàn
trống rỗng.
Kiểm tra xem Di chuyển có hợp lệ không
45. def isValidMove (bảng, gạch, xstart, ystart):
46. # Trả về Sai nếu người chơi di chuyển trên không gian xstart,
ystart không hợp lệ.
47. # Nếu đó là một nước đi hợp lệ, trả về một danh sách các khoảng trắng
điều đó sẽ trở thành của người chơi nếu họ di chuyển đến đây.
48. if board [xstart] [ystart]! = '' Hoặc không isOnBoard
(xstart, ystart):
49.
trả về sai
50.
51. board [xstart] [ystart] = gạch # tạm thời đặt
gạch trên bảng.
52.
53. nếu gạch == 'X':
54.
otherTile = 'O'
55. khác:
56.
otherTile = 'X'
57.
58. gạchToFlip = []
isValidMove () là một trong những hàm phức tạp hơn. Đưa ra một dữ liệu bảng
271
15 - Reversi

Trang 285
cấu trúc, ô của người chơi và tọa độ XY cho di chuyển của người chơi, chức năng này nên
Trả về Đúng nếu quy tắc trò chơi Reversi cho phép di chuyển và Sai nếu không.
Kiểm tra đơn giản nhất chúng ta có thể làm để loại bỏ di chuyển là xem liệu tọa độ XY có bật
không
bảng trò chơi hoặc nếu không gian tại XY không trống. Đây là những gì câu lệnh if trên dòng 48
kiểm tra isOnBoard () là một hàm chúng ta sẽ viết để đảm bảo cả X và Y
tọa độ nằm trong khoảng từ 0 đến 7 .
Đối với mục đích của chức năng này, chúng tôi sẽ tiếp tục và đánh dấu tọa độ XY được chỉ
đến bằng xstart và ystart với ô của người chơi. Chúng tôi đặt nơi này trên bảng trở lại
không gian trước khi chúng ta rời khỏi chức năng này.
Ngói của người chơi đã được chuyển cho chúng tôi, nhưng chúng tôi sẽ cần có thể xác định
người khác
gạch của người chơi. Nếu ô của người chơi là 'X' thì rõ ràng ô của người chơi khác là 'O' . Và nó

cùng một cách khác
Cuối cùng, nếu tọa độ XY đã cho kết thúc ở vị trí hợp lệ, chúng tôi sẽ trả về danh sách tất cả
gạch của đối thủ sẽ bị lật bởi di chuyển này.
59. cho xdirection, ydirection trong [[0, 1], [1, 1], [1,
0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]:
Các cho vòng lặp thông qua một danh sách liệt kê mà đại diện cho hướng bạn có thể di chuyển
trên
bảng trò chơi. Bảng trò chơi là một hệ tọa độ Descartes với X và Y
phương hướng. Có tám hướng bạn có thể di chuyển: lên, xuống, trái, phải và bốn hướng
hướng chéo. Chúng tôi sẽ di chuyển xung quanh bảng theo hướng bằng cách thêm giá trị đầu tiên
trong danh sách hai mục cho tọa độ X của chúng tôi và giá trị thứ hai cho tọa độ Y của chúng tôi.
Vì tọa độ X tăng khi bạn sang phải, bạn có thể "di chuyển" sang phải bằng cách
thêm 1 vào tọa độ X. Di chuyển sang trái là ngược lại: bạn sẽ trừ 1 (hoặc
thêm -1 ) từ tọa độ X. Chúng ta có thể di chuyển lên, xuống, trái và phải bằng cách thêm hoặc
trừ đi chỉ một tọa độ tại một thời điểm. Nhưng để di chuyển theo đường chéo, chúng ta cần thêm
hoặc
trừ cho cả hai tọa độ. Ví dụ: thêm 1 vào tọa độ X để di chuyển sang phải và
thêm -1 vào tọa độ Y để di chuyển lên sẽ dẫn đến việc di chuyển sang đường chéo bên phải
phương hướng.
Kiểm tra từng hướng trong tám hướng
Dưới đây là sơ đồ để dễ nhớ danh sách hai mục nào đại diện cho
phương hướng:
272

Trang 288
Hình 15-7: Mỗi danh sách hai mục đại diện cho một trong tám hướng.
59. cho xdirection, ydirection trong [[0, 1], [1, 1], [1,
0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]:
60.
x, y = xstart, ystart
61.
x + = xdirection # bước đầu tiên theo hướng
62.
y + = ydirection # bước đầu tiên theo hướng
Dòng 60 đặt biến x và y có cùng giá trị với xstart và ystart ,
tương ứng. Chúng tôi sẽ thay đổi x và y để "di chuyển" theo hướng xdirection và
ydirection ra lệnh. xstart và ystart sẽ giữ nguyên để chúng ta có thể nhớ
không gian ban đầu chúng tôi dự định kiểm tra. (Hãy nhớ rằng, chúng ta cần đặt lại nơi này
một ký tự khoảng trắng, vì vậy chúng ta không nên ghi đè lên các giá trị trong chúng.)
Chúng tôi thực hiện bước đầu tiên theo hướng là phần đầu tiên của thuật toán của chúng tôi.
63.
if isOnBoard (x, y) và bảng [x] [y] == otherTile:
64.
# Có một mảnh thuộc về người khác
người chơi bên cạnh mảnh của chúng tôi.
65.
x + = xdirection
66.
y + = hướng dẫn
67.
nếu không phải là OnBoard (x, y):
68.
tiếp tục
Hãy nhớ rằng, để điều này là một động thái hợp lệ, bước đầu tiên theo hướng này phải là 1)
trên bảng và 2) phải bị chiếm bởi ô của người chơi khác. Nếu không thì không
cơ hội để lật bất kỳ gạch của đối thủ. Trong trường hợp đó, nếu tuyên bố trên dòng 63 là
không thật và thực hiện quay ngược lại cho tuyên bố cho sự chỉ đạo tiếp theo.
273
15 - Reversi

Trang 288
Nhưng nếu không gian đầu tiên có ô của người chơi khác, thì chúng ta nên tiếp tục
hướng đó cho đến khi chúng ta đạt được trên gạch của chính mình. Nếu chúng ta rời khỏi bảng,
thì chúng ta
nên tiếp tục quay lại câu lệnh for để thử hướng tiếp theo.
69.
while board [x] [y] == otherTile:
70.
x + = xdirection
71.
y + = hướng dẫn
72.
nếu không phải làOnBoard (x, y): # thoát ra khỏi
Vòng lặp while, sau đó tiếp tục vòng lặp for
73.
phá vỡ
74.
nếu không phải là OnBoard (x, y):
75.
tiếp tục
Các trong khi vòng lặp trên đường dây 69 Đảm bảo rằng x và y tiếp tục đi theo hướng hiện như
miễn là chúng ta tiếp tục nhìn thấy một vệt gạch của người chơi khác. Nếu x và y rời khỏi bảng,
chúng tôi thoát ra khỏi vòng lặp for và luồng thực thi chuyển sang dòng 74. Những gì chúng tôi
thực sự
muốn làm là thoát ra khỏi trong khi vòng lặp nhưng vẫn tiếp tục trong cho vòng lặp. Nhưng nếu
chúng ta đặt một
tiếp tục tuyên bố trên dòng 73, mà sẽ chỉ tiếp tục đến khi vòng lặp trên dòng 69.
Thay vào đó, chúng tôi kiểm tra lại không phải là OnBoard (x, y) trên dòng 74 và sau đó tiếp tục
từ
ở đó, đi đến hướng tiếp theo trong câu lệnh for . Điều quan trọng là phải biết rằng
phá vỡ và tiếp tục sẽ chỉ phá vỡ hoặc tiếp tục trong vòng lặp mà chúng được gọi từ và
không phải là một vòng lặp bên ngoài có chứa vòng lặp mà chúng được gọi từ.
Tìm ra nếu có mảnh để lật
76.
if board [x] [y] == gạch:
77.
# Có những mảnh để lật. Đi vào
hướng ngược lại cho đến khi chúng ta đạt đến không gian ban đầu,
lưu ý tất cả các gạch trên đường đi
78.
trong khi Đúng:
79.
x - = x hướng
80.
y - = hướng dẫn
81.
nếu x == xstart và y == ystart:
82.
phá vỡ
83.
gạchToFlip.append ([x, y])
Nếu trong khi vòng lặp trên đường dây 69 dừng lại vòng lặp vì điều kiện là False , sau đó
chúng tôi đã tìm thấy một khoảng trống trên bảng chứa gạch của riêng chúng tôi hoặc một
khoảng trống. Dòng 76
kiểm tra nếu không gian này trên bảng giữ một trong những viên gạch của chúng tôi. Nếu có, thì
chúng tôi đã tìm thấy một
di chuyển hợp lệ. Chúng tôi bắt đầu một mới trong khi vòng lặp, lần này trừ x và y để di chuyển
chúng trong
hướng ngược lại ban đầu họ đang đi. Chúng tôi lưu ý mỗi không gian giữa các gạch của chúng
tôi trên
bảng bằng cách nối thêm không gian vào danh sách gạchToFlip .
Chúng tôi thoát ra khỏi trong khi vòng lặp lần x và y đã trở lại vị trí ban đầu
(vẫn được lưu trữ trong xstart và ystart ).
274

Trang 288
85. board [xstart] [ystart] = '' # khôi phục không gian trống
86. if len (gạchToFlip) == 0: # Nếu không có gạch nào được lật,
đây không phải là một động thái hợp lệ
87.
trả về sai
88. gạch trả lại
Chúng tôi thực hiện kiểm tra này theo tất cả tám hướng và sau đó là danh sách gạchToFlip
sẽ chứa tọa độ XY tất cả các ô của đối thủ sẽ bị lật nếu
người chơi di chuyển trên xstart , ystart . Hãy nhớ rằng, hàm isValidMove () chỉ
kiểm tra xem liệu di chuyển ban đầu có hợp lệ không, nó không thực sự thay đổi cấu trúc dữ liệu
của hội đồng quản trị trò chơi.
Nếu không ai trong số tám hướng kết thúc lật ít nhất một trong các ô của đối thủ, thì
gạchToFlip sẽ là một danh sách trống và di chuyển này sẽ không hợp lệ. Trong trường hợp đó,
isValidMove () sẽ trả về Sai . Nếu không, chúng ta nên trả lại gạchToFlip .
Kiểm tra tọa độ hợp lệ
91. def isOnBoard (x, y):
92. # Trả về Đúng nếu tọa độ nằm trên
bảng.
93. trả về x> = 0 và x <= 7 và y> = 0 và y <= 7
isOnBoard () là một hàm được gọi từ isValidMove () và chỉ là viết tắt cho
biểu thức Boolean khá phức tạp trả về True nếu cả x và y đều ở trong
từ 0 đến 7 . Hàm này cho phép chúng tôi đảm bảo rằng các tọa độ thực sự nằm trên
hội đồng quản trị trò chơi.
Nhận danh sách với tất cả các dịch chuyển hợp lệ
96. def getBoardWithValidMoves (bảng, gạch):
97. # Trả về một bảng mới với. đánh dấu các bước đi hợp lệ
người chơi nhất định có thể thực hiện.
98. dupeBoard = getBoardCopy (bảng)
99.
100. cho x, y trong getValidMoves (dupeBoard, ô vuông):
101.
dupeBoard [x] [y] = '.'
102. trả lại bản sao
getBoardWithValidMoves () được sử dụng để trả về cấu trúc dữ liệu của bảng trò chơi có
'.' ký tự cho tất cả các di chuyển hợp lệ trên bảng. Điều này được sử dụng bởi chế độ gợi ý để
hiển thị
cho người chơi một bảng với tất cả các di chuyển có thể được đánh dấu trên đó.
Lưu ý rằng chức năng này tạo ra cấu trúc dữ liệu bảng trò chơi trùng lặp thay vì
275
15 - Reversi

Trang 290
Sửa đổi cái được truyền cho nó bởi tham số bảng . Cuộc gọi 100 cuộc gọi
getValidMoves () , trả về danh sách tọa độ xy với tất cả các bước di chuyển hợp pháp
người chơi có thể làm. Bản sao bảng sau đó được đánh dấu bằng một khoảng thời gian trong các
không gian đó. Làm sao
công việc getValidMoves () được mô tả tiếp theo.
105. def getValidMoves (bảng, gạch):
106. # Trả về danh sách [x, y] danh sách các di chuyển hợp lệ cho
người chơi đã cho trên bảng đã cho.
107. validMoves = []
108.
109. cho x trong phạm vi (8):
110.
cho y trong phạm vi (8):
111.
if isValidMove (bảng, gạch, x, y)! = Sai:
112.
hợp lệMoves.append ([x, y])
113. trả về các giá trị hợp lệ
Hàm getValidMoves () trả về danh sách các danh sách hai mục chứa XY
tọa độ cho tất cả các di chuyển hợp lệ cho trình phát của lát, được cung cấp cấu trúc dữ liệu của
bảng trò chơi cụ thể
trong hội đồng quản trị .
Hàm này sử dụng hai vòng để kiểm tra mọi tọa độ XY (tất cả sáu mươi bốn trong số chúng)
bằng cách gọi isValidMove () trên không gian đó và kiểm tra xem nó trả về Sai hay danh sách
di chuyển có thể (trong trường hợp đó là một di chuyển hợp lệ). Mỗi tọa độ XY hợp lệ được
thêm vào
danh sách, hợp lệ .
Các bool () Chức năng
Hãy nhớ cách bạn có thể sử dụng các hàm int () và str () để lấy số nguyên và
chuỗi giá trị của các loại dữ liệu khác? Ví dụ: str (42) sẽ trả về chuỗi '42' và
int ('100') sẽ trả về số nguyên 100 .
Có một hàm tương tự cho kiểu dữ liệu Boolean, bool () . Hầu hết các loại dữ liệu khác
có một giá trị được coi là giá trị Sai cho loại dữ liệu đó và mọi giá trị khác
được coi là Đúng . Số nguyên 0 , số dấu phẩy động 0,0 , chuỗi rỗng,
danh sách trống và từ điển trống đều được coi là Sai khi được sử dụng làm
điều kiện cho một câu lệnh if hoặc loop. Tất cả các giá trị khác là Đúng . Hãy thử vào
sau vào vỏ tương tác:
>>> bool (0)
Sai
>>> bool (0,0)
Sai
>>> bool ('')
Sai
>>> bool ([])
276

Trang 29
Sai
>>> bool ({})
Sai
>>> bool (1)
Thật
>>> bool ('Xin chào')
Thật
>>> bool ([1, 2, 3, 4, 5])
Thật
>>> bool ({'spam': 'phô mai', 'fizz': 'buzz'})
Thật
>>>
Bất cứ khi nào bạn có một điều kiện, hãy tưởng tượng rằng toàn bộ điều kiện được đặt trong một
cuộc gọi
để bool () làm tham số. Điều kiện được tự động hiểu là giá trị Boolean.
Điều này tương tự như cách print () có thể được truyền các giá trị không phải là chuỗi và sẽ tự
động
giải thích chúng như các chuỗi khi chúng in.
Đây là lý do tại sao điều kiện trên dòng 111 hoạt động chính xác. Cuộc gọi đến isValidMove ()
Hàm trả về giá trị Boolean Sai hoặc danh sách không trống. Nếu bạn tưởng tượng rằng
toàn bộ điều kiện được đặt bên trong một lệnh gọi đến bool () , sau đó Sai trở thành bool
(Sai) (trong đó, tất nhiên, đánh giá thành Sai ). Và một danh sách không trống được đặt làm
tham số cho bool () sẽ trả về True . Đây là lý do tại sao giá trị trả về của isValidMove ()
có thể được sử dụng như một điều kiện.
Lấy điểm của Ban trò chơi
116. def getScoreOfBoard (bảng):
117. # Xác định điểm số bằng cách đếm các ô. Trả về
một từ điển với các phím 'X' và 'O'.
118. xscore = 0
119. oscore = 0
120. cho x trong phạm vi (8):
121.
cho y trong phạm vi (8):
122.
if board [x] [y] == 'X':
123.
xscore + = 1
124.
if board [x] [y] == 'O':
125.
oscore + = 1
126. return {'X': xscore, 'O': oscore}
Các getScoreOfBoard () sử dụng chức năng lồng nhau cho vòng để rà soát tất cả 64 không gian
trên
bảng (8 hàng nhân 8 cột trên mỗi hàng là 64 dấu cách) và xem lát nào (nếu có) được bật
họ Đối với mỗi ô 'X' , mã tăng xscore . Đối với mỗi ô 'O' , mã
tăng oscore .
Lưu ý rằng chức năng này không trả về danh sách hai mục của điểm số. Một danh sách hai mục
277
15 - Reversi

Trang 292
có thể hơi khó hiểu, vì bạn có thể quên mục nào dành cho X và mục nào là
cho O. Thay vào đó, hàm trả về một từ điển có khóa 'X' và 'O' có giá trị là
điểm số.
Lấy lựa chọn gạch lát nền
129. def enterPlayerTile ():
130. # Hãy là người chơi loại gạch mà họ muốn trở thành.
131. # Trả về danh sách với ô của người chơi làm đầu tiên
mục và gạch của máy tính là thứ hai.
132. gạch = ''
133. while not (gạch == 'X' hoặc gạch == 'O'):
134.
in ('Bạn muốn trở thành X hay O?')
135.
gạch = đầu vào (). trên ()
Hàm này hỏi người chơi họ muốn trở thành ô nào, 'X' hoặc 'O' . các cho
vòng lặp sẽ tiếp tục lặp cho đến khi người chơi gõ vào 'X' hoặc 'O' .
137. # yếu tố đầu tiên trong bộ dữ liệu là của người chơi
ngói, thứ hai là ngói của máy tính.
138. nếu gạch == 'X':
139.
trở lại ['X', 'O']
140. khác:
141.
trở lại ['O', 'X']
Hàm enterPlayerTile () sau đó trả về danh sách hai mục, trong đó của người chơi
lựa chọn gạch là mục đầu tiên và gạch của máy tính là mục thứ hai. Chúng tôi sử dụng một danh
sách ở đây thay thế
của một từ điển để câu lệnh gán gọi hàm này có thể sử dụng nhiều
bài tập lừa. (Xem dòng 252.)
Xác định ai đi trước
144. def whoGoesFirst ():
145. # Chọn ngẫu nhiên người chơi đi trước.
146. nếu ngẫu nhiên.randint (0, 1) == 0:
147.
trả lại 'máy tính'
148. khác:
149.
trả lại 'người chơi'
Hàm whoGoesFirst () chọn ngẫu nhiên người đi trước và trả về
chuỗi 'máy tính' hoặc chuỗi 'máy nghe nhạc' .
278

Trang 293
Yêu cầu người chơi chơi lại
152. def playAgain ():
153. # Hàm này trả về True nếu người chơi muốn
chơi lại, nếu không nó trả về Sai.
154. in ('Bạn có muốn chơi lại không? (Có hoặc không)')
155. return return (). Lower (). Startedwith ('y')
Chúng tôi đã sử dụng playAgain () trong các trò chơi trước đây của chúng tôi. Nếu người chơi gõ
vào
một cái gì đó bắt đầu bằng 'y' , sau đó hàm trả về True . Nếu không thì chức năng
trả về Sai .
Đặt một lát trên bảng trò chơi
158. def makeMove (bảng, gạch, xstart, ystart):
159. # Đặt gạch lên bảng tại xstart, ystart và
lật bất kỳ quân cờ nào của đối thủ.
160. # Trả về Sai nếu đây là một động thái không hợp lệ, Đúng nếu
Nó hợp lệ.
161. gạchToFlip = isValidMove (bảng, gạch, xstart,
bắt đầu)
makeMove () là hàm chúng ta gọi khi chúng ta muốn đặt một ô trên bảng và lật
các gạch khác theo quy tắc của Reversi. Chức năng này sửa đổi dữ liệu bảng
cấu trúc được truyền dưới dạng tham số trực tiếp. Thay đổi được thực hiện cho biến bảng
(bởi vì nó là một danh sách) cũng sẽ được thực hiện cho phạm vi toàn cầu. Hầu hết các công việc
được thực hiện bởi
isValidMove () , trả về danh sách tọa độ XY (trong danh sách hai mục) của các ô xếp
cần phải được lật (Hãy nhớ rằng, nếu các đối số xstart và ystart trỏ đến một
di chuyển không hợp lệ, sau đó isValidMove () sẽ trả về giá trị Boolean Sai .)
163. nếu gạchToFlip == Sai:
164.
trả về sai
165.
166. bảng [xstart] [ystart] = gạch
167. cho x, y trong gạchToFlip:
168.
bảng [x] [y] = gạch
169. trả lại đúng
Nếu giá trị trả về của isValidMove () là Sai , thì makeMove () cũng sẽ
Trả về Sai .
Nếu không, isValidMove () sẽ trả về một danh sách các khoảng trắng trên bảng để đặt
xuống các ô của chúng tôi (chuỗi 'X' hoặc 'O' trong ô xếp). Dòng 166 đặt không gian mà người
chơi có
di chuyển trên và vòng lặp for sau đó thiết lập tất cả các ô trong gạchToFlip .
279
15 - Reversi

Trang 294
Sao chép cấu trúc dữ liệu bảng
172. def getBoardCopy (bảng):
173. # Tạo một bản sao của danh sách bảng và trả lại
bản sao.
174. dupeBoard = getNewBoard ()
175.
176. cho x trong phạm vi (8):
177.
cho y trong phạm vi (8):
178.
dupeBoard [x] [y] = board [x] [y]
179.
180. trả lại bản sao
getBoardCopy () khác với getNewBoard () . getNewBoad () sẽ tạo
một cấu trúc dữ liệu bảng trò chơi mới chỉ có không gian trống. getBoardCopy () sẽ
tạo cấu trúc dữ liệu bảng trò chơi mới, nhưng sau đó sao chép tất cả các phần trong bảng
tham số. Chức năng này được AI của chúng tôi sử dụng để có một bảng trò chơi mà nó có thể
thay đổi xung quanh
mà không thay đổi bảng trò chơi thực sự. Điều này giống như cách bạn có thể tưởng tượng việc
di chuyển trên
một bản sao của bảng trong tâm trí của bạn, nhưng không thực sự đặt các mảnh xuống bảng thực
sự.
Một cuộc gọi đến getNewBoard () xử lý việc nhận cấu trúc dữ liệu bảng trò chơi mới. Sau đó
lồng cho các vòng lặp sao chép từng ô trong số 64 ô từ bảng sang bảng trùng lặp của chúng tôi,
lừa đảo .
Xác định nếu một không gian nằm trên một góc
183. def isOnC Corner (x, y):
184. # Trả về Đúng nếu vị trí nằm ở một trong bốn
các góc.
185. return (x == 0 và y == 0) hoặc (x == 7 và y == 0) hoặc
(x == 0 và y == 7) hoặc (x == 7 và y == 7)
Hàm này rất giống với isOnBoard () . Bởi vì tất cả các bảng Reversi có kích thước 8 x 8,
chúng ta chỉ cần các tọa độ XY được chuyển đến chức năng này, không phải dữ liệu của bảng trò
chơi
cấu trúc chính nó. Hàm này trả về True nếu tọa độ nằm trên (0,0), (7,0), (0,7)
hoặc (7,7). Nếu không thì isOnCorner () trả về Sai .
Bắt di chuyển
188. def getPlayerMove (bảng, playerTile):
189. # Hãy để người chơi gõ di chuyển.
190. # Trả về di chuyển là [x, y] (hoặc trả về chuỗi
'gợi ý' hoặc 'bỏ')
191. DIGITS1TO8 = '1 2 3 4 5 6 7 8'.split ()
280

Trang 295
Hàm getPlayerMove () được gọi để cho phép người chơi nhập vào tọa độ của
di chuyển tiếp theo của họ (và kiểm tra xem di chuyển có hợp lệ không). Người chơi cũng có thể
nhập 'gợi ý' để
bật chế độ gợi ý (nếu nó tắt) hoặc tắt (nếu nó đang bật). Người chơi cũng có thể nhập 'thoát' để
Thoát khỏi trò chơi.
Các DIGITS1TO8 biến liên tục là danh sách [ '1', '2', '3', '4', '5',
'6', '7', '8'] . Chúng tôi tạo ra hằng số này bởi vì nó là loại DIGITS1TO8 dễ dàng hơn so với
toàn bộ danh sách.
192. trong khi Đúng:
193.
in ('Nhập di chuyển của bạn hoặc nhập thoát để kết thúc
trò chơi hoặc gợi ý để tắt / bật gợi ý. ')
194.
di chuyển = đầu vào (). thấp hơn ()
195.
nếu di chuyển == 'thoát':
196.
trở lại 'bỏ'
197.
nếu di chuyển == 'gợi ý':
198.
trở lại 'gợi ý'
Các trong khi vòng lặp sẽ tiếp tục lặp cho đến khi người chơi đã gõ trong một động thái hợp
lệ. Đầu tiên chúng tôi
kiểm tra xem người chơi muốn thoát hoặc chuyển chế độ gợi ý và trả lại chuỗi 'thoát' hoặc
'gợi ý' . Chúng tôi sử dụng phương thức low () trên chuỗi được trả về bởi input () để trình phát
có thể gõ 'HINTS' hoặc 'Thoát' nhưng vẫn có lệnh được hiểu bởi trò chơi của chúng tôi.
Mã gọi getPlayerMove () sẽ xử lý những việc cần làm nếu người chơi muốn
thoát hoặc chuyển chế độ gợi ý.
200.
if len (di chuyển) == 2 và di chuyển [0] trong DIGITS1TO8 và
di chuyển [1] trong DIGITS1TO8:
201.
x = int (di chuyển [0]) - 1
202.
y = int (di chuyển [1]) - 1
203.
if isValidMove (bảng, playerTile, x, y) ==
Sai:
204.
tiếp tục
205.
khác:
206.
phá vỡ
Trò chơi của chúng tôi hy vọng rằng người chơi sẽ gõ vào tọa độ XY của họ
di chuyển như hai số mà không có bất cứ điều gì ở giữa chúng. Các nếu tuyên bố kiểm tra đầu
tiên
kích thước của chuỗi mà người chơi đã nhập là 2 . Sau đó, nếu tuyên bố cũng kiểm tra
cả hai đều di chuyển [0] (ký tự đầu tiên trong chuỗi) và di chuyển [1] (ký tự thứ hai
trong chuỗi) là các chuỗi tồn tại trong DIGITS1TO8 , mà chúng tôi đã xác định ở đầu
chức năng.
Hãy nhớ rằng cấu trúc dữ liệu bảng trò chơi của chúng tôi có các chỉ mục từ 0 đến 7, không phải
từ 1 đến 8. Chúng tôi
hiển thị 1 đến 8 khi chúng tôi in bảng bằng drawBoard () vì mọi người đã quen với
281
15 - Reversi

Trang 296
các số bắt đầu từ 1 thay vì 0. Vì vậy, khi chúng tôi chuyển đổi các chuỗi di chuyển [0] và
di chuyển [1] sang số nguyên, chúng tôi cũng trừ 1 .
Ngay cả khi người chơi đã nhập đúng, chúng ta vẫn cần kiểm tra xem di chuyển đó có phải là
được cho phép bởi các quy tắc của Reversi. Chúng tôi làm điều này bằng cách gọi isValidMove
() , vượt qua
cấu trúc dữ liệu bảng trò chơi, ô của người chơi và tọa độ XY của di chuyển. Nếu
isValidMove () trả về Sai , sau đó chúng tôi thực hiện câu lệnh continue để
chảy thực hiện quay ngược lại để đầu khi vòng lặp và yêu cầu các cầu thủ cho
di chuyển lần nữa
Nếu isValidMove () không trả về Sai , thì chúng tôi biết người chơi đã nhập hợp lệ
di chuyển và chúng ta nên thoát ra khỏi trong khi vòng lặp.
207.
khác:
208.
print ('Đó không phải là một động thái hợp lệ. Nhập x
chữ số (1-8), sau đó là chữ số y (1-8). ')
209.
in ('Ví dụ: 81 sẽ ở trên cùng bên phải
góc.')
Nếu điều kiện if của câu lệnh if trên dòng 200 là Sai , thì người chơi đã không gõ vào
di chuyển hợp lệ. Chúng ta nên hiển thị một thông báo hướng dẫn họ cách nhập các di chuyển
mà chúng ta
Chương trình Reversi có thể hiểu. Sau đó, việc thực hiện di chuyển trở lại trong khi
câu lệnh trên dòng 192 vì dòng 209 không chỉ là dòng cuối cùng trong khối khác, mà còn là dòng
dòng cuối cùng trong khối while.
211. trả lại [x, y]
Cuối cùng, getPlayerMove () trả về danh sách hai mục với tọa độ XY của
di chuyển hợp lệ của người chơi.
Di chuyển máy tính
214. def getComputerMove (bảng, computerTile):
215. # Đưa ra một bảng và gạch của máy tính, xác định
ở đâu
216. # di chuyển và trả lại di chuyển đó dưới dạng danh sách [x, y].
217.ossibleMoves = getValidMoves (bảng, computerTile)
getComputerMove () và là nơi Reversi AI của chúng tôi được triển khai. Các
Hàm getValidMoves () rất hữu ích cho AI của chúng ta. Thông thường chúng tôi sử dụng kết quả
từ
getValidMoves () cho gợi ý di chuyển. Chế độ gợi ý sẽ in '.' nhân vật thời kỳ trên
bảng để cho người chơi thấy tất cả các động tác tiềm năng mà họ có thể thực hiện. Nhưng nếu
chúng ta gọi
getValidMoves () với ô AI của máy tính (trong computerTile), chúng ta có thể nhận được tất cả
282

Trang 297
di chuyển có thể mà máy tính có thể thực hiện. Chúng tôi sẽ chọn di chuyển tốt nhất từ danh sách
này.
219. # chọn ngẫu nhiên thứ tự di chuyển có thể
220. Random.shuffle (có thểMoves)
Đầu tiên, chúng ta sẽ sử dụng hàm Random.shuffle () để ngẫu nhiên hóa thứ tự của
di chuyển trong danh sách có thể . Hãy nhớ rằng random.shuffle () chức năng
sẽ sắp xếp lại các mục trong danh sách mà bạn chuyển đến nó. Chức năng cũng sửa đổi danh
sách
trực tiếp, giống như hàm resetBoard () của chúng tôi thực hiện với cấu trúc dữ liệu của bảng trò
chơi.
Chúng tôi sẽ giải thích lý do tại sao chúng tôi muốn xáo trộn danh sách có thể có , nhưng trước
tiên hãy xem
thuật toán của chúng tôi.
Moves góc là Moves tốt nhất
222. # luôn luôn đi cho một góc nếu có sẵn.
223. cho x, y trong khả năngMoves:
224.
if isOnC Corner (x, y):
225.
trả lại [x, y]
Đầu tiên, chúng ta lặp qua từng cử động trong possibleMoves và nếu ai trong số họ đang ở trên
góc, chúng tôi trở lại đó là di chuyển của chúng tôi. Di chuyển góc là một ý tưởng tốt bởi vì một
khi gạch có
được đặt trên góc, nó không bao giờ có thể được lật lên. Vì khả năngMoves là một danh sách
danh sách hai mục, chúng tôi sử dụng thủ thuật nhiều bài tập trong vòng lặp for của chúng tôi để
đặt x và y .
Bởi vì chúng tôi ngay lập tức trở về tìm kiếm di chuyển góc đầu tiên trong possibleMoves ,
nếu có thểMoves chứa nhiều di chuyển góc, chúng tôi luôn đi với cái đầu tiên. Nhưng
vì có thểMoves được xáo trộn trên dòng 220, nó hoàn toàn ngẫu nhiên ở góc nào
di chuyển là đầu tiên trong danh sách.
Nhận danh sách các bước di chuyển tốt nhất
227. # Đi qua tất cả các động thái có thể và ghi nhớ
di chuyển điểm tốt nhất
228. bestScore = -1
229. cho x, y trong khả năngMoves:
230.
dupeBoard = getBoardCopy (bảng)
231.
makeMove (dupeBoard, computerTile, x, y)
232.
điểm = getScoreOfBoard (dupeBoard) [computerTile]
233.
nếu điểm> bestScore:
234.
bestMove = [x, y]
235.
bestScore = điểm
236. trả lại tốt nhất
283
15 - Reversi

Trang 298
Nếu không có di chuyển góc, chúng tôi sẽ đi qua toàn bộ danh sách và tìm ra di chuyển nào
cho chúng tôi điểm số cao nhất. Các cho vòng lặp sẽ thiết lập x và y cho mọi di chuyển trong
có thể có . bestMove sẽ được đặt thành di chuyển điểm cao nhất mà chúng tôi đã tìm thấy cho
đến nay
và bestScore sẽ được đặt thành điểm số di chuyển tốt nhất. Khi mã trong vòng lặp tìm thấy một
di chuyển điểm số cao hơn bestScore , chúng tôi sẽ lưu trữ di chuyển đó và ghi điểm như mới
các giá trị của bestMove và bestScore .
Mô phỏng tất cả các chuyển động có thể có trên các cấu trúc dữ liệu bảng trùng
lặp
Để tìm ra điểm số của nước đi có thể chúng tôi hiện đang lặp lại, chúng tôi
đầu tiên tạo cấu trúc dữ liệu bảng trò chơi trùng lặp bằng cách gọi getBoardCopy () . Chúng tôi
muốn
một bản sao để chúng tôi có thể sửa đổi mà không thay đổi cấu trúc dữ liệu của bảng trò chơi
thực được lưu trữ trong
bảng biến.
Sau đó, chúng ta gọi makeMove () , chuyển qua bảng trùng lặp thay vì bảng thực.
makeMove () sẽ xử lý việc đặt lát xếp của máy tính và lật các ô của trình phát lên
bảng trùng lặp.
Chúng tôi gọi getScoreOfBoard () bằng bảng trùng lặp, trả về một từ điển
trong đó các khóa là 'X' và 'O' và các giá trị là điểm số. getScoreOfBoard ()
không biết máy tính là 'X' hay 'O' , đó là lý do tại sao nó trả về một từ điển.
Bằng cách tạo một bảng trùng lặp, chúng ta có thể mô phỏng một động thái trong tương lai và
kiểm tra kết quả của điều đó
di chuyển mà không thay đổi cấu trúc dữ liệu bảng trò chơi thực tế. Điều này rất hữu ích trong
quyết định di chuyển nào là di chuyển tốt nhất có thể để thực hiện.
Giả sử getScoreOfBoard () trả về từ điển {'X': 22, 'O': 8} và
computerTile là 'X' . Sau đó getScoreOfBoard (dupeBoard)
[computerTile] sẽ đánh giá thành {'X': 22, 'O': 8} ['X'] , sau đó sẽ
đánh giá đến 22 . Nếu 22 lớn hơn bestScore , bestScore được đặt thành 22 và
bestMove được đặt thành các giá trị x và y hiện tại mà chúng tôi đang xem xét. Bởi thời gian
này cho
vòng lặp đã kết thúc, chúng ta có thể chắc chắn rằng bestScore là điểm số cao nhất có thể di
chuyển có thể
thực hiện và di chuyển đó được lưu trữ trong bestMove .
Bạn có thể nhận thấy rằng trên dòng 228, trước tiên chúng tôi đặt bestScore thành -1 . Cái này là
vậy
di chuyển đầu tiên mà chúng tôi xem xét trong vòng lặp for của chúng tôi qua khả năng. Các
phần mềm sẽ được đặt thành đầu tiên
bestMove . Điều này sẽ đảm bảo rằng bestMove được đặt thành một trong những động thái khi
chúng tôi quay lại

Nói rằng di chuyển điểm cao nhất trong khả năng. Các chế độ sẽ cung cấp cho máy tính
điểm 42 . Có gì nếu có nhiều hơn một động thái trong possibleMoves rằng sẽ cung cấp cho
điểm này? Các cho vòng lặp sử dụng, chúng tôi sẽ luôn luôn đi với động thái đầu tiên mà ghi
được 42
điểm, bởi vì bestMove và bestScore chỉ thay đổi nếu di chuyển lớn hơn
điểm cao nhất. Cà vạt sẽ không thay đổi bestMove và bestScore .
284

Trang 299
Chúng tôi không phải lúc nào cũng muốn đi với bước đầu tiên trong danh sách có thể có , bởi vì
điều đó sẽ làm cho AI của chúng ta có thể dự đoán được bởi người chơi. Nhưng đó là ngẫu
nhiên, bởi vì trên dòng 220 chúng tôi
xáo trộn danh sách có thể . Mặc dù mã của chúng tôi luôn chọn đầu tiên trong số này
di chuyển gắn liền, là ngẫu nhiên mà di chuyển sẽ là đầu tiên trong danh sách bởi vì thứ tự là
ngẫu nhiên. Điều này đảm bảo rằng AI sẽ không thể dự đoán được khi có nhiều hơn một
di chuyển.
In các điểm đến màn hình
239. def showPoints (playerTile, computerTile):
240. # In ra số điểm hiện tại.
241. điểm = getScoreOfBoard (mainBoard)
242. in ('Bạn có% s điểm. Máy tính có% s
điểm. ' % (điểm [playerTile], điểm [computerTile]))
showPoints () chỉ cần gọi hàm getScoreOfBoard () và sau đó in ra
điểm của người chơi và điểm của máy tính. Hãy nhớ rằng getScoreOfBoard ()
trả về một từ điển với các phím 'X' và 'O' và các giá trị của điểm số cho X và O
người chơi.
Đó là tất cả các chức năng chúng tôi xác định cho trò chơi Reversi của chúng tôi. Mã bắt đầu trên
dòng 246
sẽ thực hiện trò chơi thực tế và thực hiện các cuộc gọi đến các chức năng này khi cần thiết.
Bắt đầu trò chơi
246. in ('Chào mừng đến với Reversi!')
247.
248. trong khi Đúng:
249. # Đặt lại bảng và trò chơi.
250. mainBoard = getNewBoard ()
251. resetBoard (mainBoard)
252. playerTile, computerTile = enterPlayerTile ()
253. showH gợi ý = Sai
254. lượt = whoGoesFirst ()
255. in ('' 'Turn +' sẽ đi trước. ')
Các trong khi vòng lặp trên đường dây 248 là trò chơi vòng lặp chính. Chương trình sẽ lặp lại
dòng
Mỗi lần chúng tôi muốn bắt đầu một trò chơi mới. Đầu tiên chúng ta có một cấu trúc dữ liệu
bảng trò chơi mới
bằng cách gọi getNewBoard () và đặt các ô bắt đầu bằng cách gọi resetBoard () .
mainBoard là cấu trúc dữ liệu bảng trò chơi chính mà chúng tôi sẽ sử dụng cho chương trình
này. Cuộc gọi
to enterPlayerTile () sẽ cho phép người chơi nhập liệu họ muốn trở thành 'X' hay 'O' ,
mà sau đó được lưu trữ trong playerTile và computerTile .
showH gợi ý là một giá trị Boolean xác định nếu bật hoặc tắt chế độ gợi ý. Chúng tôi ban đầu
đặt nó thành tắt bằng cách đặt showH gợi ý thành Sai .
285
15 - Reversi
Trang 300
Biến lần lượt là một chuỗi sẽ có giá trị chuỗi 'player' hoặc
'máy tính' , và sẽ theo dõi xem đó là lượt của ai. Chúng tôi đặt lần lượt thành giá trị trả về của
whoGoesFirst () , chọn ngẫu nhiên ai sẽ đi trước. Sau đó chúng tôi in ra
đi đầu tiên cho người chơi trên dòng 255.
Chạy Turn Turn
257. trong khi Đúng:
258.
nếu bật == 'người chơi':
259.
# Biến lần lượt.
260.
nếu hiển thị:
261.
hợp lệMovesBoard = getBoardWithValidMoves
(mainBoard, playerTile)
262.
drawBoard (hợp lệMovesBoard)
263.
khác:
264.
drawBoard (mainBoard)
265.
showPoints (playerTile, computerTile)
Các trong khi vòng lặp bắt đầu trên đường dây 257 sẽ tiếp tục lặp mỗi lần người chơi hay
máy tính mất một lượt. Chúng tôi sẽ thoát ra khỏi vòng lặp này khi trò chơi hiện tại kết thúc.
Dòng 258 có một nếu tuyên bố mà cơ thể có mã mà chạy nếu đến lượt của người chơi.
(Khối khác bắt đầu trên dòng 282 có mã cho lượt của máy tính.) Điều đầu tiên
chúng tôi muốn làm là hiển thị bảng cho người chơi. Nếu chế độ gợi ý được bật (đó là nếu
showH gợi ý là đúng ), sau đó chúng tôi muốn có được cấu trúc dữ liệu bảng có '.' giai đoạn =
Stage
nhân vật trên mọi không gian người chơi có thể đi.
Hàm getBoardWithValidMoves () của chúng tôi thực hiện điều đó, tất cả những gì chúng tôi phải
làm là vượt qua
cấu trúc dữ liệu bảng trò chơi và nó sẽ trả về một bản sao cũng chứa '.' nhân vật thời kỳ.
Sau đó chúng ta chuyển bảng này đến hàm drawBoard () .
Nếu chế độ gợi ý bị tắt, thì chúng ta chỉ cần chuyển mainBoard sang drawBoard () .
Sau khi in bảng trò chơi cho người chơi, chúng tôi cũng muốn in ra hiện tại
điểm số bằng cách gọi showPoints () .
266.
di chuyển = getPlayerMove (mainBoard, playerTile)
Tiếp theo chúng tôi cho phép người chơi gõ di chuyển của họ. getPlayerMove () xử lý việc này

giá trị trả về là danh sách hai mục của tọa độ X và Y khi di chuyển của người chơi.
getPlayerMove () đảm bảo rằng di chuyển mà người chơi đã nhập là một di chuyển hợp lệ, vì
vậy chúng tôi
không phải lo lắng về nó ở đây.
286

Trang 301
Xử lý các lệnh Thoát hoặc Gợi ý
267.
nếu di chuyển == 'thoát':
268.
in ('Cảm ơn vì đã chơi!')
269.
sys.exit () # chấm dứt chương trình
270.
di chuyển elif == 'gợi ý':
271.
showH gợi ý = không hiển thị
272.
tiếp tục
273.
khác:
274.
makeMove (mainBoard, playerTile, di chuyển [0],
di chuyển [1])
Nếu người chơi đã nhập chuỗi 'thoát' cho di chuyển của họ, thì getPlayerMove ()
sẽ trả lại chuỗi 'thoát' . Trong trường hợp đó, chúng ta nên gọi sys.exit () để
chấm dứt chương trình.
Nếu người chơi gõ vào chuỗi 'gợi ý' cho di chuyển của họ, thì getPlayerMove ()
sẽ trả về chuỗi 'gợi ý' . Trong trường hợp đó, chúng tôi muốn bật chế độ gợi ý (nếu nó
đã tắt) hoặc tắt (nếu nó được bật). Các showHints = không showHints nhượng
câu lệnh xử lý cả hai trường hợp này, vì không sai đánh giá là Đúng và không
Đúng đánh giá thành Sai . Sau đó, chúng tôi chạy câu lệnh continue để lặp lại ( lần lượt có
không thay đổi, vì vậy nó vẫn sẽ đến lượt người chơi sau khi chúng tôi lặp).
Thực hiện di chuyển
Mặt khác, nếu người chơi không thoát hoặc chuyển chế độ gợi ý, thì chúng tôi sẽ gọi makeMove
() để làm cho người chơi di chuyển trên bảng.
276.
if getValidMoves (mainBoard, computerTile) ==
[]:
277.
phá vỡ
278.
khác:
279.
biến = 'máy tính'
Sau khi di chuyển người chơi, chúng tôi gọi Sai để xem máy tính có thể không
thực hiện bất kỳ động thái. Nếu Sai trả về một danh sách trống, thì sẽ không còn di chuyển nào
nữa
máy tính có thể thực hiện (rất có thể vì bảng đã đầy). Trong trường hợp đó, chúng tôi thoát ra
khỏi
các trong khi vòng lặp và kết thúc các trò chơi hiện tại.
Mặt khác, chúng tôi đặt biến thành 'máy tính' . Luồng thực thi bỏ qua khối khác
và đến cuối khối while, do đó, thực thi nhảy trở lại câu lệnh while
trên dòng 257. Tuy nhiên, lần này, đến lượt máy tính.
287
15 - Reversi

Trang 302
Chạy máy tính
281.
khác:
282.
# Đến lượt máy tính.
283.
drawBoard (mainBoard)
284.
showPoints (playerTile, computerTile)
285.
đầu vào ('Nhấn Enter để xem máy tính
di chuyển. ')
286.
x, y = getComputerMove (mainBoard,
máy tínhTile)
287.
makeMove (mainBoard, computerTile, x, y)
Việc đầu tiên chúng ta làm khi đến lượt máy tính là gọi drawBoard () để in ra
bảng cho người chơi. Tại sao chúng ta làm điều này bây giờ? Bởi vì hoặc máy tính đã được chọn
để thực hiện bước di chuyển đầu tiên của trò chơi, trong trường hợp đó chúng ta nên hiển thị bắt
đầu ban đầu
hình ảnh của bảng cho người chơi trước khi máy tính di chuyển. Hoặc người chơi có
đi trước, và chúng tôi muốn cho thấy bảng trông như thế nào sau khi người chơi đã di chuyển
nhưng
trước khi máy tính biến mất
Sau khi in bảng bằng drawBoard () , chúng tôi cũng muốn in ra hiện tại
ghi điểm với một cuộc gọi đến showPoints () .
Tiếp theo, chúng tôi có một lệnh gọi tới input () để tạm dừng tập lệnh trong khi trình phát có thể
nhìn vào
bảng. Điều này giống như cách chúng ta sử dụng input () để tạm dừng chương trình trong
chương Truyện cười của chúng ta.
Thay vì sử dụng lệnh gọi print () để in chuỗi trước cuộc gọi đến input () , bạn có thể vượt qua
chuỗi dưới dạng tham số cho input () . input () có một tham số chuỗi tùy chọn. Các
chuỗi chúng tôi chuyển trong cuộc gọi này là 'Nhấn Enter để xem di chuyển của máy tính.' .
Sau khi người chơi đã nhìn vào bảng và nhấn Enter (bất kỳ văn bản nào người chơi đã nhập là
bị bỏ qua vì chúng tôi không gán giá trị trả về của input () cho bất cứ thứ gì), chúng tôi gọi
getComputerMove () để lấy tọa độ X và Y của bước di chuyển tiếp theo của máy tính. Chúng tôi
lưu trữ các tọa độ này trong các biến x và y tương ứng.
Cuối cùng, chúng tôi vượt qua x và y , cùng với cấu trúc dữ liệu của bảng trò chơi và của máy
tính
xếp gạch vào hàm makeMove () để thay đổi bảng trò chơi để phản ánh sự di chuyển của máy
tính.
Cuộc gọi của chúng tôi tới getComputerMove () đã di chuyển máy tính và cuộc gọi
để makeMove
() làm cho việc di chuyển trên bảng.
289.
if getValidMoves (mainBoard, playerTile) ==
[]:
290.
phá vỡ
291.
khác:
292.
biến = 'người chơi'
Các dòng từ 289 đến 292 rất giống với các dòng từ 276 đến 279. Sau khi máy tính đã hoàn thành
288

Trang 303
di chuyển, chúng tôi kiểm tra xem có tồn tại bất kỳ động thái nào mà người chơi có thể thực hiện
không. Nếu
getValidMoves () trả về một danh sách trống, sau đó không có động thái nào khả thi. Điều đó có
nghĩa là
trò chơi kết thúc, và chúng ta nên thoát ra khỏi trong khi vòng lặp mà chúng ta đang ở.
Mặt khác, có ít nhất một khả năng di chuyển mà người chơi nên thực hiện, vì vậy chúng ta nên
thiết lập
chuyển sang 'người chơi' . Không có thêm mã trong khối while sau dòng 292, vì vậy thực thi
các vòng lặp trở lại câu lệnh while trên dòng 257.
Vẽ mọi thứ trên màn hình
294. # Hiển thị điểm số cuối cùng.
295. drawBoard (mainBoard)
296. điểm = getScoreOfBoard (mainBoard)
297. in ('X ghi% s điểm. O ghi% s điểm.'%
(điểm ['X'], điểm ['O']))
298. nếu điểm [playerTile]> điểm [computerTile]:
299.
in ('Bạn đánh bại máy tính bằng% s điểm!
Xin chúc mừng!' % (điểm [playerTile] - điểm số
[computerTile]))
300. elif points [playerTile] <points [computerTile]:
301.
print ('Bạn thua. Máy tính đánh bại bạn bằng% s
điểm. ' % (điểm [computerTile] - điểm [playerTile]))
302. khác:
303.
in ('Trò chơi là một sự ràng buộc!')
Dòng 294 là dòng đầu tiên nằm ngoài khối while bắt đầu trên dòng 257. Mã này là
thực hiện khi chúng tôi đã nổ ra điều đó trong khi vòng lặp, hoặc trên đường dây 290 hoặc 277.
(Các
trong khi điều kiện của câu lệnh trên dòng 257 chỉ đơn giản là giá trị True , vì vậy chúng ta chỉ
có thể thoát khỏi
lặp qua các câu lệnh break .)
Tại thời điểm này, trò chơi kết thúc. Chúng ta nên in ra bảng và điểm số, và xác định
người đã thắng trò chơi. getScoreOfBoard () sẽ trả về một từ điển có khóa 'X' và
'O' và giá trị điểm của cả hai người chơi. Bằng cách kiểm tra xem điểm của người chơi có lớn
hơn không,
nhỏ hơn hoặc bằng điểm số của máy tính, chúng tôi có thể biết người chơi thắng, nếu người chơi
thua,
hoặc nếu người chơi và máy tính bị trói.
Trừ một điểm từ người kia là một cách dễ dàng để xem người chơi giành được bao nhiêu
hơn người khác Các lệnh in () của chúng tôi trên các dòng 299 và 301 sử dụng phép nội suy
chuỗi để đặt
kết quả của phép trừ này vào chuỗi được in.
Yêu cầu người chơi chơi lại
305. nếu không chơiAgain ():
306.
phá vỡ
289
15 - Reversi

Trang 304
Trò chơi bây giờ đã kết thúc và người chiến thắng đã được tuyên bố. Chúng ta nên gọi
Hàm playAgain () , trả về True nếu người chơi nhập vào mà họ muốn chơi
trò chơi khác. Nếu playAgain () trả về False (mà làm cho nếu tuyên bố của
điều kiện Đúng ), chúng tôi thoát ra khỏi trong khi vòng lặp (một trong đó bắt đầu trên đường
dây 248), và
do không còn dòng mã nào sau khối while này, chương trình chấm dứt.
Mặt khác, playAgain () đã trả về True (làm cho câu lệnh if
điều kiện Sai ) và do đó, các vòng lặp thực thi trở lại câu lệnh while trên dòng 248 và a
bảng trò chơi mới được tạo ra.
Tóm tắt: Đánh giá trò chơi Reversi
AI có vẻ gần như không thể đánh bại, nhưng điều này không phải vì máy tính rất thông minh.
Chiến lược mà nó tuân theo rất đơn giản: di chuyển vào góc nếu bạn có thể, nếu không thì thực
hiện
di chuyển sẽ lật qua các gạch nhất. Chúng tôi có thể làm điều đó, nhưng chúng tôi sẽ mất nhiều
thời gian để
Tìm hiểu có bao nhiêu gạch sẽ được lật cho mỗi di chuyển hợp lệ có thể chúng ta có thể thực
hiện.
Nhưng tính toán này cho máy tính rất đơn giản. Máy tính không thông minh hơn chúng ta, nó
nhanh hơn nhiều
Trò chơi này rất giống với Sonar vì nó sử dụng lưới cho một bảng. Nó cũng là
giống như trò chơi Tic Tac Toe vì có một AI có kế hoạch di chuyển tốt nhất để chơi.
Chương này chỉ giới thiệu một khái niệm mới: sử dụng hàm bool () và thực tế là
danh sách trống, chuỗi trống và số nguyên 0 đều ước tính thành Sai trong ngữ cảnh của một
điều kiện.
Ngoài ra, trò chơi này sử dụng các khái niệm lập trình mà bạn đã biết! Bạn không
phải biết rất nhiều về lập trình để tạo ra những trò chơi thú vị. Tuy nhiên,
trò chơi này đang kéo dài bao xa bạn có thể đạt được với nghệ thuật ASCII. Hội đồng quản trị
chiếm gần như
Toàn bộ màn hình để vẽ và trò chơi không có bất kỳ màu nào.
Sau đó trong cuốn sách này, chúng ta sẽ tìm hiểu cách tạo trò chơi với đồ họa và hoạt hình,
không phải
Chỉ cần văn bản. Chúng tôi sẽ thực hiện điều này bằng cách sử dụng một mô-đun có tên Pygame,
bổ sung các chức năng mới và
các tính năng cho Python để chúng ta có thể tách ra khỏi việc chỉ sử dụng nhập văn bản và bàn phím.
290

Trang 304
Các chủ đề được đề cập trong Chương này:
● Mô phỏng
● Tỷ lệ phần trăm
● Biểu đồ hình tròn

● Bộ phận nguyên

● Các vòng () Chức năng


Trò chơi "Máy tính so với máy tính"
Thuật toán Reversi AI rất đơn giản, nhưng nó đánh bại tôi gần như mỗi lần tôi chơi nó.
Điều này là do máy tính có thể xử lý các hướng dẫn rất nhanh, vì vậy hãy kiểm tra từng hướng
dẫn có thể
vị trí trên bảng và chọn di chuyển điểm cao nhất là dễ dàng cho máy tính. Nếu tôi
dành thời gian để xem xét mọi chỗ trống trên bảng và ghi lại điểm của từng điểm có thể
di chuyển, sẽ mất một thời gian dài để tôi tìm thấy di chuyển tốt nhất.
Bạn có để ý rằng chương trình Reversi của chúng tôi trong Chương 14 có hai chức năng,
getPlayerMove () và getComputerMove () , cả hai đều trả về di chuyển
được chọn làm danh sách hai mục như [x, y] ? Cả hai cũng có cùng thông số, trò chơi
cấu trúc dữ liệu bảng và gạch mà họ đã được. getPlayerMove () đã quyết định [x,
y] di chuyển để trở về bằng cách cho phép người chơi nhập tọa độ. getComputerMove ()
quyết định [x, y] nào sẽ quay trở lại bằng cách chạy thuật toán Reversi AI.
Điều gì xảy ra khi chúng ta thay thế cuộc gọi đến getPlayerMove () bằng một cuộc gọi đến
getComputerMove () ? Sau đó, người chơi không bao giờ gõ trong một di chuyển, nó được quyết
định cho họ!
Máy tính đang chơi với chính nó!
29

Trang 306
Chúng tôi sẽ thực hiện ba chương trình mới, mỗi chương trình dựa trên chương trình Reversi cuối
cùng
chương. Chúng tôi sẽ thực hiện các thay đổi đối với Reversi.py để tạo AISim1.py . Tiếp theo
chúng tôi sẽ làm
thay đổi thành AISim1.py để tạo AISim2.py . Và cuối cùng, chúng tôi sẽ thay đổi AISim2.py
cho AISim3.py . Bạn có thể tự nhập những thay đổi này hoặc tải xuống từ
trang web của cuốn sách tại URL http://inventwithpython.com/ch CHƯƠNG16.
Làm cho máy tính chơi với chính nó
Lưu tệp Reversi.py cũ dưới dạng AISim1.py bằng cách nhấp vào Tệp và sau đó Lưu với tên , sau
đó
nhập AISim1.py cho tên tệp và nhấp vào Ok . Điều này sẽ tạo ra một bản sao của chúng tôi
Reversi mã nguồn dưới dạng một tệp mới mà chúng ta có thể thay đổi, trong khi để lại bản gốc
Trò chơi Reversi giống nhau (chúng tôi có thể muốn chơi lại). Thay đổi mã sau trong
AISim1.py :
266. di chuyển = getPlayerMove (mainBoard, playerTile)
Về điều này (sự thay đổi được in đậm):
266. di chuyển = getComputerMove (mainBoard, playerTile)
Và chạy chương trình. Lưu ý rằng trò chơi vẫn hỏi bạn nếu bạn muốn là X hoặc O, nhưng nó
sẽ không yêu cầu bạn nhập bất kỳ động thái nào. Khi chúng tôi thay thế getPlayerMove () ,
chúng tôi không
còn gọi bất kỳ mã nào lấy đầu vào này từ trình phát. Chúng tôi vẫn nhấn Enter sau
di chuyển máy tính ban đầu (vì đầu vào ('Nhấn Enter để xem
di chuyển của máy tính. ') trên dòng 285), nhưng trò chơi tự chơi!
Hãy thực hiện một số thay đổi khác đối với AISim1.py . Tất cả các chức năng chúng tôi đã xác
định cho Reversi
có thể giữ nguyên Nhưng thay thế toàn bộ phần chính của chương trình (dòng 246 trở đi) thành
trông giống như đoạn mã sau Một số mã vẫn còn, nhưng hầu hết đã được
thay đổi. Nhưng tất cả các dòng trước dòng 246 đều giống như trong Reversi trong chương
trước.
Bạn cũng có thể tránh nhập mã bằng cách tải xuống nguồn từ URL
http://inventwithpython.com/ch CHƯƠNG16.
AISim1.py
Mã này có thể được tải xuống từ http://inventwithpython.com/AISim1.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
246. in ('Chào mừng đến với Reversi!')
247.
248. trong khi Đúng:
249. # Đặt lại bảng và trò chơi.
250. mainBoard = getNewBoard ()
251. resetBoard (mainBoard)
252. if whoGoesFirst () == 'người chơi':
292

Trang 307
253.
biến = 'X'
254. khác:
255.
biến = 'O'
256. in ('' 'Turn +' sẽ xuất hiện trước. ')
257.
258. trong khi Đúng:
259.
drawBoard (mainBoard)
260.
điểm = getScoreOfBoard (mainBoard)
261.
in ('X có% s điểm. O có% s điểm'%
(điểm ['X'], điểm ['O']))
262.
đầu vào ('Nhấn Enter để tiếp tục.')
263.
264.
nếu lần lượt == 'X':
265.
Đến lượt của X.
266.
otherTile = 'O'
267.
x, y = getComputerMove (mainBoard, 'X')
268.
makeMove (mainBoard, 'X', x, y)
269.
khác:
270.
Đến lượt của tôi.
271.
otherTile = 'X'
272.
x, y = getComputerMove (mainBoard, 'O')
273.
makeMove (mainBoard, 'O', x, y)
274.
275.
if getValidMoves (mainBoard, otherTile) == []:
276.
phá vỡ
277.
khác:
278.
biến = otherTile
279.
280. # Hiển thị điểm số cuối cùng.
281. drawBoard (mainBoard)
282. điểm = getScoreOfBoard (mainBoard)
283. in ('X ghi% s điểm. O ghi% s điểm.'%
(điểm ['X'], điểm ['O']))
284.
285. nếu không chơiAgain ():
286.
sys.exit ()

Mã AISim1.py hoạt động như thế nào


Các AISim1.py chương trình cũng giống như các chương trình Reversi gốc, ngoại trừ các cuộc
gọi đến
getPlayerMove () đã được thay thế bằng một lệnh gọi tới getComputerMove () . Ở đây có
là một số thay đổi khác đối với văn bản được in ra màn hình để giúp trò chơi dễ dàng hơn
theo.
Khi bạn chạy chương trình AISim1.py , tất cả những gì bạn có thể làm là nhấn Enter cho mỗi lượt
cho đến khi
Trò chơi kết thúc. Chạy qua một vài trò chơi và xem máy tính tự chơi. Vì cả X
và người chơi O đang sử dụng cùng một thuật toán, đó thực sự chỉ là vấn đề may mắn để xem ai
thắng.
Người chơi X sẽ giành được một nửa thời gian và người chơi O sẽ giành được một nửa thời gian.
293
16 - Mô phỏng AI

Trang 308
Làm cho máy tính tự chơi vài lần
Nhưng nếu chúng ta tạo ra một thuật toán mới thì sao? Sau đó, chúng ta có thể thiết lập AI mới
này chống lại AI
triển khai trong getComputerMove () và xem cái nào tốt hơn. Chúng ta hãy làm một số
thay đổi chương trình của chúng tôi. Nhấp vào Tệp và sau đó Lưu dưới dạng và lưu tệp này
dưới dạng AISim2.py vì vậy
rằng chúng tôi có thể thay đổi mà không ảnh hưởng đến AISim1.py . Tại thời điểm
này, AISim1.py và
AISim2.py có cùng mã. Chúng tôi sẽ thay đổi AISim2.py và lưu tệp đó
rằng AISim2.py có các thay đổi mới và AISim1.py có mã gốc.
Thêm mã sau đây. Các bổ sung được in đậm và một số dòng đã bị xóa.
Khi bạn hoàn tất việc thay đổi tệp, hãy lưu nó dưới dạng AISim2.py .
Nếu điều này gây nhầm lẫn, bạn luôn có thể tải xuống mã nguồn AISim2.py từ sách
trang web tại http://inventwithpython.com/ch CHƯƠNG16.
AISim2.py
Mã này có thể được tải xuống từ http://inventwithpython.com/AISim2.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
246. in ('Chào mừng đến với Reversi!')
247.
248. xwins = 0
249. nợ = 0
250. cà vạt = 0
251. numGames = int (đầu vào ('Nhập số lượng trò chơi sẽ chạy:'))
252.
253. cho trò chơi trong phạm vi (numGames):
254. in ('Trò chơi #% s:'% (trò chơi), end = '')
255. # Đặt lại bảng và trò chơi.
256. mainBoard = getNewBoard ()
257. resetBoard (mainBoard)
258. if whoGoesFirst () == 'người chơi':
259.
biến = 'X'
260. khác:
261.
biến = 'O'
262.
263. trong khi Đúng:
264.
nếu lần lượt == 'X':
265.
Đến lượt của X.
266.
otherTile = 'O'
267.
x, y = getComputerMove (mainBoard, 'X')
268.
makeMove (mainBoard, 'X', x, y)
269.
khác:
270.
Đến lượt của tôi.
271.
otherTile = 'X'
272.
x, y = getComputerMove (mainBoard, 'O')
273.
makeMove (mainBoard, 'O', x, y)
274.
275.
if getValidMoves (mainBoard, otherTile) == []:
276.
phá vỡ
294

Trang 309
277.
khác:
278.
biến = otherTile
279.
280. # Hiển thị điểm số cuối cùng.
281. điểm = getScoreOfBoard (mainBoard)
282. in ('X ghi% s điểm. O ghi% s điểm.'%
(điểm ['X'], điểm ['O']))
283.
284. nếu điểm ['X']> điểm ['O']:
285.
xwins + = 1
286. elif điểm ['X'] <điểm ['O']:
287.
nợ + = 1
288. khác:
289.
quan hệ + = 1
290.
291. numGames = float (numGames)
292. xpercent = round (((xwins / numGames) * 100), 2)
293. opercent = round (((owins / numGames) * 100), 2)
294. tiepercent = round (((cà vạt / numGames) * 100), 2)
295. print ('X thắng% s trò chơi (% s %%), O thắng% s trò chơi (% s %%),
quan hệ cho tổng số trò chơi% s (% s %%) trong tổng số trò chơi% s. ' % (xwins,
xpercent, nợ, opercent, cà vạt, tiepercent, numGames))

Mã AISim2.py hoạt động như thế nào


Chúng tôi đã thêm các biến xwins , nợ và quan hệ để theo dõi số lần
X thắng, O thắng và khi họ hòa. Các dòng từ 284 đến 289 tăng các biến này ở cuối
mỗi trò chơi, trước khi nó lặp lại để bắt đầu một trò chơi hoàn toàn mới.
Chúng tôi đã xóa hầu hết các lệnh gọi hàm print () khỏi chương trình và các lệnh gọi đến
vẽBoard () . Khi bạn chạy AISim2.py , nó sẽ hỏi bạn có bao nhiêu trò chơi bạn muốn chạy.
Bây giờ chúng tôi đã thực hiện cuộc gọi đến drawBoard () và thay thế vòng lặp True:
với một trò chơi dành cho phạm vi (numGames): loop, chúng tôi có thể chạy một số trò chơi
mà không dừng lại để người dùng gõ bất cứ điều gì. Đây là một cuộc chạy mẫu nơi chúng tôi
chạy mười trò chơi
của máy tính so với máy tính Reversi:
Chào mừng đến với Reversi!
Nhập số lượng trò chơi để chạy: 10
Trò chơi số 0: X ghi được 40 điểm. O ghi được 23 điểm.
Trò chơi số 1: X ghi được 24 điểm. O ghi được 39 điểm.
Trò chơi số 2: X ghi được 31 điểm. O ghi được 30 điểm.
Trò chơi số 3: X ghi được 41 điểm. O ghi được 23 điểm.
Trò chơi số 4: X ghi được 30 điểm. O ghi được 34 điểm.
Trò chơi số 5: X ghi được 37 điểm. O ghi được 27 điểm.
Trò chơi số 6: X ghi được 29 điểm. O ghi được 33 điểm.
Trò chơi số 7: X ghi được 31 điểm. O ghi được 33 điểm.
Trò chơi số 8: X ghi được 32 điểm. O ghi được 32 điểm.
Trò chơi số 9: X ghi được 41 điểm. O ghi được 22 điểm.
295
16 - Mô phỏng AI

Trang 310
X thắng 5 trận (50,0%), O thắng 4 trận (40,0%),
quan hệ cho 1 trò chơi (10,0%) trong tổng số 10,0 trò chơi.
Vì thuật toán có một phần ngẫu nhiên, nên lần chạy của bạn có thể không có cùng một chính xác
số như trên.
In những thứ ra màn hình làm chậm máy tính, nhưng giờ chúng ta đã có
xóa mã đó, máy tính có thể chạy toàn bộ trò chơi Reversi trong khoảng một giây hoặc
hai. Hãy suy nghĩ về nó. Mỗi lần chương trình của chúng tôi in ra một trong những dòng đó, nó
lại chạy qua
toàn bộ trò chơi (khoảng năm mươi hoặc sáu mươi di chuyển, mỗi lần di chuyển được kiểm tra
cẩn thận là một
được nhiều điểm nhất).
Tỷ lệ phần trăm
Tỷ lệ phần trăm là một phần của tổng số tiền và
trong khoảng từ 0% đến 100%. Nếu bạn có 100% chiếc bánh,
bạn sẽ có toàn bộ chiếc bánh. Nếu bạn có 0% của một
chiếc bánh, bạn sẽ không có chiếc bánh nào cả. 50%
chiếc bánh sẽ là một nửa của chiếc bánh. Một chiếc bánh là phổ biến
hình ảnh để sử dụng cho tỷ lệ phần trăm. Trong thực tế, có một
loại biểu đồ được gọi là biểu đồ hình tròn cho thấy cách
phần lớn toàn bộ một phần nhất định là. Đây là
biểu đồ hình tròn với các phần 10%, 15%, 25% và 50%
phía dưới. Lưu ý rằng 10% + 15% + 25% + 50% thêm
lên tới 100%.
Chúng ta có thể tính tỷ lệ phần trăm với phân chia.
Để có được tỷ lệ phần trăm, hãy chia phần bạn có
tổng số, và sau đó nhân với một trăm. Ví dụ: nếu X thắng 50 trên 100 trò chơi,
bạn sẽ tính toán biểu thức 50/100 , trong đó sẽ đánh giá để 0,5 . Chúng tôi
nhân số này với 100 để có tỷ lệ phần trăm (trong trường hợp này là 50%). Lưu ý rằng nếu X
thắng 100
trong số 200 trò chơi, chúng tôi có thể tính tỷ lệ phần trăm với 100/200 , cũng sẽ
đánh giá đến 0,5 . Khi chúng tôi nhân 0,5 với 100 để có tỷ lệ phần trăm, chúng tôi nhận được
50%.
Chiến thắng 100 trên 200 trò chơi là tỷ lệ phần trăm giống nhau (nghĩa là cùng một phần) với
chiến thắng
50 trên 100 trò chơi.
Bộ phận đánh giá điểm nổi
Điều quan trọng cần lưu ý là khi bạn sử dụng toán tử / chia, biểu thức sẽ
luôn luôn đánh giá đến một số dấu phẩy động. Ví dụ: biểu thức 10/2 sẽ
ước tính cho giá trị dấu phẩy động 5.0 và không đánh giá trị số nguyên 5 .
Điều này rất quan trọng cần nhớ, vì thêm một số nguyên vào giá trị dấu phẩy động bằng
các + điều hành bổ sung cũng sẽ luôn luôn đánh giá một giá trị dấu chấm động. Ví dụ: 3
+ 4.0 sẽ ước tính giá trị dấu phẩy động 7.0 và không cho số nguyên 7 .
Hình 16-1: Biểu đồ hình tròn với 10%,
15%, 25% và 50%.
296

Trang 311
Hãy thử nhập mã sau vào vỏ tương tác:
>>> thư rác = 100/4
>>> thư rác
25,0
>>> thư rác = thư rác + 20
>>> thư rác
45,0
>>>
Lưu ý rằng trong ví dụ trên, loại dữ liệu của giá trị được lưu trong thư rác luôn là một
giá trị điểm nổi. Bạn có thể vượt qua các giá trị dấu chấm động đến int () chức năng, mà
sẽ trả về một dạng số nguyên của giá trị dấu phẩy động. Nhưng điều này sẽ luôn luôn làm tròn
giá trị điểm nổi xuống. Ví dụ: các biểu thức int (4.0) , int (4.2) và
int (4.9) tất cả sẽ đánh giá thành 4 , và không bao giờ 5 .
Các vòng () Chức năng
Hàm round () sẽ làm tròn một số float đến toàn bộ số float gần nhất. Thử
nhập thông tin sau vào shell tương tác:
>>> vòng (10.0)
10,0
>>> vòng (10.2)
10,0
>>> vòng (8.7)
9.0
>>> vòng (4.5)
5.0
>>> vòng (3.5)
4.0
>>> vòng (3.4999)
3.0
>>> vòng (2.5422, 2)
2,54
>>>
Như bạn có thể thấy, bất cứ khi nào phần phân số của một số là 0,5 hoặc lớn hơn, số đó là
làm tròn. Nếu không, số được làm tròn xuống. Hàm round () cũng có một
tham số tùy chọn, nơi bạn có thể chỉ định nơi bạn muốn làm tròn số đến.
Ví dụ: vòng biểu thức (2.5422, 2) ước tính là 2.54 .
297
16 - Mô phỏng AI

Trang 312
Hiển thị số liệu thống kê
291. numGames = float (numGames)
292. xpercent = round (((xwins / numGames) * 100), 2)
293. opercent = round (((owins / numGames) * 100), 2)
294. tiepercent = round (((cà vạt / numGames) * 100), 2)
295. print ('X thắng% s trò chơi (% s %%), O thắng% s trò chơi (% s %%),
quan hệ cho tổng số trò chơi% s (% s %%) trong tổng số trò chơi% s. ' % (xwins,
xpercent, nợ, opercent, cà vạt, tiepercent, numGames))
Mã ở cuối chương trình của chúng tôi sẽ cho người dùng biết có bao nhiêu chiến thắng X và O,
có bao nhiêu mối quan hệ, và bao nhiêu phần trăm tạo nên. Theo thống kê, càng nhiều
trò chơi bạn chạy, tỷ lệ phần trăm của bạn sẽ càng chính xác. Nếu bạn chỉ chạy mười trò chơi, và
X đã giành được ba trong số đó, sau đó dường như thuật toán của X chỉ giành được 30% thời
gian.
Tuy nhiên, nếu bạn chạy một trăm, thậm chí một nghìn trò chơi, thì bạn có thể thấy rằng X là
thuật toán thắng gần hơn 50% (nghĩa là một nửa) của trò chơi.
Để tìm tỷ lệ phần trăm, chúng tôi chia số lần thắng hoặc hòa cho tổng số
Trò chơi. Chúng tôi chuyển đổi numGames thành float để đảm bảo chúng tôi không sử dụng
phép chia số nguyên trong
phép tính. Sau đó, chúng tôi nhân kết quả với 100 . Tuy nhiên, chúng tôi có thể kết thúc với một
số
như 66.66666666666667 . Vì vậy, chúng tôi chuyển số này cho hàm round () với
tham số thứ hai là 2 để giới hạn độ chính xác đến hai chữ số thập phân, do đó, nó sẽ trả về một số
float
như 66,67 thay vào đó (dễ đọc hơn nhiều).
Hãy thử một thí nghiệm khác. Chạy lại AISim2.py , nhưng lần này nó chạy được một trăm
Trò chơi:
Chạy mẫu của AISim2.py
Chào mừng đến với Reversi!
Nhập số lượng trò chơi để chạy: 100
Trò chơi số 0: X ghi được 42 điểm. O ghi được 18 điểm.
Trò chơi số 1: X ghi được 26 điểm. O ghi được 37 điểm.
Trò chơi số 2: X ghi được 34 điểm. O ghi được 29 điểm.
Trò chơi số 3: X ghi được 40 điểm. O ghi được 24 điểm.
... bỏ qua cho ngắn gọn ...
Trò chơi # 96: X ghi được 22 điểm. O ghi được 39 điểm.
Trò chơi số 97: X ghi được 38 điểm. O ghi được 26 điểm.
Trò chơi số 98: X ghi được 35 điểm. O ghi được 28 điểm.
Trò chơi # 99: X ghi được 24 điểm. O ghi được 40 điểm.
X thắng 46 trận (46,0%), O thắng 52 trận (52,0%),
quan hệ cho 2 trò chơi (2,0%) trong tổng số 100,0 trò chơi.
298

Trang 313
Tùy thuộc vào tốc độ máy tính của bạn, lần chạy này có thể mất khoảng một vài
phút Chúng ta có thể thấy rằng kết quả của tất cả một trăm trò chơi vẫn phát triển đến khoảng
năm mươi-
Năm mươi, vì cả X và O đều sử dụng cùng một thuật toán để giành chiến thắng.
So sánh các thuật toán AI khác nhau
Hãy thêm một số chức năng mới với các thuật toán mới. Nhưng trước tiên, nhấp vào Tệp , sau
đó Lưu với tên ,
và lưu tệp này dưới dạng AISim3.py . Trước dòng in ('Chào mừng bạn đến Reversi!') ,
thêm các chức năng này:
AISim3.py
Mã này có thể được tải xuống từ http://inventwithpython.com/AISim3.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
245. def getRandomMove (bảng, gạch):
246. # Trả lại một nước đi ngẫu nhiên.
247. return Random.choice (getValidMoves (bảng, gạch))
248.
249.
250. def isOnSide (x, y):
251. trả lại x == 0 hoặc x == 7 hoặc y == 0 hoặc y == 7
252.
253.
254. def getCornerSideBestMove (bảng, gạch):
255. # Trả lại di chuyển góc, hoặc di chuyển bên, hoặc tốt nhất
di chuyển.
256.ossibleMoves = getValidMoves (bảng, gạch)
257.
258. # chọn ngẫu nhiên thứ tự di chuyển có thể
259. Random.shuffle (có thểMoves)
260.
261. # luôn luôn đi cho một góc nếu có sẵn.
262. cho x, y trong khả năngMoves:
263.
if isOnC Corner (x, y):
264.
trả lại [x, y]
265.
266. # nếu không có góc, hãy quay lại di chuyển bên.
267. cho x, y trong khả năngMoves:
268.
if isOnSide (x, y):
269.
trả lại [x, y]
270.
271. return getComputerMove (bảng, gạch)
272.
273.
274. def getSideBestMove (bảng, gạch):
275. # Trả lại di chuyển góc, hoặc di chuyển bên, hoặc tốt nhất
di chuyển.
276.ossibleMoves = getValidMoves (bảng, gạch)
277.
278. # chọn ngẫu nhiên thứ tự di chuyển có thể
299
16 - Mô phỏng AI

Trang 314
279. Random.shuffle (có thểMoves)
280.
281. # trả lại một động thái phụ, nếu có sẵn
282. cho x, y trong khả năngMoves:
283.
if isOnSide (x, y):
284.
trả lại [x, y]
285.
286. return getComputerMove (bảng, gạch)
287.
288.
289. def getWorstMove (bảng, gạch):
290. # Trả lại di chuyển lật số lượng ít nhất
gạch lát.
291.ossibleMoves = getValidMoves (bảng, gạch)
292.
293. # chọn ngẫu nhiên thứ tự di chuyển có thể
294. Random.shuffle (có thểMoves)
295.
296. # Đi qua tất cả các động thái có thể và ghi nhớ
di chuyển điểm tốt nhất
297. tệ nhất = 64
298. cho x, y trong khả năngMoves:
299.
dupeBoard = getBoardCopy (bảng)
300.
makeMove (dupeBoard, gạch, x, y)
301.
điểm = getScoreOfBoard (dupeBoard) [gạch]
302.
nếu điểm <badScore:
303.
tệ nhất = [x, y]
304.
badScore = điểm
305.
306. trở về tệ nhất
307.
308.
309. def getCornerWorstMove (bảng, gạch):
310. # Trả lại một góc, một khoảng trắng hoặc di chuyển lật
số lượng gạch ít nhất.
311.ossibleMoves = getValidMoves (bảng, gạch)
312.
313. # chọn ngẫu nhiên thứ tự di chuyển có thể
314. Random.shuffle (có thểMoves)
315.
316. # luôn luôn đi cho một góc nếu có sẵn.
317. cho x, y trong khả năngMoves:
318.
if isOnC Corner (x, y):
319.
trả lại [x, y]
320.
321. return getWorstMove (bảng, gạch)
322.
323.
324.
325. in ('Chào mừng đến với Reversi!')
300

Trang 315
Cách mã AISim3.py hoạt động
Rất nhiều chức năng này rất giống nhau và một số trong số chúng sử dụng cái mới
Hàm isOnSide () . Dưới đây là đánh giá về các thuật toán mới mà chúng tôi đã thực hiện:
So sánh thuật toán ngẫu nhiên với thuật toán thông thường
Bây giờ, điều duy nhất cần làm là thay thế một trong các lệnh gọi getComputerMove () trong
chính
một phần của chương trình với một trong những chức năng mới. Sau đó chúng ta có thể chạy
một số trò chơi và xem
tần suất một thuật toán thắng các thuật toán khác. Đầu tiên, hãy thay thế thuật toán của O bằng
một thuật toán
trong getComputerMove () với getRandomMove () trên dòng 351:
351.
x, y = getRandomMove (mainBoard, 'O')
Khi chúng tôi chạy chương trình với một trăm trò chơi bây giờ, nó có thể trông giống như thế
này:
Chào mừng đến với Reversi!
Nhập số lượng trò chơi để chạy: 100
Trò chơi số 0: X ghi được 25 điểm. O ghi được 38 điểm.
Trò chơi số 1: X ghi được 32 điểm. O ghi được 32 điểm.
Trò chơi số 2: X ghi được 15 điểm. O ghi được 0 điểm.
Trò chơi số 3: X ghi được 50 điểm. O ghi được 14 điểm.
... bỏ qua cho ngắn gọn ...
Trò chơi # 96: X ghi được 31 điểm. O ghi được 33 điểm.
Trò chơi số 97: X ghi được 41 điểm. O ghi được 23 điểm.
Bảng 17-1: Các chức năng được sử dụng cho Reversi AI của chúng tôi.
Chức năng
Sự miêu tả
getRandomMove ()
Chọn ngẫu nhiên một động thái hợp lệ để thực hiện.
getCornerSideBestMove
()
Hãy di chuyển một góc nếu có sẵn. Nếu không có góc,
lấy một khoảng trống ở bên cạnh Nếu không có mặt nào, sử dụng
thuật toán getComputerMove () thông thường .
getSideBestMove ()
Lấy một không gian bên nếu có sẵn. Nếu không thì
sử dụng thuật toán getComputerMove () thông thường (bên
không gian được chọn trước không gian góc).
getWorstMove ()
Lấy không gian sẽ dẫn đến ít gạch nhất
lật.
getCornerWorstMove ()
Lấy một không gian góc, nếu có sẵn. Nếu không, sử dụng
thuật toán getWorstMove () .
301
16 - Mô phỏng AI

Trang 316
Trò chơi số 98: X ghi được 33 điểm. O ghi được 31 điểm.
Trò chơi # 99: X ghi được 45 điểm. O ghi được 19 điểm.
X thắng 84 trận (84,0%), O thắng 15 trận (15,0%),
quan hệ cho 1 trò chơi (1,0%) trong tổng số 100,0 trò chơi.
Ồ X thắng nhiều hơn thường xuyên so với O đã làm. Điều đó có nghĩa là thuật toán trong
getComputerMove () (lấy bất kỳ góc có sẵn nào, nếu không, hãy lấy khoảng trống lật
hầu hết các ô) thắng nhiều trò chơi hơn thuật toán trong getRandomMove () (chỉ tạo ra
di chuyển ngẫu nhiên). Điều này có ý nghĩa, bởi vì việc đưa ra lựa chọn thông minh thường sẽ
tốt hơn là chỉ chọn những thứ ngẫu nhiên.
So sánh thuật toán ngẫu nhiên với chính nó
Điều gì xảy ra nếu chúng tôi thay đổi thuật toán của O để sử dụng thuật toán
trong getRandomMove () ?
Hãy tìm hiểu bằng cách thay đổi lệnh gọi hàm O trên dòng 351 từ getComputerMove () thành
getRandomMove () và chạy lại chương trình.
Chào mừng đến với Reversi!
Nhập số lượng trò chơi để chạy: 100
Trò chơi số 0: X ghi được 37 điểm. O ghi được 24 điểm.
Trò chơi số 1: X ghi được 19 điểm. O ghi được 45 điểm.
... bỏ qua cho ngắn gọn ...
Trò chơi số 98: X ghi được 27 điểm. O ghi được 37 điểm.
Trò chơi # 99: X ghi được 38 điểm. O ghi được 22 điểm.
X thắng 42 trận (42,0%), O thắng 54 trận (54,0%),
quan hệ cho 4 trò chơi (4.0%) trong tổng số 100.0 trò chơi.
Như bạn có thể thấy, khi cả hai người chơi đang thực hiện các bước di chuyển ngẫu nhiên, mỗi
người chơi sẽ giành được khoảng 50%
của thời gian (Trong trường hợp trên, O tình cờ gặp may mắn và giành được nhiều hơn một chút
so với
một nửa thời gian.)
Cũng giống như di chuyển trên các không gian góc là một ý tưởng tốt bởi vì chúng không thể
được lật,
di chuyển trên các mảnh bên cũng có thể là một ý tưởng tốt. Ở bên cạnh, gạch có cạnh của
bảng và không phải là ra ngoài như các phần khác. Các góc vẫn thích hơn
không gian bên, nhưng di chuyển ở hai bên (ngay cả khi có một di chuyển có thể lật nhiều mảnh
hơn)
có thể là một chiến lược tốt
So sánh thuật toán thông thường với CornersSideBest
Thuật toán
Thay đổi thuật toán của X trên dòng 346 để sử dụng getComputerMove () (bản gốc của chúng tôi
thuật toán) và thuật toán của O trên dòng 351 để sử dụng getCornerSideBestMove () và hãy
302

Trang 317
chạy một trăm trò chơi để xem cái nào tốt hơn Hãy thử thay đổi chức năng gọi và chạy
chương trình một lần nữa.
Chào mừng đến với Reversi!
Nhập số lượng trò chơi để chạy: 100
Trò chơi số 0: X ghi được 52 điểm. O ghi được 12 điểm.
Trò chơi số 1: X ghi được 10 điểm. O ghi được 54 điểm.
... bỏ qua cho ngắn gọn ...
Trò chơi số 98: X ghi được 41 điểm. O ghi được 23 điểm.
Trò chơi # 99: X ghi được 46 điểm. O ghi được 13 điểm.
X thắng 65 trận (65,0%), O thắng 31 trận (31,0%),
quan hệ cho 4 trò chơi (4.0%) trong tổng số 100.0 trò chơi.
Ồ Điều đó thật bất ngờ. Có vẻ như việc chọn các không gian bên trên một không gian lật
nhiều gạch là một chiến lược xấu để sử dụng. Lợi ích của không gian bên không lớn hơn chi phí
về việc chọn một không gian lật ít gạch của đối thủ. Chúng ta có thể chắc chắn về những kết quả
này?
Hãy chạy lại chương trình, nhưng lần này hãy để chương trình chơi một nghìn trò chơi.
Điều này có thể mất vài phút để máy tính của bạn chạy (nhưng sẽ mất vài ngày để bạn làm
cái này bằng tay!) Hãy thử thay đổi các cuộc gọi chức năng và chạy lại chương trình.
Chào mừng đến với Reversi!
Nhập số lượng trò chơi để chạy: 1000
Trò chơi số 0: X ghi được 20 điểm. O ghi được 44 điểm.
Trò chơi số 1: X ghi được 54 điểm. O ghi được 9 điểm.
... bỏ qua cho ngắn gọn ...
Trò chơi # 998: X ghi được 38 điểm. O ghi được 23 điểm.
Trò chơi số 999: X ghi được 38 điểm. O ghi được 26 điểm.
X thắng 611 trận (61,1%), O thắng 363 trận
(36,3%), quan hệ cho 26 trò chơi (2,6%) trong số 1000.0 trò chơi
toàn bộ.
Số liệu thống kê chính xác hơn từ hàng nghìn trò chơi đang chạy giống như
thống kê từ hàng trăm trò chơi chạy. Có vẻ như việc chọn di chuyển lật nhiều nhất
gạch là một ý tưởng tốt hơn so với việc chọn một bên di chuyển.
So sánh thuật toán thông thường với thuật toán tệ nhất
Bây giờ, đặt thuật toán của trình phát X trên dòng 346 để sử dụng getComputerMove () và O
thuật toán của người chơi trên dòng 351 để getWorstMove () và chạy hàng trăm trò chơi. Thử
thay đổi chức năng gọi và chạy lại chương trình.
303
16 - Mô phỏng AI

Trang 318
Chào mừng đến với Reversi!
Nhập số lượng trò chơi để chạy: 100
Trò chơi số 0: X ghi được 50 điểm. O ghi được 14 điểm.
Trò chơi số 1: X ghi được 38 điểm. O ghi được 8 điểm.
... bỏ qua cho ngắn gọn ...
Trò chơi số 98: X ghi được 36 điểm. O ghi được 16 điểm.
Trò chơi # 99: X ghi được 19 điểm. O ghi được 0 điểm.
X thắng 98 trò chơi (98,0%), O thắng 2 trò chơi (2,0%),
quan hệ cho 0 trò chơi (0,0%) trong tổng số 100,0 trò chơi.
Ái chà! Thuật toán trong getWorstMove () , luôn chọn di chuyển lật
ít gạch nhất, hầu như sẽ luôn thua thuật toán thông thường của chúng tôi. Điều này không thực
sự đáng ngạc nhiên
ở tất cả.
So sánh thuật toán thông thường chống lại điều tồi tệ nhất
Thuật toán
Làm thế nào về khi chúng ta thay thế getWorstMove () trên dòng 351 bằng
getCornerWorstMove () , là thuật toán tương tự ngoại trừ nó có sẵn bất kỳ
góc miếng. Hãy thử thay đổi các cuộc gọi chức năng và chạy lại chương trình.
Chào mừng đến với Reversi!
Nhập số lượng trò chơi để chạy: 100
Trò chơi số 0: X ghi được 36 điểm. O ghi được 7 điểm.
Trò chơi số 1: X ghi được 44 điểm. O ghi được 19 điểm.
... bỏ qua cho ngắn gọn ...
Trò chơi số 98: X ghi được 47 điểm. O ghi được 17 điểm.
Trò chơi # 99: X ghi được 36 điểm. O ghi được 18 điểm.
X thắng 94 trận (94,0%), O thắng 6 trận (6,0%),
quan hệ cho 0 trò chơi (0,0%) trong tổng số 100,0 trò chơi.
Các getCornerWorstMove () vẫn mất hầu hết các trò chơi, nhưng có vẻ như để giành chiến thắng
một
ít trò chơi hơn getWorstMove () (6% so với 2%). Có lấy góc không
không gian khi chúng có sẵn thực sự làm cho một sự khác biệt?
So sánh thuật toán tệ nhất với thuật toán xấu nhất
Chúng ta có thể kiểm tra bằng cách đặt thuật toán của X thành thuật toán getWorstMove () và O
thành
getCornerWorstMove () , rồi chạy chương trình. Hãy thử thay đổi chức năng
304

Trang 319
gọi và chạy chương trình một lần nữa.
Chào mừng đến với Reversi!
Nhập số lượng trò chơi để chạy: 100
Trò chơi số 0: X ghi được 25 điểm. O ghi được 39 điểm.
Trò chơi số 1: X ghi được 26 điểm. O ghi được 33 điểm.
... bỏ qua cho ngắn gọn ...
Trò chơi số 98: X ghi được 36 điểm. O ghi được 25 điểm.
Trò chơi # 99: X ghi được 29 điểm. O ghi được 35 điểm.
X thắng 32 trận (32,0%), O thắng 67 trận (67,0%),
quan hệ cho 1 trò chơi (1,0%) trong tổng số 100,0 trò chơi.
Vâng, có vẻ như lấy thuật toán lấy các góc khi nó có thể
dịch thành nhiều chiến thắng hơn. Trong khi chúng tôi đã phát hiện ra rằng đi về phía khiến bạn
thua cuộc
thường xuyên hơn, đi cho các góc luôn luôn là một ý tưởng tốt.
Học những điều mới bằng cách chạy mô phỏng
Thí nghiệm
Chương này không thực sự bao gồm một trò chơi, nhưng nó mô hình hóa các chiến lược khác
nhau cho Reversi. Nếu
chúng tôi nghĩ rằng việc di chuyển bên trong Reversi là một ý tưởng tốt, chúng tôi sẽ phải chi
tiêu
nhiều ngày, thậm chí vài tuần, cẩn thận chơi các trò chơi của Reversi bằng tay và viết ra kết quả.
Nhưng nếu chúng ta biết cách lập trình máy tính để chơi Reversi, thì chúng ta có thể có máy tính
chơi Reversi bằng cách sử dụng các chiến lược này cho chúng tôi. Nếu bạn nghĩ về nó, bạn sẽ
nhận ra rằng
máy tính đang thực thi hàng triệu dòng chương trình Python của chúng tôi trong vài giây! Của
bạn
các thử nghiệm với mô phỏng Reversi có thể giúp bạn tìm hiểu thêm về cách chơi Reversi
trong đời thực.
Trong thực tế, chương này sẽ làm cho một dự án hội chợ khoa học tốt. Vấn đề của bạn có thể là
bộ di chuyển dẫn đến nhiều chiến thắng nhất so với các bộ di chuyển khác và đưa ra một giả
thuyết
về đó là chiến lược tốt nhất. Sau khi chạy một vài mô phỏng, bạn có thể xác định
chiến lược nào hoạt động tốt nhất. Bạn có thể thực hiện một dự án hội chợ khoa học từ một mô
phỏng của bất kỳ
trò chơi trên bàn cờ! Và đó là tất cả bởi vì bạn biết chính xác làm thế nào để hướng dẫn máy tính
làm điều đó,
Từng bước, từng dòng một. Bạn có thể nói ngôn ngữ của máy tính và làm cho nó phát triển lớn
số lượng xử lý dữ liệu và số giòn cho bạn.
Đó là tất cả cho các trò chơi dựa trên văn bản trong cuốn sách này. Các trò chơi chỉ sử dụng văn
bản có thể thú vị,
mặc dù có đơn giản. Nhưng hầu hết các game hiện đại đều sử dụng đồ họa, âm thanh và hoạt
hình để
làm cho trò chơi tìm kiếm thú vị hơn nhiều. Đối với phần còn lại của các chương trong cuốn sách
này, chúng tôi sẽ
tìm hiểu cách tạo trò chơi với đồ họa bằng cách sử dụng mô-đun Python có tên Pygame.
304
16 - Mô phỏng AI

Trang 320
Các chủ đề được đề cập trong Chương này:
● Thư viện phần mềm
● Cài đặt Pygame

● Giao diện người dùng đồ họa (GUI)

● Vẽ nguyên thủy

● Tạo cửa sổ GUI với Pygame

● Màu sắc trong Pygame

● Phông chữ trong Pygame

● Đồ họa có răng cưa và chống răng cưa

● Các thuộc tính

● Các pygame.font.Font Data Type

● Các pygame.Surface Data Type

● Các pygame.Rect Data Type

● Các pygame.PixelArray Data Type

● Hàm xây dựng

● Các loại () Chức năng

● Chức năng vẽ của Pygame

● Các blit () Phương pháp cho Surface Objects

● Sự kiện

● Vòng lặp trò chơi

● Hoạt hình

Cho đến nay, tất cả các trò chơi của chúng tôi chỉ sử dụng văn bản. Văn bản được hiển thị trên
màn hình dưới dạng đầu ra,
và trình phát gõ văn bản từ bàn phím làm đầu vào. Điều này là đơn giản và là một cách dễ dàng
để
học lập trình. Nhưng trong chương này, chúng tôi sẽ tạo ra một số trò chơi thú vị hơn với
đồ họa và âm thanh tiên tiến sử dụng thư viện Pygame. Chương 17, 18 và 19 sẽ dạy
306

Trang 321
bạn làm thế nào để sử dụng thư viện Pygame để tạo ra các trò chơi có đồ họa, hoạt hình và âm thanh.
Trong các chương này, chúng tôi sẽ tìm mã nguồn cho các chương trình đơn giản không phải là
trò chơi nhưng
chứng minh các khái niệm Pygame chúng ta đã học. Chương 20 sẽ trình bày mã nguồn
cho một trò chơi Pygame hoàn chỉnh sử dụng tất cả các khái niệm bạn đã học.
Một thư viện phần mềm là mã không có nghĩa là tự chạy, nhưng được bao gồm trong khác
các chương trình để thêm các tính năng mới. Bằng cách sử dụng một thư viện, một lập trình viên
không phải viết
toàn bộ chương trình, nhưng có thể sử dụng công việc mà một lập trình viên khác đã làm trước
đó
họ Pygame là một thư viện phần mềm có các mô-đun cho đồ họa, âm thanh và các tính năng
khác
mà các trò chơi thường sử dụng.
Cài đặt Pygame
Pygame không đi kèm với Python. Giống như Python, Pygame có sẵn miễn phí. Bạn sẽ
phải tải xuống và cài đặt Pygame, dễ dàng như tải xuống và cài đặt
Thông dịch viên Python. Trong trình duyệt web, hãy truy cập URL http://pygame.org và nhấp
vào
Liên kết "Tải xuống" ở bên trái của trang web. Cuốn sách này giả định rằng bạn có
Hệ điều hành Windows, nhưng Pygame hoạt động giống nhau cho mọi hệ điều hành. Bạn
cần tải xuống trình cài đặt Pygame cho hệ điều hành của bạn và phiên bản Python
bạn đã cài đặt (3.1).
Bạn không muốn tải xuống "nguồn" cho Pygame, mà là Pygame cho bạn
hệ điều hành. Đối với Windows, tải xuống tệp pygame-1.9.1.win32-py3.1.msi . (Đây là
Pygame cho Python 3.1 trên Windows. Nếu bạn đã cài đặt một phiên bản Python khác (chẳng
hạn như
2.5 hoặc 2.4) tải xuống tệp .msi cho phiên bản Python của bạn.) Phiên bản hiện tại của
Pygame tại thời điểm cuốn sách này được viết là 1.9.1. Nếu bạn thấy một phiên bản mới hơn trên
trang web, tải xuống và cài đặt Pygame mới hơn. Đối với Mac OS X và Linux, hãy làm theo
hướng dẫn trên trang tải về để được hướng dẫn cài đặt.
Hình 17-1: Trang web pygame.org.
Trên Windows, nhấp đúp chuột vào tệp đã tải xuống để cài đặt Pygame. Để kiểm tra
Pygame được cài đặt chính xác, nhập nội dung sau vào vỏ tương tác:
>>> nhập khẩu pygame
307
17 - Đồ họa và hoạt hình

Trang 322
Nếu không có gì xuất hiện sau khi bạn nhấn phím Enter, thì bạn biết Pygame đã thành công
đã được cài đặt. Nếu lỗi ImportError: Không có mô-đun có tên pygame xuất hiện,
sau đó thử cài đặt lại Pygame (và đảm bảo bạn đã nhập chính xác nhập pygame ).
Chương này có năm chương trình nhỏ trình bày cách sử dụng các tính năng khác nhau
mà Pygame cung cấp. Trong chương cuối, bạn sẽ sử dụng các tính năng này cho một trò chơi
hoàn chỉnh
được viết bằng Python với Pygame.
Hướng dẫn bằng video về cách cài đặt Pygame có sẵn từ trang web của cuốn sách này tại
http://inventwithpython.com/ideo/.
Xin chào thế giới ở Pygame
Chúng tôi sẽ tạo ra một "Hello World!" Mới chương trình, giống như bạn đã tạo tại
bắt đầu của cuốn sách. Lần này, chúng tôi sẽ sử dụng Pygame để thực hiện "Xin chào thế
giới!" xuất hiện trong một
giao diện người dùng đồ họa (GUI, được phát âm là cửa sổ "gooey"). Đồ họa
giao diện người dùng cung cấp cho bạn một cửa sổ mà màu sắc, hình dạng và hình ảnh có thể
được vẽ bởi
chương trình, cũng như chấp nhận đầu vào chuột (và không chỉ đầu vào bàn phím). Các hình
dạng cơ bản
mà chúng ta vẽ trên màn hình được gọi là vẽ nguyên thủy . Cửa sổ GUI được sử dụng thay thế
của cửa sổ văn bản (còn được gọi là cửa sổ giao diện điều khiển hoặc cửa sổ đầu cuối ) mà
chúng tôi đã sử dụng
cho tất cả các trò chơi trước đây của chúng tôi.
Pygame không hoạt động tốt với trình vỏ tương tác vì nó dựa vào vòng lặp trò chơi
(chúng tôi sẽ mô tả các vòng lặp trò chơi sau). Vì điều này, bạn chỉ có thể viết các chương trình
Pygame
và không thể gửi lệnh tới Pygame một lần thông qua trình vỏ tương tác.
Các chương trình Pygame cũng không sử dụng hàm input () . Không có nhập văn bản và
đầu ra. Thay vào đó, chương trình hiển thị đầu ra trong một cửa sổ bằng cách vẽ đồ họa và văn
bản tới
cửa sổ. Đầu vào của chương trình Pygame đến từ bàn phím và chuột thông qua
những thứ gọi là sự kiện, mà chúng ta sẽ đi qua trong chương tiếp theo. Tuy nhiên, nếu chương
trình của chúng tôi
có lỗi khiến Python hiển thị thông báo lỗi, thông báo lỗi sẽ hiển thị trong
cửa sổ giao diện điều khiển.
Bạn cũng có thể sử dụng lệnh gọi print () để hiển thị văn bản trong cửa sổ giao diện điều khiển,
tuy nhiên trong
Pygame các print () chức năng chỉ được sử dụng cho tạm in thông điệp để giúp bạn
tìm lỗi trong chương trình của bạn Nó có thể hữu ích để in ra các giá trị của các biến trong khi
của bạn
chương trình đang chạy để bạn có thể chắc chắn rằng nó đang hoạt động chính xác.
Bạn cũng có thể tra cứu thông tin về cách sử dụng thư viện Pygame bằng cách truy cập
trang web http://pygame.org/docs/ref/.
Xin chào mã nguồn thế giới
Nhập mã sau vào trình chỉnh sửa tệp và lưu mã dưới dạng pygameHelloWorld.py . Hoặc là
bạn có thể tải xuống mã nguồn này bằng cách truy cập trang web của cuốn sách này tại
http://inventwithpython.com/ch CHƯƠNG17
308

Trang 323
pygameHelloWorld.py
Mã này có thể được tải xuống từ http://inventwithpython.com/pygameHelloWorld.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. nhập pygame, sys
2. từ nhập pygame.locals *
3.
4. # thiết lập pygame
5. pygame.init ()
6.
7. # thiết lập cửa sổ
8. windowSurface = pygame.display.set_mode ((500, 400), 0,
32)
9. pygame.display.set_caption ('Xin chào thế giới!')
10.
11. # thiết lập màu sắc
12. ĐEN = (0, 0, 0)
13. TRẮNG = (255, 255, 255)
14. ĐỎ = (255, 0, 0)
15. XANH = (0, 255, 0)
16. XANH = (0, 0, 255)
17.
18. # thiết lập phông chữ
19. basicFont = pygame.font.SysFont (Không có, 48)
20.
21. # thiết lập văn bản
22. text = basicFont.render ('Xin chào thế giới!', Đúng, TRẮNG,
MÀU XANH DA TRỜI)
23. textRect = text.get_rect ()
24. textRect.centerx = windowSurface.get_rect (). Centerx
25. textRect.centery = windowSurface.get_rect (). Centery
26.
27. # vẽ nền trắng lên bề mặt
28. windowSurface.fill (TRẮNG)
29.
30. # vẽ một đa giác màu xanh lá cây lên bề mặt
31. pygame.draw.polygon (windowSurface, XANH, ((146, 0),
(291, 106), (236, 277), (56, 277), (0, 106)))
32.
33. # vẽ một số đường màu xanh lên bề mặt
34. pygame.draw.line (windowSurface, BLUE, (60, 60), (120,
60), 4)
35. pygame.draw.line (windowSurface, BLUE, (120, 60), (60,
120))
36. pygame.draw.line (windowSurface, BLUE, (60, 120), (120,
120), 4)
37.
38. # vẽ một vòng tròn màu xanh lên bề mặt
39. pygame.draw.circle (windowSurface, BLUE, (300, 50), 20, 0)
40.
41. # vẽ một hình elip màu đỏ lên bề mặt
42. pygame.draw.ellipse (windowSurface, RED, (300, 250, 40,
309
17 - Đồ họa và hoạt hình

Trang 324
80), 1)
43.
44. # vẽ hình chữ nhật nền của văn bản lên bề mặt
45. pygame.draw.rect (windowSurface, RED, (textRect.left - 20,
textRect.top - 20, textRect. thong + 40, textRect.height +
40)
46.
47. # lấy một mảng pixel của bề mặt
48. pixArray = pygame.PixelArray (windowSurface)
49. pixArray [480] [380] = ĐEN
50. del pixArray
51.
52. # vẽ văn bản lên bề mặt
53. windowSurface.blit (text, textRect)
54.
55. # vẽ cửa sổ lên màn hình
56. pygame.display.update ()
57.
58. # chạy vòng lặp trò chơi
59. trong khi Đúng:
60. cho sự kiện trong pygame.event.get ():
61.
if event.type == QUIT:
62.
pygame.quito ()
63.
sys.exit ()

Chạy chương trình Hello World


Khi bạn chạy chương trình này, bạn sẽ thấy một cửa sổ GUI mới xuất hiện trông giống như
Hình 17-2.
Điều tuyệt vời khi sử dụng GUI thay vì bảng điều khiển là văn bản có thể xuất hiện ở bất cứ đâu
trong cửa sổ, không chỉ sau văn bản trước mà chúng tôi đã in. Văn bản có thể là bất kỳ màu nào
hoặc
kích thước.
Một điều bạn có thể nhận thấy là Pygame sử dụng rất nhiều bộ dữ liệu thay vì danh sách. Nhớ lại
các bộ dữ liệu gần giống như danh sách (chúng có thể chứa các giá trị khác) ngoại trừ chúng
được nhập
với dấu ngoặc đơn ( và ) , thay vì dấu ngoặc vuông [ và ] . Sự khác biệt chính là
một khi bạn tạo một tuple, bạn không thể thay đổi, thêm hoặc xóa bất kỳ giá trị nào trong
tuple. Dành cho
lý do kỹ thuật, biết rằng nội dung của bộ dữ liệu không bao giờ thay đổi cho phép Python
xử lý dữ liệu này hiệu quả hơn, đó là lý do tại sao Pygame sử dụng bộ dữ liệu thay vì danh sách.
310

Trang 325
Hình 17-2: Chương trình "Hello World".
Nhập mô-đun Pygame
Chúng ta hãy đi qua từng dòng mã này và tìm hiểu những gì họ làm.
1. nhập pygame, sys
2. từ nhập pygame.locals *
Đầu tiên chúng ta cần nhập mô-đun pygame để có thể gọi các hàm trong Pygame
thư viện phần mềm. Bạn có thể nhập một số mô-đun trên cùng một dòng bằng cách phân định
tên mô-đun với dấu phẩy. Dòng 1 nhập cả hai mô-đun pygame và sys .
Dòng thứ hai nhập mô-đun pygame.locals . Mô-đun này chứa nhiều
các biến không đổi mà chúng ta sẽ sử dụng với Pygame như QUIT hoặc K_ESCAPE (mà chúng
ta
sẽ giải thích sau). Tuy nhiên, bằng cách sử dụng biểu mẫu từ nhập tên module * chúng ta có thể
nhập mô-đun pygame.locals nhưng không phải nhập pygame.locals trước
mỗi lần chúng tôi sử dụng các chức năng và biến của mô-đun trong chương trình của chúng
tôi. Các * biểu tượng
có nghĩa là chúng ta nên nhập mọi thứ bên trong mô-đun.
Các pygame.locals mô-đun chứa một số biến liên tục, chúng tôi sẽ sử dụng trong này
chương trình . Nếu bạn có từ nhập sys * thay vì nhập sys trong chương trình của bạn,
311
17 - Đồ họa và hoạt hình

Trang 327
bạn có thể gọi exit () thay vì sys.exit () trong mã của mình. (Nhưng hầu hết thời gian
tốt hơn là sử dụng tên hàm đầy đủ để bạn biết mô-đun thoát () nằm trong.)
Các pygame.init () Chức năng
4. # thiết lập pygame
5. pygame.init ()
Thư viện phần mềm Pygame có một số mã ban đầu cần được chạy trước khi chúng ta có thể
sử dụng nó. Tất cả các chương trình Pygame phải chạy mã này bằng cách gọi pygame.init () sau
nhập mô-đun pygame nhưng trước khi gọi bất kỳ chức năng Pygame nào khác.
Các pygame.display.set_mode () và
pygame.display.set_caption () Chức năng
7. # thiết lập cửa sổ
8. windowSurface = pygame.display.set_mode ((500, 400), 0,
32)
9. pygame.display.set_caption ('Xin chào thế giới!')
Dòng 8 tạo một cửa sổ GUI cho chương trình của chúng tôi bằng cách gọi phương
thức set_mode () trong
các pygame.display module. ( Mô-đun hiển thị là một mô-đun bên trong pygame
mô-đun. Pygame tiên tiến đến mức ngay cả mô-đun pygame cũng có mô-đun riêng!)
Chỉ để tránh nhầm lẫn, bạn nên biết sự khác biệt giữa cửa sổ
tạo là khác nhau và hệ điều hành Windows. Giao diện người dùng đồ họa là
được in dưới dạng "cửa sổ" (chữ thường và số ít) và hệ điều hành Microsoft là
"Windows" (chữ hoa và số nhiều).
Có ba tham số cho phương thức set_mode () . Tham số đầu tiên là một tuple của
hai số nguyên cho chiều rộng và chiều cao của cửa sổ, tính bằng pixel. Một pixel là dấu chấm
nhỏ nhất trên
màn hình máy tính của bạn. Một pixel trên màn hình của bạn có thể biến thành bất kỳ màu
nào. Tất cả các pixel
trên màn hình của bạn làm việc cùng nhau để hiển thị tất cả các hình ảnh bạn nhìn thấy. Để xem
một pixel nhỏ như thế nào,
nhìn vào góc dưới bên phải của "Hello World!" cửa sổ. Chương trình này chỉ thiết lập một
pixel như màu trắng.
Chúng tôi muốn cửa sổ rộng 500 pixel và cao 400 pixel, vì vậy chúng tôi sử dụng bộ dữ liệu
(500, 400) cho tham số đầu tiên. Để có được tổng số pixel trong cửa sổ của chúng tôi,
nhân chiều rộng và chiều cao. Cửa sổ của chúng tôi được tạo thành từ 20.000 pixel và không
thậm chí chiếm toàn bộ màn hình máy tính!
Tham số thứ hai dành cho các tùy chọn cửa sổ GUI nâng cao. Bạn sẽ không thực sự cần điều này
cho các trò chơi của bạn, vì vậy bạn luôn có thể vượt qua 0 cho tham số này. Tham số thứ ba là
312

Trang 327
một tùy chọn nâng cao khác gọi là độ sâu màu. Bạn chỉ có thể vượt qua giá trị 32.
Cuộc gọi set_caption () trả về một đối tượng pygame.Surface (mà chúng ta sẽ gọi
Các đối tượng bề mặt cho ngắn). Đối tượng là các giá trị của kiểu dữ liệu có phương thức cũng
như
dữ liệu. Ví dụ: chuỗi là các đối tượng trong Python vì chúng có dữ liệu (chính chuỗi)
và các phương thức (chẳng hạn như thấp hơn () và split () ). Bạn có thể lưu trữ các đối tượng
trong các biến chỉ
như bất kỳ giá trị nào khác Các bề mặt vật thể đại diện cho cửa sổ và chúng tôi sẽ bao gồm các
biến windowSurface trong tất cả các lệnh gọi hàm vẽ của chúng ta.
Biến tham chiếu cửa hàng tham chiếu đến các đối tượng
Bạn nên biết rằng các biến không bao giờ giữ các đối tượng (bao gồm danh sách và từ điển),
chúng
chỉ giữ các tham chiếu đến các đối tượng. Đây chính xác là cách mà các biến không bao giờ giữ
danh sách
nhưng chỉ giữ tài liệu tham khảo cho danh sách. Sự khác biệt giữa giữ vật và giữ
tham chiếu đến đối tượng là nếu bạn sao chép biến sang biến thứ hai, mọi thay đổi
thực hiện cho đối tượng trong một trong các biến cũng sẽ thay đổi đối tượng trong biến
khác. Điều này
là bởi vì cả hai biến giữ tham chiếu đến cùng một đối tượng vì chỉ là một bản sao của
tài liệu tham khảo đã được thực hiện, không phải là một bản sao của đối tượng.
Dưới đây là một ví dụ với các danh sách (giống như trong chương Hangman). Nhập nội dung sau
vào
vỏ tương tác:
>>> x = ['a', 'b', 'c']
>>> y = x
>>> x [2] = 'Xin chào!'
>>> in (y)
['A', 'b', 'Xin chào!']
Lưu ý rằng việc thay đổi danh sách x cũng đã thay đổi danh sách y , vì cả hai đều chứa
tài liệu tham khảo cho cùng một danh sách. y đã tạo một bản sao của tài liệu tham khảo trong x ,
không phải là bản sao của danh sách.
Áp dụng tương tự cho các đối tượng. Hãy xem xét các mã sau đây:
>>> nhập khẩu pygame
>>> pygame.init ()
>>> windowSurface = pygame.display.set_mode ((500,
500), 0, 32)
>>> secondSurface = windowSurface
windowSurface và secondSurface chứa các tham chiếu đến cùng một Surface
vật. Mọi thay đổi được thực hiện cho windowSurface sẽ thay đổi cùng một đối tượng
secondSurface tài liệu tham khảo. Điều tương tự cũng đúng với mọi thay đổi đối
với windowSurface
sẽ thay đổi windowSurface .
313
17 - Đồ họa và hoạt hình

Trang 328
Màu sắc trong Pygame
11. # thiết lập màu sắc
12. ĐEN = (0, 0, 0)
13. TRẮNG = (255, 255, 255)
14. ĐỎ = (255, 0, 0)
15. XANH = (0, 255, 0)
16. XANH = (0, 0, 255)
Có ba màu chính của ánh sáng: đỏ, xanh lá cây
và màu xanh. Bằng cách kết hợp số lượng khác nhau của những
ba màu bạn có thể tạo thành bất kỳ màu nào khác. Trong Python
chúng tôi đại diện cho màu sắc với bộ ba số nguyên. Các
Giá trị đầu tiên trong bộ dữ liệu là bao nhiêu màu đỏ trong
màu sắc. Giá trị 0 có nghĩa là không có màu đỏ trong này
màu sắc và giá trị 255 có nghĩa là có tối đa
số lượng màu đỏ trong màu sắc. Giá trị thứ hai là cho
màu xanh lá cây và giá trị thứ ba là cho màu xanh.
Ví dụ: chúng tôi sẽ tạo bộ dữ liệu (0, 0, 0)
và lưu trữ nó trong một biến có tên BLACK . Với không
số lượng màu đỏ, xanh lá cây hoặc xanh dương, màu kết quả là
hoàn toàn màu đen. Màu đen là sự vắng mặt của
bất cứ màu nào.
Trên dòng 13, chúng tôi sử dụng bộ dữ liệu (255, 255, 255)
cho tối đa màu đỏ, xanh lá cây và xanh dương
kết quả là màu trắng. Màu trắng là đầy đủ
sự kết hợp của màu đỏ, xanh lá cây và xanh dương. Chúng tôi lưu trữ này
tuple trong biến TRẮNG . (255, 0, 0)
đại diện cho số lượng tối đa của màu đỏ nhưng không
lượng màu xanh lá cây và màu xanh lam, vì vậy màu kết quả là
đỏ. Tương tự, (0, 255, 0) có màu xanh lá cây và (0, 0,
255) có màu xanh.
Các tên biến này nằm trong tất cả các chữ viết hoa vì chúng là các biến không đổi. Nó chỉ
dễ dàng nhập BLACK trong mã của chúng tôi hơn (0, 0, 0) mỗi lần chúng tôi muốn chỉ định
màu đen, vì vậy chúng tôi thiết lập các biến màu này khi bắt đầu chương trình của chúng tôi.
Nếu bạn muốn làm cho màu nhạt hơn, hãy thử thêm một lượng bằng nhau từ cả ba giá trị.
Ví dụ: giá trị RGB cho màu xám là (128, 128, 128) . Bạn có thể nhận được RGB
giá trị cho màu xám nhạt hơn bằng cách thêm 20 vào mỗi giá trị để nhận (148, 148, 148) . Bạn có
thể
lấy giá trị RGB cho màu xám đậm hơn bằng cách trừ 20 từ mỗi giá trị để nhận (108,
108, 108) . Và bạn có thể nhận giá trị RGB cho màu xám hơi đỏ hơn bằng cách thêm 20 vào
chỉ giá trị màu đỏ để có được (148, 128, 128) . Bảng 17-1 có một số màu phổ biến và
Bảng 17-1: Màu sắc và RGB của chúng
các giá trị.
Màu sắc
Giá trị RGB
Thủy
(0, 255, 255)
Đen
(0, 0, 0)
Màu xanh da trời
(0, 0, 255)
Màu xanh hoa ngô (100, 149, 237)
Fuchsia
(255, 0, 255)
Xám
(128, 128, 128)
màu xanh lá
(0, 128, 0)
Vôi
(0, 255, 0)
Bỏ rơi
(128, 0, 0)
Màu xanh hải quân
(0, 0, 128)
Ôliu
(128, 128, 0)
Màu tím
(128, 0, 128)
Màu đỏ
(255, 0, 0)
Bạc
(192, 192, 192)
Teal
(0, 128, 128)
trắng
(255, 255, 255)
Màu vàng
(255, 255, 0)
314

Trang 329
Phông chữ và Hàm pygame.font.SysFont ()
18. # thiết lập phông chữ
19. basicFont = pygame.font.SysFont (Không có, 48)

Các render () Phương pháp Font Objects


21. # thiết lập văn bản
22. text = basicFont.render ('Xin chào thế giới!', Đúng, TRẮNG,
MÀU XANH DA TRỜI)
23. textRect = text.get_rect ()
giá trị RGB của chúng.
Một phông chữ là một tập hợp đầy đủ các chữ cái,
số, ký hiệu và ký tự của
một phong cách duy nhất. Đây là một ví dụ về
cùng một câu được in khác nhau
phông chữ:
Trong các trò chơi trước đây của chúng tôi, chúng tôi chỉ nói
Python để in văn bản. Màu,
kích thước và phông chữ được sử dụng để hiển thị
văn bản này đã được xác định hoàn toàn
bằng bất cứ phông chữ nào hoạt động của bạn
hệ thống sử dụng cho các cửa sổ điều khiển. Các chương trình của chúng tôi không thể thay đổi
phông chữ.
Tuy nhiên, vì chúng tôi sẽ vẽ các chữ cái cho một cửa sổ GUI, chúng tôi cần nói với Pygame
chính xác những phông chữ để sử dụng khi vẽ văn bản.
Trên dòng 19, chúng ta tạo một đối tượng pygame.font.Font (mà chúng ta sẽ chỉ gọi là Font
viết tắt là các đối tượng) bằng cách gọi hàm pygame.font.SysFont () . Đầu tiên
tham số là tên của phông chữ, nhưng chúng ta sẽ chuyển giá trị Không để sử dụng mặc định
phông chữ hệ thống. Tham số thứ hai sẽ là kích thước của phông chữ. Trong cuộc gọi của chúng
tôi trên đường 19, chúng tôi
muốn cỡ chữ là 48 điểm.
Hình 17-3: Ví dụ về các phông chữ khác nhau.
Các Font đối tượng mà chúng ta đã lưu trữ trong basicFont biến có một phương pháp gọi là
kết xuất () . Phương pháp này sẽ tạo ra một đối tượng Surface với văn bản được vẽ trên đó. Các
tham số đầu tiên để render () là chuỗi của văn bản cần vẽ. Tham số thứ hai là một
boolean cho dù chúng ta có muốn khử răng cưa hay không. Chống răng cưa là một kỹ thuật để
thực hiện một
vẽ trông bớt khối. Trên dòng 22, chúng tôi vượt qua True để nói rằng chúng tôi muốn sử dụng
khử răng cưa.
Hình 17-4 là một ví dụ về hình dạng của một dòng (khi chúng ta phóng to các pixel riêng lẻ)
thích và không có khử răng cưa:
315
17 - Đồ họa và hoạt hình

Trang 330
Thuộc tính
24. textRect.centerx = windowSurface.get_rect (). Centerx
25. textRect.centery = windowSurface.get_rect (). Centery
Các pygame.Rect kiểu dữ liệu (mà chúng tôi sẽ chỉ cần gọi Rect cho ngắn) làm cho làm việc
với những thứ hình chữ nhật dễ dàng. Để tạo một đối tượng Rect mới, hãy gọi hàm
pygame.Rect () . Các tham số là số nguyên cho tọa độ XY ở trên cùng bên trái
góc, tiếp theo là chiều rộng và chiều cao. Các số nguyên này có số lượng pixel.
Tên hàm với các tham số trông như thế này: pygame.Rect (trái, trên cùng,
chiều rộng, chiều cao)
Giống như các phương thức là các hàm được liên kết với một đối tượng, các thuộc tính là
các biến được liên kết với một đối tượng. Kiểu dữ liệu Rect (nghĩa là kiểu dữ liệu của tất cả
Các đối tượng Rect ) có nhiều thuộc tính mô tả hình chữ nhật mà chúng đại diện. Đây là một
danh sách
thuộc tính của một đối tượng Rect có tên myRect :
Khử răng cưa có thể làm cho văn bản và dòng của bạn
nhìn mờ nhưng mượt mà hơn. Phải mất thêm một chút
Thời gian tính toán để khử răng cưa, mặc dù vậy
đồ họa có thể trông tốt hơn, chương trình của bạn có thể
chạy chậm hơn (nhưng chỉ một chút thôi).
Hình 17-4: Một dòng răng cưa và một dòng chống răng cưa.
pygame.Rect
Thuộc tính
Sự miêu tả
myRect.left
Giá trị int của tọa độ X ở phía bên trái của
hình chữ nhật.
myRect.right
Giá trị int của tọa độ X của bên phải của
hình chữ nhật.
myRect.top
Giá trị int của tọa độ Y ở phía trên cùng của
hình chữ nhật.
myRect.bottom
Giá trị int của tọa độ Y ở phía dưới cùng của
hình chữ nhật.
myRect.centerx
Giá trị int của tọa độ X của tâm của
hình chữ nhật.
Giá trị int của tọa độ Y của tâm của
316

Trang 331
Điều tuyệt vời về các đối tượng Rect là nếu bạn sửa đổi bất kỳ biến nào trong số này, tất cả các
các biến khác cũng sẽ tự động sửa đổi. Ví dụ: nếu bạn tạo một
Rect đối tượng đó là 20 pixels rộng và cao 20 pixel, và có góc trên bên trái tại
tọa độ (30, 40), sau đó tọa độ X của bên phải sẽ tự động được đặt thành 50
(vì 20 + 30 = 50). Tuy nhiên, nếu bạn thay đổi thuộc tính bên trái với dòng
myRect.left = 100 , sau đó Pygame sẽ tự động thay đổi thuộc tính đúng thành
120 (vì 20 + 100 = 120). Mọi thuộc tính khác cho đối tượng Rect đó cũng sẽ là
cập nhật là tốt.
Các phương thức get_rect () cho pygame.font.Font
và pygame. Đối tượng mặt
Lưu ý rằng cả đối tượng Phông chữ (được lưu trữ trong biến văn bản ) và đối tượng Surface
(được lưu trữ trong biến windowSurface ) cả hai đều có một phương thức gọi là get_rect () .
Về mặt kỹ thuật, đây là hai phương pháp khác nhau. Nhưng các lập trình viên của Pygame đã
cho họ
cùng tên vì cả hai đều làm cùng một thứ và trả về các đối tượng Rect đại diện
kích thước và vị trí của đối tượng Font hoặc Surface .
Ngoài ra, hãy nhớ rằng pygame là một mô-đun mà chúng ta nhập khẩu và bên trong pygame
mô-đun là các mô-đun phông chữ và bề mặt . Bên trong các mô-đun đó là Phông chữ và
Các loại dữ liệu bề mặt . Các lập trình viên Pygame đã tạo ra các mô-đun bắt đầu bằng chữ
thường
chữ cái và các kiểu dữ liệu bắt đầu bằng một chữ cái viết hoa. Điều này giúp dễ dàng phân biệt
các kiểu dữ liệu và các mô-đun mà các kiểu dữ liệu có thể được tìm thấy.
Hàm xây dựng và hàm type () .
Chúng ta tạo một đối tượng pygame.Rect bằng cách gọi một hàm có tên pygame.Rect () . Các
Hàm pygame.Rect () có cùng tên với kiểu dữ liệu pygame.Rect .
myRect.centery
hình chữ nhật.
myRect. thong
Giá trị int của chiều rộng của hình chữ nhật.
myRect.height
Giá trị int của chiều cao của hình chữ nhật.
myRect.size
Một bộ gồm hai số nguyên: (chiều rộng, chiều cao)
myRect.topleft
Một bộ gồm hai số nguyên: (trái, trên cùng)
myRect.topright
Một bộ gồm hai số nguyên: (phải, trên cùng)
myRect.bottomleft Một bộ gồm hai số nguyên: (trái, dưới)
myRect.bottomright Một bộ gồm hai số nguyên: (phải, dưới)
myRect.midleft
Một bộ gồm hai số nguyên: (trái, chính giữa)
myRect.midright
Một bộ gồm hai số nguyên: (phải, chính giữa)
myRect.midtop
Một bộ gồm hai số nguyên: (centre, top)
myRect.midbottom
Một tuple của hai ints: (centre, bottom)
317
17 - Đồ họa và hoạt hình

Trang 332
Các hàm có cùng tên với kiểu dữ liệu của chúng và tạo các đối tượng hoặc giá trị của điều này
kiểu dữ liệu được gọi là hàm xây dựng .
Các hàm int () và str () cũng là các hàm xây dựng. Các int () chức năng
trả về một phiên bản int của bất cứ thứ gì bạn vượt qua, cho dù đó là int (5) hay int ('5') . (Các
tên thích hợp cho các chuỗi trong Python là str .)
Bạn luôn có thể tìm ra tên thích hợp của kiểu dữ liệu của giá trị với kiểu ()
chức năng. Ví dụ: thử gõ nội dung sau vào vỏ tương tác:
>>> gõ ('Đây là một chuỗi')
<gõ 'str'>
>>> loại (5)
<gõ 'int'>
>>> spam = 'Chuỗi khác'
>>> loại (thư rác)
<gõ 'str'>
>>> nhập khẩu pygame
>>> pygame.init ()
>>> myRect = pygame.Rect (10, 10, 40, 50)
>>> loại (myRect)
<gõ 'pygame.Rect'>
>>> pygame.quito ()
(Bạn cần gọi hàm pygame.quito () khi bạn nhập xong
Chức năng Pygame vào vỏ tương tác. Nếu không, bạn có thể khiến Python bị sập.)
Lưu ý rằng giá trị trả về từ hàm type () không phải là một chuỗi, mà là giá trị của dữ liệu
loại gọi là "loại"! Hãy thử gõ cái này vào vỏ tương tác:
>>> loại (loại ('Đây là một chuỗi'))
<loại 'loại'>
Đối với hầu hết các phần, bạn không cần phải biết về các loại dữ liệu và các loại () chức năng
khi lập trình trò chơi. Nhưng nó có thể rất hữu ích nếu bạn cần tìm ra kiểu dữ liệu của
giá trị được lưu trữ trong một biến trong chương trình của bạn.
Các fill () Phương pháp cho Surface Objects
27. # vẽ nền trắng lên bề mặt
28. windowSurface.fill (TRẮNG)
Đây là cuộc gọi chức năng vẽ đầu tiên trong chương trình của chúng tôi. Chúng tôi muốn lấp đầy
toàn bộ bề mặt
318

Trang 333
được lưu trữ trong windowSurface với màu trắng. Hàm fill () sẽ hoàn toàn
bao phủ toàn bộ bề mặt với màu chúng ta chuyển qua làm tham số. (Trong trường hợp này,
chúng tôi vượt qua
ĐEN để làm nền đen.)
Một điều quan trọng cần biết về Pygame là cửa sổ trên màn hình sẽ không
thay đổi khi chúng ta gọi phương thức fill () hoặc bất kỳ hàm vẽ nào khác. Những điều này sẽ
vẽ trên đối tượng Surface , nhưng đối tượng Surface sẽ không được vẽ trên người dùng
màn hình cho đến khi hàm pygame.display.update () được gọi. Đây là vì
vẽ trên đối tượng Surface (được lưu trong bộ nhớ của máy tính) nhanh hơn nhiều
hơn là vẽ lên màn hình máy tính. Sẽ hiệu quả hơn nhiều khi vẽ lên màn hình một lần
và chỉ sau khi tất cả các chức năng vẽ của chúng tôi để vẽ lên bề mặt.
Các pygame.draw.polygon () Chức năng
30. # vẽ một đa giác màu xanh lá cây lên bề mặt
31. pygame.draw.polygon (windowSurface, XANH, ((146, 0),
(291, 106), (236, 277), (56, 277), (0, 106)))
Một đa giác là bất kỳ hình dạng đa diện với các cạnh chỉ là các đường thẳng. Các
Hàm pygame.draw.polygon () có thể vẽ bất kỳ hình dạng nào bạn cung cấp cho nó và điền vào
không gian bên trong đa giác. Bộ dữ liệu bạn vượt qua nó đại diện cho tọa độ XY
của các điểm để vẽ theo thứ tự. Bộ dữ liệu cuối cùng sẽ tự động kết nối với bộ dữ liệu đầu tiên để
Hoàn thành hình dạng.
Hình 17-5: Ví dụ về đa giác.
Đa giác chỉ có các đường thẳng cho các cạnh (hình tròn và hình elip không phải là đa
giác). Nhân vật
17-5 có một số ví dụ về đa giác.
319
17 - Đồ họa và hoạt hình

Trang 334
Các pygame.draw.line () Chức năng
33. # vẽ một số đường màu xanh lên bề mặt
34. pygame.draw.line (windowSurface, BLUE, (60, 60), (120,
60), 4)
35. pygame.draw.line (windowSurface, BLUE, (120, 60), (60,
120))
36. pygame.draw.line (windowSurface, BLUE, (60, 120), (120,
120), 4)
Hàm pygame.draw.line () sẽ vẽ một đường trên đối tượng Surface
bạn cung cấp. Lưu ý rằng tham số cuối cùng (chiều rộng của dòng) là tùy chọn. Nếu bạn vượt
qua 4
đối với chiều rộng, đường kẻ sẽ dày bốn pixel. Nếu bạn không chỉ định tham số chiều rộng ,
nó sẽ lấy giá trị mặc định là 1 .
Các pygame.draw.circle () Chức năng
38. # vẽ một vòng tròn màu xanh lên bề mặt
39. pygame.draw.circle (windowSurface, BLUE, (300, 50), 20, 0)
Hàm pygame.draw.circle () sẽ vẽ một vòng tròn trên đối tượng Surface
bạn cung cấp. Tham số thứ ba dành cho tọa độ X và Y của tâm vòng tròn
như một tuple của hai ints. Tham số thứ tư là một int cho bán kính (nghĩa là kích thước) của
khoanh tròn bằng pixel. Một chiều rộng của 0 phương tiện mà các vòng tròn sẽ được điền vào.
Các pygame.draw.ellipse () Chức năng
41. # vẽ một hình elip màu đỏ lên bề mặt
42. pygame.draw.ellipse (windowSurface, RED, (300, 250, 40,
80), 1)
Hàm pygame.draw.ellipse () sẽ vẽ một hình elip. Nó tương tự như
Hàm pygame.draw.circle () , ngoại trừ việc thay vì chỉ định tâm của
hình tròn, một bộ bốn số nguyên được truyền cho bên trái, trên cùng, chiều rộng và chiều cao của
hình elip.
Các pygame.draw.rect () Chức năng
44. # vẽ hình chữ nhật nền của văn bản lên bề mặt
45. pygame.draw.rect (windowSurface, RED, (textRect.left - 20,
textRect.top - 20, textRect. thong + 40, textRect.height +
320

Trang 335
40)
Hàm pygame.draw.rect () sẽ vẽ một hình chữ nhật. Tham số thứ ba là một
tuple bốn ints cho bên trái, trên cùng, chiều rộng và chiều cao của hình chữ nhật. Thay vì một
tuple bốn
ints cho tham số thứ ba, bạn cũng có thể truyền đối tượng Rect . Trong dòng 45, chúng tôi muốn
hình chữ nhật chúng tôi vẽ là 20 pixel xung quanh tất cả các cạnh của văn bản. Đây là lý do tại
sao chúng tôi muốn
vẽ hình chữ nhật bên trái và trên cùng là bên trái và trên cùng của textRect trừ 20 . (Nhớ lại,
chúng tôi trừ vì tọa độ giảm khi bạn đi sang trái và lên.) Và chiều rộng và chiều cao
sẽ bằng chiều rộng và chiều cao của textRect cộng với 40 (vì bên trái và trên cùng
đã được di chuyển trở lại 20 pixel, vì vậy chúng tôi cần bù vào khoảng trống đó).
Kiểu dữ liệu pygame.PixelArray
47. # lấy một mảng pixel của bề mặt
48. pixArray = pygame.PixelArray (windowSurface)
49. pixArray [480] [380] = ĐEN
Trên dòng 48, chúng ta tạo một đối tượng pygame.PixelArray (mà chúng ta sẽ chỉ gọi một
Đối tượng PixelArray cho ngắn). Các PixelArray đối tượng là một danh sách liệt kê của các bộ
màu
đại diện cho đối tượng Surface mà bạn đã vượt qua. Chúng tôi đã thông qua đối
tượng windowSurface
khi chúng ta gọi hàm xây dựng PixelArray () trên dòng 48, vì vậy việc gán BLACK
thành pixArray [480] [380] sẽ thay đổi pixel ở tọa độ (480, 380) thành một
pixel đen. Pygame sẽ tự động sửa đổi đối tượng windowSurface bằng cái này
thay đổi.
Chỉ mục đầu tiên trong đối tượng PixelArray dành cho tọa độ X. Chỉ số thứ hai là
cho tọa độ Y. Các đối tượng PixelArray giúp dễ dàng đặt từng pixel trên một
Đối tượng PixelArray đến một màu cụ thể.
50. del pixArray
Tạo một đối tượng PixelArray từ một đối tượng Surface sẽ khóa đối tượng Surface đó.
Bị khóa có nghĩa là không có lệnh gọi hàm blit () nào được mô tả tiếp theo
Đối tượng bề mặt . Để mở khóa đối tượng Surface , bạn phải xóa PixelArray
đối tượng với toán tử del. Nếu bạn quên xóa đối tượng Surface , bạn sẽ nhận được một
thông báo lỗi cho biết pygame.error: Bề mặt không được khóa
trong blit .
321
17 - Đồ họa và hoạt hình

Trang 336
Các blit () Phương pháp cho Surface Objects
52. # vẽ văn bản lên bề mặt
53. windowSurface.blit (text, textRect)
Phương thức blit () sẽ vẽ nội dung của một đối tượng Surface lên một đối tượng Surface khác
Đối tượng bề mặt . Dòng 54 sẽ vẽ "Xin chào thế giới!" văn bản (được vẽ trên
Đối tượng Surface được lưu trữ trong biến văn bản ) và vẽ nó vào đối tượng Surface được lưu trữ
trong cửa sổ Biến mặt.
Hãy nhớ rằng đối tượng văn bản có "Xin chào thế giới!" văn bản được vẽ trên đó trên dòng 22
bởi
các render () phương pháp. Các đối tượng bề mặt chỉ được lưu trữ trong bộ nhớ của máy tính
(như
bất kỳ biến nào khác) và không được vẽ trên màn hình. Các bề mặt đối tượng trong
windowSurface được vẽ trên màn hình (khi chúng ta gọi
Hàm pygame.display.update () trên dòng 56 bên dưới) vì đây là
Đối tượng bề mặt được tạo bởi hàm pygame.display .set_mode ().
Tham số thứ hai để blit () chỉ định vị trí trên cửa sổ Bề mặt bề mặt
bề mặt văn bản nên được vẽ. Chúng tôi sẽ chỉ vượt qua đối tượng Rect mà chúng tôi nhận được
từ cuộc gọi
text.get_rect () (được lưu trữ trong textRect trên dòng 23).
Hàm pygame.display.update ()
55. # vẽ cửa sổ lên màn hình
56. pygame.display.update ()
Trong Pygame, không có gì được vẽ lên màn hình cho đến khi pygame.display.update ()
chức năng được gọi là. Điều này được thực hiện bởi vì vẽ lên màn hình là một hoạt động chậm
cho
máy tính so với vẽ trên các đối tượng Surface khi chúng ở trong bộ nhớ. Bạn làm
không muốn vẽ lên màn hình sau mỗi chức năng vẽ được gọi mà chỉ vẽ
màn hình một lần sau khi tất cả các chức năng vẽ đã được gọi.
Bạn sẽ cần gọi pygame.display.update () mỗi lần bạn muốn cập nhật
màn hình để hiển thị nội dung của đối tượng Surface được trả về bởi
pygame.display.set_mode () . (Trong chương trình này, đối tượng đó là đối tượng được lưu trữ
trong
windowSurface .) Điều này sẽ trở nên quan trọng hơn trong chương trình tiếp theo của chúng tôi
bao gồm
hoạt hình.
Sự kiện và Vòng lặp trò chơi
Trong các trò chơi trước của chúng tôi, tất cả các chương trình in ra mọi thứ ngay lập tức cho
đến khi chúng
đạt được một lệnh gọi hàm input () . Tại thời điểm đó, chương trình dừng lại và chờ người dùng
322

Trang 337
gõ một cái gì đó vào và nhấn Enter. Các chương trình Pygame không hoạt động theo cách này. Thay
thế,
Các chương trình Pygame liên tục chạy qua một vòng gọi là vòng lặp trò chơi. (Trong này
chương trình, chúng tôi thực hiện tất cả các dòng mã trong vòng lặp trò chơi khoảng một trăm
lần
thứ hai.)
Các trò chơi vòng lặp là một vòng lặp liên tục kiểm tra các sự kiện mới, cập nhật trạng thái của
cửa sổ, và vẽ cửa sổ trên màn hình. Sự kiện là giá trị của
Kiểu dữ liệu pygame.event.Event được tạo bởi Pygame bất cứ khi nào người dùng
nhấn một phím, nhấp hoặc di chuyển chuột hoặc làm cho một số sự kiện khác xảy ra. Gọi điện
thoại
pygame.event.get () lấy bất kỳ đối tượng pygame.event.Event mới nào có
đã được tạo từ cuộc gọi cuối cùng tới pygame.event.get () .
58. # chạy vòng lặp trò chơi
59. trong khi Đúng:
Đây là sự khởi đầu của vòng lặp trò chơi của chúng tôi. Điều kiện cho câu lệnh while được đặt
thành True
để chúng tôi lặp đi lặp lại mãi mãi. Lần duy nhất chúng ta thoát khỏi vòng lặp là nếu một sự kiện
khiến chương trình
chấm dứt.
Hàm pygame.event.get ()
60. cho sự kiện trong pygame.event.get ():
61.
if event.type == QUIT:
Hàm pygame.event.get () trả về danh sách pygame.event.Event
các đối tượng. Danh sách này có mọi sự kiện đã xảy ra từ lần trước
pygame.event.get () đã được gọi. Tất cả các đối tượng pygame.event.Event có một
thuộc tính được gọi là loại cho chúng ta biết loại sự kiện đó là gì. (Một danh sách các loại sự
kiện được đưa ra trong
chương tiếp theo. Trong chương này, chúng tôi chỉ đề cập đến sự kiện QUIT .)
Pygame được cung cấp với các biến không đổi của chính nó trong pygame.locals
mô-đun. Hãy nhớ rằng chúng tôi đã nhập mô-đun pygame.locals với dòng
từ nhập pygame.locals * , có nghĩa là chúng ta không phải gõ
pygame.locals trước các biến và hàm trong mô-đun đó.
Trên dòng 60, chúng tôi thiết lập một vòng lặp for để kiểm tra từng đối
tượng pygame.event.Event trong
danh sách được trả về bởi pygame.event.get () . Nếu thuộc tính loại của sự kiện bằng
giá trị của biến QUIT không đổi (được cung cấp bởi mô-đun pygame.locals ),
sau đó chúng tôi biết người dùng đã đóng cửa sổ và muốn chấm dứt chương trình.
Pygame tạo sự kiện QUIT khi người dùng nhấp vào nút X ở trên cùng bên phải
của cửa sổ chương trình. Nó cũng được tạo ra nếu máy tính đang tắt và cố gắng
chấm dứt tất cả các chương trình đang chạy Vì lý do nào sự kiện QUIT được tạo, chúng tôi
323
17 - Đồ họa và hoạt hình

Trang 339
biết rằng chúng ta nên chạy bất kỳ mã nào mà chúng ta muốn xảy ra để dừng chương trình. Bạn
có thể chọn bỏ qua hoàn toàn sự kiện QUIT , nhưng điều đó có thể khiến chương trình bị
gây nhầm lẫn cho người dùng.
Hàm pygame.quito ()
62.
pygame.quito ()
63.
sys.exit ()
Nếu sự kiện QUIT đã được tạo, thì chúng ta có thể biết rằng người dùng đã cố gắng đóng
cửa sổ. Trong trường hợp đó, chúng ta nên gọi các hàm thoát cho cả Pygame
( pygame.quito () ) và Python ( sys.exit () ).
Đây là đơn giản "Xin chào thế giới!" chương trình từ Pygame. Chúng tôi đã bao gồm nhiều
chủ đề mới mà chúng tôi đã không phải đối phó trong các trò chơi trước đây của chúng tôi. Mặc
dù họ
phức tạp hơn, các chương trình Pygame cũng có thể thú vị và hấp dẫn hơn nhiều so với chúng tôi
trò chơi văn bản trước đó. Hãy tìm hiểu cách tạo trò chơi với đồ họa hoạt hình di chuyển.
Hoạt hình
Trong chương trình này, chúng tôi có một số khối khác nhau nảy ra từ các cạnh của
cửa sổ. Các khối có màu sắc và kích cỡ khác nhau và chỉ di chuyển theo hướng chéo. Trong
để làm động các khối (nghĩa là làm cho chúng trông giống như chúng đang di chuyển) chúng ta
sẽ di chuyển
khối một vài pixel trên mỗi lần lặp qua vòng lặp trò chơi. Bằng cách vẽ mới
các khối được đặt khác nhau một chút so với các khối trước đó, chúng ta có thể làm cho nó trông
giống như các khối đang di chuyển xung quanh màn hình.
Mã nguồn của chương trình hoạt hình
Nhập chương trình sau vào trình chỉnh sửa tệp và lưu nó dưới dạng hình ảnh động . Bạn cũng có
thể
tải về mã nguồn này từ http://inventwithpython.com/ch CHƯƠNG17.
hình ảnh động
Mã này có thể được tải xuống từ http://inventwithpython.com/animation.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. nhập pygame, sys, thời gian
2. từ nhập pygame.locals *
3.
4. # thiết lập pygame
5. pygame.init ()
6.
7. # thiết lập cửa sổ
8. WINDOWWIDTH = 400
324

Trang 339
9. CỬA SỔ = 400
10. windowSurface = pygame.display.set_mode ((WINDOWWIDTH,
Cửa sổ), 0, 32)
11. pygame.display.set_caption ('Hoạt hình')
12.
13. # thiết lập các biến hướng
14. XUỐNG = 1
15. XUỐNG = 3
16. LÊN = 7
17. LÊN = 9
18.
19. CHUYỂN ĐỔI = 4
20.
21. # thiết lập màu sắc
22. ĐEN = (0, 0, 0)
23. ĐỎ = (255, 0, 0)
24. XANH = (0, 255, 0)
25. XANH = (0, 0, 255)
26.
27. # thiết lập cấu trúc dữ liệu khối
28. b1 = {'orth': pygame.Rect (300, 80, 50, 100), 'color': ĐỎ,
'dir': TĂNG LÊN}
29. b2 = {'orth': pygame.Rect (200, 200, 20, 20), 'color': XANH,
'dir': UPLEFT}
30. b3 = {'orth': pygame.Rect (100, 150, 60, 60), 'màu': BLUE,
'dir': DOWNLEFT}
31. khối = [b1, b2, b3]
32.
33. # chạy vòng lặp trò chơi
34. trong khi Đúng:
35. # kiểm tra sự kiện QUIT
36. cho sự kiện trong pygame.event.get ():
37.
if event.type == QUIT:
38.
pygame.quito ()
39.
sys.exit ()
40.
41. # vẽ nền đen lên bề mặt
42. windowSurface.fill (ĐEN)
43.
44. cho b trong các khối:
45.
# di chuyển cấu trúc dữ liệu khối
46.
if b ['dir'] == DOWNLEFT:
47.
b ['orth']. left - = MOVESPEED
48.
b ['orth']. top + = MOVESPEED
49.
if b ['dir'] == TẢI XUỐNG:
50.
b ['orth']. left + = MOVESPEED
51.
b ['orth']. top + = MOVESPEED
52.
if b ['dir'] == UPLEFT:
53.
b ['orth']. left - = MOVESPEED
54.
b ['orth']. top - = MOVESPEED
55.
if b ['dir'] == UPRIGHT:
56.
b ['orth']. left + = MOVESPEED
57.
b ['orth']. top - = MOVESPEED
58.
325
17 - Đồ họa và hoạt hình

Trang 340
59.
# kiểm tra xem khối đã di chuyển ra khỏi cửa sổ chưa
60.
if b ['orth']. top <0:
61.
# khối đã di chuyển qua đầu
62.
if b ['dir'] == UPLEFT:
63.
b ['dir'] = TẢI XUỐNG
64.
if b ['dir'] == UPRIGHT:
65.
b ['dir'] = TẢI XUỐNG
66.
if b ['orth']. bottom> WINDOWHEIGHT:
67.
# khối đã di chuyển qua đáy
68.
if b ['dir'] == DOWNLEFT:
69.
b ['dir'] = LÊN
70.
if b ['dir'] == TẢI XUỐNG:
71.
b ['dir'] = TĂNG LÊN
72.
if b ['orth']. left <0:
73.
# khối đã di chuyển qua phía bên trái
74.
if b ['dir'] == DOWNLEFT:
75.
b ['dir'] = TẢI XUỐNG
76.
if b ['dir'] == UPLEFT:
77.
b ['dir'] = TĂNG LÊN
78.
nếu b ['orth']. phải> WINDOWWIDTH:
79.
# khối đã di chuyển qua phía bên phải
80.
if b ['dir'] == TẢI XUỐNG:
81.
b ['dir'] = TẢI XUỐNG
82.
if b ['dir'] == UPRIGHT:
83.
b ['dir'] = LÊN
84.
85.
# vẽ khối lên bề mặt
86.
pygame.draw.rect (windowSurface, b ['color'], b
['trực tràng'])
87.
88. # vẽ cửa sổ lên màn hình
89. pygame.display.update ()
90. thời gian ngủ (0,02)
327

Trang 341
Hình 17-6: Chương trình Animation.

Chương trình hoạt hình hoạt động như thế nào


Trong chương trình này, chúng ta sẽ có ba khối màu khác nhau di chuyển xung quanh và
bật ra khỏi các bức tường. Để làm điều này, trước tiên chúng ta cần xem xét chính xác cách
chúng ta muốn
các khối để di chuyển.
Di chuyển và nảy các khối
Mỗi khối sẽ di chuyển theo một trong bốn hướng chéo: xuống và trái, xuống và phải,
lên và trái, hoặc lên và phải. Khi khối chạm vào bên cạnh cửa sổ, chúng tôi muốn nó
"bật" ra khỏi tường và di chuyển theo hướng chéo mới. Các khối sẽ nảy như
thể hiện trong bức tranh này:
Hướng mới mà một khối di chuyển sau khi nó nảy phụ thuộc vào hai điều:
hướng nó đang di chuyển trước khi nảy và bức tường nào bị bật ra. Có một
tổng cộng tám cách có thể mà một khối có thể nảy: hai cách khác nhau cho mỗi trong bốn cách
tường. Ví dụ: nếu một khối di chuyển xuống và sang phải, và sau đó bật ra khỏi
cạnh dưới của cửa sổ, chúng tôi muốn hướng mới của khối lên và phải.
Chúng ta có thể biểu diễn các khối bằng một đối tượng Rect để thể hiện vị trí và kích thước của
khối, một bộ ba số nguyên để biểu thị màu của khối và một số nguyên để biểu diễn
327
17 - Đồ họa và hoạt hình

Trang 342
mà trong bốn hướng chéo mà khối hiện đang di chuyển. Trên mỗi lần lặp trong
vòng lặp trò chơi, chúng tôi sẽ điều chỉnh vị trí X và Y của khối trong đối tượng Rect . Cũng
trong
mỗi lần lặp chúng ta sẽ vẽ tất cả các khối trên màn hình ở vị trí hiện tại của chúng. Như
chương trình thực hiện các vòng lặp thông qua vòng lặp trò chơi, các khối sẽ dần dần di chuyển
qua
màn hình để có vẻ như chúng đang di chuyển trơn tru và tự nảy xung quanh.
Hình 17-7: Sơ đồ về cách các khối sẽ nảy.
Tạo và thiết lập Pygame và cửa sổ chính
1. nhập pygame, sys, thời gian
Trong chương trình này, chúng tôi cũng muốn nhập mô-đun thời gian .
7. # thiết lập cửa sổ
8. WINDOWWIDTH = 400
9. CỬA SỔ = 400
10. windowSurface = pygame.display.set_mode ((WINDOWWIDTH,
Cửa sổ), 0, 32)
328

Trang 343
Trong chương trình này, kích thước chiều rộng và chiều cao của cửa sổ được sử dụng không chỉ cho
gọi tới set_mode () . Chúng tôi sẽ sử dụng một biến không đổi để làm cho chương trình nhiều
hơn
có thể đọc được. Hãy nhớ rằng, khả năng đọc là vì lợi ích của lập trình viên chứ không phải máy
tính. Nếu
chúng tôi muốn thay đổi kích thước của cửa sổ, chúng tôi chỉ phải thay đổi dòng 8 và 9.
Nếu chúng ta không sử dụng biến không đổi, chúng ta sẽ phải thay đổi từng trường hợp
giá trị int 400 . Nếu bất kỳ giá trị không liên quan nào trong chương trình cũng là 400 , chúng tôi
có thể nghĩ rằng đó là
cho chiều rộng hoặc chiều cao và cũng vô tình thay đổi nó quá. Điều này sẽ đặt một lỗi trong
chúng tôi
chương trình. Vì chiều rộng và chiều cao cửa sổ không bao giờ thay đổi trong quá trình thực hiện
chương trình,
một biến không đổi là một ý tưởng tốt.
11. pygame.display.set_caption ('Hoạt hình')
Đối với chương trình này, chúng tôi sẽ đặt chú thích ở đầu cửa sổ thành 'Hoạt hình'
với một cuộc gọi đến pygame.display.set_caption () .
13. Thiết lập các biến liên tục cho hướng
14. # thiết lập các biến hướng
15. XUỐNG = 1
16. XUỐNG = 3
17. LÊN = 7
18. TĂNG LÊN = 9
Chúng tôi sẽ sử dụng các phím trên bàn phím số của bàn phím để nhắc chúng tôi thuộc về
hướng nào. Điều này sẽ tương tự như trò chơi Tic Tac Toe của chúng tôi. 1 là xuống và trái, 3 là
xuống và phải, 7 lên và trái, và 'Hoạt hình' 9 lên và phải. Tuy nhiên, nó có thể là
khó nhớ điều này, vì vậy thay vào đó chúng ta sẽ sử dụng các biến không đổi thay vì các số
nguyên này
các giá trị.
Chúng tôi có thể sử dụng bất kỳ giá trị nào chúng tôi muốn cho các hướng này, miễn là chúng tôi
có sự khác biệt
các giá trị cho mỗi hướng. Ví dụ: chúng ta có thể sử dụng chuỗi 'downleft' để biểu diễn
hướng chéo xuống và trái. Tuy nhiên, nếu chúng ta gõ nhầm chuỗi 'downleft'
(ví dụ: 'fownleft' ), máy tính sẽ không nhận ra rằng chúng tôi muốn gõ
'Downleft' thay vì 'downleft' . Lỗi này sẽ khiến chương trình của chúng tôi hoạt động
Thật kỳ lạ.
Nhưng nếu chúng ta sử dụng các biến không đổi và vô tình gõ tên biến FOWNLEFT
thay vì tên DOWNLEFT , Python sẽ nhận thấy rằng không có biến như vậy được đặt tên
FOWNLEFT và lỗi chương trình với một lỗi. Đây vẫn sẽ là một lỗi khá xấu, nhưng
ít nhất chúng ta sẽ biết ngay về nó và có thể sửa nó. Nếu không thì có thể khó
nhận thấy rằng có một lỗi ở tất cả.
19. CHUYỂN ĐỔI = 4
329
17 - Đồ họa và hoạt hình

Trang 344
Chúng tôi sẽ sử dụng một biến không đổi để xác định các khối sẽ di chuyển nhanh như thế nào. Một
giá trị
4 ở đây có nghĩa là mỗi khối sẽ di chuyển 4 pixel trên mỗi lần lặp thông qua vòng lặp trò chơi.
Thiết lập biến không đổi cho màu
21. # thiết lập màu sắc
22. ĐEN = (0, 0, 0)
23. ĐỎ = (255, 0, 0)
24. XANH = (0, 255, 0)
25. XANH = (0, 0, 255)
Chúng tôi thiết lập các biến không đổi cho màu sắc chúng tôi sẽ sử dụng. Hãy nhớ rằng, Pygame
sử dụng một tuple
trong ba giá trị int cho số lượng màu đỏ, xanh lục và xanh lam được gọi là giá trị RGB. Các
số nguyên là từ 0 đến 255 . Không giống như chương trình "Hello World" của chúng tôi, chương
trình này không sử dụng
màu trắng, vì vậy chúng tôi bỏ nó ra.
Một lần nữa, việc sử dụng các biến không đổi là để dễ đọc. Máy tính không quan tâm nếu chúng
ta
sử dụng một biến có tên XANH cho màu xanh lá cây. Nhưng nếu sau này chúng ta xem chương
trình này, thì đó là
dễ dàng hơn để biết rằng XANH tượng trưng cho màu xanh lá cây chứ không phải là một loạt
các giá trị trong một int
tuple
Thiết lập cấu trúc dữ liệu khối
27. # thiết lập cấu trúc dữ liệu khối
28. b1 = {'orth': pygame.Rect (300, 80, 50, 100), 'color': ĐỎ,
'dir': TĂNG LÊN}
Chúng tôi sẽ thiết lập một từ điển để trở thành cấu trúc dữ liệu đại diện cho mỗi khối.
(Từ điển đã được giới thiệu ở cuối chương Hangman.) Từ điển sẽ có
các phím của 'orth' (với một đối tượng Rect cho một giá trị), 'color' (với một bộ ba ints
cho một giá trị) và 'dir' (với một trong các biến không đổi hướng của chúng tôi cho một giá trị).
Chúng tôi sẽ lưu trữ một trong những cấu trúc dữ liệu này trong một biến có tên r1 . Khối này sẽ

góc trên cùng bên trái của nó nằm ở tọa độ X là 300 và tọa độ Y là 80. Nó sẽ có một
chiều rộng 50 pixel và chiều cao 100 pixel. Màu của nó sẽ là màu đỏ (vì vậy chúng tôi sẽ sử
dụng màu ĐỎ của chúng tôi
biến không đổi, có bộ dữ liệu (255, 0, 0) được lưu trữ trong đó). Và hướng của nó sẽ
được đặt thành TĂNG .
29. b2 = {'orth': pygame.Rect (200, 200, 20, 20),
'màu': XANH, 'dir': UPLEFT}
30. b3 = {'orth': pygame.Rect (100, 150, 60, 60), 'màu': BLUE,
'dir': DOWNLEFT}
Ở đây chúng tôi tạo thêm hai cấu trúc dữ liệu tương tự cho các khối sẽ có kích thước khác nhau,
330

Trang 345
vị trí, màu sắc và hướng.
31. khối = [b1, b2, b3]
Trên dòng 31, chúng tôi đặt tất cả các cấu trúc dữ liệu này vào một danh sách và lưu trữ danh
sách đó vào một biến
đặt tên hình chữ nhật .
hình chữ nhật là một danh sách. hình chữ nhật [0] sẽ là cấu trúc dữ liệu từ điển trong r1 .
hình chữ nhật [0] ['color'] sẽ là phím 'color' trong r1 (mà chúng tôi đã lưu trữ
giá trị trong RED in), do đó, hình chữ nhật biểu thức [0] ['color'] sẽ đánh giá thành
(255, 0, 0) . Theo cách này, chúng ta có thể tham chiếu đến bất kỳ giá trị nào trong bất kỳ dữ liệu
khối nào
cấu trúc bằng cách bắt đầu với hình chữ nhật .
Chạy vòng lặp trò chơi
33. # chạy vòng lặp trò chơi
34. trong khi Đúng:
Trong vòng lặp trò chơi, chúng tôi muốn di chuyển tất cả các khối xung quanh màn hình trong
hướng mà họ đang đi, sau đó bật khối nếu họ đã va vào tường, sau đó vẽ tất cả
các khối tới bề mặt cửa sổ , và cuối cùng gọi
pygame.display.update () để vẽ bề mặt lên màn hình. Ngoài ra, chúng tôi sẽ gọi
pygame.event.get () để kiểm tra xem sự kiện QUIT đã được tạo bởi người dùng chưa
đóng cửa sổ
Các cho vòng lặp để kiểm tra tất cả các sự kiện trong danh sách trả về bởi pygame.event.get ()
giống như trong "Hello World!" của chúng tôi chương trình, vì vậy chúng tôi sẽ bỏ qua lời giải
thích của nó và đi đến
dòng 44.
41. # vẽ nền đen lên bề mặt
42. windowSurface.fill (ĐEN)
Trước khi chúng tôi vẽ bất kỳ khối nào trên bề mặt windowSurface , chúng tôi muốn điền vào
toàn bộ bề mặt với màu đen để bất cứ thứ gì chúng ta vẽ trước đó trên bề mặt đều được che phủ.
Khi chúng ta đã bôi đen toàn bộ bề mặt, chúng ta có thể vẽ lại các khối bằng mã
phía dưới.
Di chuyển từng khối
44. cho b trong các khối:
331
17 - Đồ họa và hoạt hình

Trang 346
Chúng tôi muốn cập nhật vị trí của từng khối, vì vậy chúng tôi phải lặp qua
danh sách hình chữ nhật và thực hiện cùng một mã trên cấu trúc dữ liệu của mỗi khối. Bên trong
vòng lặp, chúng ta sẽ đề cập đến khối hiện tại đơn giản là r nên sẽ dễ gõ.
45.
# di chuyển cấu trúc dữ liệu khối
46.
if b ['dir'] == DOWNLEFT:
47.
b ['orth']. left - = MOVESPEED
48.
b ['orth']. top + = MOVESPEED
49.
if b ['dir'] == TẢI XUỐNG:
50.
b ['orth']. left + = MOVESPEED
51.
b ['orth']. top + = MOVESPEED
52.
if b ['dir'] == UPLEFT:
53.
b ['orth']. left - = MOVESPEED
54.
b ['orth']. top - = MOVESPEED
55.
if b ['dir'] == UPRIGHT:
56.
b ['orth']. left + = MOVESPEED
57.
b ['orth']. top - = MOVESPEED
Giá trị mới mà chúng tôi muốn đặt thuộc tính bên trái và trên cùng phụ thuộc vào
hướng khối đang di chuyển. Hãy nhớ rằng tọa độ X bắt đầu từ 0 ở bên trái
cạnh của cửa sổ, và tăng khi bạn đi đúng. Các tọa độ Y bắt đầu từ 0 trên
trên cùng của cửa sổ, và tăng khi bạn đi xuống. Vì vậy, nếu hướng của khối (mà,
hãy nhớ rằng, được lưu trữ trong khóa 'dir' ) là DOWNLEFT hoặc DOWNRIGHT , chúng tôi
muốn
tăng thuộc tính hàng đầu . Nếu hướng là UPLEFT hoặc UPRIGHT , chúng tôi muốn giảm
các đỉnh thuộc tính.
Nếu sự chỉ đạo của khối này là hết sức hoặc UPRIGHT , chúng tôi muốn tăng các trái
thuộc tính. Nếu hướng là DOWNLEFT hoặc UPLEFT , chúng tôi muốn giảm các trái
thuộc tính.
Chúng ta cũng có thể sửa đổi bên phải thay vì thuộc tính bên trái hoặc dưới cùng
thuộc tính thay vì thuộc tính trên cùng , vì Pygame sẽ cập nhật đối tượng Rect
đường. Dù bằng cách nào, chúng tôi muốn thay đổi giá trị của các thuộc tính này bằng số nguyên
được lưu trữ trong
MOVESPEED , nơi lưu trữ bao nhiêu pixel trên chúng ta sẽ di chuyển khối.
Kiểm tra xem Khối đã bị trả về chưa
59.
# kiểm tra xem khối đã di chuyển ra khỏi cửa sổ chưa
60.
if b ['orth']. top <0:
61.
# khối đã di chuyển qua đầu
62.
if b ['dir'] == UPLEFT:
63.
b ['dir'] = TẢI XUỐNG
64.
if b ['dir'] == UPRIGHT:
65.
b ['dir'] = TẢI XUỐNG
Sau khi chúng tôi đã di chuyển khối, chúng tôi muốn kiểm tra xem khối đã đi qua rìa của
332
Trang 349
cửa sổ. Nếu có, chúng tôi muốn "trả lại" khối, trong mã có nghĩa là đặt mới
giá trị cho khóa 'dir' của khối . Khi hướng được đặt, khối sẽ di chuyển theo hướng mới
hướng trên lần lặp tiếp theo của vòng lặp trò chơi.
Chúng ta cần kiểm tra xem khối đã di chuyển qua bốn cạnh của cửa sổ chưa.
Trong câu lệnh if ở trên , chúng tôi quyết định khối đã di chuyển qua cạnh trên của cửa sổ
nếu thuộc tính trên cùng của đối tượng Rect nhỏ hơn 0 . Nếu có, thì chúng ta cần thay đổi
hướng dựa trên hướng mà khối đang di chuyển.
Thay đổi hướng của khối nảy
Nhìn vào sơ đồ nảy trước đó trong chương này. Để di chuyển qua các cạnh trên
của cửa sổ, khối phải di chuyển theo hướng UPLEFT hoặc UPRIGHT .
Nếu khối đang di chuyển theo hướng UPLEFT , hướng mới (theo chúng tôi
biểu đồ thoát) sẽ là DOWNLEFT . Nếu khối đang di chuyển theo hướng UPRIGHT ,
hướng đi mới sẽ TẢI XUỐNG .
66.
if b ['orth']. bottom> WINDOWHEIGHT:
67.
# khối đã di chuyển qua đáy
68.
if b ['dir'] == DOWNLEFT:
69.
b ['dir'] = LÊN
70.
if b ['dir'] == TẢI XUỐNG:
71.
b ['dir'] = TĂNG LÊN
Ở đây chúng ta thấy nếu khối đã di chuyển qua cạnh dưới của cửa sổ bằng cách kiểm tra xem
các đáy thuộc tính (không phải là hàng đầu thuộc tính) là lớn hơn so với giá trị
trong WINDOWHEIGHT .
Hãy nhớ rằng tọa độ Y bắt đầu từ 0 ở đầu cửa sổ và tăng lên
WINDOWHEIGHT vì chúng tôi đã vượt qua WINDOWHEIGHT như chiều cao trong cuộc gọi
của chúng tôi
pygame.display.set_mode () .
Phần còn lại của mã thay đổi hướng dựa trên những gì sơ đồ thoát của chúng tôi nói.
72.
if b ['orth']. left <0:
73.
# khối đã di chuyển qua phía bên trái
74.
if b ['dir'] == DOWNLEFT:
75.
b ['dir'] = TẢI XUỐNG
76.
if b ['dir'] == UPLEFT:
77.
b ['dir'] = TĂNG LÊN
Điều này tương tự như mã trên, nhưng kiểm tra xem bên trái của khối đã di chuyển đến
bên trái của cạnh trái của cửa sổ. Hãy nhớ rằng, tọa độ X bắt đầu từ 0 trên cạnh trái
của cửa sổ và tăng lên WINDOWWIDTH ở cạnh phải của cửa sổ.
78.
nếu b ['orth']. phải> WINDOWWIDTH:
79.
# khối đã di chuyển qua phía bên phải
333
17 - Đồ họa và hoạt hình

Trang 349
80.
if b ['dir'] == TẢI XUỐNG:
81.
b ['dir'] = TẢI XUỐNG
82.
if b ['dir'] == UPRIGHT:
83.
b ['dir'] = LÊN
Mã này tương tự như các đoạn mã trước đó, nhưng nó kiểm tra xem khối đã di chuyển chưa
qua mép ngoài cùng bên phải của cửa sổ.
Vẽ các khối trên cửa sổ ở vị trí mới của chúng
85.
# vẽ khối lên bề mặt
86.
pygame.draw.rect (windowSurface, b ['color'], b
['trực tràng'])
Bây giờ chúng tôi đã di chuyển khối (và đặt hướng mới nếu khối bị bật ra
các cạnh của cửa sổ), chúng tôi muốn vẽ nó trên bề mặt windowSurface . Chúng ta có thể vẽ
điều này bằng cách sử dụng hàm pygame.draw.rect () . Chúng tôi vượt qua windowSurface , bởi
vì điều đó
là đối tượng Surface mà chúng ta muốn vẽ. Chúng tôi vượt qua giá trị b ['color'] , bởi vì điều này
là màu chúng tôi muốn sử dụng. Sau đó, chúng tôi vượt qua b ['orth'] , bởi vì đối tượng Rect đó

thông tin về vị trí và kích thước của hình chữ nhật mà chúng ta muốn vẽ.
Đây là dòng cuối cùng của vòng lặp for . Chúng tôi muốn chạy di chuyển, nảy và vẽ
mã trên mỗi khối được lưu trữ trong danh sách khối, đó là lý do tại sao chúng ta lặp qua từng
khối
họ Ngoài ra, nếu chúng tôi muốn thêm các khối mới hoặc xóa các khối khỏi chương trình của
mình, chúng tôi chỉ
phải sửa đổi danh sách các khối và phần còn lại của mã vẫn hoạt động.
Vẽ cửa sổ trên màn hình
88. # vẽ cửa sổ lên màn hình
89. pygame.display.update ()
90. thời gian ngủ (0,02)
Sau khi chúng tôi đã chạy mã này trên mỗi khối trong danh sách khối, cuối cùng chúng tôi muốn
gọi pygame.display.update () để bề mặt windowSurface được vẽ trên
màn. Sau dòng này, chúng tôi lặp lại bắt đầu vòng lặp trò chơi và bắt đầu quá trình tất cả
hơn một lần nữa Bằng cách này, các khối liên tục di chuyển một chút, bật ra khỏi các bức tường,

được vẽ trên màn hình ở vị trí mới của họ. Trong khi đó, chúng tôi cũng kiểm tra xem QUIT
sự kiện đã được tạo bởi thư viện Pygame (xảy ra nếu người chơi đóng
cửa sổ hoặc tắt máy tính của họ). Trong trường hợp đó, chúng tôi chấm dứt chương trình.
Hàm gọi hàm time.s ngủ () ở đó vì máy tính có thể di chuyển,
nảy và vẽ các khối nhanh đến mức nếu chương trình chạy hết tốc lực, tất cả các khối sẽ
chỉ trông giống như một vệt mờ. (Hãy thử nhận xét dòng time.s ngủ (0,02) và chạy dòng
chương trình để thấy điều này.) Cuộc gọi này đến time.s ngủ () sẽ dừng chương trình trong 20
334
Trang 349
mili giây. Có 1000 mili giây trong một giây, vì vậy 0,001 giây bằng 1
mili giây và 0,02 bằng 20 mili giây.
Một số sửa đổi nhỏ
Vẽ càng nhanh càng tốt
Để giải trí, hãy thực hiện một số sửa đổi nhỏ cho chương trình của chúng tôi để chúng tôi có thể
thấy những gì nó
làm. Hãy thử thêm một # ở phía trước dòng 90 (dòng time.s ngủ (0.2) ) của hình ảnh động của
chúng tôi
chương trình. Điều này sẽ khiến Python bỏ qua dòng này vì bây giờ nó là một bình luận. Bây giờ
cố gắng
chạy chương trình.
Nếu không có hàm time.s ngủ () để cố tình làm chậm chương trình,
máy tính của bạn sẽ chạy qua vòng lặp trò chơi nhanh nhất có thể. Điều này sẽ làm cho
hình chữ nhật nảy xung quanh màn hình quá nhanh, chúng sẽ chỉ trông giống như một vệt
mờ. Bây giờ bạn có thể thấy
tại sao điều quan trọng đối với chúng tôi là làm chậm chương trình với dòng này.
Vẽ Trails of Blocks
Xóa # khỏi mặt trước của dòng 90 để dòng không còn là nhận xét và
trở thành một phần của chương trình một lần nữa. Lần này, bình luận ra dòng 42 (
dòng windowSurface.fill (BLACK) ) bằng cách thêm # vào phía trước của dòng. Bây giờ chạy
chương trình.
Nếu không có lệnh gọi windowSurface.fill (BLACK) , chúng tôi sẽ không bôi đen toàn bộ
cửa sổ trước khi vẽ các hình chữ nhật ở vị trí mới của chúng. Điều này sẽ gây ra dấu vết của
hình chữ nhật xuất hiện trên màn hình thay vì hình chữ nhật riêng lẻ. Những con đường mòn
xuất hiện
bởi vì tất cả các hình chữ nhật cũ được vẽ trong các lần lặp trước thông qua vòng lặp trò chơi
đừng biến mất
Hãy nhớ rằng các khối không thực sự di chuyển. Chúng tôi chỉ đang vẽ lại toàn bộ
cửa sổ nhiều lần. Trên mỗi lần lặp qua vòng lặp trò chơi, chúng tôi vẽ lại toàn bộ
cửa sổ với các khối mới được đặt một vài pixel mỗi lần. Khi chương trình
chạy rất nhanh, chúng tôi làm cho nó chỉ là một khối mỗi lần. Để thấy rằng chúng tôi chỉ
vẽ lại các khối nhiều lần, thay đổi dòng 90 thành time.s ngủ (1.0) . Điều này
sẽ làm cho chương trình (và bản vẽ) chậm hơn năm mươi lần so với bình thường. Bạn sẽ thấy
mỗi
bản vẽ được thay thế bằng bản vẽ tiếp theo mỗi giây.
Tóm tắt: Lập trình Pygame
Chương này đã trình bày một cách hoàn toàn mới để tạo ra các chương trình máy tính. Của
chúng tôi
các chương trình trước sẽ dừng lại và chờ người chơi nhập văn bản. Tuy nhiên, trong chúng tôi
chương trình hoạt hình, chúng tôi liên tục cập nhật cấu trúc dữ liệu của mọi thứ mà không phải
chờ đợi
cho đầu vào từ người chơi. Hãy nhớ trong các trò chơi Hangman và Tic Tac Toe của chúng tôi,
chúng tôi đã có dữ liệu
cấu trúc đại diện cho trạng thái của bảng và các cấu trúc dữ liệu này sẽ là
được chuyển đến một hàm drawBoard () sẽ được hiển thị trên màn hình. Chương trình hoạt hình
của chúng tôi
335
17 - Đồ họa và hoạt hình

Trang 350
rất giống nhau Biến khối giữ một danh sách các cấu trúc dữ liệu đại diện cho mọi thứ
được vẽ lên màn hình và những cái này được vẽ vào màn hình bên trong vòng lặp trò chơi.
Nhưng không có cuộc gọi đến input () , làm thế nào để chúng ta nhận được đầu vào từ trình
phát? Trong tiếp theo của chúng tôi
Chương này, chúng tôi sẽ đề cập đến cách chương trình của chúng tôi có thể biết khi người chơi
nhấn bất kỳ phím nào trên
bàn phím. Chúng ta cũng sẽ tìm hiểu về một khái niệm gọi là phát hiện va chạm, được sử dụng
trong nhiều
trò chơi máy tính đồ họa.
336

Trang 351
Các chủ đề được đề cập trong Chương này:
● Phát hiện va chạm
● Không sửa đổi danh sách trong khi lặp lại nó
● Nhập bàn phím trong Pygame

● Nhập chuột vào Pygame

Một hành vi rất phổ biến trong hầu hết các trò chơi đồ họa là phát hiện va chạm. Va chạm
phát hiện là tìm ra khi hai thứ trên màn hình chạm vào nhau (nghĩa là va chạm với)
lẫn nhau. Điều này được sử dụng rất thường xuyên trong các trò chơi máy tính. Ví dụ: nếu người
chơi chạm vào
một kẻ thù họ có thể mất sức khỏe hoặc một cuộc sống trò chơi. Hoặc chúng tôi có thể muốn biết
khi nào người chơi
đã chạm vào một đồng xu để họ tự động nhặt nó lên. Phát hiện va chạm có thể giúp
xác định xem nhân vật trong game đang đứng trên mặt đất vững chắc hay không có gì ngoài
trống rỗng
không khí bên dưới chúng. Trong các trò chơi của chúng tôi, phát hiện va chạm đang xác định
xem hai hình chữ nhật có
chồng chéo nhau hay không. Chương trình ví dụ tiếp theo của chúng tôi sẽ bao gồm kỹ thuật cơ
bản này.
Ở phần sau của chương này, chúng ta sẽ xem xét làm thế nào các chương trình Pygame của
chúng ta có thể chấp nhận đầu vào từ
người dùng thông qua bàn phím và chuột. Nó phức tạp hơn một chút so với việc gọi
hàm input () giống như chúng ta đã làm cho các chương trình văn bản của mình. Nhưng sử dụng
bàn phím thì nhiều
tương tác trong các chương trình GUI và sử dụng chuột thậm chí không thể có trong các trò chơi
văn bản của chúng tôi.
Biết hai khái niệm này sẽ làm cho trò chơi của chúng tôi tiên tiến và thú vị hơn!
Mã nguồn của Chương trình Phát hiện Va chạm
Phần lớn mã này tương tự như chương trình hoạt hình, vì vậy chúng tôi sẽ bỏ qua phần giải thích
làm thế nào để làm cho bouncer di chuyển và bật ra khỏi các bức tường. (Xem chương trình hoạt
hình trong
337

Trang 352
chương trước để giải thích về mã đó.) Chúng tôi sẽ sử dụng một danh sách
pygame.Rect đối tượng để đại diện cho các hình vuông thực phẩm. Mỗi đối
tượng pygame.Rect trong
danh sách đại diện cho một hình vuông thực phẩm duy nhất. Trên mỗi lần lặp qua vòng lặp trò
chơi, chương trình của chúng tôi
sẽ đọc từng đối tượng pygame.Rect trong danh sách và vẽ một hình vuông màu xanh lá cây trên
cửa sổ.
Cứ sau bốn mươi lần lặp qua vòng lặp trò chơi, chúng tôi sẽ thêm một pygame mới. Hãy vào
danh sách
để màn hình liên tục có hình vuông thực phẩm mới trong đó.
Bouncer được đại diện bởi một từ điển. Từ điển có một khóa được đặt tên
'orth' (có giá trị là một đối tượng pygame.Rect ) và một khóa có tên 'dir' (có giá trị
là một trong những biến hướng không đổi giống như chúng ta đã có trong Hoạt hình của chương
trước
chương trình). Khi bouncer nảy xung quanh cửa sổ, chúng tôi kiểm tra xem nó có va chạm với
bất kỳ
các ô vuông thực phẩm. Nếu có, chúng tôi xóa hình vuông thực phẩm đó để nó không còn được
vẽ trên
màn hình.
Nhập nội dung sau vào một tệp mới và lưu nó dưới dạng va chạmDetection.py . Nếu bạn không
muốn
để nhập tất cả mã này, bạn có thể tải xuống nguồn từ trang web của cuốn sách tại
http://inventwithpython.com/ch CHƯƠNG18.
va chạm
Mã này có thể được tải xuống từ http://inventwithpython.com/collisionDetection.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. nhập pygame, sys, ngẫu nhiên
2. từ nhập pygame.locals *
3.
4. def doRectsOverlap (orth1, orth2):
5. cho a, b trong [(orth1, orth2), (orth2, orth1)]:
6.
# Kiểm tra xem góc của a có ở trong b không
7.
if ((isPointInsideRect (a.left, a.top, b)) hoặc
số 8.
(isPointInsideRect (a.left, a.bottom, b)) hoặc
9.
(isPointInsideRect (a.right, a.top, b)) hoặc
10.
(isPointInsideRect (a.right, a.bottom, b))):
11.
trả lại đúng
12.
13. trả về sai
14.
15. def isPointInsideRect (x, y, orth):
16. if (x> orth.left) và (x <orth.right) và (y>
orth.top) và (y <orth.bottom):
17.
trả lại đúng
18. khác:
19.
trả về sai
20.
21.
22. # thiết lập pygame
23. pygame.init ()
24. mainClock = pygame.time.Clock ()
25.
26. # thiết lập cửa sổ
27. WINDOWWIDTH = 400
339

Trang 353
28. CỬA SỔ = 400
29. windowSurface = pygame.display.set_mode ((WINDOWWIDTH,
Cửa sổ), 0, 32)
30. pygame.display.set_caption ('Phát hiện va chạm')
31.
32. # thiết lập các biến hướng
33. XUỐNG = 1
34. TẢI XUỐNG = 3
35. LÊN = 7
36. TĂNG LÊN = 9
37.
38. CHUYỂN ĐỔI = 4
39.
40. # thiết lập màu sắc
41. ĐEN = (0, 0, 0)
42. XANH = (0, 255, 0)
43. TRẮNG = (255, 255, 255)
44.
45. # thiết lập cấu trúc dữ liệu thực phẩm và bouncer
46. foodCount = 0
47. THỰC PHẨM = 40
48. THỰC PHẨM = 20
49. bouncer = {'orth': pygame.Rect (300, 100, 50, 50),
'dir': UPLEFT}
50. thực phẩm = []
51. cho i trong phạm vi (20):
52. thực phẩm.append (pygame.Rect (ngẫu nhiên.randint (0, WINDOWWIDTH
- THỰC PHẨM), ngẫu nhiên.randint (0, WINDOWHEIGHT - FOODSIZE),
THỰC PHẨM, THỰC PHẨM))
53.
54. # chạy vòng lặp trò chơi
55. trong khi Đúng:
56. # kiểm tra sự kiện QUIT
57. cho sự kiện trong pygame.event.get ():
58.
if event.type == QUIT:
59.
pygame.quito ()
60.
sys.exit ()
61.
62. foodCount + = 1
63. if foodCount> = NEWFOOD:
64.
# thêm thức ăn mới
65.
thức ăn = 0
66.
thực phẩm.append (pygame.Rect (ngẫu nhiên.randint (0,
WINDOWWIDTH - THỰC PHẨM), ngẫu nhiên.randint (0, WINDOWHEIGHT -
THỰC PHẨM), THỰC PHẨM, THỰC PHẨM))
67.
68. # vẽ nền đen lên bề mặt
69. windowSurface.fill (ĐEN)
70.
71. # di chuyển cấu trúc dữ liệu bouncer
72. if bouncer ['dir'] == DOWNLEFT:
73.
bouncer ['orth']. left - = MOVESPEED
74.
bouncer ['orth']. top + = MOVESPEED
75. if bouncer ['dir'] == DOWNRIGHT:
339
18 - Phát hiện va chạm

Trang 354
76.
bouncer ['orth']. left + = MOVESPEED
77.
bouncer ['orth']. top + = MOVESPEED
78. nếu bouncer ['dir'] == UPLEFT:
79.
bouncer ['orth']. left - = MOVESPEED
80.
bouncer ['orth']. top - = MOVESPEED
81. if bouncer ['dir'] == UPRIGHT:
82.
bouncer ['orth']. left + = MOVESPEED
83.
bouncer ['orth']. top - = MOVESPEED
84.
85. # kiểm tra xem bouncer có di chuyển ra khỏi cửa sổ không
86. if bouncer ['orth']. Top <0:
87.
# bouncer đã di chuyển qua đầu
88.
if bouncer ['dir'] == UPLEFT:
89.
bouncer ['dir'] = DOWNLEFT
90.
if bouncer ['dir'] == UPRIGHT:
91.
bouncer ['dir'] = TẢI XUỐNG
92. if bouncer ['orth']. Bottom> WINDOWHEIGHT:
93.
# bouncer đã di chuyển qua đáy
94.
if bouncer ['dir'] == DOWNLEFT:
95.
bouncer ['dir'] = UPLEFT
96.
if bouncer ['dir'] == DOWNRIGHT:
97.
bouncer ['dir'] = UPRIGHT
98. if bouncer ['orth']. Left <0:
99.
# bouncer đã di chuyển qua phía bên trái
100.
if bouncer ['dir'] == DOWNLEFT:
101.
bouncer ['dir'] = TẢI XUỐNG
102.
if bouncer ['dir'] == UPLEFT:
103.
bouncer ['dir'] = UPRIGHT
104. if bouncer ['orth']. Right> WINDOWWIDTH:
105.
# bouncer đã di chuyển qua phía bên phải
106.
if bouncer ['dir'] == DOWNRIGHT:
107.
bouncer ['dir'] = DOWNLEFT
108.
if bouncer ['dir'] == UPRIGHT:
109.
bouncer ['dir'] = UPLEFT
110.
111. # vẽ bouncer lên bề mặt
112. pygame.draw.rect (windowSurface, TRẮNG, bouncer
['trực tràng'])
113.
114. # kiểm tra xem bouncer có giao nhau với bất kỳ thực phẩm nào không
hình vuông.
115. cho thực phẩm trong thực phẩm [:]:
116.
if doRectsOverlap (bouncer ['orth'], thức ăn):
117.
thực phẩm.remove (thực phẩm)
118.
119. # vẽ thức ăn
120. cho i trong phạm vi (len (thực phẩm)):
121.
pygame.draw.rect (windowSurface, XANH, thực phẩm [i])
122.
123. # vẽ cửa sổ lên màn hình
124. pygame.display.update ()
125. mainClock.tick (40)
340

Trang 355
Khi bạn chạy mã này, đây là chương trình trông như thế nào. Hình vuông màu trắng (
bouncer) sẽ nảy xung quanh cửa sổ và khi nó va chạm với các ô vuông màu xanh lá cây (
thức ăn) sẽ biến mất khỏi màn hình.
Hình 18-1: Chương trình Phát hiện Va chạm.
Nhập khẩu các mô-đun
1. nhập pygame, sys, ngẫu nhiên
2. từ nhập pygame.locals *
Chương trình phát hiện va chạm nhập những thứ tương tự như chương trình Hoạt hình trong
chương cuối cùng, cùng với các mô-đun ngẫu nhiên .
Chức năng phát hiện va chạm
4. def doRectsOverlap (orth1, orth2):
Để thực hiện phát hiện va chạm, chúng ta sẽ cần một hàm có thể xác định nếu hai
hình chữ nhật có giao nhau hay không. Dưới đây là hình ảnh của các hình chữ nhật giao nhau
(trên
bên trái) và hình chữ nhật không giao nhau (bên phải):
341
18 - Phát hiện va chạm

Trang 356
Hình 18-2: Ví dụ về các hình chữ nhật cắt nhau (ở bên trái) và các hình chữ nhật không giao nhau (ở bên phải).
Chúng ta sẽ tạo một hàm duy nhất được truyền hai đối tượng pygame.Rect . Chức năng,
doRectsOverlap () , sẽ trả về True nếu họ làm và Sai nếu họ không.
Có một quy tắc rất đơn giản chúng ta có thể làm theo để xác định xem hình chữ nhật có giao
nhau không (nghĩa là
va chạm). Nhìn vào bốn góc trên cả hai hình chữ nhật. Nếu ít nhất một trong tám
các góc nằm bên trong hình chữ nhật khác, sau đó chúng ta biết rằng hai hình chữ nhật đã va
chạm.
Chúng tôi sẽ sử dụng thực tế này để xác định xem doRectsOverlap () trả về Đúng hay Sai .
5. cho a, b trong [(orth1, orth2), (orth2, orth1)]:
6.
# Kiểm tra xem góc của a có ở trong b không
7.
if ((isPointInsideRect (a.left, a.top, b)) hoặc
số 8.
(isPointInsideRect (a.left, a.bottom, b)) hoặc
9.
(isPointInsideRect (a.right, a.top, b)) hoặc
10.
(isPointInsideRect (a.right, a.bottom, b))):
11.
trả lại đúng
Trên đây là mã kiểm tra nếu các góc của hình chữ nhật nằm trong một góc khác. Sau này chúng
ta sẽ
tạo một hàm có tên isPointInsideRect () trả về True nếu XY
tọa độ của điểm nằm bên trong hình chữ nhật. Chúng tôi gọi chức năng này cho mỗi trong số tám
các góc và nếu bất kỳ cuộc gọi nào trong số này trả về True , toán tử hoặc toán tử sẽ thực hiện
toàn bộ
điều kiện Đúng .
Các tham số cho doRectsOverlap () là orth1 và orth2 . Trước tiên chúng tôi muốn
kiểm tra xem rect1 'góc s là bên rect2 và sau đó kiểm tra nếu rect2 ' góc s trong
trực tràng1 .
342

Trang 357
Chúng tôi không muốn lặp lại mã kiểm tra tất cả bốn góc cho cả orth1 và
orth2 , vì vậy thay vì chúng ta sử dụng a và b trên các dòng 7 đến 10. Vòng lặp for trên dòng 5 sử
dụng
nhiều thủ thuật gán để trong lần lặp đầu tiên, a được đặt thành orth1 và b được đặt thành
trực tràng2 . Ở lần lặp thứ hai qua vòng lặp, nó ngược lại. a được đặt thành orth2 và
b được đặt thành orth1 .
Chúng tôi làm điều này vì sau đó chúng ta chỉ cần gõ mã cho nếu tuyên bố trên dòng 7
Một lần. Điều này là tốt, bởi vì đây là một tuyên bố nếu rất dài . Mã ít hơn chúng ta phải gõ
cho chương trình của chúng tôi, tốt hơn.
13. trả về sai
Nếu chúng ta không bao giờ trả về True từ các câu lệnh if trước đó , thì không có góc nào trong
tám góc
chúng tôi đã kiểm tra trong hình chữ nhật khác. Trong trường hợp đó, các hình chữ nhật không
va chạm và chúng tôi
Trả về Sai .
Xác định xem một điểm có nằm trong hình chữ nhật không
15. def isPointInsideRect (x, y, orth):
16. if (x> orth.left) và (x <orth.right) và (y>
orth.top) và (y <orth.bottom):
17.
trả lại đúng
Hàm isPointInsideRect () được sử dụng bởi doRectsOverlap ()
chức năng. isPointInsideRect () sẽ trả về True nếu tọa độ XY được truyền cho nó
vì các tham số thứ nhất và thứ hai được đặt "bên trong" đối tượng pygame.Rect đó là
thông qua như là tham số thứ ba. Nếu không, hàm này trả về Sai .
Hình 18-3 là một hình ảnh ví dụ về một hình chữ nhật và một vài dấu chấm. Các dấu chấm và
các góc của hình chữ nhật được dán nhãn bằng tọa độ.
Mẫu mà các điểm bên trong hình chữ nhật có tọa độ X lớn hơn
Tọa độ X của bên trái và nhỏ hơn tọa độ X của bên phải và Y-
tọa độ lớn hơn tọa độ Y của phía trên và nhỏ hơn tọa độ Y
tọa độ của mặt dưới. Nếu bất kỳ điều kiện nào là sai, thì điểm nằm ngoài
hình chữ nhật.
Chúng tôi kết hợp cả bốn điều kiện này vào điều kiện if của câu lệnh với và
Toán tử vì cả bốn điều kiện phải là True .
343
18 - Phát hiện va chạm

Trang 358
Hình 18-3: Ví dụ về tọa độ bên trong và bên ngoài
của một hình chữ nhật. Các điểm (50, 30), (85, 30) và (50, 50)
ở bên trong hình chữ nhật, và tất cả những cái khác ở bên ngoài.
18. khác:
19.
trả về sai
Nếu chỉ một trong bốn biểu thức trong điều kiện trên dòng 16 là Sai , thì chúng ta nên
có isPointInsideRect () trả về giá trị Sai .
Hàm này sẽ được gọi từ hàm doRectsOverlap () để xem có bất kỳ
các góc trong hai đối tượng pygame.Rect nằm trong nhau. Hai chức năng này cho
cho chúng tôi sức mạnh để phát hiện va chạm giữa hai hình chữ nhật.
Các pygame.time.Clock Object và đánh dấu ()
phương pháp
Phần lớn các dòng từ 22 đến 43 làm điều tương tự như chương trình Hoạt hình trong chương
trước đã làm:
khởi tạo thư viện Pygame, đặt WINDOWHEIGHT và WINDOWWIDTH và đặt cùng nhau
các hằng số màu và hướng. Tuy nhiên, dòng 24 là mới:
24. mainClock = pygame.time.Clock ()
Trong chương trình Hoạt hình trước đó, chúng tôi đã có lệnh gọi time.s ngủ (0,02) bên trong
vòng lặp trò chơi để làm chậm chương trình đủ để chúng ta có thể thấy các khối
di chuyển. Vấn đề với điều này là chương trình có thể chạy quá nhanh trên máy tính nhanh và
quá chậm trên máy tính chậm. Chúng tôi muốn giới hạn số lần lặp tối đa thông qua
vòng lặp trò chơi có mỗi giây.
344

Trang 359
Một đối tượng pygame.time.Clock có thể làm điều này cho chúng ta. Bạn có thể thấy trên dòng 125
mà chúng tôi
gọi mainClock.tick (40) trong vòng lặp trò chơi. Cuộc gọi này đến đồng hồ của đối tượng đánh
dấu
Phương thức () sẽ kiểm tra xem chúng tôi đã lặp qua vòng lặp trò chơi hơn 40 lần trong
Giây cuối. Nếu vậy, nó đặt một giấc ngủ ngắn vào chương trình cho chúng tôi dựa trên việc
thường xuyên đánh dấu ()
đang được gọi Điều này đảm bảo rằng trò chơi không bao giờ chạy nhanh hơn chúng ta mong
đợi. Hãy chắc chắn gọi
tick () chỉ một lần trong vòng lặp trò chơi.
Thiết lập cấu trúc cửa sổ và dữ liệu
30. pygame.display.set_caption ('Phát hiện va chạm')
31.
32. # thiết lập cấu trúc dữ liệu thực phẩm và bouncer
33. FoodCount = 0
34. THỰC PHẨM = 40
35. THỰC PHẨM = 20
Chúng tôi sẽ thiết lập một vài biến cho các khối thực phẩm xuất hiện trên màn hình.
FoodCount sẽ bắt đầu ở giá trị
49. bouncer = {'orth': pygame.Rect (300, 100, 50, 50),
'dir': UPLEFT}
Chúng tôi sẽ thiết lập một cấu trúc dữ liệu mới gọi là bouncer . bouncer là một từ điển
với hai phím. Giá trị được lưu trữ trong khóa ' orth ' sẽ là một đối tượng pygame.Rect mà
đại diện cho kích thước và vị trí của bouncer. Giá trị được lưu trong khóa 'dir' sẽ là một
hướng mà bouncer hiện đang di chuyển. Bouncer sẽ di chuyển theo cùng một cách
các khối đã làm trong chương trình hoạt hình trước đây của chúng tôi: di chuyển theo hướng
chéo và nảy
tắt các cạnh của cửa sổ.
50. thực phẩm = []
51. cho i trong phạm vi (20):
52. food.append (pygame.Rect (Random.randint (0,
WINDOWWIDTH - THỰC PHẨM), ngẫu nhiên.randint (0, WINDOWHEIGHT -
THỰC PHẨM), THỰC PHẨM, THỰC PHẨM))
Chương trình của chúng tôi sẽ theo dõi mọi quảng trường thực phẩm với một danh sách các đối
tượng pygame.Rect
gọi là thực phẩm . Khi bắt đầu chương trình, chúng tôi muốn tạo ngẫu nhiên hai mươi ô vuông
thực phẩm
đặt xung quanh màn hình. Chúng ta có thể sử dụng hàm Random.randint () để đưa ra
tọa độ XY ngẫu nhiên.
Trên dòng 52, chúng ta sẽ gọi hàm xây dựng pygame.Rect () để trả về một cái mới
đối tượng pygame.Rect sẽ đại diện cho vị trí và kích thước của hình vuông thực phẩm. Các
345
18 - Phát hiện va chạm

Trang 360
Vẽ Bouncer trên màn hình
Các dòng 71 đến 109 làm cho bouncer di chuyển xung quanh cửa sổ và bật ra khỏi
các cạnh của cửa sổ. Mã này rất giống với dòng 44 đến 83 của chương trình hoạt hình của chúng
tôi
trong chương cuối, vì vậy chúng ta sẽ không xem lại chúng ở đây.
111. # vẽ bouncer lên bề mặt
112. pygame.draw.rect (windowSurface, TRẮNG, bouncer
['trực tràng'])
Sau khi di chuyển bouncer, bây giờ chúng tôi muốn vẽ nó trên cửa sổ ở vị trí mới.
Chúng ta gọi hàm pygame.draw.rect () để vẽ hình chữ nhật. Các
windowSurface được truyền cho tham số đầu tiên cho máy tính biết
đối tượng pygame.Surface để vẽ hình chữ nhật trên. Biến WHITE , có
(255, 255, 255) được lưu trữ trong đó, sẽ báo cho máy tính vẽ một hình chữ nhật màu trắng. Các
Đối tượng pygame.Rect được lưu trữ trong từ điển bouncer tại phím 'orth' cho biết
vị trí và kích thước của hình chữ nhật để vẽ. Đây là tất cả các thông tin cần thiết để vẽ
hình chữ nhật màu trắng trên windowSurface .
hai tham số đầu tiên cho
pygame.Rect () là XY
tọa độ của góc trên cùng bên trái. Chúng tôi
muốn tọa độ ngẫu nhiên được
từ 0 đến kích thước của cửa sổ
trừ đi kích thước của hình vuông thực phẩm. Nếu
chúng tôi đã phối hợp ngẫu nhiên
từ 0 đến kích thước của cửa sổ,
sau đó quảng trường thực phẩm có thể được đẩy
bên ngoài cửa sổ hoàn toàn.
Nhìn vào sơ đồ trong Hình 18-4.
Hình vuông bên trái có dấu X-
tọa độ của góc trên cùng bên trái của nó tại
380 . Vì quảng trường ẩm thực là 20
pixel rộng, cạnh phải của thực phẩm
hình vuông là 400. (Điều này là do 380
+ 20 = 400.) Hình vuông bên phải
có tọa độ X của góc trên bên trái của nó
góc ở 400. Vì hình vuông thực phẩm rộng 20 pixel, cạnh phải của hình vuông thực phẩm
ở mức 420, đặt toàn bộ hình vuông bên ngoài cửa sổ (và không thể xem được
người sử dụng).
Tham số thứ ba cho pygame.Rect () là một bộ chứa chiều rộng và chiều cao
của quảng trường thực phẩm. Cả chiều rộng và chiều cao sẽ bằng với giá trị trong FOODSIZE
không thay đổi.
Hình 18-4: Đối với hình chữ nhật 20 x 20, có đỉnh trên cùng bên trái
góc ở (400, 200) trong cửa sổ 400 x 400 sẽ đặt
hình chữ nhật bên ngoài cửa sổ. Ở bên trong,
góc trên bên trái nên ở (380, 200) thay vào đó.
346
Trang 361
Hãy nhớ rằng, chúng ta chưa hoàn thành việc vẽ mọi thứ trên đối tượng windowSurface . Chúng tôi
vẫn cần vẽ một hình vuông màu xanh lá cây cho mỗi hình vuông thực phẩm trong danh sách thực
phẩm . Và chúng tôi chỉ
"vẽ" hình chữ nhật trên đối tượng windowSurface . Đây pygame.Surface đối tượng là
chỉ bên trong bộ nhớ của máy tính, tốc độ sửa đổi nhanh hơn nhiều so với các pixel trên
màn. Cửa sổ trên màn hình sẽ không được cập nhật cho đến khi chúng tôi gọi
Hàm pygame.display.update () .
Va chạm với Squares thực phẩm
114. # kiểm tra xem bouncer có giao nhau với bất kỳ thực phẩm nào không
hình vuông.
115. cho thực phẩm trong thực phẩm [:]:
Trước khi chúng ta vẽ các ô vuông thực phẩm, chúng ta muốn xem liệu bouncer có chồng chéo
bất kỳ
các ô vuông thực phẩm. Nếu có, chúng tôi sẽ loại bỏ hình vuông thực phẩm đó khỏi danh
sách thực phẩm . Cách này,
máy tính sẽ không vẽ bất kỳ ô vuông thực phẩm nào mà bouncer đã "ăn".
Trên mỗi lần lặp qua vòng lặp for , hình vuông thực phẩm hiện tại từ thực phẩm (số nhiều)
danh sách sẽ được lưu trữ bên trong một biến gọi là thực phẩm (số ít).
Không thêm vào hoặc xóa khỏi danh sách trong khi lặp qua nó
Lưu ý rằng có một cái gì đó hơi khác với vòng lặp for này . Nếu bạn xem xét cẩn thận
ở dòng 116, chúng tôi không lặp lại về thực phẩm mà thực tế là qua thực phẩm [:] . Cũng
như thực phẩm
[: 2] sẽ trả về một bản sao của danh sách với các mục từ đầu và đến (nhưng không
bao gồm) mục ở chỉ số 2 và giống như thực phẩm [3:] sẽ trả về một bản sao của danh sách với
các mục từ chỉ mục 3 đến cuối danh sách, thực phẩm [:] sẽ cung cấp cho bạn một bản sao của
danh sách
với các mục từ đầu đến cuối. Về cơ bản, thực phẩm [:] tạo ra một danh sách mới với một
bản sao của tất cả các mặt hàng trong thực phẩm . (Đây là cách ngắn hơn để sao chép danh sách
so với danh sách của chúng tôi
Hàm getBoardCopy () trong trò chơi Tic Tac Toe.)
Tại sao chúng ta muốn lặp lại một bản sao của danh sách thay vì chính danh sách đó? Nó là
bởi vì chúng tôi không thể thêm hoặc xóa các mục khỏi danh sách trong khi chúng tôi đang lặp
lại nó. Con trăn
có thể mất theo dõi giá trị tiếp theo của biến thực phẩm là gì nếu kích thước của thực phẩm
danh sách luôn luôn thay đổi. Hãy nghĩ về việc bạn sẽ khó khăn như thế nào nếu bạn cố gắng
đếm
số hạt thạch trong một cái lọ trong khi ai đó đang thêm hoặc loại bỏ hạt thạch. Nhưng nếu chúng
ta
Lặp lại một bản sao của danh sách (và bản sao không bao giờ thay đổi), sau đó thêm hoặc xóa
các mục
từ danh sách ban đầu sẽ không phải là một vấn đề.
Loại bỏ các ô vuông thực phẩm
116.
if doRectsOverlap (bouncer ['orth'], thức ăn):
117.
thực phẩm.remove (thực phẩm)
347
18 - Phát hiện va chạm

Trang 362
Dòng 116 là nơi hàm doRectsOverlap () mà chúng ta đã xác định trước đó xuất hiện
tiện dụng. Chúng ta truyền hai đối tượng pygame.Rect cho doRectsOverlap () : bouncer và
quảng trường thực phẩm hiện nay. Nếu hai hình chữ nhật này trùng nhau, thì doRectsOverlap
() sẽ
Trả về True và chúng tôi sẽ xóa các ô vuông thực phẩm chồng chéo khỏi danh sách thực phẩm .
Vẽ các ô vuông thực phẩm trên màn hình
119. # vẽ thức ăn
120. cho i trong phạm vi (len (thực phẩm)):
121.
pygame.draw.rect (windowSurface, XANH, thực phẩm [i])
Mã trên các dòng 120 và 121 rất giống với cách chúng ta vẽ hình vuông màu trắng cho
người chơi. Chúng tôi sẽ lặp qua từng ô thực phẩm trong danh sách thực phẩm và sau đó rút ra
hình chữ nhật trên bề mặt cửa sổ Bề mặt. Trình diễn phát hiện va chạm này là
khá dễ. Chương trình này rất giống với chương trình nảy của chúng tôi trước đây
chương, ngoại trừ bây giờ hình vuông nảy sẽ "ăn" các hình vuông khác khi nó đi qua chúng.
Những chương trình trước đây rất thú vị để xem, nhưng người dùng không thực sự hiểu
kiểm soát bất cứ điều gì. Trong chương trình tiếp theo này, chúng ta sẽ tìm hiểu làm thế nào để
có được đầu vào từ bàn phím.
Đầu vào bàn phím được xử lý trong Pygame bằng cách sử dụng các sự kiện.
Mã nguồn của chương trình nhập bàn phím
Bắt đầu một tệp mới và nhập mã sau đây, sau đó lưu nó dưới dạng pygameInput.py .
pygameInput.py
Mã này có thể được tải xuống từ http://inventwithpython.com/pygameInput.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. nhập pygame, sys, ngẫu nhiên
2. từ nhập pygame.locals *
3.
4. # thiết lập pygame
5. pygame.init ()
6. mainClock = pygame.time.Clock ()
7.
8. # thiết lập cửa sổ
9. WINDOWWIDTH = 400
10. CỬA SỔ = 400
11. windowSurface = pygame.display.set_mode ((WINDOWWIDTH,
Cửa sổ), 0, 32)
12. pygame.display.set_caption ('Đầu vào')
13.
14. # thiết lập màu sắc
348

Trang 363
15. ĐEN = (0, 0, 0)
16. XANH = (0, 255, 0)
17. TRẮNG = (255, 255, 255)
18.
19. # thiết lập cấu trúc dữ liệu người chơi và thực phẩm
20. thức ăn = 0
21. THỰC PHẨM = 40
22. THỰC PHẨM = 20
23. người chơi = pygame.Rect (300, 100, 50, 50)
24. thực phẩm = []
25. cho tôi trong phạm vi (20):
26. food.append (pygame.Rect (Random.randint (0, WINDOWWIDTH
- THỰC PHẨM), ngẫu nhiên.randint (0, WINDOWHEIGHT - FOODSIZE),
THỰC PHẨM, THỰC PHẨM))
27.
28. # thiết lập các biến chuyển động
29. moveLeft = Sai
30. moveRight = Sai
31. moveUp = Sai
32. moveDown = Sai
33.
34. CHUYỂN ĐỔI = 6
35.
36.
37. # chạy vòng lặp trò chơi
38. trong khi Đúng:
39. # kiểm tra các sự kiện
40. cho sự kiện trong pygame.event.get ():
41.
if event.type == QUIT:
42.
pygame.quito ()
43.
sys.exit ()
44.
if event.type == KEYDOWN:
45.
# thay đổi các biến bàn phím
46.
if event.key == K_LEFT hoặc event.key == ord
('a'):
47.
moveRight = Sai
48.
moveLeft = Đúng
49.
if event.key == K_RIGHT hoặc event.key == ord
('D'):
50.
moveLeft = Sai
51.
moveRight = Đúng
52.
if event.key == K_UP hoặc event.key == ord ('w'):
53.
moveDown = Sai
54.
moveUp = Đúng
55.
if event.key == K_DOWN hoặc event.key == ord
('S'):
56.
moveUp = Sai
57.
moveDown = Đúng
58.
if event.type == KEYUP:
59.
if event.key == K_ESCAPE:
60.
pygame.quito ()
61.
sys.exit ()
62.
if event.key == K_LEFT hoặc event.key == ord
('a'):
349
18 - Phát hiện va chạm

Trang 364
63.
moveLeft = Sai
64.
if event.key == K_RIGHT hoặc event.key == ord
('D'):
65.
moveRight = Sai
66.
if event.key == K_UP hoặc event.key == ord ('w'):
67.
moveUp = Sai
68.
if event.key == K_DOWN hoặc event.key == ord
('S'):
69.
moveDown = Sai
70.
if event.key == ord ('x'):
71.
player.top = Random.randint (0,
WINDOWHEIGHT - player.height)
72.
player.left = Random.randint (0,
WINDOWWIDTH - player. Thong)
73.
74.
if event.type == MOUSEBUTTONUP:
75.
thực phẩm.append (pygame.Rect (event.pose [0],
event.pose [1], FOODSIZE, FOODSIZE))
76.
77. foodCount + = 1
78. if foodCount> = NEWFOOD:
79.
# thêm thức ăn mới
80.
thức ăn = 0
81.
thực phẩm.append (pygame.Rect (ngẫu nhiên.randint (0,
WINDOWWIDTH - THỰC PHẨM), ngẫu nhiên.randint (0, WINDOWHEIGHT -
THỰC PHẨM), THỰC PHẨM, THỰC PHẨM))
82.
83. # vẽ nền đen lên bề mặt
84. windowSurface.fill (ĐEN)
85.
86. # di chuyển người chơi
87. nếu moveDown và player.bottom <WINDOWHEIGHT:
88.
player.top + = MOVESPEED
89. nếu moveUp và player.top> 0:
90.
player.top - = CHUYỂN ĐỔI
91. nếu moveLeft và player.left> 0:
92.
player.left - = MOVESPEED
93. nếu moveRight và player.right <WINDOWWIDTH:
94.
player.right + = MOVESPEED
95.
96. # vẽ người chơi lên bề mặt
97. pygame.draw.rect (windowSurface, TRẮNG, trình phát)
98.
99. # kiểm tra xem người chơi có giao nhau với bất kỳ thực phẩm nào không
hình vuông.
100. cho thực phẩm trong thực phẩm [:]:
101.
if player.colliderect (thức ăn):
102.
thực phẩm.remove (thực phẩm)
103.
104. # vẽ thức ăn
105. cho tôi trong phạm vi (len (thực phẩm)):
106.
pygame.draw.rect (windowSurface, XANH, thực phẩm [i])
107.
108. # vẽ cửa sổ lên màn hình
350

Trang 365
109. pygame.display.update ()
110. mainClock.tick (40)
Chương trình này trông giống hệt với chương trình phát hiện va chạm trước đó trong chương
này. Nhưng
trong chương trình này, bouncer chỉ di chuyển xung quanh khi chúng ta giữ phím trên bàn phím.
Giữ phím "W" để di chuyển bouncer lên. Phím "A" di chuyển bouncer đến
bên trái và phím "D" di chuyển người bouncer sang bên phải. Phím "S" di chuyển bouncer
xuống.
Bạn cũng có thể di chuyển bouncer bằng cách giữ phím mũi tên trên bàn phím. Người dùng
cũng có thể sử dụng các phím mũi tên của bàn phím.
Chúng tôi cũng có thể nhấp vào bất cứ nơi nào trong cửa sổ GUI và tạo các đối tượng thực phẩm
mới tại
tọa độ nơi chúng tôi nhấp. Ngoài ra, phím ESC sẽ thoát khỏi chương trình và "X"
Phím sẽ dịch chuyển bouncer đến một vị trí ngẫu nhiên trên màn hình.
Thiết lập cấu trúc cửa sổ và dữ liệu
Đầu tiên, chúng tôi đặt chú thích của thanh tiêu đề của cửa sổ thành chuỗi thành 'Chuột' trên
dòng 12. Chúng tôi
thiết lập các caption của cửa sổ với một cuộc gọi đến pygame.display.set_caption () các
giống như cách chúng tôi đã làm trong các chương trình Pygame trước đây của chúng tôi. Tiếp
theo chúng tôi muốn thiết lập một số
các biến theo dõi sự chuyển động của bouncer.
28. # thiết lập các biến chuyển động
29. moveLeft = Sai
30. moveRight = Sai
31. moveUp = Sai
32. moveDown = Sai
Chúng tôi sẽ sử dụng bốn biến boolean khác nhau để theo dõi mũi tên nào
chìa khóa đang được giữ Ví dụ: khi người dùng nhấn phím mũi tên trái trên cô ấy
bàn phím, chúng tôi sẽ đặt biến moveLeft thành True . Khi cô ấy buông chìa khóa, chúng tôi sẽ
đặt biến moveLeft trở lại Sai . Phím "W" ảnh hưởng đến biến moveUp ,
Phím "S" ảnh hưởng đến biến moveDown và phím "D" ảnh hưởng đến biến moveRight
theo cách tương tự
Các dòng 34 đến 43 giống hệt với mã trong các chương trình Pygame trước đó. Những dòng này
xử lý
bắt đầu vòng lặp trò chơi và xử lý những việc cần làm khi người dùng muốn thoát khỏi chương
trình.
Chúng tôi sẽ bỏ qua phần giải thích cho mã này ở đây vì chúng tôi đã trình bày nó trong phần
trước
chương.
351
18 - Phát hiện va chạm

Trang 365
Sự kiện và xử lý sự kiện KEYDOWN
Mã để xử lý các sự kiện nhấn phím và phát hành khóa bên dưới. Nhưng khi bắt đầu
chương trình, chúng tôi sẽ đặt tất cả các biến này thành Sai .
Bảng 18-1: Sự kiện và nguyên nhân khiến chúng được tạo.
Biến cố
Sự miêu tả
QUIT
Tạo khi người dùng đóng với cửa sổ.
KHÓA
Được tạo khi người dùng nhấn xuống một phím. Có chìa khóa
thuộc tính cho biết phím nào được nhấn. Cũng có một mod
thuộc tính cho biết nếu phím Shift, Ctrl, Alt hoặc các phím khác được giữ
xuống khi phím này được nhấn.
TỪ KHÓA
Được tạo khi người dùng phát hành một khóa. Có chìa khóa và mod
thuộc tính tương tự như thuộc tính cho KEYDOWN .
KIẾM TIỀN
Tạo ra bất cứ khi nào chuột di chuyển qua cửa sổ. Có một
thuộc tính pos trả về tuple (x, y) cho tọa độ của
chuột ở đâu trong cửa sổ Các rel thuộc tính cũng
trả về một tuple (x, y), nhưng nó cho tọa độ tương đối kể từ khi
sự kiện MOUSEMOTION cuối cùng . Ví dụ: nếu chuột di chuyển
còn lại bốn pixel từ (200, 200) đến (196, 200), sau đó rel sẽ
được (-4, 0) . Các nút thuộc tính trả về một tuple của ba
số nguyên. Số nguyên đầu tiên trong tuple dành cho chuột trái
nút, số nguyên thứ hai cho nút chuột giữa (nếu có
là nút chuột giữa) và số nguyên thứ ba ở bên phải
Bẫy chuột. Các số nguyên này sẽ là 0 nếu chúng không tồn tại
nhấn xuống khi chuột di chuyển và 1 nếu chúng được nhấn
xuống.
MOUSEBUTTONDOWN
Được tạo khi nhấn nút chuột trong cửa sổ.
Sự kiện này có một thuộc tính pos là một tuple (x, y) cho
tọa độ của con chuột khi nút là
ép. Ngoài ra còn có một thuộc tính nút là một số nguyên
từ 1 đến 5 cho biết nút chuột nào đã được nhấn:
Giá trị của nút
Bẫy chuột
1
Nút trái
2
Phím giữa
3
Nút phải
4
Bánh xe di chuyển lên
5
Bánh xe di chuyển xuống
MOUSEBUTTONUP
Được tạo khi nút chuột được phát hành. Cái này có cùng
các thuộc tính như MOUSEBUTTONDOWN
352

Trang 367
44.
if event.type == KEYDOWN:
Pygame có một loại sự kiện khác gọi là KEYDOWN . Trên dòng 41, chúng tôi kiểm tra nếu
Thuộc tính event.type bằng giá trị QUIT để kiểm tra xem chúng ta có nên thoát khỏi chương
trình không.
Nhưng có những sự kiện khác mà Pygame có thể tạo ra. Một danh sách ngắn gọn về các sự kiện
có thể
được trả về bởi pygame.event.get () trong Bảng 18-1.
Đặt bốn biến bàn phím
45.
# thay đổi các biến bàn phím
46.
if event.key == K_LEFT hoặc event.key == ord
('a'):
47.
moveRight = Sai
48.
moveLeft = Đúng
49.
if event.key == K_RIGHT hoặc event.key == ord
('D'):
50.
moveLeft = Sai
51.
moveRight = Đúng
52.
if event.key == K_UP hoặc event.key == ord
('w'):
53.
moveDown = Sai
54.
moveUp = Đúng
55.
if event.key == K_DOWN hoặc event.key == ord
('S'):
56.
moveUp = Sai
57.
moveDown = Đúng
Nếu loại sự kiện là KEYDOWN , thì đối tượng sự kiện sẽ có thuộc tính khóa sẽ
cho chúng tôi biết phím nào được nhấn xuống. Trên dòng 46, chúng ta có thể so sánh giá trị này
với K_LEFT ,
đại diện cho phím mũi tên trái trên bàn phím. Chúng tôi sẽ làm điều này cho mỗi mũi tên
các phím: K_LEFT , K_RIGHT , K_UP , K_DOWN .
Khi nhấn một trong các phím này, chúng tôi sẽ đặt chuyển động tương ứng
biến thành True . Chúng tôi cũng sẽ đặt biến chuyển động của hướng ngược lại thành
Sai . Ví dụ, chương trình thực thi các dòng 47 và 48 khi phím mũi tên trái có
bị ép. Trong trường hợp này, chúng tôi sẽ đặt moveLeft thành True và moveRight thành false
(mặc dù moveRight có thể đã sai , chúng tôi đặt nó thành Sai chỉ để đảm bảo).
Bạn có thể nhận thấy rằng trên dòng 46, in event.key có thể bằng K_LEFT hoặc ord
('a') . Giá trị trong event.key được đặt thành giá trị ASCII nguyên của khóa đó là
nhấn trên bàn phím. (Không có giá trị ASCII cho các phím mũi tên, đó là lý do tại sao chúng tôi
sử dụng
biến không đổi K_LEFT .) Bạn có thể sử dụng hàm ord () để lấy giá trị ASCII của
bất kỳ ký tự đơn lẻ nào để so sánh nó với event.key .
Bằng cách thực thi mã trên các dòng 47 và 48 nếu tổ hợp phím là K_LEFT hoặc ord
353
18 - Phát hiện va chạm

Trang 368
('a') , chúng tôi tạo phím mũi tên trái và phím A làm điều tương tự. Bạn có thể nhận thấy
rằng các phím W, A, S và D đều được sử dụng thay thế cho việc thay đổi các biến chuyển động.
Điều này là do một số người có thể muốn sử dụng tay trái để nhấn các phím WASD
thay vì tay phải của họ để nhấn các phím mũi tên. Chương trình của chúng tôi cung cấp cả hai!
Xử lý sự kiện KEYUP
58.
if event.type == KEYUP:
Khi người dùng giải phóng khóa mà họ đang giữ, một sự kiện KEYUP được tạo.
59.
if event.key == K_ESCAPE:
60.
pygame.quito ()
61.
sys.exit ()
Nếu khóa mà người dùng phát hành là khóa Esc, thì chúng tôi muốn chấm dứt chương trình.
Hãy nhớ rằng, trong Pygame, bạn phải gọi hàm pygame.quito () trước khi gọi hàm
Hàm sys.exit () . Chúng tôi muốn làm điều này khi người dùng nhả phím Esc, không phải khi
đầu tiên họ nhấn phím Esc xuống.
Các dòng từ 62 đến 69 sẽ đặt biến chuyển động thành Sai nếu khóa của hướng đó bị mất.
62.
if event.key == K_LEFT hoặc event.key == ord
('a'):
63.
moveLeft = Sai
64.
if event.key == K_RIGHT hoặc event.key == ord
('D'):
65.
moveRight = Sai
66.
if event.key == K_UP hoặc event.key == ord
('w'):
67.
moveUp = Sai
68.
if event.key == K_DOWN hoặc event.key == ord
('S'):
69.
moveDown = Sai
Dịch chuyển tức thời
Nếu người dùng phát hành một trong các phím di chuyển trình phát, thì chúng tôi muốn đặt
biến chuyển động tương ứng với khóa thành Sai . Điều này sẽ nói với các phần sau của
chương trình của chúng tôi không còn di chuyển hình vuông của người chơi trên màn hình.
70.
if event.key == ord ('x'):
71.
player.top = Random.randint (0,
WINDOWHEIGHT - player.height)
354

Trang 369
72.
player.left = Random.randint (0,
WINDOWWIDTH - player. Thong)
Chúng tôi cũng sẽ thêm dịch chuyển tức thời vào trò chơi của chúng tôi. Nếu người dùng nhấn
phím "X", thì chúng tôi sẽ
đặt vị trí của hình vuông của người dùng đến một vị trí ngẫu nhiên trên cửa sổ. Điều này sẽ cung
cấp cho người dùng
khả năng dịch chuyển tức thời xung quanh cửa sổ bằng cách nhấn phím "X" (mặc dù họ không
thể điều khiển
nơi họ sẽ dịch chuyển tức thời: nó hoàn toàn ngẫu nhiên).
Xử lý sự kiện MOUSEBUTTONUP
74.
if event.type == MOUSEBUTTONUP:
75.
thực phẩm.append (pygame.Rect (event.pose [0],
event.pose [1], FOODSIZE, FOODSIZE))
Đầu vào chuột được xử lý bởi các sự kiện giống như đầu vào bàn
phím. các MOUSEBUTTONUP
sự kiện xảy ra khi người dùng nhấp vào nút chuột ở đâu đó trong cửa sổ của chúng tôi và phát
hành
nút chuột. Các pos thuộc tính trong tổ chức sự kiện đối tượng được đặt thành một tuple của hai
số nguyên cho
tọa độ XY. Trên dòng 75, tọa độ X được lưu trữ trong event.pose [0] và Y-
tọa độ được lưu trữ trong event.pose [1] . Chúng ta sẽ tạo một đối tượng Rect mới để thể hiện
một
thực phẩm mới và đặt nó ở nơi xảy ra sự kiện MOUSEBUTTONUP . Bằng cách thêm
một Rect mới
Đối tượng trong danh sách thực phẩm , một hình vuông thực phẩm mới sẽ được hiển thị trên màn
hình.
Di chuyển Bouncer quanh màn hình
86. # di chuyển người chơi
87. nếu moveDown và player.bottom <WINDOWHEIGHT:
88.
player.top + = MOVESPEED
89. nếu moveUp và player.top> 0:
90.
player.top - = CHUYỂN ĐỔI
91. nếu moveLeft và player.left> 0:
92.
player.left - = MOVESPEED
93. nếu moveRight và player.right <WINDOWWIDTH:
94.
player.right + = MOVESPEED
Chúng tôi đã đặt các biến chuyển động ( moveDown , moveUp , moveLeft và
moveRight ) thành Đúng hoặc Sai tùy thuộc vào phím nào người dùng đã nhấn. Bây giờ chúng ta
sẽ thực sự di chuyển hình vuông của người chơi (được đại diện bởi đối tượng pygame.Rect
được lưu trong trình phát ) xung quanh bằng cách điều chỉnh tọa độ XY của
trình phát . Nếu moveDown được đặt
thành True (và dưới cùng của hình vuông của người chơi không nằm dưới cạnh dưới của cửa sổ),
sau đó chúng tôi di chuyển hình vuông của người chơi xuống bằng cách
thêm MOVESPEED vào đầu hiện tại của người chơi
thuộc tính. Chúng tôi cũng làm điều tương tự cho ba hướng khác.
355
18 - Phát hiện va chạm

Trang 370
Các colliderect () Phương pháp
99. # kiểm tra xem người chơi có giao nhau với bất kỳ thực phẩm nào không
hình vuông.
100. cho thực phẩm trong thực phẩm [:]:
101.
if player.colliderect (thức ăn):
102.
thực phẩm.remove (thực phẩm)
Trong chương trình Phát hiện Va chạm trước đây của chúng tôi, chúng tôi có chức năng riêng để
kiểm tra xem có
hình chữ nhật đã va chạm với nhau. Chức năng đó đã được bao gồm trong cuốn sách này để bạn
có thể hiểu làm thế nào mã đằng sau phát hiện va chạm hoạt động. Trong chương trình này,
chúng ta có thể
sử dụng chức năng phát hiện va chạm đi kèm với Pygame. Các colliderect ()
Phương thức cho các đối tượng pygame.Rect được truyền vào một đối
tượng pygame.Rect khác dưới dạng một
đối số và trả về Đúng nếu hai hình chữ nhật va chạm và Sai nếu không. Đây là
hành vi chính xác giống như hàm doRectsOverlap () trong Va chạm trước của chúng tôi
Chương trình phát hiện.
110. mainClock.tick (40)
Phần còn lại của mã tương tự như mã trong chương trình Nhập liệu tương tự như trước đó
Chương trình phát hiện va chạm: vẽ các ô vuông thực phẩm và các ô vuông của người chơi vào
windowSurface bề mặt, thỉnh thoảng thêm một hình vuông thực phẩm mới tại một vị trí ngẫu
nhiên vào
danh sách thực phẩm , kiểm tra xem hình vuông của người chơi có va chạm với bất kỳ hình
vuông thực phẩm nào không, và gọi
mainClock.tick (40) để làm cho chương trình chạy ở tốc độ phù hợp.
Tóm tắt: Phát hiện va chạm và đầu vào Pygame
Chương này đã giới thiệu khái niệm phát hiện va chạm, được sử dụng trong hầu hết
trò chơi đồ họa. Phát hiện va chạm giữa hai hình chữ nhật thật dễ dàng: chúng tôi chỉ cần kiểm
tra xem
bốn góc của một trong hai hình chữ nhật nằm trong hình chữ nhật khác. Đây là một điều phổ
biến
để kiểm tra xem Pygame có cung cấp phương pháp phát hiện va chạm của riêng mình không
colliderect () cho các đối tượng pygame.Rect .
Một số trò chơi đầu tiên trong cuốn sách này là dựa trên văn bản. Đầu ra chương trình là văn bản
được in ra màn hình và đầu vào được gõ bởi người dùng trên bàn phím. Nhưng GUI
các chương trình có thể chấp nhận đầu vào bàn phím và chuột. Hơn nữa, các chương trình GUI
có thể đáp ứng
nhấn phím đơn khi người dùng ấn xuống hoặc bật một phím. Người dùng không
phải gõ toàn bộ phản hồi và nhấn Enter. Điều này cho phép phản hồi ngay lập tức
khi người chơi nhấn xuống bất kỳ phím nào trên bàn phím và nhiều trò chơi tương tác hơn.
Các chương trình Pygame mà chúng tôi đã hiển thị cho đến nay đã vẽ hình chữ nhật, đường
thẳng, hình tròn và thậm chí
pixel riêng lẻ cho màn hình. Chúng được gọi là nguyên thủy vẽ. Nhưng chúng tôi cũng muốn
353

Trang 371
sử dụng hình ảnh và hình ảnh thay vì nguyên thủy vẽ đơn giản. Chương tiếp theo sẽ kể
bạn làm thế nào để tải hình ảnh và vẽ chúng trên màn hình. Chúng tôi cũng sẽ học cách chơi
âm thanh và âm nhạc cho người chơi nghe.
357
18 - Phát hiện va chạm

Trang 372
Các chủ đề được đề cập trong Chương này:
● Tập tin hình ảnh và âm thanh
● Vẽ Sprites
● Hàm pygame.image.load ()

● Kiểu dữ liệu pygame.mixer.Sound

● Mô-đun pygame.mixer.music

Trong hai chương trước, chúng tôi đã học cách tạo các chương trình GUI có đồ họa
và có thể chấp nhận đầu vào từ bàn phím và chuột. Chúng tôi cũng đã học cách vẽ hình
trong các màu sắc khác nhau trên màn hình. Trong chương này, chúng ta sẽ học cách hiển thị
hình ảnh và
hình ảnh (được gọi là spites) và phát âm thanh và âm nhạc trong các trò chơi của chúng tôi.
Một sprite là một cái tên cho một hình ảnh hai chiều duy nhất được sử dụng như một phần của
đồ họa trên màn hình. Dưới đây là một số sprite ví dụ:
358

Trang 373
Hình 19-1: Một số ví dụ về các sprite.
Đây là một ví dụ về các sprite đang được sử dụng trong một cảnh hoàn chỉnh.
Hình 19-2: Một ví dụ về một cảnh hoàn chỉnh, với các họa tiết được vẽ trên đỉnh của một nền.
Các hình ảnh sprite được vẽ trên đầu của nền. Lưu ý rằng chúng ta có thể lật sprite
hình ảnh theo chiều ngang để các họa tiết được đối diện theo cách khác. Chúng ta có thể vẽ cùng
một sprite
hình ảnh nhiều lần trên cùng một cửa sổ. Chúng tôi cũng có thể thay đổi kích thước các sprite để
lớn hơn hoặc
nhỏ hơn hình ảnh sprite ban đầu. Hình nền cũng có thể được coi là một
359
19 - Âm thanh và hình ảnh

Trang 374
sprite lớn.
Chương trình tiếp theo chúng tôi thực hiện sẽ trình bày cách phát âm thanh và vẽ các họa tiết
bằng cách sử dụng
Pygame.
Tập tin hình ảnh và âm thanh
Sprites được lưu trữ trong các tập tin hình ảnh trên máy tính của bạn. Có một số hình ảnh khác
nhau
các định dạng mà Pygame có thể sử dụng. Bạn có thể cho biết tập tin hình ảnh sử dụng định dạng
nào bằng cách nhìn vào
cuối tên tập tin Ví dụ: tệp happy.png có định dạng PNG. Bức hình
các định dạng hỗ trợ Pygame bao gồm BMP, PNG, JPG (và JPEG) và GIF.
Bạn có thể tải hình ảnh từ trình duyệt web của bạn. Trên hầu hết các trình duyệt web, bạn chỉ cần
phải nhấp chuột phải vào hình ảnh trong trang web và chọn Lưu từ menu
xuất hiện. Nhớ nơi trên ổ cứng bạn đã lưu tệp hình ảnh. Bạn cũng có thể tạo
hình ảnh của riêng bạn với một chương trình vẽ như MS Paint hoặc Tux Paint.
Các định dạng tệp âm thanh mà Pygame hỗ trợ là MID, WAV và MP3. Bạn có thể
tải xuống các hiệu ứng âm thanh từ Internet giống như các tệp hình ảnh, miễn là các hiệu ứng âm
thanh
là một trong ba định dạng này. Nếu bạn có micrô, bạn cũng có thể ghi lại âm thanh
với máy tính của bạn và sử dụng chúng trong các trò chơi của bạn.
Chương trình âm thanh và âm thanh
Chương trình này giống như chương trình Nhập bàn phím và chuột từ lần trước
chương. Tuy nhiên, trong chương trình này, chúng tôi sẽ sử dụng các họa tiết thay vì hình vuông
trông đơn giản. Chúng tôi
sẽ sử dụng một sprite của một người đàn ông nhỏ thay vì hình vuông của người chơi trắng và
một sprite của anh đào
thay vì các ô vuông thực phẩm xanh. Chúng tôi cũng chơi nhạc nền và hiệu ứng âm thanh khi
người chơi sprite ăn một trong những anh đào.
Mã nguồn của Chương trình Sprites và Âm thanh
Nếu bạn biết cách sử dụng phần mềm đồ họa như Photoshop hoặc MS Paint, bạn có thể vẽ
hình ảnh của riêng bạn và sử dụng các tập tin hình ảnh trong các trò chơi của bạn. Nếu bạn
không biết cách sử dụng
các chương trình, bạn chỉ có thể tải xuống đồ họa từ các trang web và sử dụng các tệp hình ảnh
đó để thay thế.
Điều tương tự áp dụng cho các tập tin âm nhạc và âm thanh. Bạn cũng có thể tìm thấy hình ảnh
trên các trang web hoặc
hình ảnh từ một máy ảnh kỹ thuật số. Bạn có thể tải xuống các tập tin hình ảnh và âm thanh từ
đây
trang web của cuốn sách tại http://inventwithpython.com/resource/. Bạn có thể tải về nguồn
mã trong chương này từ URL http://inventwithpython.com/ch CHƯƠNG19.
spitesAndSound.py
Mã này có thể được tải xuống từ http://inventwithpython.com/spritesAndSound.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
360

Trang 375
1. nhập pygame, sys, thời gian, ngẫu nhiên
2. từ nhập pygame.locals *
3.
4. # thiết lập pygame
5. pygame.init ()
6. mainClock = pygame.time.Clock ()
7.
8. # thiết lập cửa sổ
9. WINDOWWIDTH = 400
10. CỬA SỔ = 400
11. windowSurface = pygame.display.set_mode ((WINDOWWIDTH,
Cửa sổ), 0, 32)
12. pygame.display.set_caption ('Sprites và âm thanh')
13.
14. # thiết lập màu sắc
15. ĐEN = (0, 0, 0)
16.
17. # thiết lập cấu trúc dữ liệu khối
18. người chơi = pygame.Rect (300, 100, 40, 40)
19. playerImage = pygame.image.load ('player.png')
20. playerStretchedImage = pygame.transform.scale (playerImage,
(40, 40))
21. foodImage = pygame.image.load ('cherry.png')
22. thực phẩm = []
23. cho tôi trong phạm vi (20):
24. thực phẩm.append (pygame.Rect (ngẫu nhiên.randint (0, WINDOWWIDTH
- 20), ngẫu nhiên.randint (0, WINDOWHEIGHT - 20), 20, 20))
25.
26. FoodCount = 0
27. THỰC PHẨM = 40
28.
29. # thiết lập các biến bàn phím
30. moveLeft = Sai
31. moveRight = Sai
32. moveUp = Sai
33. moveDown = Sai
34.
35. CHUYỂN ĐỘNG = 6
36.
37. # thiết lập âm nhạc
38. pickUpSound = pygame.mixer.Sound ('Pick.wav')
39. pygame.mixer.music.load ('nền.mid')
40. pygame.mixer.music.play (-1, 0.0)
41. nhạcPlay = True
42.
43. # chạy vòng lặp trò chơi
44. trong khi Đúng:
45. # kiểm tra sự kiện QUIT
46. cho sự kiện trong pygame.event.get ():
47.
if event.type == QUIT:
48.
pygame.quito ()
49.
sys.exit ()
50.
if event.type == KEYDOWN:
51.
# thay đổi các biến bàn phím
361
19 - Âm thanh và hình ảnh

Trang 376
52.
if event.key == K_LEFT hoặc event.key == ord
('a'):
53.
moveRight = Sai
54.
moveLeft = Đúng
55.
if event.key == K_RIGHT hoặc event.key == ord
('D'):
56.
moveLeft = Sai
57.
moveRight = Đúng
58.
if event.key == K_UP hoặc event.key == ord ('w'):
59.
moveDown = Sai
60.
moveUp = Đúng
61.
if event.key == K_DOWN hoặc event.key == ord
('S'):
62.
moveUp = Sai
63.
moveDown = Đúng
64.
if event.type == KEYUP:
65.
if event.key == K_ESCAPE:
66.
pygame.quito ()
67.
sys.exit ()
68.
if event.key == K_LEFT hoặc event.key == ord
('a'):
69.
moveLeft = Sai
70.
if event.key == K_RIGHT hoặc event.key == ord
('D'):
71.
moveRight = Sai
72.
if event.key == K_UP hoặc event.key == ord ('w'):
73.
moveUp = Sai
74.
if event.key == K_DOWN hoặc event.key == ord
('S'):
75.
moveDown = Sai
76.
if event.key == ord ('x'):
77.
player.top = Random.randint (0,
WINDOWHEIGHT - player.height)
78.
player.left = Random.randint (0,
WINDOWWIDTH - player. Thong)
79.
if event.key == ord ('m'):
80.
nếu nghe nhạc:
81.
pygame.mixer.music.stop ()
82.
khác:
83.
pygame.mixer.music.play (-1, 0.0)
84.
MusicPlay = không âm nhạcPlay
85.
86.
if event.type == MOUSEBUTTONUP:
87.
thực phẩm.append (pygame.Rect (event.pose [0] - 10,
event.pose [1] - 10, 20, 20))
88.
89. foodCount + = 1
90. if foodCount> = NEWFOOD:
91.
# thêm thức ăn mới
92.
thức ăn = 0
93.
thực phẩm.append (pygame.Rect (ngẫu nhiên.randint (0,
WINDOWWIDTH - 20), ngẫu nhiên.randint (0, WINDOWHEIGHT - 20),
20, 20))
94.
362

Trang 377
95. # vẽ nền đen lên bề mặt
96. windowSurface.fill (ĐEN)
97.
98. # di chuyển người chơi
99. nếu moveDown và player.bottom <WINDOWHEIGHT:
100.
player.top + = MOVESPEED
101. nếu moveUp và player.top> 0:
102.
player.top - = CHUYỂN ĐỔI
103. nếu moveLeft và player.left> 0:
104.
player.left - = MOVESPEED
105. nếu moveRight và player.right <WINDOWWIDTH:
106.
player.right + = MOVESPEED
107.
108.
109. # vẽ khối lên bề mặt
110. windowSurface.blit (playerStretchedImage, player)
111.
112. # kiểm tra xem khối đã giao nhau với bất kỳ thực phẩm nào chưa
hình vuông.
113. cho thực phẩm trong thực phẩm [:]:
114.
if player.colliderect (thức ăn):
115.
thực phẩm.remove (thực phẩm)
116.
player = pygame.Rect (player.left, player.top,
player. thong + 2, player.height + 2)
117.
playerStretchedImage = pygame.transform.scale
(playerImage, (player. thong, player.height))
118.
nếu nghe nhạc:
119.
pickUpSound.play ()
120.
121. # vẽ thức ăn
122. cho thực phẩm trong thực phẩm:
123.
windowSurface.blit (foodImage, thực phẩm)
124.
125. # vẽ cửa sổ lên màn hình
126. pygame.display.update ()
127. mainClock.tick (40)
363
19 - Âm thanh và hình ảnh

Trang 378
Hình 19-3: Trò chơi Sprites và Âm thanh.

Thiết lập cửa sổ và cấu trúc dữ liệu


Hầu hết các mã trong chương trình này đã được giải thích trong chương trước, vì vậy chúng tôi
sẽ chỉ
tập trung vào các phần có thêm họa tiết và âm thanh.
12. pygame.display.set_caption ('Sprites và âm thanh')
Trước tiên, hãy đặt chú thích của thanh tiêu đề thành một chuỗi mô tả chương trình này. Vượt
qua
chuỗi 'Sprites và Sound' cho pygame.display.set_caption ()
chức năng.
17. # thiết lập cấu trúc dữ liệu khối
18. người chơi = pygame.Rect (300, 100, 40, 40)
19. playerImage = pygame.image.load ('player.png')
20. playerStretchedImage = pygame.transform.scale
(người chơi Hình ảnh, (40, 40))
21. foodImage = pygame.image.load ('cherry.png')
Chúng tôi sẽ sử dụng ba biến khác nhau để đại diện cho người chơi, không giống như trước đây
các chương trình chỉ sử dụng một. Biến người chơi sẽ lưu trữ một đối tượng Rect giữ
364

Trang 379
theo dõi vị trí và mức độ lớn của người chơi. Biến người chơi không chứa
hình ảnh của người chơi, chỉ kích thước và vị trí của người chơi. Khi bắt đầu chương trình, đầu
trang
góc bên trái của người chơi sẽ nằm ở (300, 100) và người chơi sẽ có chiều cao và
chiều rộng 40 pixel để bắt đầu.
Biến thứ hai đại diện cho người chơi sẽ là playerImage . Các
Hàm pygame.image.load () được truyền một chuỗi tên tệp của hình ảnh tới
tải trọng. Giá trị trả về của pygame.image.load () là một đối tượng Surface có
hình ảnh trong tập tin hình ảnh được vẽ trên bề mặt của nó. Chúng tôi lưu trữ đối
tượng Surface này bên trong
người chơi Hình ảnh .
Các pygame.transform.scale () Chức năng
Trên dòng 20, chúng tôi sẽ sử dụng một chức năng mới trong mô-đun pygame.transform . Các
Hàm pygame.transform.scale () có thể thu nhỏ hoặc phóng to một sprite. Đầu tiên
đối số là một đối tượng pygame.Surface với hình ảnh được vẽ trên đó. Thư hai
đối số là một tuple cho chiều rộng và chiều cao mới của hình ảnh trong đối số đầu tiên. Các
Hàm pygame.transform.scale () trả về một đối tượng pygame.Surface với
hình ảnh được vẽ ở một kích thước mới. Chúng tôi sẽ lưu trữ hình ảnh gốc trong playerImage
biến nhưng hình ảnh kéo dài trong biến playerStretchedImage .
Trên dòng 21, chúng ta gọi lại pygame.image.load () để tạo một đối tượng Surface với
hình ảnh anh đào vẽ trên đó.
Hãy chắc chắn rằng bạn có tệp player.png và cherry.png trong cùng thư mục với
tập tin spitesAndSound.py , nếu không Pygame sẽ không thể tìm thấy chúng và sẽ cung cấp cho
lỗi.
Các đối tượng Surface được lưu trữ trong playerImage và foodImage giống nhau
làm đối tượng Surface mà chúng ta sử dụng cho cửa sổ. Trong trò chơi của chúng tôi, chúng tôi
sẽ làm mờ các bề mặt này
trên bề mặt của cửa sổ để tạo cửa sổ mà người dùng nhìn thấy. Điều này giống hệt nhau
như khi chúng ta có một đối tượng Surface được trả về từ phương thức render () cho Font
các đối tượng trong chương trình Hello World của chúng tôi. Để thực sự hiển thị văn bản, chúng
tôi đã phải làm mờ điều này
Đối tượng bề mặt (mà văn bản được vẽ trên) đối tượng Surface của cửa sổ . (Và
sau đó, tất nhiên, gọi phương thức update () trên đối tượng Surface của cửa sổ .)
Thiết lập âm nhạc và âm thanh
37. # thiết lập âm nhạc
38. pickUpSound = pygame.mixer.Sound ('Pick.wav')
39. pygame.mixer.music.load ('nền.mid')
40. pygame.mixer.music.play (-1, 0.0)
41. nhạcPlay = True
Tiếp theo chúng ta cần tải các tập tin âm thanh. Có hai mô-đun cho âm thanh trong Pygame. Các
365
19 - Âm thanh và hình ảnh

Trang 380
Mô-đun pygame.mixer chịu trách nhiệm phát các hiệu ứng âm thanh ngắn trong trò chơi.
Các pygame.mixer.music Module này được sử dụng để chơi nhạc nền.
Chúng ta sẽ gọi hàm xây dựng pygame.mixer.Sound () để tạo
đối tượng pygame.mixer.Sound (mà chúng ta sẽ gọi đơn giản là một đối tượng Âm thanh ). Đối
tượng này
có một phương thức play () mà khi được gọi sẽ phát hiệu ứng âm thanh.
Trên dòng 39, chúng tôi tải nhạc nền bằng cách gọi pygame.mixer.music.load (). Chúng tôi
sẽ bắt đầu phát nhạc nền ngay lập tức bằng cách gọi
pygame.mixer.music.play () . Tham số đầu tiên cho Pygame biết bao nhiêu lần
phát nhạc nền sau lần đầu tiên chúng tôi chơi nó. Vì vậy, vượt qua 5 sẽ gây ra Pygame
để phát nhạc nền 6 lần. Nếu bạn vượt qua -1 cho tham số đầu tiên,
nhạc nền sẽ lặp lại mãi mãi.
Tham số thứ hai cho pygame.mixer.music.play () cho biết điểm nào trong
tập tin âm thanh để bắt đầu chơi. Vượt qua 0,0 sẽ phát nhạc nền bắt đầu từ
rất bắt đầu Nếu bạn vượt qua 2.5 cho tham số thứ hai, điều này sẽ gây ra hậu cảnh
âm nhạc để bắt đầu phát hai giây rưỡi sau khi bắt đầu âm nhạc.
Cuối cùng, chúng ta có một biến boolean đơn giản có tên là MusicPlay sẽ cho chúng ta biết
chương trình có nên phát nhạc nền và hiệu ứng âm thanh hay không. Thật tốt khi cho
người chơi tùy chọn để chạy chương trình mà không có âm thanh phát.
Bật và tắt âm thanh
79.
if event.key == ord ('m'):
80.
nếu nghe nhạc:
81.
pygame.mixer.music.stop ()
82.
khác:
83.
pygame.mixer.music.play (-1, 0.0)
84.
MusicPlay = không âm nhạcPlay
Chúng tôi sẽ kiểm tra xem người dùng đã nhấn phím M. Phím M sẽ chuyển nền
bật hoặc tắt nhạc Nếu MusicPlay được đặt thành True , thì điều đó có nghĩa là nhạc nền
hiện đang phát và chúng ta nên dừng âm nhạc bằng cách gọi
pygame.mixer.music.stop () . Nếu MusicPlay được đặt thành Sai , thì đó
có nghĩa là nhạc nền hiện không phát và nên được bắt đầu bằng cách gọi
pygame.mixer.music.play () . Các tham số chúng tôi chuyển đến
Hàm pygame.mixer.music.play () giống như chúng ta đã chuyển trên dòng 40.
Cuối cùng, không có vấn đề gì, chúng tôi muốn chuyển đổi giá trị trong MusicPlay . Đi bộ một
giá trị boolean có nghĩa là chúng ta đặt nó ngược lại với giá trị hiện tại của nó. Dòng
musicPlay = not musicPlay sẽ đặt biến thành Sai nếu đó là
hiện tại True hoặc đặt thành True nếu hiện tại là Sai . Hãy nghĩ về việc chuyển đổi như những gì
xảy ra khi bạn bật hoặc tắt công tắc đèn.
365

Trang 381
Chuyển đổi giá trị trong âm nhạcPlay sẽ đảm bảo rằng lần sau người dùng nhấn
Phím M, nó sẽ làm ngược lại với những gì nó đã làm trước đó.
Vẽ Trình phát trên Cửa sổ
109. # vẽ khối lên bề mặt
110. windowSurface.blit (playerStretchedImage, player)
Hãy nhớ rằng giá trị được lưu trữ trong playerStretchedImage là một đối tượng Surface .
"Blits" là quá trình vẽ nội dung của một đối tượng Surface sang đối tượng Surface khác
Đối tượng bề mặt . Trong trường hợp này, chúng tôi muốn vẽ sprite của trình phát lên trên cửa sổ
Đối tượng bề mặt (được lưu trữ trong windowSurface ). (Cũng nhớ rằng bề mặt
Được sử dụng để hiển thị trên màn hình là đối tượng Surface được trả về bởi
pygame.display.set_caption () .)
Tham số thứ hai cho phương thức blit () là một đối tượng Rect chỉ định nơi
sprite nên được làm mờ. Các Rect đối tượng được lưu trữ trong máy nghe nhạc là những gì theo
dõi
vị trí của người chơi trong cửa sổ.
Kiểm tra xem người chơi có va chạm với anh đào không
114.
if player.colliderect (thức ăn):
115.
thực phẩm.remove (thực phẩm)
116.
player = pygame.Rect (player.left, player.top,
player. thong + 2, player.height + 2)
117.
playerStretchedImage = pygame.transform.scale
(playerImage, (player. thong, player.height))
118.
nếu nghe nhạc:
119.
pickUpSound.play ()
Mã này tương tự như mã trong các chương trình trước. Nhưng ở đây chúng tôi đang thêm một
vài dòng mới. Chúng tôi muốn gọi phương thức play () trên đối tượng Sound được lưu trữ trong
biến PickUpSound . Nhưng chúng tôi chỉ muốn làm điều này nếu MusicPlay được đặt
thành True
(cho chúng tôi biết rằng âm thanh được bật).
Khi người chơi ăn một trong những quả anh đào, chúng ta sẽ phóng to kích thước của người chơi
bằng cách
hai pixel chiều cao và chiều rộng. Trên dòng 116, chúng tôi tạo một đối tượng Rect mới để lưu
trữ trong
biến người chơi sẽ có cùng kích thước với đối tượng Rect cũ được lưu trong trình phát.
Ngoại trừ chiều rộng và chiều cao của đối tượng Rect mới sẽ lớn hơn 2 pixel.
Khi đối tượng Rect đại diện cho vị trí và kích thước của người chơi, nhưng hình ảnh
của trình phát được lưu trữ trong trình phátStretchedImage dưới dạng đối tượng Surface . Chúng
tôi muốn
tạo một hình ảnh kéo dài mới bằng cách gọi pygame.transform.scale () . Chắc chắn rằng
vượt qua đối tượng Surface ban đầu trong playerImage và không
367
19 - Âm thanh và hình ảnh

Trang 382
người chơiStretchedImage . Kéo dài một hình ảnh thường làm biến dạng nó một chút. Nếu chúng ta
giữ
khôi phục lại một hình ảnh kéo dài nhiều lần, các biến dạng tăng lên nhanh chóng. Nhưng bởi
kéo dài hình ảnh gốc sang kích thước mới, chúng tôi chỉ làm biến dạng hình ảnh một lần. Đây là
lý do tại sao
chúng tôi vượt qua playerImage làm đối số đầu tiên cho pygame.transform.scale () .
Vẽ anh đào trên cửa sổ
121. # vẽ thức ăn
122. cho thực phẩm trong thực phẩm:
123.
windowSurface.blit (foodImage, thực phẩm)
Trong các chương trình trước đây của chúng tôi, chúng tôi đã gọi hàm pygame.draw.rect () để vẽ
hình vuông màu xanh lá cây cho mỗi đối tượng Rect được lưu trữ trong danh sách thực
phẩm . Tuy nhiên, trong chương trình này, chúng tôi
muốn vẽ những cành anh đào thay thế. Chúng ta sẽ gọi phương thức blit () và truyền
Đối tượng bề mặt được lưu trữ trong foodImage . (Đây là bề mặt có hình anh đào
vẽ trên đó.)
Chúng tôi chỉ sử dụng biến thực phẩm (có chứa từng đối tượng Rect trong thực phẩm trên
mỗi lần lặp qua vòng lặp for ) để cho biết phương thức blit () nơi vẽ
foodImage .
Tóm tắt: Các trò chơi với Đồ họa và Âm thanh
Trò chơi này đã bổ sung thêm đồ họa cao cấp hơn và được giới thiệu sử dụng âm thanh trong
chúng tôi
Trò chơi. Các hình ảnh (được gọi là spites) trông tốt hơn nhiều so với các nguyên thủy vẽ đơn
giản
được sử dụng trong các chương trình trước đây của chúng tôi. Trò chơi được trình bày trong
chương này cũng có chơi nhạc
trong nền trong khi cũng chơi hiệu ứng âm thanh.
Sprites có thể được thu nhỏ (nghĩa là kéo dài) đến kích thước lớn hơn hoặc nhỏ hơn. Bằng cách
này chúng ta có thể
hiển thị các họa tiết ở bất kỳ kích thước nào chúng ta muốn. Điều này sẽ có ích trong trò chơi
được trình bày trong
chương tiếp theo.
Bây giờ chúng ta đã biết cách tạo một cửa sổ GUI, hiển thị các họa tiết và vẽ nguyên thủy,
thu thập đầu vào bàn phím và chuột, phát âm thanh và thực hiện phát hiện va chạm, chúng tôi
đang
bây giờ đã sẵn sàng để tạo một trò chơi đồ họa trong Pygame. Chương tiếp theo mang đến tất cả
những điều này
các yếu tố cùng nhau cho trò chơi tiên tiến nhất của chúng tôi chưa.
368

Trang 383
Các chủ đề được đề cập trong Chương này:
● Các pygame.FULLSCREEN cờ
● Biến liên tục Pygame cho các phím bàn phím
● Các move_ip () Phương pháp Rect đối tượng

● Hàm pygame.mouse.set_pose ()

● Triển khai mã gian lận trong trò chơi của bạn

● Sửa đổi trò chơi Dodger

Ba chương cuối đã đi qua thư viện phần mềm Pygame và đã trình diễn
Làm thế nào để sử dụng nhiều tính năng của nó. (Bạn không cần phải đọc những chương đó
trước khi đọc
chương, mặc dù nó có thể làm cho chương này dễ hiểu hơn.) Trong chương này, chúng tôi sẽ sử
dụng
kiến thức đó để tạo ra một trò chơi đồ họa với âm thanh nhận đầu vào từ bàn phím
và chuột.
Trò chơi Dodger có người chơi điều khiển một người đàn ông nhỏ (mà chúng ta gọi là của người
chơi
nhân vật) người phải né tránh cả đống baddies rơi từ trên màn hình xuống.
Người chơi có thể tiếp tục né tránh các baddies càng lâu, số điểm họ sẽ nhận được càng cao.
Để giải trí, chúng tôi cũng sẽ thêm một số chế độ gian lận vào trò chơi. Nếu người chơi nhấn giữ
phím "x", mọi tốc độ của baddie sẽ bị giảm xuống mức siêu chậm. Nếu người chơi giữ
xuống phím "z", các baddies sẽ đảo ngược hướng của chúng và di chuyển lên màn hình thay vì
hướng xuống dưới
369

Trang 384
Đánh giá các kiểu dữ liệu Pygame cơ bản
Hãy xem lại một số loại dữ liệu cơ bản được sử dụng trong thư viện Pygame:
● pygame.Rect - Các đối tượng Rect đại diện cho vị trí và kích thước của không gian hình chữ

nhật.
Vị trí có thể được xác định bởi thuộc tính topleft của đối tượng Rect (hoặc
thuộc tính topright , bottomleft và bottomright ). Những thuộc tính góc
là một bộ số nguyên cho tọa độ X và Y. Kích thước có thể được xác định bởi
thuộc tính chiều rộng và chiều cao, là số nguyên có bao nhiêu pixel dài hay cao
diện tích hình chữ nhật là. Các đối tượng Rect có phương thức colliderect () để kiểm tra xem
chúng có
đang giao nhau với một đối tượng Rect khác .
● pygame.Surface - Các đối tượng bề mặt là các vùng có pixel màu. Bề mặt

các đối tượng đại diện cho một hình ảnh hình chữ nhật, trong khi các đối tượng Rect chỉ đại diện
cho một
không gian hình chữ nhật và vị trí. Các đối tượng bề mặt có một phương thức blit ()
được sử dụng để vẽ hình ảnh trên một đối tượng Surface lên một đối tượng Surface khác . Các
Đối tượng bề mặt được trả về bởi hàm pygame.display.set_mode () là
đặc biệt bởi vì mọi thứ được vẽ trên đối tượng Surface đó sẽ được hiển thị trên người dùng
màn.
● Hãy nhớ rằng Surface có những thứ được vẽ trên chúng, nhưng chúng ta không thể thấy điều

này
bởi vì nó chỉ tồn tại trong bộ nhớ của máy tính. Chúng ta chỉ có thể thấy một đối tượng Surface
khi nó được "làm mờ" (nghĩa là được vẽ) trên màn hình. Điều này cũng giống như với
bất kỳ phần dữ liệu khác. Nếu bạn nghĩ về nó, bạn không thể thấy chuỗi được lưu trữ
trong một biến cho đến khi biến được in ra màn hình.
● pygame.event.Event - Kiểu dữ liệu Sự kiện trong mô-đun pygame.event

tạo các đối tượng Sự kiện bất cứ khi nào người dùng cung cấp bàn phím, chuột hoặc vật thể khác
loại đầu vào. Hàm pygame.event.get () trả về danh sách Sự kiện
các đối tượng. Bạn có thể kiểm tra loại sự kiện mà đối tượng Sự kiện là bằng cách kiểm tra loại
sự kiện
thuộc tính. QUIT , KEYDOWN và MOUSEBUTTONUP là ví dụ về một số sự kiện
các loại.
● pygame.font.Font - Mô-đun pygame.font có kiểu dữ liệu Phông chữ

đại diện cho kiểu chữ được sử dụng cho văn bản trong Pygame. Bạn có thể tạo một đối
tượng Phông chữ
bằng cách gọi hàm xây dựng pygame.font.SysFont () . Các đối số để
pass là một chuỗi của tên phông chữ và một số nguyên của kích thước phông chữ, tuy nhiên nó

chung để vượt qua Không cho tên phông chữ để có được phông chữ hệ thống mặc định. Ví dụ,
lệnh gọi hàm phổ biến để tạo đối tượng Font là pygame.font.SysFont
(Không có, 48) .
● pygame.time.Clock - Đối tượng Đồng hồ trong mô-đun pygame.time là

rất hữu ích để giữ cho trò chơi của chúng tôi chạy nhanh nhất có thể. (Điều này thường
quá nhanh để người chơi theo kịp máy tính và khiến các trò chơi không vui.)
Các Clock đối tượng có một đánh dấu () phương pháp, mà chúng tôi vượt qua bao nhiêu khung
hình trên
thứ hai (khung hình / giây) chúng tôi muốn trò chơi chạy. Tốc độ khung hình / giây càng cao, trò
chơi chạy càng nhanh.
Thông thường chúng ta sử dụng 40 khung hình / giây. Lưu ý rằng mô-đun pygame.time là khác
nhau
mô đun hơn mô đun thời gian có chứa hàm ngủ () .
Nhập mã sau đây và lưu nó vào một tệp có tên dodger.py . Trò chơi này cũng
370

Trang 385
yêu cầu một số tệp hình ảnh và âm thanh khác mà bạn có thể tải xuống từ URL
http://inventwithpython.com/resource.
Mã nguồn của Dodger
Bạn có thể tải xuống mã này từ URL http://inventwithpython.com/ch CHƯƠNG20.
dodger.py
Mã này có thể được tải xuống từ http://inventwithpython.com/dodger.py
Nếu bạn gặp lỗi sau khi nhập mã này, hãy so sánh nó với mã của sách với trực tuyến
công cụ tìm khác biệt tại http://inventwithpython.com/diff hoặc gửi email cho tác giả tại
al@inventwithpython.com
1. nhập pygame, ngẫu nhiên, sys
2. từ nhập pygame.locals *
3.
4. WINDOWWIDTH = 600
5. CỬA SỔ = 600
6. TEXTCOLOR = (255, 255, 255)
7. BACKGROUNDCOLOR = (0, 0, 0)
8. FPS = 40
9. BADDIEMINIZE = 10
10. BADDIEMAXSIZE = 40
11. BADDIEMINSPEED = 1
12. BADDIEMAXSPEED = 8
13. ĐỊA CHỈ THÊM = 6
14. CHƠI TRÒ CHƠI = 5
15.
16. def terminating ():
17. pygame.quito ()
18. sys.exit ()
19.
20. def WaitForPlayerToPressKey ():
21. trong khi Đúng:
22.
cho sự kiện trong pygame.event.get ():
23.
if event.type == QUIT:
24.
chấm dứt ()
25.
if event.type == KEYDOWN:
26.
if event.key == K_ESCAPE: # nhấn
thoát khỏi
27.
chấm dứt ()
28.
trở về
29.
30. def playerHasHitBaddie (playerRect, baddies):
31. cho b trong baddies:
32.
if playerRect.colliderect (b ['orth']):
33.
trả lại đúng
34. trả về sai
35.
36. def drawText (văn bản, phông chữ, bề mặt, x, y):
37. textobj = font.render (văn bản, 1, TEXTCOLOR)
38. textrect = textobj.get_rect ()
371
20 - Dodger

Trang 386
39. textrect.topleft = (x, y)
40. Surface.blit (textobj, textrect)
41.
42. # thiết lập pygame, cửa sổ và con trỏ chuột
43. pygame.init ()
44. mainClock = pygame.time.Clock ()
45. windowSurface = pygame.display.set_mode ((WINDOWWIDTH,
Cửa sổ)
46. pygame.display.set_caption ('Dodger')
47. pygame.mouse.set_visible (Sai)
48.
49. # thiết lập phông chữ
50. font = pygame.font.SysFont (Không có, 48)
51.
52. # thiết lập âm thanh
53. gameOverSound = pygame.mixer.Sound ('gameover.wav')
54. pygame.mixer.music.load ('nền.mid')
55.
56. # thiết lập hình ảnh
57. playerImage = pygame.image.load ('player.png')
58. playerRect = playerImage.get_rect ()
59. baddieImage = pygame.image.load ('baddie.png')
60.
61. # hiển thị màn hình "Bắt đầu"
62. drawText ('Dodger', phông chữ, windowSurface, (WINDOWWIDTH / 3),
(CỬA SỔ / 3))
63. drawText ('Bấm phím để bắt đầu.', Phông chữ, windowSurface,
(WINDOWWIDTH / 3) - 30, (WINDOWheIGHT / 3) + 50)
64. pygame.display.update ()
65. WaitForPlayerToPressKey ()
66.
67.
68. topScore = 0
69. trong khi Đúng:
70. # thiết lập bắt đầu trò chơi
71. baddies = []
72. điểm = 0
73. playerRect.topleft = (WINDOWWIDTH / 2, WINDOWHEIGHT -
50)
74. moveLeft = moveRight = moveUp = moveDown = false
75. ReverseCheat = SlowCheat = Sai
76. baddieAddCount = 0
77. pygame.mixer.music.play (-1, 0.0)
78.
79. while True: # vòng lặp trò chơi chạy trong khi phần trò chơi
đang chơi
80.
điểm + = 1 # tăng điểm
81.
82.
cho sự kiện trong pygame.event.get ():
83.
if event.type == QUIT:
84.
chấm dứt ()
85.
86.
if event.type == KEYDOWN:
87.
if event.key == ord ('z'):
372

Trang 387
88.
ReverseCheat = Đúng
89.
if event.key == ord ('x'):
90.
SlowCheat = Đúng
91.
if event.key == K_LEFT hoặc event.key == ord
('a'):
92.
moveRight = Sai
93.
moveLeft = Đúng
94.
if event.key == K_RIGHT hoặc event.key ==
mệnh lệnh ('d'):
95.
moveLeft = Sai
96.
moveRight = Đúng
97.
if event.key == K_UP hoặc event.key == ord
('w'):
98.
moveDown = Sai
99.
moveUp = Đúng
100.
if event.key == K_DOWN hoặc event.key == ord
('S'):
101.
moveUp = Sai
102.
moveDown = Đúng
103.
104.
if event.type == KEYUP:
105.
if event.key == ord ('z'):
106.
ReverseCheat = Sai
107.
điểm = 0
108.
if event.key == ord ('x'):
109.
SlowCheat = Sai
110.
điểm = 0
111.
if event.key == K_ESCAPE:
112.
chấm dứt ()
113.
114.
if event.key == K_LEFT hoặc event.key == ord
('a'):
115.
moveLeft = Sai
116.
if event.key == K_RIGHT hoặc event.key ==
mệnh lệnh ('d'):
117.
moveRight = Sai
118.
if event.key == K_UP hoặc event.key == ord
('w'):
119.
moveUp = Sai
120.
if event.key == K_DOWN hoặc event.key == ord
('S'):
121.
moveDown = Sai
122.
123.
if event.type == MOUSEMOTION:
124.
# Nếu chuột di chuyển, di chuyển người chơi
con trỏ ở đâu
125.
playerRect.move_ip (event.pose [0] -
playerRect.centerx, event.pose [1] - playerRect.centery)
126.
127.
# Thêm baddies mới ở đầu màn hình, nếu
cần thiết
128.
nếu không đảo ngượcCheat và không SlowCheat:
129.
baddieAddCount + = 1
130.
if baddieAddCorer == ADDNEWBADDIERATE:
373
20 - Dodger

Trang 388
131.
baddieAddCount = 0
132.
baddieSize = Random.randint (BADDIEMINSIZE,
BADDIEMAXSIZE)
133.
newBaddie = {'orth': pygame.Rect
(ngẫu nhiên.randint (0, WINDOWWIDTH-baddieSize), 0 -
baddieSize, baddieSize, baddieSize),
134.
'tốc độ': ngẫu nhiên.randint
(BADDIEMINSPEED, BADDIEMAXSPEED),
135.
'bề mặt': pygame.transform.scale
(baddieImage, (baddieSize, baddieSize)),
136.
}
137.
138.
baddies.append (newBaddie)
139.
140.
# Di chuyển người chơi xung quanh.
141.
nếu moveLeft và playerRect.left> 0:
142.
playerRect.move_ip (-1 * PLAYERMOVERATE, 0)
143.
if moveRight và playerRect.right <WINDOWWIDTH:
144.
playerRect.move_ip (PLAYERMOVERATE, 0)
145.
nếu moveUp và playerRect.top> 0:
146.
playerRect.move_ip (0, -1 * PLAYERMOVERATE)
147.
if moveDown và playerRect.bottom <WINDOWHEIGHT:
148.
playerRect.move_ip (0, PLAYERMOVERATE)
149.
150.
# Di chuyển con trỏ chuột để khớp với trình phát.
151.
pygame.mouse.set_pose (playerRect.centerx,
playerRect.centery)
152.
153.
# Di chuyển các baddies xuống.
154.
cho b trong baddies:
155.
nếu không đảo ngượcCheat và không SlowCheat:
156.
b ['orth']. move_ip (0, b ['speed'])
157.
elif đảo ngược:
158.
b ['orth']. move_ip (0, -5)
159.
elif chậm cheat:
160.
b ['orth']. move_ip (0, 1)
161.
162.
# Xóa các baddies đã vượt qua
đáy.
163.
cho b trong baddies [:]:
164.
if b ['orth']. top> WINDOWHEIGHT:
165.
baddies.remove (b)
166.
167.
# Vẽ thế giới trò chơi trên cửa sổ.
168.
windowSurface.fill (BACKGROUNDCOLOR)
169.
170.
# Vẽ số điểm và điểm cao nhất.
171.
drawText ('Điểm:% s'% (điểm), phông chữ,
cửa sổ Mặt, 10, 0)
172.
drawText ('Điểm cao nhất:% s'% (topScore), phông chữ,
cửa sổ Mặt, 10, 40)
173.
174.
# Vẽ hình chữ nhật của người chơi
175.
windowSurface.blit (playerImage, playerRect)
374

Trang 389
176.
177.
# Vẽ mỗi baddie
178.
cho b trong baddies:
179.
windowSurface.blit (b ['bề mặt'], b ['orth'])
180.
181.
pygame.display.update ()
182.
183.
# Kiểm tra xem có bất kỳ baddies nào đánh người chơi không.
184.
if playerHasHitBaddie (playerRect, baddies):
185.
nếu điểm> topScore:
186.
topScore = điểm # thiết lập điểm số cao nhất mới
187.
phá vỡ
188.
189.
mainClock.tick (FPS)
190.
191. # Dừng trò chơi và hiển thị màn hình "Trò chơi kết thúc".
192. pygame.mixer.music.stop ()
193. gameOverSound.play ()
194.
195. drawText ('TRÒ CHƠI TRÊN', phông chữ, windowSurface,
(WINDOWWIDTH / 3), (WINDOWheIGHT / 3))
196. drawText ('Bấm phím để phát lại.', Phông chữ,
windowSurface, (WINDOWWIDTH / 3) - 80, (WINDOWHEIGHT / 3)
+ 50)
197. pygame.display.update ()
198. WaitForPlayerToPressKey ()
199.
200. gameOverSound.stop ()
Khi bạn chạy chương trình này, trò chơi sẽ như thế này:
375
20 - Dodger

Trang 390
Hình 20-1: Ảnh chụp màn hình của trò chơi Dodger đang hoạt động.
Nhập khẩu các mô-đun
1. nhập pygame, ngẫu nhiên, sys
2. từ nhập pygame.locals *
Trò chơi Dodger sẽ nhập cùng các mô-đun mà các trò chơi Pygame trước đây của chúng tôi có:
pygame , ngẫu nhiên , sys và pygame.locals . Các pygame.locals mô-đun
chứa một số biến không đổi mà thư viện Pygame sử dụng, chẳng hạn như các loại sự kiện
( QUIT , KEYDOWN , v.v.) và các phím bàn phím ( K_ESCAPE , K_LEFT , v.v.). Bằng cách
sử dụng
từ cú pháp nhập pygame.locals * , chúng ta chỉ cần gõ QUIT thay vì
pygame.locals.QUIT .
Thiết lập các biến không đổi
Có một số biến không đổi trong trò chơi này. Chúng tôi sử dụng các biến không đổi bởi vì
tên biến được mô tả nhiều hơn một số. Ví dụ: từ dòng
windowSurface.fill (BACKGROUNDCOLOR) chúng tôi biết rằng đối số đang được gửi
376

Trang 391
là một màu cho nền. Tuy nhiên, cửa sổ dòngSurface.fill
(BACKGROUNDCOLOR) không rõ ràng đối số được thông qua nghĩa là gì.
Chúng tôi cũng có thể dễ dàng thay đổi một số khía cạnh đơn giản về trò chơi của mình mà
không cần
thay đổi nhiều mã bằng cách thay đổi các giá trị được lưu trữ trong các biến không đổi này. Bởi
thay đổi WINDOWWIDTH trên dòng 4, chúng tôi tự động thay đổi mã ở mọi nơi
WINDOWWIDTH được sử dụng. Nếu chúng ta đã sử dụng giá trị 600 thay thế, thì chúng ta sẽ
phải
thay đổi mỗi lần xuất hiện của 600 trong mã. Điều này sẽ đặc biệt khó hiểu bởi vì
600 cũng sẽ được sử dụng cho chiều cao của cửa sổ, và chúng tôi không muốn
thay đổi những giá trị đó
4. WINDOWWIDTH = 600
5. CỬA SỔ = 600
6. TEXTCOLOR = (255, 255, 255)
7. BACKGROUNDCOLOR = (0, 0, 0)
Ở đây chúng tôi đặt chiều cao và chiều rộng của cửa sổ chính. Vì phần còn lại của mã của chúng
tôi hoạt động
tắt các biến không đổi này, thay đổi giá trị ở đây sẽ thay đổi nó ở mọi nơi trong chúng ta
chương trình.
Thay vì lưu trữ các bộ màu vào một biến có tên TRẮNG hoặc ĐEN , chúng tôi sẽ sử dụng
các biến không đổi cho màu của văn bản và nền. Hãy nhớ rằng ba
các số nguyên trong bộ màu nằm trong khoảng từ 0 đến 255 và đại diện cho màu đỏ, xanh lá cây
và xanh dương.
8. FPS = 40
Để máy tính không chạy trò chơi quá nhanh để người dùng xử lý, chúng tôi sẽ gọi
mainClock.tick () trên mỗi lần lặp của vòng lặp trò chơi để làm chậm nó. Chúng ta cần phải
chuyển một số nguyên cho mainClock.tick () để hàm biết thời gian tạm dừng
chương trình. Số nguyên này sẽ là số khung hình mỗi giây mà chúng tôi muốn trò chơi
chạy. Một
"frame" là bản vẽ đồ họa trên màn hình cho một lần lặp duy nhất trong trò chơi
vòng. Chúng tôi sẽ thiết lập một biến FPS không đổi thành 40 và luôn gọi mainClock.tick
(FPS) . Bạn có thể thay đổi FPS thành giá trị cao hơn để trò chơi chạy nhanh hơn hoặc giá trị
thấp hơn
để làm chậm trò chơi
9. BADDIEMINIZE = 10
10. BADDIEMAXSIZE = 40
11. BADDIEMINSPEED = 1
12. BADDIEMAXSPEED = 8
13. ĐỊA CHỈ THÊM = 6
Ở đây chúng tôi đặt một số biến liên tục hơn sẽ mô tả các baddies rơi. Các
chiều rộng và chiều cao của baddies sẽ nằm giữa BADDIEMINSIZE và
BADDIEMAXSIZE . Tốc độ các baddies rơi xuống màn hình sẽ ở giữa
377
20 - Dodger
Trang 392
Các pixel BADDIEMINSPEED và BADDIEMAXSPEED mỗi lần lặp qua trò chơi
vòng. Và một baddie mới sẽ được thêm vào đầu cửa sổ mỗi
ADDNEWBADDIERATE lặp đi lặp lại qua vòng lặp trò chơi.
14. CHƠI TRÒ CHƠI = 5
Các PLAYERMOVERATE sẽ lưu trữ số lượng điểm ảnh di chuyển nhân vật của người chơi
trong
cửa sổ trên mỗi lần lặp thông qua vòng lặp trò chơi (nếu nhân vật đang di chuyển). Bởi
tăng số lượng này, bạn có thể tăng tốc độ di chuyển của nhân vật. Nếu bạn đặt
CHƠI TRÒ CHƠI về 0 , sau đó nhân vật của người chơi sẽ không thể di chuyển được (
người chơi sẽ di chuyển 0 pixel mỗi lần lặp). Đây sẽ không phải là một trò chơi rất thú vị.
Hàm xác định
Chúng tôi sẽ tạo ra một số chức năng cho trò chơi của chúng tôi. Bằng cách đưa mã vào các hàm,
chúng ta có thể
tránh phải gõ cùng một mã nhiều lần trong chương trình của chúng tôi. Và bởi vì mã là
ở một nơi, nếu chúng tôi tìm thấy một lỗi, mã chỉ cần được sửa ở một nơi.
16. def terminating ():
17. pygame.quito ()
18. sys.exit ()
Có một vài nơi trong trò chơi của chúng tôi mà chúng tôi muốn chấm dứt chương trình. Mặt khác
của chúng tôi
các chương trình, điều này chỉ cần một cuộc gọi đến sys.exit () . Nhưng vì Pygame yêu cầu điều
đó
chúng ta gọi cả pygame.quito () và sys.exit () , chúng ta sẽ đặt chúng vào một hàm
được gọi là terminating () và chỉ cần gọi hàm. Điều này giữ cho chúng ta không lặp lại tương tự
mã nhiều lần. Và hãy nhớ rằng, chúng ta càng gõ, chúng ta càng có nhiều khả năng
một lỗi và tạo ra một lỗi trong chương trình của chúng tôi.
20. def WaitForPlayerToPressKey ():
21. trong khi Đúng:
22.
cho sự kiện trong pygame.event.get ():
Cũng có một vài nơi mà chúng tôi muốn trò chơi tạm dừng và chờ người chơi
để bấm một phím. Chúng tôi sẽ tạo một hàm mới gọi là WaitForPlayerToPressKey ()
để làm điều này. Bên trong hàm này, chúng ta có một vòng lặp vô hạn chỉ bị phá vỡ khi
một KEYDOWN
hoặc sự kiện QUIT được nhận. Khi bắt đầu vòng lặp, chúng ta gọi pygame.event.get () để
trả về một danh sách các đối tượng Sự kiện để kiểm tra.
23.
if event.type == QUIT:
24.
chấm dứt ()
378

Trang 393
Nếu người chơi đã đóng cửa sổ trong khi chương trình đang chờ người chơi nhấn
chính, Pygame sẽ tạo ra một sự kiện QUIT và chúng ta nên chấm dứt chương trình. Chúng tôi sẽ
gọi hàm terminating () của chúng ta ở đây, thay vì gọi pygame.quito () và sys.exit
() chính họ.
25.
if event.type == KEYDOWN:
26.
if event.key == K_ESCAPE: # nhấn
thoát khỏi
27.
chấm dứt ()
28.
trở về
Nếu chúng tôi nhận được một sự kiện KEYDOWN , thì trước tiên chúng tôi nên kiểm tra xem đó
có phải là phím Esc không
ép. Nếu chúng tôi đang chờ người chơi bấm phím và người chơi nhấn phím Esc,
chúng tôi muốn chấm dứt chương trình Nếu đó không phải là trường hợp, thì thực thi sẽ bỏ qua
if-
chặn trên dòng 27 và đi thẳng đến câu lệnh return , thoát khỏi câu lệnh
Hàm WaitForPlayerToPressKey () .
Nếu một sự kiện QUIT hoặc KEYDOWN không được tạo, thì vòng lặp này sẽ tiếp tục lặp cho
đến khi nó xảy ra.
Điều này sẽ đóng băng trò chơi cho đến khi người chơi nhấn phím hoặc đóng cửa sổ.
30. def playerHasHitBaddie (playerRect, baddies):
31. cho b trong baddies:
32.
if playerRect.colliderect (b ['orth']):
33.
trả lại đúng
34. trả về sai
Chúng tôi cũng sẽ định nghĩa một hàm có tên playerHasHitBaddie () sẽ trả về
Đúng nếu nhân vật của người chơi đã va chạm với một trong những điều xấu. Những điều xấu
tham số là một danh sách các cấu trúc dữ liệu baddie. Các cấu trúc dữ liệu này chỉ là từ điển, vì
vậy nó
là chính xác để nói rằng baddies là một danh sách các đối tượng từ điển. Mỗi từ điển này
có khóa 'orth' và giá trị của khóa đó là đối tượng Rect đại diện cho baddie
kích thước và vị trí.
playerRect cũng là một đối tượng Rect . Hãy nhớ rằng các đối tượng Rect có một phương thức
tên colliderect () trả về True nếu đối tượng Rect đã va chạm với Rect
đối tượng được truyền cho phương thức. Nếu không, colliderect () sẽ trả về Sai .
Chúng ta có thể sử dụng phương thức này trong hàm playerHasHitBaddie () . Đầu tiên chúng tôi
lặp lại
thông qua mỗi cấu trúc dữ liệu baddie trong danh sách baddies . Nếu bất kỳ một trong những
baddies va chạm với
nhân vật của người chơi, sau đó playerHasHitBaddie () sẽ trả về True . Nếu mã
quản lý để lặp qua tất cả các baddies trong danh sách baddies mà không va chạm với bất kỳ
trong số họ, chúng tôi sẽ trả lại sai .
36. def drawText (văn bản, phông chữ, bề mặt, x, y):
379
20 - Dodger

Trang 394
37. textobj = font.render (văn bản, 1, TEXTCOLOR)
38. textrect = textobj.get_rect ()
39. textrect.topleft = (x, y)
40. Surface.blit (textobj, textrect)
Vẽ văn bản trên cửa sổ bao gồm nhiều bước khác nhau. Đầu tiên, chúng ta phải tạo ra một
Đối tượng bề mặt có chuỗi được hiển thị trong một phông chữ cụ thể trên đó. Kết xuất ()
phương pháp làm điều này. Tiếp theo, chúng ta cần biết kích thước và vị trí của đối
tượng Surface mà chúng ta
chỉ cần thực hiện. Chúng ta có thể nhận được một đối tượng Rect với thông tin này bằng phương
thức get_rect ()
cho các đối tượng Surface .
Đối tượng Rect này không có kết nối đặc biệt với đối tượng Surface với văn bản được vẽ trên đó
nó, ngoài thực tế là nó có một bản sao thông tin chiều rộng và chiều cao từ
Đối tượng bề mặt . Chúng ta có thể thay đổi vị trí của đối tượng Rect bằng cách đặt một bộ dữ
liệu mới
giá trị cho thuộc tính topleft của nó .
Cuối cùng, chúng tôi làm mờ đối tượng Surface của văn bản được hiển thị lên đối tượng Surface
đã được chuyển đến hàm drawText () của chúng tôi . Hiển thị văn bản trong Pygame mất thêm
một vài bước
thay vì chỉ gọi hàm print () , nhưng nếu chúng ta đặt mã này vào một hàm duy nhất
( drawText () ), sau đó chúng ta chỉ cần gọi hàm thay vì gõ tất cả mã
mỗi khi chúng tôi muốn hiển thị văn bản trên màn hình.
Khởi tạo Pygame và thiết lập cửa sổ
Bây giờ các biến và hàm hằng đã kết thúc, chúng ta có thể bắt đầu gọi
Các hàm Pygame sẽ thiết lập Pygame để sử dụng trong mã của chúng tôi. Nhiều người trong số
các cuộc gọi chức năng này
là để thiết lập cửa sổ GUI và tạo các đối tượng mà chúng ta sẽ sử dụng trong trò chơi.
42. # thiết lập pygame, cửa sổ và con trỏ chuột
43. pygame.init ()
44. mainClock = pygame.time.Clock ()
45. windowSurface = pygame.display.set_mode ((WINDOWWIDTH,
Cửa sổ)
46. pygame.display.set_caption ('Dodger')
47. pygame.mouse.set_visible (Sai)
Dòng 43 thiết lập thư viện Pygame. Hãy nhớ rằng, hàm pygame.init () phải là
được gọi trước khi chúng ta có thể sử dụng bất kỳ chức năng hoặc kiểu dữ liệu nào của
Pygame. Dòng 44 tạo ra một
đối tượng pygame.time.Clock () và lưu trữ nó trong biến mainClock . Đối tượng này
sẽ giúp chúng tôi giữ cho chương trình chạy quá nhanh.
Dòng 45 tạo một đối tượng Surface mới sẽ được sử dụng cho cửa sổ hiển thị trên
màn hình. Chúng tôi sẽ chỉ định chiều rộng và chiều cao của đối tượng Surface này (và cửa sổ)
bằng cách thông qua một tuple với WINDOWWIDTH và WINDOWHEIGHT biến đổi. Để ý
rằng chỉ có một đối số được truyền cho pygame.display.set_mode () : một tuple. Các
380

Trang 395
đối số cho pygame.display.set_mode () không phải là hai số nguyên mà là một bộ
hai số nguyên.
Trên dòng 46, chú thích của cửa sổ được đặt thành chuỗi 'Dodger' . Chú thích này sẽ
xuất hiện trong thanh tiêu đề ở đầu cửa sổ.
Trong trò chơi của chúng tôi, chúng tôi không muốn con trỏ chuột (con trỏ chuột là mũi tên di
chuyển
xung quanh màn hình khi chúng ta di chuyển chuột) để hiển thị. Điều này là bởi vì chúng tôi
muốn
chuột để có thể di chuyển nhân vật của người chơi xung quanh màn hình và con trỏ mũi tên
sẽ cản trở hình ảnh của nhân vật trên màn hình. Chúng tôi vượt qua Sai để nói với Pygame
để làm cho con trỏ vô hình. Nếu chúng ta muốn làm cho con trỏ hiển thị lại tại một số điểm trong
chương trình, chúng ta có thể gọi pygame.mouse.set_visible (Đúng) .
Chế độ toàn màn hình
Hàm pygame.display.set_mode () có tham số tùy chọn thứ hai
bạn có thể vượt qua nó Giá trị bạn có thể vượt qua cho tham số này là pygame.FULLSCREEN ,
thích sửa đổi này cho dòng 45 trong chương trình Dodger của chúng tôi:
45. windowSurface = pygame.display.set_mode ((WINDOWWIDTH,
CỬA SỔ), pygame.FULLSCREEN)
Vượt qua pygame.FULLSCREEN sẽ khiến chương trình chiếm toàn bộ không gian của
màn. Nó vẫn sẽ có kích thước WINDOWWIDTH và WINDOWHEIGHT cho chiều rộng cửa sổ
và chiều cao, nhưng hình ảnh sẽ được kéo dài lớn hơn để vừa với màn hình. Có thể lãng phí
không gian dọc theo cạnh trên và dưới (hoặc bên trái và bên phải) của màn hình nếu bạn không
đặt
kích thước cửa sổ tỷ lệ thuận với độ phân giải của màn hình.) Để tránh không gian bị lãng phí,
bạn
nên đặt kích thước của cửa sổ thành tỷ lệ 4: 3 (cứ 4 pixel chiều rộng, có 3 pixel
cho chiều cao).
Nếu bạn không sử dụng chế độ toàn màn hình, thì bạn không cần phải lo lắng về việc sử dụng tỷ
lệ 4: 3
tỷ lệ cho chiều rộng và chiều cao. Chỉ cần sử dụng bất cứ chiều rộng và chiều cao nào hoạt động
tốt nhất cho bạn
trò chơi.
49. # thiết lập phông chữ
50. font = pygame.font.SysFont (Không có, 48)
Chúng ta cần tạo một đối tượng Phông chữ để sử dụng khi chúng ta tạo một đối
tượng Surface bằng
hình ảnh của văn bản vẽ trên đó. (Quá trình này được gọi là "kết xuất".) Chúng tôi muốn tạo một
cái chung
phông chữ, vì vậy chúng tôi sẽ sử dụng đối tượng Phông chữ mặc định mà pygame.font.SysFont
()
Hàm xây dựng trả về. Chúng tôi vượt qua Không để phông chữ mặc định được sử dụng và chúng
tôi vượt qua 48
sao cho phông chữ có kích thước 48 điểm.
52. # thiết lập âm thanh
381
20 - Dodger

Trang 396
53. gameOverSound = pygame.mixer.Sound ('gameover.wav')
54. pygame.mixer.music.load ('nền.mid')
Tiếp theo chúng tôi muốn tạo các đối tượng Âm thanh và cũng thiết lập nhạc nền. Các
nhạc nền sẽ liên tục được phát trong trò chơi, nhưng các đối tượng Âm thanh sẽ chỉ
được chơi khi chúng tôi đặc biệt muốn họ. Trong trường hợp này, đối tượng Âm thanh sẽ được
phát
khi người chơi thua trò chơi
Bạn có thể sử dụng bất kỳ tệp .wav hoặc .mid nào cho trò chơi này . Bạn có thể tải xuống các tệp
âm thanh này từ
trang web của cuốn sách này tại URL http://inventwithpython.com/resource. Hoặc bạn có thể sử
dụng
sở hữu các tệp âm thanh cho trò chơi này, miễn là chúng có tên tệp của gameover.wav và
nền.mid . (Hoặc bạn có thể thay đổi các chuỗi được sử dụng trên các dòng 53 và 54 để phù hợp
với
tên tập tin.)
Hàm xây dựng pygame.mixer.Sound () tạo ra một đối tượng Âm thanh mới và
lưu trữ một tham chiếu đến đối tượng này trong biến gameOverSound . Trong trò chơi của riêng
bạn, bạn
có thể tạo bao nhiêu đối tượng Âm thanh tùy thích, mỗi đối tượng có một tệp âm thanh khác
nhau mà nó sẽ
chơi.
Hàm pygame.mixer.music.load () tải một tệp âm thanh để phát cho
nhạc nền. Hàm này không tạo bất kỳ đối tượng nào và chỉ có một tệp âm thanh có thể
tải tại một thời điểm.
56. # thiết lập hình ảnh
57. playerImage = pygame.image.load ('player.png')
58. playerRect = playerImage.get_rect ()
59. baddieImage = pygame.image.load ('baddie.png')
Tiếp theo, chúng tôi sẽ tải các tệp hình ảnh được sử dụng cho nhân vật của người chơi và các
baddies trên
màn hình. Hình ảnh cho nhân vật được lưu trữ trong player.png và hình ảnh cho các baddies
được lưu trữ trong baddie.png . Tất cả các baddies trông giống nhau, vì vậy chúng tôi chỉ cần
một tệp hình ảnh cho
họ Bạn có thể tải xuống những hình ảnh này từ trang web của cuốn sách tại URL
http://inventwithpython.com/resource.
Hiển thị màn hình bắt đầu
Khi trò chơi bắt đầu, chúng tôi muốn hiển thị tên của trò chơi trên màn hình. Chúng tôi
cũng muốn hướng dẫn người chơi rằng họ có thể bắt đầu trò chơi bằng cách ấn bất kỳ phím
nào. Màn hình này
xuất hiện để người chơi có thời gian sẵn sàng bắt đầu chơi sau khi chạy chương trình.
Ngoài ra, trước khi mỗi trò chơi bắt đầu, chúng tôi muốn đặt lại giá trị của điểm số cao nhất trở
về 0 .
61. # hiển thị màn hình "Bắt đầu"
62. drawText ('Dodger', phông chữ, windowSurface, (WINDOWWIDTH / 3),
(CỬA SỔ / 3))
63. drawText ('Bấm phím để bắt đầu.', Phông chữ, windowSurface,
382

Trang 397
(WINDOWWIDTH / 3) - 30, (WINDOWheIGHT / 3) + 50)
64. pygame.display.update ()
65. WaitForPlayerToPressKey ()
Trên các dòng 62 và 63, chúng ta gọi hàm drawText () và truyền cho nó năm đối số: 1)
chuỗi văn bản chúng tôi muốn xuất hiện, 2) phông chữ mà chúng tôi muốn chuỗi xuất hiện, 3)
Đối tượng bề mặt để hiển thị văn bản và 4) và 5) tọa độ X và Y trên
Đối tượng bề mặt để vẽ văn bản tại.
Điều này có vẻ giống như nhiều đối số để vượt qua cho một cuộc gọi chức năng, nhưng hãy nhớ
rằng điều này
chức năng gọi thay thế năm dòng mã mỗi lần chúng ta gọi nó. Điều này rút ngắn chương trình
của chúng tôi và
làm cho nó dễ dàng hơn để tìm lỗi vì có ít mã để kiểm tra.
Hàm WaitForPlayerToPressKey () sẽ tạm dừng trò chơi bằng cách nhập vào
vòng lặp kiểm tra bất kỳ sự kiện KEYDOWN . Khi một sự kiện KEYDOWN được tạo,
thực hiện thoát ra khỏi vòng lặp và chương trình tiếp tục chạy.
Bắt đầu mã trò chơi chính
68. topScore = 0
69. trong khi Đúng:
Chúng tôi đã hoàn thành việc xác định các hàm trợ giúp và các biến mà chúng tôi cần cho trò
chơi này.
Dòng 68 là sự khởi đầu của mã trò chơi chính. Giá trị trong biến topScore bắt đầu từ 0
chỉ khi chương trình đầu tiên chạy. Bất cứ khi nào người chơi thua và có số điểm lớn hơn
điểm số cao nhất hiện tại, điểm số cao nhất được thay thế bằng điểm số của người chơi.
Vòng lặp vô hạn bắt đầu trên dòng 69 về mặt kỹ thuật không phải là "vòng lặp trò chơi". (Trò
chơi chính
loop xử lý các sự kiện và vẽ cửa sổ trong khi trò chơi đang chạy.) Thay vào đó, điều này
trong khi vòng lặp sẽ lặp mỗi lần các cầu thủ bắt đầu một trò chơi mới. Chúng tôi sẽ thiết lập mã
như vậy
rằng khi người chơi thua và chúng tôi cần thiết lập lại trò chơi, việc thực hiện chương trình sẽ
diễn ra
trở lại bắt đầu của vòng lặp này.
70. # thiết lập bắt đầu trò chơi
71. baddies = []
72. điểm = 0
Lúc đầu, chúng tôi muốn đặt danh sách baddies thành một danh sách trống. Những điều xấu
list là danh sách các đối tượng từ điển với các khóa sau:
● 'orth' - Đối tượng Rect mô tả vị trí và kích thước của baddie.

● 'tốc độ' - Baddie rơi xuống màn hình nhanh như thế nào. Số nguyên này đại diện cho pixel

mỗi lần lặp qua vòng lặp trò chơi.


● 'Surface' - Đối tượng Surface có hình ảnh thu nhỏ của hình ảnh baddie

383
20 - Dodger

Trang 398
vẽ trên đó. Đây là đối tượng Surface sẽ được chuyển sang đối tượng Surface
được trả về bởi pygame.display.set_mode () và được vẽ trên màn hình.
Tiếp theo, chúng tôi muốn đặt lại điểm của người chơi về 0 .
73. playerRect.topleft = (WINDOWWIDTH / 2, WINDOWHEIGHT -
50)
Vị trí bắt đầu của trình phát sẽ ở giữa màn hình và tăng 50 pixel
từ dưới lên. Bộ dữ liệu mà chúng ta đặt thuộc tính topleft sẽ thay đổi vị trí của
các playerRect đối tượng. Mục đầu tiên trong bộ dữ liệu là tọa độ X của cạnh trái.
Mục thứ hai trong bộ dữ liệu là tọa độ Y của cạnh trên.
74. moveLeft = moveRight = moveUp = moveDown = false
75. ReverseCheat = SlowCheat = Sai
76. baddieAddCount = 0
Cũng vào lúc bắt đầu trò chơi, chúng tôi muốn có các biến chuyển động moveLeft ,
moveRight , moveUp và moveDown được đặt thành Sai . Các reverseCheat và
Các biến SlowCheat sẽ được đặt thành True chỉ khi người chơi kích hoạt các mánh gian lận này
bằng cách
giữ phím "z" và "x" tương ứng.
Biến baddieAddCorer được sử dụng cho một bộ đếm để báo cho chương trình biết khi nào
thêm một baddie mới ở đầu màn hình. Giá trị trong baddieAddCorer sẽ là
tăng thêm một lần mỗi khi vòng lặp trò chơi lặp lại. Khi baddieAddCount
bộ đếm bằng với giá trị trong ADDNEWBADDIERATE , sau đó là baddieAddCorer
bộ đếm được đặt lại về 0 và một baddie mới được thêm vào phía trên màn hình.
77. pygame.mixer.music.play (-1, 0.0)
Khi bắt đầu trò chơi, chúng tôi muốn nhạc nền bắt đầu chơi. Chung ta co thể lam được viê ̣c nay
với một cuộc gọi đến pygame.mixer.music.play () . Đối số đầu tiên là số
lần âm nhạc nên lặp lại chính nó. -1 là một giá trị đặc biệt cho Pygame biết, chúng tôi muốn
âm nhạc để lặp lại vô tận. Đối số thứ hai là một số float cho biết có bao nhiêu giây vào
âm nhạc chúng tôi muốn nó bắt đầu chơi. Vượt qua 0,0 có nghĩa là chúng tôi muốn chơi nhạc
bắt đầu từ đầu tập tin âm nhạc (Vượt qua 2.0 , ví dụ, sẽ có
bắt đầu âm nhạc hai giây vào tệp nhạc.)
Vòng lặp trò chơi
Vòng lặp trò chơi chứa mã được thực thi trong khi trò chơi đang được chơi. Các
vòng lặp trò chơi liên tục cập nhật trạng thái của thế giới trò chơi bằng cách thay đổi vị trí của
người chơi và baddies, xử lý các sự kiện do Pygame tạo ra và vẽ trạng thái của
384

Trang 399
thế giới trò chơi trên màn hình. Tất cả điều này xảy ra vài chục lần một giây, trong đó
làm cho có vẻ như trò chơi đang diễn ra trong thời gian thực cho người chơi.
79. while True: # vòng lặp trò chơi chạy trong khi phần trò chơi
đang chơi
80.
điểm + = 1 # tăng điểm
Dòng 79 là bắt đầu của vòng lặp trò chơi chính. Trong vòng lặp trò chơi chính, chúng tôi sẽ tăng
điểm của người chơi, xử lý mọi sự kiện đã được tạo, thêm bất kỳ thành tích xấu nào vào đầu
màn hình nếu cần, di chuyển các baddies xuống một chút, và sau đó vẽ mọi thứ trên màn hình.
Mã này sẽ được thực thi lặp đi lặp lại khi thực hiện chương trình lặp lại thông qua
vòng lặp trò chơi. Vòng lặp sẽ chỉ thoát khi người chơi thua trò chơi hoặc thoát khỏi
chương trình.
Đầu tiên, chúng tôi sẽ tăng điểm của người chơi. Người chơi có thể đi càng lâu mà không thua,
điểm số của họ sẽ càng cao.
Xử lý sự kiện
Có bốn loại sự kiện khác nhau mà chúng tôi sẽ xử lý trong trò chơi của
mình: QUIT , KEYDOWN ,
KEYUP , và MOUSEMOTION . Sự kiện QUIT được tạo bởi Pygame nếu người chơi đóng
cửa sổ của chương trình hoặc tắt máy tính. Trong trường hợp đó, chúng tôi muốn chương trình
đóng chính nó Các sự kiện KEYDOWN và KEYUP được tạo khi người chơi ấn xuống
và phát hành các phím bàn phím, tương ứng. Những sự kiện này sẽ là cách chúng ta có thể biết
hướng người chơi muốn di chuyển nhân vật. Người chơi cũng có thể đã nhấn
Phím Esc để báo hiệu rằng họ muốn tắt chương trình. Mỗi khi người chơi di chuyển
chuột, Pygame sẽ tạo ra một sự kiện MOUSEMOTION sẽ cho chúng ta biết X và Y
tọa độ của con trỏ chuột trên cửa sổ.
82.
cho sự kiện trong pygame.event.get ():
83.
if event.type == QUIT:
84.
chấm dứt ()
Dòng 82 là bắt đầu của mã xử lý sự kiện. Đầu tiên chúng ta gọi pygame.event.get () ,
trong đó trả về một danh sách các đối tượng sự kiện . Mỗi đối tượng Sự kiện đại diện cho một sự
kiện có
đã được tạo từ cuộc gọi cuối cùng tới pygame.event.get () . Chúng tôi sẽ kiểm tra loại
thuộc tính của đối tượng sự kiện để xem nó là loại sự kiện nào và xử lý sự kiện
phù hợp.
Nếu thuộc tính loại của đối tượng Sự kiện bằng QUIT , thì điều này cho chúng ta biết rằng
người dùng đã đóng chương trình bằng cách nào đó. Các QUIT biến liên tục được nhập khẩu từ
các
mô-đun pygame.locals , nhưng vì chúng tôi đã nhập mô-đun đó với dòng từ
nhập pygame.locals * thay vì chỉ nhập pygame.locals , chúng tôi chỉ
cần gõ QUIT và không pygame.locals.QUIT .
385
20 - Dodger

Trang 400
86.
if event.type == KEYDOWN:
87.
if event.key == ord ('z'):
88.
ReverseCheat = Đúng
89.
if event.key == ord ('x'):
90.
SlowCheat = Đúng
Nếu loại sự kiện là KEYDOWN , thì chúng tôi biết rằng người chơi đã nhấn phím.
Các tổ chức sự kiện đối tượng cho các sự kiện bàn phím cũng sẽ có một chìa khóa thuộc tính
được thiết lập để
giá trị ASCII số của phím được nhấn. Hàm ord () sẽ trả về giá trị ASCII
của bức thư được chuyển đến nó.
Ví dụ: trên dòng 87, chúng tôi có thể kiểm tra xem sự kiện có mô tả phím "z" được nhấn không
xuống bằng cách kiểm tra xem event.key == ord ('z') . Nếu điều kiện này là True , thì chúng ta
muốn đặt biến ReverseCheat thành True để chỉ ra rằng cheat ngược có
đã được kích hoạt. Chúng tôi cũng sẽ kiểm tra xem phím "x" đã được nhấn để kích hoạt cheat
chậm chưa
theo cách tương tự
Các sự kiện bàn phím của Pygame luôn sử dụng các giá trị ASCII của các chữ cái viết thường,
không phải
chữ hoa Điều này có nghĩa gì với mã của bạn là bạn phải luôn sử dụng event.key ==
ord ('z') thay vì event.key == ord ('Z') . Nếu không, chương trình của bạn có thể hành động
như thể chìa khóa chưa được nhấn.
91.
if event.key == K_LEFT hoặc event.key ==
mệnh lệnh ('a'):
92.
moveRight = Sai
93.
moveLeft = Đúng
94.
if event.key == K_RIGHT hoặc event.key ==
mệnh lệnh ('d'):
95.
moveLeft = Sai
96.
moveRight = Đúng
97.
if event.key == K_UP hoặc event.key == ord
('w'):
98.
moveDown = Sai
99.
moveUp = Đúng
100.
if event.key == K_DOWN hoặc event.key ==
ord ('s'):
101.
moveUp = Sai
102.
moveDown = Đúng
Chúng tôi cũng muốn kiểm tra xem sự kiện có được tạo bởi người chơi nhấn một trong các mũi
tên không
chìa khóa. Không có giá trị ASCII cho mọi phím trên bàn phím, chẳng hạn như các phím mũi tên
hoặc
phím Esc. Thay vào đó, Pygame cung cấp một số biến không đổi để sử dụng thay thế.
Chúng tôi có thể kiểm tra xem người chơi đã nhấn phím mũi tên trái với điều kiện:
event.key == K_LEFT . Một lần nữa, lý do chúng ta có thể sử dụng K_LEFT thay vì
pygame.locals.K_LEFT là vì chúng tôi đã nhập pygame.locals với dòng
386

Trang 401
từ nhập pygame.locals * thay vì nhập pygame.locals .
Lưu ý rằng nhấn xuống một trong các phím mũi tên không chỉ thiết lập một trong các chuyển
động
biến thành True , nhưng nó cũng đặt biến chuyển động theo hướng ngược lại thành
Sai . Ví dụ: nếu phím mũi tên trái được ấn xuống, thì mã trên dòng 93 được đặt
moveLeft thành True , nhưng nó cũng đặt moveRight thành false . Điều này ngăn người chơi
từ việc nhầm lẫn chương trình thành nghĩ rằng nhân vật của người chơi nên di chuyển thành hai
ngược chiều cùng một lúc.
Dưới đây là danh sách các biến hằng được sử dụng phổ biến cho thuộc tính khóa của bàn phím-
đối tượng sự kiện liên quan:
104.
if event.type == KEYUP:
105.
if event.key == ord ('z'):
106.
ReverseCheat = Sai
107.
điểm = 0
108.
if event.key == ord ('x'):
109.
SlowCheat = Sai
Bảng 20-1: Biến liên tục cho các phím bàn phím
Pygame liên tục
Biến đổi
Bàn phím
Chìa khóa
Pygame liên tục
Biến đổi
Bàn phím
Chìa khóa
K_LEFT
Mũi tên trái
K_HOME
Trang Chủ
K_RIGHT
Mũi tên bên phải
K_END
Kết thúc
K_UP
Mũi tên lên
K_PAGEUP
PGUp
K_DOWN
Mũi tên xuống
K_PAGEDOWN
PGS
K_ESCAPE
Trốn
K_F1
F1
K_BACKSPACE
Backspace
K_F2
F2
K_TAB
Chuyển hướng
K_F3
F3
KIẾM
Trả lại hoặc
Đi vào
K_F4
F4
KinksACE
Thanh không gian
K_F5
F5
K_DELLEX
Del
K_F6
F6
K_LSHIFT
Dịch trái
K_F7
F7
K_RSHift
Phải dịch chuyển
K_F8
F8
K_LCTRL
Ctrl trái
K_F9
F9
K_RCTRL
Ctrl phải
K_F10
F10
K_LALT
Phím Alt trái
K_F11
F11
K_RALT
Phải Alt
K1212
F12
387
20 - Dodger

Trang 404
110.
điểm = 0
Sự kiện KEYUP được tạo bất cứ khi nào người chơi dừng nhấn phím trên bàn phím
và nó trở lại vị trí bình thường, lên. Các đối tượng KEYUP với một loại KEYUP cũng có một
thuộc tính khóa giống như các sự kiện KEYDOWN .
Trên dòng 105, chúng tôi kiểm tra xem trình phát đã phát hành phím "z" hay chưa, phím này sẽ
hủy kích hoạt
gian lận ngược. Trong trường hợp đó, chúng tôi đặt ReverseCheat thành Sai và đặt lại điểm
thành 0 . Các
thiết lập lại điểm số là để khuyến khích người chơi sử dụng các mánh gian lận.
Các dòng 108 đến 110 thực hiện tương tự cho phím "x" và cheat chậm. Khi phím "x"
được phát hành, SlowCheat được đặt thành Sai và điểm của người chơi được đặt lại về 0 .
111.
if event.key == K_ESCAPE:
112.
chấm dứt ()
Bất cứ lúc nào trong trò chơi, người chơi có thể nhấn phím Esc trên bàn phím để thoát khỏi
trò chơi. Ở đây chúng tôi kiểm tra xem khóa được phát hành có phải là khóa Esc hay không bằng
cách kiểm tra event.key
== K_ESCAPE . Nếu vậy, chúng ta gọi hàm terminating () sẽ thoát khỏi chương trình.
114.
if event.key == K_LEFT hoặc event.key == ord
('a'):
115.
moveLeft = Sai
116.
if event.key == K_RIGHT hoặc event.key ==
mệnh lệnh ('d'):
117.
moveRight = Sai
118.
if event.key == K_UP hoặc event.key == ord
('w'):
119.
moveUp = Sai
120.
if event.key == K_DOWN hoặc event.key == ord
('S'):
121.
moveDown = Sai
Các dòng 114 đến 121 kiểm tra xem người chơi đã dừng giữ một trong các phím mũi tên (hay
phím WASD tương ứng). Trong sự kiện đó, chúng tôi sẽ thiết lập chuyển động tương ứng
biến thành Sai . Ví dụ: nếu người chơi đang giữ phím mũi tên trái, thì
moveLeft sẽ được đặt thành True trên dòng 93. Khi họ phát hành nó, điều kiện trên
dòng 114 sẽ đánh giá thành True và biến moveLeft sẽ được đặt thành false .
Các move_ip () Phương pháp Rect đối tượng
123.
if event.type == MOUSEMOTION:
124.
# Nếu chuột di chuyển, di chuyển người chơi
con trỏ ở đâu
397

Trang 403
125.
playerRect.move_ip (event.pose [0] -
playerRect.centerx, event.pose [1] - playerRect.centery)
Bây giờ chúng tôi đã xử lý các sự kiện bàn phím, hãy xử lý mọi sự kiện chuột có thể
đã được tạo ra. Trong trò chơi Dodger, chúng tôi không làm gì nếu người chơi đã nhấp vào
nút chuột, nhưng trò chơi sẽ phản hồi khi người chơi di chuyển chuột. Điều này mang lại cho
Người chơi có hai cách điều khiển nhân vật người chơi trong trò chơi: bàn phím và
chuột.
Nếu loại sự kiện là MOUSEMOTION , thì chúng tôi muốn chuyển nhân vật của người chơi sang
vị trí của con trỏ chuột. Sự kiện MOUSEMOTION được tạo bất cứ khi nào chuột
di chuyển. Tổ chức sự kiện đối tượng với một loại của MOUSEMOTION cũng có một thuộc tính
tên là pos .
Các pos cửa hàng thuộc tính một tuple của các tọa độ X và Y của nơi con trỏ chuột
di chuyển trong cửa sổ.
Phương thức move_ip () cho các đối tượng Rect sẽ di chuyển vị trí của đối tượng Rect
theo chiều ngang hoặc chiều dọc bởi một số pixel. Ví dụ: playerRect.move_ip
(10, 20) sẽ di chuyển đối tượng Rect 10 pixel sang phải và 20 pixel xuống. Đến
di chuyển đối tượng Rect sang trái hoặc lên, truyền các giá trị âm. Ví dụ,
playerRect.move_ip (-5, -15) sẽ di chuyển đối tượng Rect sang trái 5 pixel trở lên
15 pixel.
"Ip" ở cuối move_ip () là viết tắt của "tại chỗ". Điều này là do phương pháp
thay đổi chính đối tượng Rect , ở vị trí của chính nó. Ngoài ra còn có một phương thức move ()
không thay đổi đối tượng Rect , mà thay vào đó tạo ra một đối tượng Rect mới có đối tượng mới
vị trí. Điều này hữu ích nếu bạn muốn giữ vị trí của đối tượng Rect ban đầu giống nhau nhưng
cũng có một đối tượng Rect với vị trí mới.
Thêm Baddies mới
127.
# Thêm baddies mới ở đầu màn hình, nếu
cần thiết
128.
nếu không đảo ngượcCheat và không SlowCheat:
129.
baddieAddCount + = 1
Trên mỗi lần lặp của vòng lặp trò chơi, chúng tôi muốn tăng baddieAddCorer
biến bằng một. Tuy nhiên, chúng tôi chỉ muốn làm điều này nếu các mánh gian lận không được
kích hoạt. Nhớ lại
that ReverseCheat và SlowCheat : chỉ được đặt thành True miễn là "z" và "x"
Các phím đang được giữ tương ứng. Và trong khi những phím đó đang được giữ,
baddieAddCorer không tăng. Điều này có nghĩa là không có baddies mới sẽ xuất hiện tại
phía trên cùng của màn hình.
130.
if baddieAddCorer == ADDNEWBADDIERATE:
131.
baddieAddCount = 0
389
20 - Dodger

Trang 404
132.
baddieSize = Random.randint (BADDIEMINSIZE,
BADDIEMAXSIZE)
133.
newBaddie = {'orth': pygame.Rect
(ngẫu nhiên.randint (0, WINDOWWIDTH-baddieSize), 0 -
baddieSize, baddieSize, baddieSize),
134.
'tốc độ': ngẫu nhiên.randint
(BADDIEMINSPEED, BADDIEMAXSPEED),
135.
'bề mặt': pygame.transform.scale
(baddieImage, (baddieSize, baddieSize)),
136.
}
Khi baddieAddCorer đạt giá trị trong ADDNEWBADDIERATE , thì
điều kiện trên dòng 130 là True và đã đến lúc thêm một baddie mới vào đầu màn hình.
Đầu tiên, bộ đếm baddieAddCorer được đặt lại về 0 (nếu không, khi nó giữ
tăng nó sẽ luôn luôn lớn hơn ADDNEWBADDIERATE và không bao giờ bằng nó.
Điều này sẽ khiến baddies ngừng xuất hiện ở phía trên màn hình.)
Dòng 132 tạo kích thước cho baddie tính bằng pixel. Kích thước sẽ ở giữa
BADDIEMINSIZE và BADDIEMAXSIZE , mà chúng tôi đã đặt thành 10 và 40 trong phần này
chương trình.
Dòng 133 là nơi cấu trúc dữ liệu baddie mới được tạo. Hãy nhớ rằng, cấu trúc dữ liệu cho
baddies đơn giản là một từ điển với các phím 'orth' , 'speed' và 'Surface' . Các
Phím 'orth' chứa tham chiếu đến đối tượng Rect lưu trữ vị trí và kích thước của
tồi tệ. Hàm gọi hàm xây dựng pygame.Rect () có bốn tham số: X-
tọa độ của cạnh trên của khu vực, tọa độ Y của cạnh trái của khu vực, chiều rộng
tính bằng pixel và chiều cao tính bằng pixel.
Chúng tôi muốn baddie xuất hiện ngẫu nhiên trên đầu cửa sổ, vì vậy chúng tôi vượt qua
Random.randint (0, WINDOWWIDTH-baddieSize) cho tọa độ X của bên trái
cạnh. Điều này sẽ đánh giá đến một nơi ngẫu nhiên trên đầu cửa sổ. Lý do chúng tôi vượt qua
WINDOWWIDTH-baddieSize thay vì WINDOWWIDTH là vì giá trị này dành cho
cạnh trái của baddie. Nếu cạnh trái của baddie quá xa ở phía bên phải màn hình,
sau đó một phần của baddie sẽ ở ngoài rìa cửa sổ và không nhìn thấy được.
Chúng tôi muốn cạnh dưới của baddie ở ngay trên cạnh trên của cửa sổ. Các
Tọa độ Y của cạnh trên của cửa sổ là 0, vì vậy để đặt cạnh dưới của baddie ở đó, chúng tôi
muốn đặt cạnh trên thành 0 - baddieSize .
Chiều rộng và chiều cao của baddie phải giống nhau (hình ảnh là một hình vuông), vì vậy chúng
tôi sẽ vượt qua
baddieSize cho đối số thứ ba và thứ tư.
Tốc độ của baddie di chuyển xuống màn hình sẽ được đặt trong phím 'speed' ,
và được đặt thành một số nguyên ngẫu nhiên
giữa BADDIEMINSPEED và BADDIEMAXSPEED .
138.
baddies.append (newBaddie)
390

Trang 405
Dòng 138 sẽ thêm cấu trúc dữ liệu baddie mới được tạo vào danh sách dữ liệu baddie
cấu trúc. Chương trình của chúng tôi sẽ sử dụng danh sách này để kiểm tra xem người chơi có va
chạm với bất kỳ
baddies và để biết nơi để vẽ baddies trên cửa sổ.
Di chuyển nhân vật trên mạng
140.
# Di chuyển người chơi xung quanh.
141.
nếu moveLeft và playerRect.left> 0:
142.
playerRect.move_ip (-1 * PLAYERMOVERATE, 0)
Bốn biến chuyển động moveLeft , moveRight , moveUp và moveDown là
được đặt thành Đúng và Sai khi Pygame tạo các sự kiện KEYDOWN và KEYUP ,
tương ứng. (Mã này là từ dòng 86 đến dòng 121.)
Nếu nhân vật của người chơi di chuyển sang trái và cạnh trái của nhân vật của người chơi lớn
hơn
hơn 0 (là cạnh trái của cửa sổ), sau đó chúng tôi muốn di chuyển Rect của nhân vật
đối tượng (được lưu trữ trong playerRect ).
Chúng tôi sẽ luôn di chuyển đối tượng playerRect theo số pixel trong
CHƠI TRÒ CHƠI . Để có được dạng âm của một số nguyên, bạn chỉ cần nhân nó bằng cách
-1 . Vì vậy, trên dòng 142, vì 5 được lưu trữ trong PLAYERMOVERATE , biểu thức -1 *
PLAYERMOVERATE đánh giá đến -5 .
Điều này có nghĩa là gọi playerRect.move_ip (-1 * PLAYERMOVERATE, 0)
sẽ thay đổi vị trí của playerRect thêm 5 pixel ở bên trái vị trí hiện tại của nó.
143.
if moveRight và playerRect.right <WINDOWWIDTH:
144.
playerRect.move_ip (PLAYERMOVERATE, 0)
145.
nếu moveUp và playerRect.top> 0:
146.
playerRect.move_ip (0, -1 * PLAYERMOVERATE)
147.
if moveDown và playerRect.bottom <WINDOWHEIGHT:
148.
playerRect.move_ip (0, PLAYERMOVERATE)
Chúng tôi muốn làm điều tương tự cho ba hướng còn lại: đúng, lên và xuống. Mỗi
ba nếu báo cáo trong đường dây 143-148 kiểm tra rằng biến chuyển động của họ được thiết lập
để
Đúng và cạnh của đối tượng Rect của người chơi nằm trong cửa sổ trước
gọi phương thức move_ip () để di chuyển đối tượng Rect .
Các pygame.mouse.set_pos () Chức năng
150.
# Di chuyển con trỏ chuột để khớp với trình phát.
151.
pygame.mouse.set_pose (playerRect.centerx,
playerRect.centery)
391
20 - Dodger

Trang 406
Dòng 151 di chuyển con trỏ chuột đến cùng vị trí với nhân vật của người chơi. Các
Hàm pygame.mouse.set_pose () di chuyển con trỏ chuột đến X và Y
tọa độ mà bạn vượt qua nó. Cụ thể, con trỏ sẽ ở ngay giữa
nhân vật của hình chữ nhật đối tượng bởi vì chúng tôi vượt qua centerx và centery thuộc tính của
playerRect cho tọa độ. Con trỏ chuột vẫn tồn tại và có thể được di chuyển, thậm chí
mặc dù nó vô hình vì chúng tôi đã gọi pygame.mouse.set_visible (Sai) trên
dòng 47.
Lý do chúng tôi muốn con trỏ chuột khớp với vị trí của nhân vật người chơi là
tránh nhảy đột ngột. Hãy tưởng tượng rằng con trỏ chuột và nhân vật của người chơi đang ở
cùng một vị trí ở phía bên trái của cửa sổ. Khi người chơi nhấn giữ mũi tên phải
Phím, ký tự di chuyển sang cạnh phải của cửa sổ nhưng con trỏ chuột sẽ ở lại
ở cạnh trái của màn hình. Nếu người chơi sau đó di chuyển chuột chỉ một chút, người chơi sẽ
nhân vật sẽ ngay lập tức nhảy đến vị trí con trỏ chuột ở cạnh trái của
màn hình. Bằng cách di chuyển con trỏ chuột cùng với nhân vật của người chơi, bất kỳ con chuột
nào
chuyển động sẽ không dẫn đến một cú nhảy bất ngờ qua cửa sổ.
153.
# Di chuyển các baddies xuống.
154.
cho b trong baddies:
Bây giờ chúng tôi muốn lặp qua từng cấu trúc dữ liệu baddie trong danh sách baddies để di
chuyển
họ xuống một chút.
155.
nếu không đảo ngượcCheat và không SlowCheat:
156.
b ['orth']. move_ip (0, b ['speed'])
Nếu không có mánh gian lận nào được kích hoạt (do người chơi nhấn các phím "z" hoặc "x"
lần lượt đặt ReverseCheat hoặc SlowCheat thành True ), sau đó di chuyển
vị trí của baddie xuống một số pixel bằng tốc độ của nó, được lưu trữ trong
Phím 'tốc độ' .
Triển khai mã cheat
157.
elif đảo ngược:
158.
b ['orth']. move_ip (0, -5)
Nếu cheat ngược đã được kích hoạt, thì baddie thực sự nên được di chuyển lên bởi
năm pixel. Truyền -5 cho đối số thứ hai cho move_ip () sẽ di chuyển Rect
đối tượng lên trên năm pixel.
159.
elif chậm cheat:
160.
b ['orth']. move_ip (0, 1)
392

Trang 407
Nếu cheat chậm đã được kích hoạt, thì baddie sẽ di chuyển xuống dưới, nhưng chỉ
bởi tốc độ chậm của một pixel mỗi lần lặp qua vòng lặp trò chơi. Baddie bình thường
tốc độ (được lưu trữ trong khóa 'tốc độ' của cấu trúc dữ liệu của baddie) sẽ bị bỏ qua
trong khi gian lận chậm được kích hoạt.
Loại bỏ các Baddies
162.
# Xóa các baddies đã vượt qua
đáy.
163.
cho b trong baddies [:]:
Sau khi di chuyển các baddies xuống cửa sổ, chúng tôi muốn loại bỏ bất kỳ baddies rơi
bên dưới cạnh dưới của cửa sổ từ danh sách baddies . Hãy nhớ rằng chúng ta trong khi chúng ta
đang lặp qua một danh sách, chúng ta không nên sửa đổi nội dung của danh sách bằng cách thêm
hoặc
loại bỏ các mặt hàng. Vì vậy, thay vì lặp qua danh sách baddies với vòng lặp baddies của chúng
tôi ,
chúng tôi sẽ lặp lại thông qua một bản sao của danh sách baddies .
Hãy nhớ rằng một lát danh sách sẽ đánh giá một bản sao của các mục trong danh sách. Ví dụ: thư
rác
[2: 4] sẽ trả về một danh sách mới với các mục từ chỉ mục 2 cho đến (nhưng không bao gồm) chỉ
mục 4 .
Để trống chỉ mục đầu tiên sẽ chỉ ra rằng chỉ số 0 nên được sử dụng. Ví dụ: thư rác
[: 4] sẽ trả về một danh sách với các mục từ đầu danh sách cho đến (nhưng không bao gồm) mục
đó
tại chỉ số 4 . Để trống chỉ mục thứ hai sẽ chỉ ra rằng tối đa (và bao gồm) cuối cùng
chỉ số nên được sử dụng. Ví dụ: spam [2:] sẽ trả về một danh sách với các mục từ chỉ mục 2
tất cả các cách để (và bao gồm) mục cuối cùng trong danh sách.
Nhưng để cả hai chỉ mục vào ô trống là một cách để thể hiện toàn bộ danh sách. Các
biểu thức baddies [:] là một lát danh sách của toàn bộ danh sách, vì vậy nó ước tính thành một
bản sao của
toàn bộ danh sách. Điều này rất hữu ích vì trong khi chúng ta lặp đi lặp lại trên bản sao của danh
sách, chúng ta có thể
sửa đổi danh sách ban đầu và loại bỏ bất kỳ cấu trúc dữ liệu baddie nào đã vượt qua
cạnh dưới của cửa sổ.
Vòng lặp for của chúng tôi trên dòng 163 sử dụng biến b cho mục hiện tại trong vòng lặp thông
qua
baddies [:] .
164.
if b ['orth']. top> WINDOWHEIGHT:
165.
baddies.remove (b)
Hãy đánh giá biểu thức b ['orth']. Top. b là cấu trúc dữ liệu baddie hiện tại từ
danh sách xấu [:]. Mỗi cấu trúc dữ liệu baddie trong danh sách là một từ điển với khóa 'orth' ,
nơi lưu trữ một đối tượng Rect. Vì vậy b ['orth'] là đối tượng Rect cho baddie. Cuối cùng, hàng
đầu là
tọa độ Y của cạnh trên của khu vực hình chữ nhật. Hãy nhớ rằng trong tọa độ
hệ thống, tọa độ Y tăng giảm. Vì vậy, b ['orth']. Top>
WINDOWHEIGHT sẽ kiểm tra xem cạnh trên của baddie có nằm dưới đáy của
cửa sổ.
393
20 - Dodger

Trang 408
Nếu điều kiện này là True , thì chúng ta sẽ xóa cấu trúc dữ liệu baddie khỏi
danh sách baddies.
Vẽ cửa sổ
Nó không đủ để trò chơi của chúng tôi cập nhật trạng thái của thế giới trò chơi trong bộ nhớ của
nó. Của chúng tôi
chương trình cũng sẽ phải hiển thị thế giới trò chơi cho người chơi. Chúng ta có thể làm điều này
bằng cách vẽ
đồ họa của baddies và nhân vật của người chơi trên màn hình. Bởi vì vòng lặp trò chơi là
thực hiện nhiều lần trong một giây, vẽ các baddies và người chơi ở các vị trí mới làm cho
chuyển động của họ trông trơn tru và tự nhiên. Nhưng mọi yếu tố trên màn hình phải được rút ra
từng cái một bằng cách gọi hàm Pygame thích hợp.
167.
# Vẽ thế giới trò chơi trên cửa sổ.
168.
windowSurface.fill (BACKGROUNDCOLOR)
Bây giờ chúng tôi đã cập nhật tất cả các cấu trúc dữ liệu cho các baddies và của người chơi
Nhân vật, hãy vẽ mọi thứ trên màn hình. Đầu tiên, trước khi chúng tôi vẽ bất cứ điều gì khác trên
Đối tượng bề mặt được gọi bởi windowSurface , chúng tôi muốn bôi đen toàn bộ màn hình
để xóa bất cứ thứ gì được vẽ trên đó trong lần lặp trước thông qua vòng lặp trò chơi.
Hãy nhớ rằng đối tượng Surface trong windowSurface là Surface đặc biệt
đối tượng bởi vì nó là đối tượng được trả về bởi pygame.display.set_mode () . Điều này có nghĩa

rằng mọi thứ được vẽ trên đối tượng Surface đó sẽ xuất hiện trên màn hình, nhưng chỉ sau
Hàm pygame.display.update () được gọi.
Vẽ điểm số
170.
# Vẽ số điểm và điểm cao nhất.
171.
drawText ('Điểm:% s'% (điểm), phông chữ,
cửa sổ Mặt, 10, 0)
172.
drawText ('Điểm cao nhất:% s'% (topScore), phông chữ,
cửa sổ Mặt, 10, 40)
Tiếp theo, chúng tôi sẽ kết xuất văn bản cho điểm và điểm cao nhất vào góc trên cùng bên trái
của cửa sổ.
Các 'Điểm:% s' % (điểm) sử dụng chuỗi suy để chèn giá trị trong điểm số
biến thành chuỗi. Đây là điều tương tự như 'Điểm:' + str (điểm) . Chúng tôi vượt qua
chuỗi này, đối tượng Font được lưu trữ trong biến phông chữ , đối tượng Surface trên đó
vẽ văn bản trên và tọa độ X và Y nơi đặt văn bản.
Hãy nhớ rằng drawText () của chúng tôi sẽ xử lý lệnh gọi đến render () và blit ()
phương pháp.
Đối với điểm số cao nhất, chúng tôi làm điều tương tự chính xác. Thay vào đó, chúng tôi vượt
qua 40 cho tọa độ Y
bằng 0 (giống như chúng tôi làm cho điểm số) để văn bản điểm số cao nhất xuất hiện bên dưới
văn bản điểm số.
394

Trang 409
Vẽ nhân vật
174.
# Vẽ hình chữ nhật của người chơi
175.
windowSurface.blit (playerImage, playerRect)
Hãy nhớ rằng thông tin về người chơi được giữ ở hai biến khác nhau.
playerImage là một đối tượng Surface chứa tất cả các pixel màu tạo nên
hình ảnh nhân vật của người chơi. playerRect là một đối tượng Rect lưu trữ thông tin
về kích thước và vị trí của nhân vật người chơi.
Chúng tôi gọi phương thức blit () trên windowSurface và vượt qua playerImage và
người chơiRect . Điều này vẽ hình ảnh của nhân vật người chơi trên windowSurface tại
vị trí thích hợp.
177.
# Vẽ mỗi baddie
178.
cho b trong baddies:
179.
windowSurface.blit (b ['bề mặt'], b ['orth'])
Chúng tôi sử dụng một vòng lặp for ở đây để vẽ mọi baddie trên đối tượng windowSurface .
Hãy nhớ rằng mỗi mục trong danh sách baddies là một từ điển có 'bề mặt' và
Các phím 'orth' chứa đối tượng Surface với hình ảnh baddie và đối tượng Rect
với thông tin vị trí và kích thước, tương ứng.
181.
pygame.display.update ()
Bây giờ chúng ta đã vẽ xong mọi thứ cho đối tượng windowSurface , chúng ta
nên vẽ bề mặt này lên màn hình bằng lệnh gọi pygame.display.update () .
Phát hiện va chạm
183.
# Kiểm tra xem có bất kỳ baddies nào đã tấn công
người chơi.
184.
if playerHasHitBaddie (playerRect, baddies):
185.
nếu điểm> topScore:
186.
topScore = điểm # thiết lập điểm số cao nhất mới
187.
phá vỡ
Bây giờ, hãy kiểm tra xem người chơi có va chạm với bất kỳ baddies nào không. Chúng tôi đã
viết một
Hàm để kiểm tra điều này: playerHasHitBaddie () . Hàm này sẽ trả về True nếu
nhân vật của người chơi đã va chạm với bất kỳ baddies nào trong danh sách baddies . Nếu không
thì,
hàm sẽ trả về Sai .
395
20 - Dodger

Trang 410
Nếu nhân vật của người chơi bị trúng baddie, thì chúng tôi sẽ kiểm tra xem điểm hiện tại của người
chơi có phải là
lớn hơn điểm số cao nhất. Nếu đúng như vậy, chúng tôi đặt điểm số cao nhất mới là điểm số hiện
tại của người chơi.
Dù bằng cách nào, chúng tôi thoát ra khỏi vòng lặp trò chơi. Việc thực hiện chương trình sẽ nhảy
xuống dòng
191.
189.
mainClock.tick (FPS)
Để giữ cho máy tính không chạy qua vòng lặp trò chơi càng nhanh càng tốt (mà
sẽ quá nhanh để người chơi theo kịp), chúng tôi gọi mainClock.tick () để
tạm dừng trong một khoảng thời gian ngắn Việc tạm dừng sẽ đủ dài để đảm bảo khoảng 40
(giá trị chúng tôi lưu trữ bên trong biến FPS ) lặp đi lặp lại qua vòng lặp trò chơi xảy ra mỗi
thứ hai.
Trò chơi trên màn hình
191. # Dừng trò chơi và hiển thị màn hình "Trò chơi kết thúc".
192. pygame.mixer.music.stop ()
193. gameOverSound.play ()
Khi người chơi thua, chúng tôi muốn dừng phát nhạc nền và phát
"trò chơi kết thúc" hiệu ứng âm thanh. Chúng ta gọi hàm stop () trong pygame.mixer.music
mô-đun để dừng nhạc nền. Sau đó, chúng ta gọi phương thức play () trên Sound
đối tượng được lưu trữ trong gameOverSound .
195. drawText ('TRÒ CHƠI TRÊN', phông chữ, windowSurface,
(WINDOWWIDTH / 3), (WINDOWheIGHT / 3))
196. drawText ('Bấm phím để phát lại.', Phông chữ,
windowSurface, (WINDOWWIDTH / 3) - 80, (WINDOWHEIGHT / 3)
+ 50)
197. pygame.display.update ()
198. WaitForPlayerToPressKey ()
Bây giờ chúng tôi muốn hiển thị văn bản trên cửa sổ để cho người chơi biết rằng trò chơi đã kết
thúc và
họ nên nhấn một phím để bắt đầu chơi một trò chơi mới. Hai cuộc gọi đến drawText () của chúng
tôi
Hàm sẽ vẽ văn bản này vào đối tượng windowSurface và gọi đến
pygame.display.update () sẽ vẽ đối tượng Surface này lên màn hình.
Sau khi hiển thị văn bản này, chúng tôi muốn trò chơi dừng lại cho đến khi người chơi nhấn
phím, vì vậy chúng tôi
gọi hàm WaitForPlayerToPressKey () của chúng tôi .
200. gameOverSound.stop ()
396

Trang 411
Sau khi người chơi nhấn một phím, việc thực hiện chương trình sẽ trở lại từ
WaitForPlayerToPressKey () gọi trên đường dây 198. Tùy thuộc vào thời gian người chơi
cần nhấn một phím, hiệu ứng âm thanh "trò chơi kết thúc" có thể hoặc không thể vẫn đang
chơi. Chúng tôi muốn
để dừng hiệu ứng âm thanh này trước khi vòng lặp này kết thúc và chúng tôi bắt đầu một trò chơi
mới, vì vậy chúng tôi có một cuộc gọi đến
gameOverSound.stop () tại đây.
Sửa đổi trò chơi Dodger
Đó là nó cho trò chơi đồ họa của chúng tôi. Bạn có thể thấy rằng trò chơi quá dễ hoặc quá
khó. Nhưng
trò chơi rất dễ sửa đổi vì chúng tôi đã dành thời gian để sử dụng các biến không đổi thay thế
gõ vào các giá trị trực tiếp. Bây giờ tất cả những gì chúng ta cần làm để thay đổi trò chơi là sửa
đổi
giá trị được đặt trong các biến không đổi.
Ví dụ: nếu bạn muốn trò chơi chạy chậm hơn nói chung, hãy thay đổi biến FPS trên
dòng 8 đến một giá trị nhỏ hơn như 20 . Điều này sẽ làm cho cả baddies và người chơi
thay vào đó, nhân vật di chuyển chậm hơn vì vòng lặp trò chơi sẽ chỉ được thực hiện 20 lần một
giây
của 40.
Nếu bạn chỉ muốn làm chậm các baddies và không phải người chơi, sau đó thay đổi
BADDIEMAXSPEED đến một giá trị nhỏ hơn, chẳng hạn như 4 . Điều này sẽ làm cho tất cả các
baddies di chuyển
giữa 1 (giá trị trong BADDIEMINSPEED ) và 4 pixel mỗi lần lặp trong trò chơi
vòng lặp thay vì 1 và 8.
Nếu bạn muốn trò chơi có ít baddies hơn nhưng lớn hơn thay vì nhiều baddies nhanh, thì
tăng ADDNEWBADDIERATE lên 12 , BADDIEMINIZE lên 40 và BADDIEMAXSIZE
đến 80 . Bây giờ các baddies đang được thêm vào sau mỗi 12 lần lặp qua vòng lặp trò chơi
cứ sau 6 lần lặp lại, sẽ có một nửa số baddies như trước. Nhưng để giữ cho trò chơi
Thật thú vị, các baddies bây giờ lớn hơn nhiều so với trước đây.
Mặc dù trò chơi cơ bản vẫn giữ nguyên, bạn có thể sửa đổi bất kỳ biến không đổi nào thành
ảnh hưởng mạnh mẽ đến hành vi của trò chơi. Tiếp tục thử các giá trị mới cho hằng số
biến cho đến khi bạn tìm thấy một tập hợp các giá trị bạn thích nhất.
Tóm tắt: Tạo trò chơi của riêng bạn
Không giống như các trò chơi dựa trên văn bản trước đây của chúng tôi, Dodger thực sự trông
giống như kiểu hiện đại
trò chơi máy tính chúng ta thường chơi. Nó có đồ họa và âm nhạc và sử dụng chuột. Trong khi
Pygame cung cấp các hàm và kiểu dữ liệu dưới dạng các khối xây dựng, chính bạn là người lập
trình
đặt chúng lại với nhau để tạo ra các trò chơi tương tác thú vị.
Và đó là tất cả bởi vì bạn biết chính xác cách hướng dẫn máy tính làm điều đó, từng bước một,
từng dòng một. Bạn có thể nói ngôn ngữ của máy tính và khiến nó thực hiện một lượng lớn
số crunching và vẽ cho bạn. Đây là một kỹ năng rất hữu ích và tôi hy vọng bạn sẽ
tiếp tục tìm hiểu thêm về lập trình Python. (Và vẫn còn nhiều thứ để tìm hiểu!)
Dưới đây là một số trang web có thể dạy cho bạn thêm về lập trình Python:
397
20 - Dodger

Trang 412
Hoặc bạn có thể tìm hiểu thêm về Python bằng cách tìm kiếm World Wide Web. Đi đến
trang web công cụ tìm kiếm http://google.com và tìm kiếm "lập trình Python" hoặc "Python
hướng dẫn "để tìm các trang web có thể dạy bạn nhiều hơn về lập trình Python.
Bây giờ hãy đi và phát minh ra trò chơi của riêng bạn. Và chúc may mắn!
http://www.python.org/doc/
Thêm hướng dẫn về Python và tài liệu của tất cả
các mô-đun và chức năng Python.
http://www.pygame.org/docs/
Hoàn thành tài liệu về các mô-đun và
chức năng cho Pygame.
http://inventwithpython.com
Trang web của cuốn sách này, bao gồm tất cả các nguồn
mã cho các chương trình này và thông tin bổ sung.
Trang web này cũng có các tập tin hình ảnh và âm thanh được sử dụng trong
các chương trình Pygame.
http://inventwithpython.com/traces
Một ứng dụng web giúp bạn theo dõi thông qua
thực hiện các chương trình trong cuốn sách này, từng bước một.
http://inventwithpython.com/ideo Video đi kèm với các chương trình trong cuốn sách này.
http://gamedevlessons.com
Một trang web hữu ích về cách thiết kế và lập trình
trò chơi điện tử.
al@inventwithpython.com
Địa chỉ email của tác giả. Vui lòng gửi email cho Al
câu hỏi của bạn về cuốn sách này hoặc về Python
lập trình.
398

Trang 413
Hầu hết các chương trình trong cuốn sách này sử dụng phiên bản 3 mới hơn của Python. Các
Trò chơi Pygame sử dụng Python 2 vì thư viện Pygame chưa tương thích
với Python 3. Python 3 sửa nhiều lỗi với phiên bản 2 của langugae,
tuy nhiên, những thay đổi này cũng có thể khiến các chương trình Python 3 không thể chạy được
với
Trình thông dịch Python 2 và ngược lại.
Chỉ có một vài thay đổi giữa hai phiên bản và phụ lục này sẽ đi
thông qua những cái có liên quan đến cuốn sách này. Học cả Python 2 và 3 khá đơn giản. Tất cả
các mô-đun được nhập bởi các chương trình trong cuốn sách này (ngoại trừ mô-đun pygame) là
một phần
của thư viện chuẩn và hoạt động với cả Python 2 và Python 3.
Nói tóm lại, hãy sử dụng Python 3 trừ khi bạn cần sử dụng thư viện chưa tương thích với
phiên bản 3. Học cả Python 2 và 3 đều dễ dàng vì chỉ có một vài thay đổi
giữa họ.
Hàm print () và câu lệnh in
Trong Python 3, print () là một hàm giống như input () hoặc len () . Phiên bản chức năng
yêu cầu dấu ngoặc đơn giống như bất kỳ lệnh gọi hàm nào khác (mặc dù bạn có thể thêm dấu
ngoặc đơn vào
các in tuyên bố tùy chọn).
Câu lệnh in trong Python 2 sẽ luôn in một ký tự dòng mới ở cuối phần
chuỗi. Để làm cho nó in một khoảng trắng thay vào đó, hãy đặt dấu phẩy ở cuối câu lệnh in:
>>> # Python 2
399

Trang 414
>>> in "Xin chào",
Để in một cái gì đó bên cạnh một dòng mới ở cuối hàm print () trong Python 3,
sử dụng kết thúc đối số từ khóa :
>>> # Python 3
>>> in ("Xin chào", end = "")
Các đầu vào () và raw_input () Chức năng
Trong Python 2, chức năng nhận đầu vào từ bàn phím là raw_input () . Trong Python 3,
hàm input () thực hiện điều này. Bạn chỉ có thể đổi tên hàm bất cứ nơi nào nó xuất hiện
trong mã của bạn.
>>> # Python 2
>>> tên = raw_input ()
>>> # Python 3
>>> tên = đầu vào ()
Các phạm vi () Return Value Chức năng của
Trong Python 2, hàm phạm vi () trả về một danh sách thực tế với các số nguyên. Trong Python 3,
hàm phạm vi () trả về một "đối tượng phạm vi". Cả hai có thể được sử dụng chính xác theo cùng
một cách trong
cho các vòng lặp:
>>> cho tôi trong phạm vi (10): # Hoạt động trong Python 2 và
3
... in (i)
Tuy nhiên, nếu bạn muốn tạo một danh sách số nguyên thực tế trong Python 3, bạn phải chuyển
đổi
"đối tượng phạm vi" vào một danh sách có hàm list () :
>>> # Python 2
>>> listOfInts = phạm vi (10)
>>> # Python 3
400

Trang 415
>>> listOfInts = list (phạm vi (10))
Bộ phận với / Người điều hành
Trong Python 2, thực hiện phép chia với toán tử / dẫn đến một số dấu phẩy động (nghĩa là
một số có dấu thập phân) chỉ khi một trong các số đó tự nổi:
>>> # Python 2
>>> 25.0 / 8
3.125
>>> 25/1
3.125
Tuy nhiên, trong Python 2, nếu cả hai số đều là số nguyên, thì kết quả của phép chia là
số nguyên làm tròn xuống. Trong Python 3, kết quả là một số dấu phẩy động bất kể là gì:
>>> # Python 2
>>> 25/8
3
>>> # Python 3
>>> 25/8
3.125
Chuỗi định dạng với phương thức định dạng () và
%S
Trong cả Python 2 và 3, bạn có thể bao gồm % s bên trong một chuỗi và theo dõi nó với một
danh sách
các giá trị cho mỗi % s, chẳng hạn như:
>>> # Python 2 và 3
>>> 'Tên tôi là% s và tôi đến từ% s.' % ('Al',
'Houston')
'Tên tôi là Al và tôi đến từ Houston.'
Tuy nhiên, Python 3 thêm một phương thức chuỗi mới gọi là format () . Chuỗi này cho phép bạn
cung cấp một danh sách các đối số dưới dạng tham số để định dạng (). Thay vì% s, bạn sử
dụng {0} và {1}
và {2} , v.v.
401
A - Sự khác biệt giữa Python 2 và 3

Trang 420
>>> # Python 3
>>> 'Tên tôi là {0} và tôi đến từ {1}.'.
('Al', 'Houston')
'Tên tôi là Al và tôi đến từ Houston.'
Các số bên trong dấu ngoặc nhọn là chỉ số của các tham số cần định dạng () . Vì thế
chuyển đổi xung quanh trong chuỗi sẽ chuyển xung quanh nơi các tham số định dạng ()
được đặt:
>>> # Python 3
>>> 'Tên tôi là {1} và tôi đến từ {0}.'.
('Al', 'Houston')
'Tên tôi là Houston và tôi đến từ Al.'
Một tính năng hay là bạn có thể sử dụng cùng một tham số để định dạng () nhiều lần trong
chuỗi mà không tạo thêm tham số cho định dạng ( ):
>>> # Python 3
>>> 'Tên tôi là {0}, {0}, {0}, {0} và tôi đến từ
Định dạng {1}. '. (' Jimmy Four Times ',' Houston ')
'Tên tôi là Jimmy Four Times, Jimmy Four Times,
Jimmy Four Times, Jimmy Four Times và tôi đến từ
Houston. '
Cùng với các chỉ mục số, bạn cũng có thể đặt văn bản bên trong dấu ngoặc nhọn. Văn bản này có
thể
khớp với các đối số từ khóa được chuyển sang định dạng () :
>>> # Python 3
>>> 'Tên tôi là {0} và tôi thích {điều} và tôi
từ định dạng {quê hương}. '. (' Al ', quê hương =' Houston ',
điều = 'mèo')
'Tên tôi là Al và tôi thích mèo và tôi đến từ
Houston. '
402

Trang 417
Hành động!
Phụ lục này chứa danh sách tất cả các câu lệnh, hàm và phương thức được trình bày trong
Cuốn sách này. Nó không trình bày bất kỳ thông tin mới, nhưng nó là một danh sách tiện dụng.
Hóa ra, không có đủ không gian trong cuốn sách này để đưa nó vào. Nhưng bạn có thể tìm thấy

trực tuyến miễn phí tại http://inventwithpython.com/appcillb.html
Phần còn lại của cuốn sách cũng có sẵn miễn phí trực tuyến tại http://inventwithpython.com
403

Trang 418
Bạn có thể muốn chia sẻ các chương trình trò chơi bạn thực hiện với người khác. Có khác
mọi người chơi trò chơi của bạn là một cách tuyệt vời để thể hiện kỹ năng của bạn. Tuy nhiên,
họ có thể không có
Python cài đặt trên máy tính của họ. Có một cách để chạy các chương trình Python mà không
cần
cài đặt trình thông dịch Python: Bạn sẽ phải biên dịch tập lệnh .py của bạn thành .exe
chương trình thực thi.
Biên dịch mã nguồn có nghĩa là chuyển đổi mã nguồn đó thành ngôn ngữ máy
là ngôn ngữ lập trình máy tính của bạn hiểu. Lập trình trong máy
ngôn ngữ rất dài và tẻ nhạt và các ngôn ngữ cấp cao hơn như Python tạo ra
lập trình dễ dàng hơn.
Phụ lục này sẽ chỉ cho bạn cách biên dịch các tệp .py Python của bạn thành các chương trình .exe
có thể chạy trên Windows mà không cần cài đặt Python.
Bước 1: Tải xuống và cài đặt py2exe
Trước tiên, bạn sẽ cần tải xuống và cài đặt một mô-đun có tên py2exe từ
http://sourceforge.net/projects/py2exe/files/. Hãy chắc chắn tải xuống phiên bản chính xác của
py2exe cho phiên bản Python của bạn. (Ví dụ: tải xuống py2exe-0.6.9.win32-py2.6.exe
nếu bạn đã cài đặt Python 2.6 trên máy tính của mình.)
Mô-đun py2exe chỉ hoạt động trên các phiên bản 2.x của Python chứ không phải Python 3. Bạn
sẽ
phải chuyển đổi các chương trình của bạn để chạy trên Python 2 nếu bạn đã viết chúng cho
Python 3.
Điều này khá dễ dàng. Không có nhiều sự khác biệt giữa Python 2 và 3 và chúng là
tài liệu trong Phụ lục A của cuốn sách này.
Sau khi tải xuống trình cài đặt py2exe, nhấp đúp chuột vào nó để cài đặt nó. Cài đặt này là
404

Trang 419
giống như cách bạn cài đặt Python. Chỉ cần tiếp tục nhấp vào nút Tiếp theo cho đến khi bạn đạt được
kết thúc.
Sau khi bạn đã cài đặt py2exe, bạn có thể chắc chắn rằng việc cài đặt đã thành công
bằng cách chạy shell tương tác và gõ như sau:
>>> nhập py2exe
>>>
Nếu bạn không thấy gì thì cài đặt đã thành công. Nếu bạn thấy lỗi này:
>>> nhập py2exe
>>> nhập py2exe
TracBack (cuộc gọi gần đây nhất vừa qua):
Tệp "<stdin>", dòng 1, trong?
ImportError: Không có mô-đun có tên py2exe
>>>
Bước 2: Tạo tập tin setup.py của bạn
Sau khi bạn đã cài đặt py2exe và xác nhận rằng nó đã được cài đặt, bạn sẽ cần phải tạo
một chương trình Python với tên setup.py. Nội dung của chương trình này phải là
sau:
từ thiết lập nhập distutils.core
nhập khẩu py2exe
thiết lập (console = ['hello.py'])
Thay thế hello.py trong mã nguồn ở trên bằng tên tệp của chương trình Python bạn
muốn biên dịch. Hãy chắc chắn lưu setup.py trong cùng thư mục với chương trình Python bạn
muốn biên dịch (nghĩa là hello.py hoặc bất cứ tên tệp nào).
Bước 3: Chạy Script setup của bạn
Tiếp theo, bạn sẽ phải chạy setup.py với tùy chọn dòng lệnh. Bạn không thể chạy
setup.py từ IDLE bằng cách nhấn phím F5 hoặc chọn Run Module từ menu Run.
Bạn phải sử dụng dòng lệnh Windows.
Để bắt đầu dòng lệnh Windows, nhấp vào nút Bắt đầu ở góc dưới bên trái
và chọn "Chạy". Trong các cửa sổ mở ra, gõ "cmd" và bấm OK.
Một cửa sổ màu đen với văn bản sẽ xuất hiện. Nhập " cd c: \ Python26 " (hoặc thư mục
bạn đã lưu các chương trình của mình vào) để thay đổi các thư mục vào thư mục chứa Python của
bạn
405
C - Chạy chương trình Python mà không cần cài đặt Python

Trang 420
tập lệnh và setup.py. Từ thư mục đó, nhập " c: \ Python26 \ python.exe
setup.py py2exe ". Phần đầu tiên ( c: \ Python26 \ python.exe ) chạy Python
thông dịch viên từ dòng lệnh. Tùy chọn dòng lệnh đầu tiên ( setup.py ) là
kịch bản mà trình thông dịch nên chạy. Tùy chọn dòng lệnh thứ hai ( py2exe ) cho biết
kịch bản để chạy với tùy chọn py2exe.
Sẽ có một lượng lớn văn bản từ việc chạy chương trình này. Bạn có thể bỏ qua văn bản này.
Khi quá trình biên dịch kết thúc, sẽ có hai thư mục mới, được đặt tên là build và dist.
Bạn có thể xóa thư mục bản dựng vì nó chỉ chứa các tệp được sử dụng trong thời gian
biên soạn. Thư mục dist chứa tất cả các tệp bạn muốn cung cấp cho người khác,
bao gồm thực thi nhị phân hello.exe. (Tập tin thực thi của bạn có thể có một tên khác. Nếu
thiết lập của bạn có hello.py, sau đó quá trình biên dịch tạo ra một chương trình có tên hello.exe.)
Bước 4: Phân phối chương trình của bạn
Không dễ để gửi email tất cả các tệp trong thư mục dist cho ai đó. Bạn có thể sử dụng "zip
chương trình "để gói nhiều tệp này thành một tệp (được gọi là tệp zip vì nó có
phần mở rộng .zip). Chương trình Zip có thể được tải xuống từ Internet miễn phí. Một số phổ
biến,
chương trình zip miễn phí là:
Bạn có thể đổi tên thư mục dist thành một cái khác nếu bạn muốn. Các tập tin chỉ đơn giản là
phải
trong cùng một thư mục.
Tóm lược
Quá trình biến tập lệnh Python .py của bạn thành chương trình thực thi nhị phân .exe cho
Windows rất đơn giản:
Bước 1: Tải xuống và cài đặt py2exe từ http://sourceforge.net/projects/py2exe/files/
Bước 2: Tạo một tệp setup.py trông như thế này:
từ thiết lập nhập distutils.core
nhập khẩu py2exe
thiết lập (console = ['hello.py'])
Bước 3: Chạy " c: \ Python26 \ python.exe setup.py py2exe "
Bước 4: Gói thư mục dist vào một tệp zip.
Phần mềm Zip
Trang mạng
7-zip
http://www.7-zip.org/doad.html
WinRAR
http://www.rarlab.com/doad.htmlm
406
Trang 421
Thông báo lỗi trong Python thường có thể gây nhầm lẫn. Dưới đây là danh sách các lỗi phổ biến
tin nhắn bạn có thể tìm thấy, cùng với một lời giải thích bằng tiếng Anh. Những thông báo lỗi
này là
kết quả của lỗi thời gian chạy. Họ sẽ ngay lập tức đánh sập Dưới đây là các thông báo lỗi
đã giải thích (thông báo lỗi của bạn có thể hơi khác nhau nhưng có nghĩa là điều tương tự):
● Cú phápError: cú pháp không hợp lệ

● ImportError: Không có mô-đun có tên raandom

● Cú phápError: EOL trong khi quét chuỗi ký tự

● AttributionError: đối tượng 'str' không có thuộc tính 'lowr'

● IndentationError: dự kiến một khối thụt lề

● thụt lềError: thụt lề bất ngờ

● thụt lềError: unindent không khớp với bất kỳ mức thụt ngoài nào

● TypeError: loại toán hạng xấu cho abs (): 'str'

● TypeError: abs () nhận chính xác một đối số (2 đã cho)

● IndexError: liệt kê chỉ mục ra khỏi phạm vi

● KeyError: 'thư rác'

Lỗi cú pháp: cú pháp không hợp lệ


Đây là thông báo lỗi chung nhất mà trình thông dịch Python sẽ cung cấp cho bạn. Nó có nghĩa là
Python đã mong đợi một cái gì đó không có ở đó, hoặc có một cái gì đó mà nó đã không
chờ đợi. Có lẽ bạn đã quên bao gồm hoặc chèn thêm một ký tự. Đây là một số
ví dụ:
nếu đoán = 5:
407

Trang 422
Trong trường hợp trên, lập trình viên đã sử dụng = (toán tử gán) thay vì == (
bằng toán tử so sánh). Python không bao giờ mong đợi các câu lệnh gán ở đó
nên là một điều kiện.
def foo (:
Trong trường hợp trên, lập trình viên quên khớp với kết thúc) đóng ngoặc.
def foo ()
Trong trường hợp trên, lập trình viên quên đặt dấu hai chấm ở cuối def
tuyên bố. Điều này cũng có thể xảy ra với các câu lệnh for , while , if , elif và các câu lệnh khác .
ImportError: Không có mô-đun có tên raandom
Lỗi này xuất hiện khi bạn cố gắng nhập một mô-đun không tồn tại. Nhiều khả năng,
bạn có một lỗi đánh máy trong tên mô-đun. Ví dụ: bạn có thể đã gõ raandom thay thế
của ngẫu nhiên .
Cú pháp: EOL trong khi quét chuỗi ký tự
in ('Xin chào thế giới!)
in ("Xin chào thế giới! ')
Lỗi này xảy ra khi bạn không có hai dấu ngoặc kép cho một chuỗi hoặc bạn sử dụng
dấu ngoặc kép khác nhau cho cùng một chuỗi. Nhìn vào hai ví dụ sau:
AttributionError: đối tượng 'str' không có thuộc tính 'lowr'
'Xin chào'.lowerr ()
'Xin chào'.append (' x ')
Lỗi này xuất hiện khi bạn gọi một phương thức hoặc truy cập một thuộc tính không tồn tại. Điều
này
rất có thể vì 1) bạn có một lỗi đánh máy trong tên phương thức hoặc thuộc tính hoặc 2) bạn là
gọi phương thức hoặc thuộc tính trên một giá trị là kiểu dữ liệu sai. Ví dụ: chuỗi
có một phương thức có tên dưới () , nhưng không phải lowerr () (có nghĩa là một lỗi đánh
máy). Và phần bổ sung ()
phương thức là một phương thức danh sách, vì vậy việc gọi nó trên một giá trị chuỗi sẽ gây ra lỗi
này.
408

Trang 423
IndentationError: dự kiến một khối thụt lề
def foo ():
in ('Xin chào thế giới!')
Lỗi này xảy ra nếu bạn không thụt mã của mình cho một khối. Trong ví dụ trên
lệnh print () ở cùng mức thụt lề như câu lệnh def , khi cần
một vết lõm lớn hơn.
Lỗi ghi chú: thụt lề không mong muốn
def foo ():
in ('Xin chào thế giới!')
in ('Tạm biệt')
Một lỗi thụt lề bất ngờ xảy ra khi bạn thêm một vết lõm mà không có lý do. Bạn
chỉ nên thêm thụt sau một def , nếu , khác , elif , trong khi , hoặc cho thống kê (hoặc
bất kỳ tuyên bố nào kết thúc bằng dấu hai chấm.)
IndentationError: unindent không khớp với bất kỳ vết lõm bên ngoài nào
cấp độ
def foo ():
in ('Xin chào thế giới!')
in ('Tạm biệt')
Lỗi thụt lề này xuất hiện khi bạn đang giảm thụt đầu dòng, nhưng không
giảm nó xuống cùng mức với vết lõm trước đó. Các print ( 'Tạm biệt') gọi
nên ở cùng một vết lõm như lệnh gọi print () khác (và nằm trong if
khối) hoặc tại thụt đầu dòng giống như nếu tuyên bố (và được bên ngoài nếu khối).
409
D - Thông báo lỗi phổ biến trong Python

Trang 424
TypeError: loại toán hạng xấu cho abs (): 'str'
abs ('xin chào')
Lỗi này xảy ra khi giá trị của một đối số bạn truyền cho hàm hoặc phương thức là
loại dữ liệu sai. Trong ví dụ trên, hàm abs () lấy một số nguyên hoặc nổi
số điểm. Truyền chuỗi cho đối số dẫn đến lỗi.
TypeError: abs () nhận chính xác một đối số (2 đã cho)
cơ bụng (42, 50)
Lỗi này xuất hiện khi bạn chuyển sai số lượng đối số cho hàm hoặc
phương pháp, quá nhiều hoặc quá ít. Hàm abs () lấy chính xác một (và chỉ một)
tranh luận. Trong ví dụ của chúng tôi, chúng tôi vượt qua hai đối số, dẫn đến lỗi này.
IndexError: liệt kê chỉ mục ngoài phạm vi
myList = ['spam', 'fizz', 'trứng']
in (myList [3])
IndexError xảy ra khi chỉ mục bạn sử dụng lớn hơn hoặc bằng số lượng
mục thực tế trong danh sách. Trong ví dụ trên của chúng tôi, danh sách myList chỉ có 3 mục
trong đó, vì vậy
chỉ các chỉ mục hợp lệ để sử dụng là 0 , 1 và 2 . Chỉ số 3 (hoặc bất kỳ chỉ số nào lớn hơn 2 ) là
lớn hơn bất kỳ chỉ mục nào trong số này, do đó mã dẫn đến IndexError.
KeyError: 'thư rác'
myDict = {'fizz': 42, 'trứng': 100}
myDict ['spam']
KeyError xảy ra khi bạn cố truy cập khóa trong một đối tượng từ điển không
hiện hữu. Khóa không bao giờ được thêm vào từ điển, đã bị xóa trước đó với
toán tử del , hoặc khóa bạn đang sử dụng có một lỗi đánh máy trong đó.
410

Trang 425
Bảng chú giải
giá trị tuyệt đối - Dạng tích cực của một số âm. Ví dụ: tuyệt đối
giá trị -2 là 2. Giá trị tuyệt đối của số dương chỉ đơn giản là số dương
chinh no.
AI - thấy, trí tuệ nhân tạo
thuật toán - Một loạt các hướng dẫn để tính toán một cái gì đó.
ứng dụng - Một chương trình được chạy bởi một hệ điều hành. Xem thêm, chương trình.
đối số - Các giá trị được truyền cho tham số trong lệnh gọi hàm.
trí tuệ nhân tạo - Mã hoặc một chương trình có thể thông minh đưa ra quyết định (cho
ví dụ, quyết định khi chơi trò chơi) để đáp ứng với hành động của người dùng.
Nghệ thuật ASCII - Sử dụng các ký tự văn bản và khoảng trắng để vẽ các hình ảnh đơn giản.
ngôn ngữ lắp ráp - Ngôn ngữ lập trình đơn giản nhất. Hợp ngữ
hướng dẫn là một hình thức dễ đọc với con người có thể dịch trực tiếp thành mã máy
hướng dẫn.
toán tử gán - Dấu =. Được sử dụng để gán giá trị cho các biến.
câu lệnh gán - Một dòng mã gán giá trị cho một biến bằng cách sử dụng
điều hành chuyển nhượng. Điều này định nghĩa, tạo ra biến khi được sử dụng với một cái mới
Biến đổi. Ví dụ: spam = 42
dấu hoa thị - Biểu tượng *. Dấu hoa thị được sử dụng như một dấu hiệu nhân.
Toán tử gán tăng - Các toán tử + = , - = , * = và / = . Các
gán thư rác + = 42 tương đương với thư rác = thư rác + 42 .
khối - Một nhóm các dòng mã với cùng số lượng thụt. Khối có thể
chứa các khối khác thụt vào bên trong chúng.
boolean - Một kiểu dữ liệu chỉ có hai giá trị, Đúng và Sai .
Toán tử boolean - Toán tử Boolean bao gồm và , hoặc , không .
điểm dừng - Điểm ngắt có thể được đặt trên một dòng mã cụ thể, điều này sẽ gây ra
trình gỡ lỗi sẽ tiếp quản khi dòng đó được thực thi trong khi chạy chương trình theo
trình gỡ lỗi.
Lệnh break - Các phá vỡ tuyên bố ngay lập tức nhảy ra khỏi dòng điện trong khi
411

Trang 426
hoặc cho vòng lặp đến dòng đầu tiên sau khi kết thúc khối của vòng lặp.
sức mạnh vũ phu - Trong mật mã học, để thử mọi khóa có thể để giải mã
tin nhắn được mã hóa.
lỗi - Lỗi trong mã chương trình của bạn. Ba loại lỗi là lỗi cú pháp, thời gian chạy
lỗi, và lỗi ngữ nghĩa.
mật mã caesar - Một mật mã thay thế đơn giản trong đó mỗi ký hiệu được thay thế bằng một
và chỉ có một biểu tượng khác.
hệ tọa độ cartesian - Một hệ tọa độ được sử dụng để xác định các điểm chính xác
trong một số khu vực của không gian (như màn hình, hoặc trên bảng trò chơi). Tọa độ Descartes
các hệ thống thường có hai tọa độ, một trong các trục X (nghĩa là ngang trái-phải
trục) và một trong các trục Y (nghĩa là trục lên xuống thẳng đứng).
trường hợp nhạy cảm - Tuyên bố vốn hóa khác nhau của một tên có ý nghĩa khác nhau
nhiều thứ. Python là ngôn ngữ phân biệt chữ hoa chữ thường, vì vậy spam , Spam và SPAM là
ba ngôn ngữ khác nhau
biến.
bộ xử lý trung tâm - CPU, chip chính mà máy tính của bạn sử dụng để xử lý
hướng dẫn phần mềm.
mật mã - Trong mật mã học, một thuật toán được sử dụng để mã hóa và giải mã tin nhắn bằng
một
khóa nhất định.
bản mã - Trong mật mã, hình thức mã hóa của tin nhắn.
bình luận - Một phần của mã nguồn bị bỏ qua bởi trình thông dịch Python. Bình luận
có mặt để nhắc nhở lập trình viên về một cái gì đó về mã. Nhận xét bắt đầu bằng
một dấu # và đi tiếp cho phần còn lại của dòng.
tài sản giao hoán - Tính chất của phép cộng và phép nhân mô tả
làm thế nào thứ tự của các số được thêm hoặc nhân không quan trọng. Ví dụ: 2 +
4 = 6 và 4 + 2 = 6. Ngoài ra, 3 * 5 = 15 và 5 * 3 = 15.
toán tử so sánh - Các toán tử < ("nhỏ hơn"), <= ("nhỏ hơn hoặc bằng"), >
("lớn hơn"), > = ("lớn hơn hoặc bằng"), == ("bằng") và ! = ("Không bằng quá").
điều kiện - Một tên cho một biểu hiện, một trong những tồn tại trong một nếu hoặc khi
câu lệnh ước lượng thành giá trị boolean Đúng hoặc Sai .
biến không đổi - Các biến có giá trị không thay đổi. Các biến không đổi là
thường được sử dụng vì dễ dàng nhập tên của biến sau đó là giá trị mà chúng lưu trữ.
Theo quy ước, các tên biến không đổi được gõ trong tất cả các chữ cái viết hoa.
quy ước - Một cách làm những việc không bắt buộc, nhưng thường được thực hiện để thực hiện
412

Trang 427
nhiệm vụ dễ dàng hơn.
chỉ định chuyển đổi - Văn bản bên trong một chuỗi sử dụng phép nội suy chuỗi.
Công cụ xác định chuyển đổi phổ biến nhất là % s , xác định rằng biến đó
nội suy nên được chuyển đổi thành một chuỗi.
cpu - xem, Đơn vị xử lý trung tâm
sự cố - Một sự kiện xảy ra do lỗi thời gian chạy. Sau khi gặp sự cố, chương trình
chấm dứt ngay lập tức.
tiền điện tử - Khoa học phá mã bí mật và mật mã.
mật mã học - Khoa học tạo mã bí mật và mật mã.
kiểu dữ liệu - Một loại giá trị. Một số loại trong Python là: chuỗi, số nguyên, số float,
boolean, danh sách và noneType.
trình gỡ lỗi - Một chương trình cho phép bạn thực hiện từng dòng mã của mình (trong phần
cùng thứ tự mà Python thực thi chúng) và hiển thị những giá trị nào được lưu trữ trong tất cả các
biến.
giảm dần - Để giảm một giá trị số xuống một.
giải mã - Để chuyển đổi một tin nhắn được mã hóa thành phiên bản văn bản gốc có thể đọc
được.
def statement - Một câu lệnh xác định hàm mới. Tuyên bố def bắt đầu bằng
các def từ khóa, tiếp theo là tên hàm và một tập hợp các dấu ngoặc đơn, với bất kỳ
số lượng tên tham số được phân cách bằng dấu phẩy. Cuối cùng là một: ký tự đại tràng. Dành
cho
ví dụ: def funcName (param1, param2):
phân định - Để tách với. Ví dụ: chuỗi 'mèo, chó, chuột' được phân định
bằng dấu phẩy.
dictionary - Một kiểu dữ liệu chứa có thể lưu trữ các giá trị khác. Các giá trị được truy cập bởi
một
Chìa khóa. Ví dụ: spam ['foo'] = 42 gán khóa 'foo' của từ điển spam
giá trị 42 .
else statement - Một khác tuyên bố luôn sau một nếu tuyên bố, và mã
bên trong khối khác được thực thi nếu điều kiện if của câu lệnh là Sai .
danh sách trống - Danh sách [] , không chứa giá trị và có độ dài bằng không. Xem thêm,
chuỗi rỗng.
chuỗi rỗng - Chuỗi '' , không chứa ký tự và có độ dài bằng không.
Xem thêm, danh sách trống.
413
Bảng chú giải

Trang 428
mã hóa - Để chuyển đổi tin nhắn thành một dạng giống với dữ liệu rác và không thể
được hiểu ngoại trừ bởi một người biết ciphr và khóa được sử dụng để mã hóa
thông điệp.
ký tự thoát - Ký tự thoát cho phép lập trình viên chỉ định các ký tự trong
Python khó hoặc không thể nhập vào mã nguồn. Tất cả các nhân vật thoát
được đặt trước bởi một ký tự dấu gạch chéo ngược. Ví dụ: \ n hiển thị một dòng mới
ký tự khi nó được in.
đánh giá - Giảm một biểu thức xuống một giá trị duy nhất. Biểu thức 2 + 3 +
1 đánh giá giá trị 6 .
thực thi - Trình thông dịch Python thực thi các dòng mã, bằng cách đánh giá bất kỳ biểu thức
nào
hoặc thực hiện nhiệm vụ mà mã thực hiện.
thoát - Khi một chương trình kết thúc. "Chấm dứt" có nghĩa là điều tương tự.
biểu thức - Giá trị và các lệnh gọi hàm được kết nối bởi các toán tử. Biểu thức có thể là
đánh giá xuống một giá trị duy nhất.
trình chỉnh sửa tệp - Một chương trình được sử dụng để nhập hoặc thay đổi tệp, bao gồm các
tệp của nguồn Python
mã. Chương trình IDLE có trình chỉnh sửa tệp mà bạn sử dụng để nhập vào chương trình của
mình.
số dấu phẩy động - Các số có phân số hoặc dấu thập phân không phải là số nguyên.
Các số 3.5 và 42.1 và 5.0 là các số dấu phẩy động.
biểu đồ luồng - Biểu đồ hiển thị không chính thức luồng thực thi cho chương trình và
các sự kiện chính xảy ra trong chương trình và theo thứ tự nào.
báo cáo kiểm soát luồng - Các câu lệnh làm cho luồng thực thi thay đổi,
thường tùy thuộc vào điều kiện. Ví dụ, một lời gọi hàm gửi thực thi đến
bắt đầu một chức năng. Ngoài ra, một vòng lặp làm cho việc thực thi lặp đi lặp lại trên một phần
của mã
vài lần.
luồng thực thi - Thứ tự mà các lệnh Python được thực thi. Thường là
Trình thông dịch Python sẽ bắt đầu ở đầu chương trình và đi xuống thực thi một dòng tại
thời gian. Các câu lệnh điều khiển luồng có thể di chuyển luồng thực thi đến các phần khác nhau
của mã trong
chương trình.
Hàm - Một tập hợp các lệnh sẽ được thực thi khi hàm được gọi.
Các hàm cũng có giá trị trả về, là giá trị mà lệnh gọi hàm ước tính.
chức năng gọi - Lệnh truyền thực thi cho mã chứa bên trong hàm,
cũng truyền đối số cho hàm. Các hàm gọi ước tính giá trị trả về của
chức năng.
dữ liệu rác - Dữ liệu ngẫu nhiên hoặc các giá trị không có ý nghĩa.
414

Trang 429
phạm vi toàn cầu - Phạm vi của các biến ngoài tất cả các chức năng. Mã Python trong
phạm vi toàn cầu không thể thấy các biến trong phạm vi cục bộ của bất kỳ chức năng nào.
mã hóa cứng - Sử dụng một giá trị trong một chương trình, thay vì sử dụng một biến. Trong khi
một biến
có thể cho phép chương trình thay đổi, bằng cách mã hóa cứng một giá trị trong chương trình,
giá trị vẫn giữ nguyên
cố định vĩnh viễn trừ khi mã nguồn được thay đổi.
phần cứng - Các bộ phận của máy tính mà bạn có thể chạm vào, chẳng hạn như bàn phím, màn
hình,
trường hợp, hoặc chuột. Xem thêm, phần mềm.
ngôn ngữ lập trình cấp cao hơn - Ngôn ngữ lập trình mà con người có thể
hiểu, chẳng hạn như Python. Một thông dịch viên có thể dịch một ngôn ngữ cấp cao hơn sang
mã máy, là ngôn ngữ duy nhất máy tính có thể hiểu.
IDLE - Môi trường DeveLopment tương tác. IDLE là một chương trình giúp bạn nhập
chương trình và trò chơi của bạn.
I / O - Đầu vào / Đầu ra. Đây là một thuật ngữ được sử dụng để tham chiếu dữ liệu được gửi vào
một chương trình
(đầu vào) và được sản xuất bởi chương trình (đầu ra).
chuỗi bất biến - Một kiểu dữ liệu chứa không thể có giá trị được thêm hoặc xóa
từ nó. Trong Python, hai kiểu dữ liệu chuỗi bất biến là chuỗi và bộ dữ liệu.
nhập câu lệnh - Một dòng mã với từ khóa nhập theo sau là tên của
một mô-đun. Điều này cho phép bạn gọi bất kỳ chức năng nào có trong mô-đun.
tăng - Để tăng giá trị của một biến số lên một.
thụt lề - thụt lề của một dòng mã là số khoảng trắng trước khi bắt đầu
của mã thực tế. Sự thụt lề trong Python được sử dụng để đánh dấu khi các khối bắt đầu và kết
thúc.
Việc thụt lề thường được thực hiện theo bội số của bốn không gian.
chỉ mục - Một số nguyên giữa các dấu ngoặc vuông được đặt ở cuối một thứ tự
biến chứa (thường là một danh sách) để đánh giá một mục cụ thể trong vùng chứa đó. Các
chỉ mục đầu tiên bắt đầu từ 0, không phải 1. Ví dụ: nếu thư rác đề cập đến danh sách ['a', 'b', 'c',
'D'] , sau đó spam [2] ước tính thành 'c' .
lỗi chỉ mục - Xảy ra lỗi chỉ mục khi bạn cố truy cập vào một chỉ mục không
hiện hữu. Điều này giống như sử dụng một biến không tồn tại. Ví dụ: nếu thư rác đề cập đến
danh sách ['a', 'b', 'c', 'd'] , sau đó spam [10] sẽ gây ra lỗi chỉ mục.
vòng lặp vô hạn - Một vòng lặp có một điều kiện luôn luôn đánh giá là True , điều này tạo ra
các vòng lặp cứ lặp đi lặp lại mãi mãi. Cách duy nhất để thoát khỏi một vòng lặp vô hạn là nghỉ
tuyên bố.
đầu vào - Văn bản hoặc dữ liệu mà người dùng hoặc người chơi nhập vào một chương trình, chủ
yếu là từ
bàn phím.
415
Bảng chú giải

Trang 430
phép chia số nguyên - Bộ phận bỏ qua mọi phần dư và làm tròn số được đánh giá
số xuống. Phân chia số nguyên xảy ra khi cả hai số trong biểu thức phân chia là
số nguyên. Ví dụ: 20/7 đánh giá cho số nguyên 6 , mặc dù câu trả lời là 6,666
hoặc 6 phần còn lại 2.
số nguyên - Số nguyên là các số nguyên như 4 và 99 và 0 . Các số 3,5 và
42.1 và 5.0 không phải là số nguyên.
shell tương tác - Một phần của IDLE cho phép bạn thực thi mã Python mỗi dòng một lần.
Nó cho phép bạn ngay lập tức thấy giá trị mà biểu thức bạn nhập vào đánh giá.
thông dịch viên - Một chương trình dịch các hướng dẫn được viết ở cấp độ cao hơn
ngôn ngữ lập trình (như Python) thành mã máy mà máy tính có thể hiểu
và thực hiện.
Lặp lại - Một lần chạy qua mã trong khối của một vòng lặp. Ví dụ: nếu mã
trong một khối while được thực thi mười lần trước khi thực thi rời khỏi vòng lặp, chúng tôi nói
rằng có
là mười lần lặp của mã while-block.
cặp khóa-giá trị - Trong các loại dữ liệu từ điển, khóa là các giá trị được sử dụng để truy cập
vào
các giá trị trong từ điển, giống như chỉ mục của danh sách được sử dụng để truy cập các giá trị
trong danh sách. không giống
danh sách, khóa từ điển có thể thuộc bất kỳ loại dữ liệu nào, không chỉ là số nguyên.
khóa - Trong từ điển, khóa là chỉ mục được sử dụng để
khóa - Trong mật mã, một giá trị cụ thể (thường là một số) xác định cách
mật mã mã hóa một tin nhắn. Để giải mã tin nhắn, bạn phải biết cả mật mã và
giá trị chính đã được sử dụng.
danh sách - Kiểu dữ liệu chứa chính, danh sách có thể chứa một số giá trị khác, bao gồm cả các
giá trị khác
danh sách. Các giá trị trong danh sách được truy cập bởi một chỉ số nguyên giữa các dấu ngoặc
vuông. Ví dụ,
nếu thư rác được chỉ định danh sách ['a', 'b', 'c'] , thì thư rác [2] sẽ đánh giá thành 'c' .
nối danh sách - Kết hợp nội dung của một danh sách với cuối danh sách khác với
+ toán tử. Ví dụ: [1, 2, 3] + ['a', 'b', 'c'] ước tính thành [1, 2,
3, 'a', 'b', 'c'] .
phạm vi cục bộ - Phạm vi của các biến trong một hàm duy nhất. Mã Python bên trong một
Hàm có thể đọc giá trị của các biến trong phạm vi toàn cầu, nhưng bất kỳ thay đổi hoặc mới
các biến được thực hiện sẽ chỉ tồn tại trong khi thực thi bên trong lệnh gọi hàm đó.
vòng lặp - Một khối mã bên trong một vòng lặp (sau câu lệnh for hoặc while ) sẽ lặp lại
thực hiện cho đến khi một số điều kiện được đáp ứng.
unrolling loop - Thay thế mã bên trong một vòng lặp bằng nhiều bản sao của mã đó. Dành cho
ví dụ, thay vì cho i trong phạm vi (10): in 'Xin chào' , bạn có thể hủy đăng ký
lặp bằng cách có mười dòng in 'Xin chào'
420

Trang 432
mã máy - Ngôn ngữ mà CPU của máy tính hiểu được. Mã máy
hướng dẫn là một loạt các số không và số không, và thường không thể đọc được bởi con người.
Các trình thông dịch (như trình thông dịch Python) dịch ngôn ngữ cấp cao hơn vào máy
mã.
phương thức - Các hàm được liên kết với các giá trị của kiểu dữ liệu. Ví dụ:
phương thức chuỗi Upper () sẽ được gọi trên một chuỗi như thế này: 'Hello'.upper ()
mô-đun - Một chương trình Python riêng có thể được bao gồm trong các chương trình của bạn
để bạn
có thể sử dụng các chức năng trong mô-đun.
toán tử mô đun - Toán tử "phần còn lại" được biểu thị bằng% phần trăm
ký tên. Ví dụ: trong khi 20/7 là 6 với phần còn lại là 2, 20% 7 sẽ đánh giá thành 2 .
chuỗi có thể thay đổi - Một kiểu dữ liệu chứa được sắp xếp và có thể có các giá trị được thêm
vào
hoặc loại bỏ nó. Danh sách là một kiểu dữ liệu chuỗi có thể thay đổi trong Python.
số âm - Tất cả các số nhỏ hơn 0. Số âm có dấu trừ
phía trước chúng để phân biệt chúng với các số dương, ví dụ, -42 hoặc -10.
các vòng lặp lồng nhau - Các vòng lặp tồn tại bên trong các vòng lặp khác.
Không có - Giá trị duy nhất trong kiểu dữ liệu noneType. "Không" thường được sử dụng để đại
diện cho
thiếu một giá trị.
hệ điều hành - Một chương trình lớn chạy các chương trình phần mềm khác (được gọi là
các ứng dụng) cùng một cách trên phần cứng khác nhau. Windows, Mac OS và Linux là
ví dụ về hệ điều hành.
Toán tử - Toán tử kết nối các giá trị trong biểu thức. Toán tử bao gồm + , - , * , / ,
và , và hoặc
thứ tự - Trong ASCII, số đại diện cho một ký tự ASCII. Ví dụ:
Ký tự ASCII "A" có số thứ tự 65.
gốc - Trong các hệ tọa độ cartesian, điểm tại tọa độ 0, 0.
HĐH - xem, hệ điều hành
đầu ra - Văn bản mà một chương trình tạo ra cho người dùng. Ví dụ: in báo cáo
sản xuất đầu ra.
ghi đè - Để thay thế một giá trị được lưu trữ trong một biến bằng một giá trị mới.
tham số - Một biến được chỉ định để có giá trị được truyền trong lệnh gọi hàm. Dành cho
ví dụ, câu lệnh def spam (trứng, phô mai) định nghĩa một hàm có hai
417
Bảng chú giải

Trang 432
thông số có tên trứng và phô mai .
biểu đồ hình tròn - Biểu đồ tròn hiển thị các phần trăm dưới dạng các phần của toàn bộ
vòng tròn.
plaintext - Dạng tin nhắn được giải mã, có thể đọc được của con người.
người chơi - Một người chơi trò chơi máy tính.
số dương - Tất cả các số bằng hoặc lớn hơn 0.
dấu thăng - Dấu #. Dấu hiệu Pound được sử dụng để bắt đầu bình luận.
câu lệnh in - Từ khóa in theo sau là một giá trị sẽ được hiển thị trên
màn hình.
chương trình - Tập hợp các hướng dẫn có thể xử lý đầu vào và tạo đầu ra khi
chạy bằng máy tính.
lập trình viên - Một người viết chương trình máy tính.
tham chiếu - Thay vì tự chứa các giá trị, các biến danh sách thực sự chứa
tài liệu tham khảo cho danh sách. Ví dụ: spam = [1, 2, 3] chỉ định thư rác tham chiếu đến
danh sách. cheese = spam sẽ sao chép tham chiếu vào danh sách mà thư rác đề cập đến. Bất kỳ
thay đổi nào
làm cho biến phô mai hoặc thư rác sẽ được phản ánh trong biến khác.
tuyên bố trả về - Trả về theo sau bởi một giá trị, đó là những gì cuộc gọi đến
chức năng của câu lệnh return sẽ đánh giá.
return value - Giá trị mà một lệnh gọi hàm sẽ đánh giá. Bạn có thể chỉ định
giá trị trả về là gì với từ khóa return theo sau là giá trị. Chức năng với
không có tuyên bố trả lại sẽ trả về giá trị Không có .
lỗi thời gian chạy - Một lỗi xảy ra khi chương trình đang chạy. Một lỗi thời gian chạy sẽ
khiến chương trình bị sập và ngừng thực thi.
phạm vi - Xem, phạm vi địa phương và phạm vi toàn cầu.
chuỗi - Kiểu dữ liệu chuỗi là loại dữ liệu chứa theo thứ tự và có "đầu tiên" hoặc
"Mục cuối cùng. Các kiểu dữ liệu chuỗi trong Python là danh sách, bộ dữ liệu và chuỗi. Từ điển

không theo trình tự, chúng không có thứ tự.
lỗi ngữ nghĩa - Một lỗi sẽ không khiến chương trình bị sập ngay lập tức, nhưng
sẽ khiến chương trình chạy một cách ngoài ý muốn. Một lỗi ngữ nghĩa có thể gây ra thời gian
chạy
lỗi và sự cố sau này trong chương trình.
418
Trang 433
shell - xem, vỏ tương tác
mật mã thay thế đơn giản - Một mật mã trong đó mỗi chữ cái được thay thế bằng một và chỉ
một lá thư khác.
lát - Một tập hợp con của các giá trị trong danh sách. Chúng được truy cập bằng ký tự: dấu hai
chấm trong
giữa các dấu ngoặc vuông. Ví dụ: nếu thư rác có giá trị ['a', 'b', 'c',
'd', 'e', 'f'] , sau đó thư rác lát [2: 4] có giá trị ['c', 'd'] . Tương tự như một
chuỗi con.
phần mềm - xem, chương trình
mã nguồn - Văn bản mà bạn nhập để viết chương trình.
câu lệnh - Một lệnh hoặc dòng mã Python không đánh giá thành giá trị.
bước - Thực hiện một dòng mã tại một trình gỡ lỗi, điều này có thể làm cho nó dễ dàng hơn
để tìm hiểu khi xảy ra sự cố trong mã.
nối chuỗi - Kết hợp hai chuỗi với toán tử + để tạo thành một
chuỗi mới. Ví dụ: 'Xin chào' + 'Thế giới!' ước tính cho chuỗi 'Xin chào
Thế giới! '
định dạng chuỗi - Một thuật ngữ khác cho nội suy chuỗi.
nội suy chuỗi - Sử dụng các công cụ xác định chuyển đổi trong một chuỗi làm chủ sở hữu địa
điểm cho người khác
các giá trị. Sử dụng phép nội suy chuỗi là một cách thay thế thuận tiện hơn cho nối chuỗi.
Ví dụ: 'Xin chào,% s. Bạn sẽ đến% s trên% s? ' % (Tên,
hoạt động, ngày) ước tính theo chuỗi 'Xin chào, Albert. Bạn đang đi à
để lập trình vào thứ năm? ' , nếu các biến có các giá trị tương ứng.
chuỗi - Một giá trị được tạo thành từ văn bản. Các chuỗi được nhập bằng một trích dẫn 'hoặc gấp
đôi "trên
hai bên. Ví dụ: 'Xin chào'
chuỗi con - Một tập hợp con của một giá trị chuỗi. Ví dụ: nếu thư rác là chuỗi 'Xin chào' ,
thì thư rác chuỗi con [1: 4] là 'ell' . Tương tự như một lát danh sách.
biểu tượng - Trong mật mã, các ký tự riêng lẻ được mã hóa.
cú pháp - Các quy tắc về cách mã được sắp xếp theo ngôn ngữ lập trình, giống như
ngữ pháp được tạo thành từ các quy tắc cho các câu tiếng Anh dễ hiểu.
lỗi cú pháp - Một lỗi xảy ra khi trình thông dịch Python không hiểu
mã vì mã không đầy đủ hoặc sai thứ tự. Một chương trình có lỗi cú pháp
sẽ không chạy
419
Bảng chú giải

Trang 434
chấm dứt - Khi một chương trình kết thúc. "Thoát" có nghĩa là điều tương tự.
theo dõi - Để theo dõi các dòng mã trong một chương trình theo thứ tự mà chúng sẽ
hành hình.
bảng chân lý - Bảng hiển thị mọi kết hợp có thể có của
tuple - Một kiểu dữ liệu chứa tương tự như một danh sách. Tuples là kiểu dữ liệu chuỗi bất biến,
có nghĩa là chúng không thể có giá trị được thêm hoặc xóa khỏi chúng. Ví dụ: (1, 2,
'mèo', 'xin chào') là một bộ gồm bốn giá trị.
loại - xem, loại dữ liệu
không có thứ tự - Trong các loại dữ liệu chứa, các loại dữ liệu không có thứ tự không có "đầu
tiên" hoặc "cuối cùng"
giá trị chứa bên trong chúng, chúng chỉ đơn giản chứa các giá trị. Từ điển là duy nhất
kiểu dữ liệu không có thứ tự trong Python. Danh sách, bộ dữ liệu và chuỗi được sắp xếp theo
kiểu dữ liệu. Xem thêm,
sự nối tiếp.
người dùng - Người sử dụng chương trình.
value - Một thể hiện cụ thể của một kiểu dữ liệu. 42 là một giá trị của kiểu số nguyên. 'Xin
chào' là
một giá trị của kiểu chuỗi.
biến - Một container có thể lưu trữ một giá trị. Danh sách biến chứa tham chiếu đến danh sách.
Câu lệnh vòng lặp while - Từ khóa while , theo sau là một điều kiện, kết thúc bằng:
nhân vật đại tràng. Các thời điểm tuyên bố bắt đầu của một trong khi vòng lặp.
Trục X - Trong các hệ tọa độ cartesian, trục tọa độ ngang (trái - phải).
Trục Y - Trong các hệ tọa độ cartesian, trục tọa độ dọc (lên xuống).
420

Trang 435

Giới thiệu về tác giả


Albert Sweigart (nhưng bạn có thể gọi anh ta là Al), là một nhà phát triển phần mềm ở San
Francisco,
California thích đi xe đạp, đọc sách, tình nguyện, bảo mật máy tính, cà phê ám ảnh
cửa hàng, và làm cho phần mềm hữu ích.
Anh ấy đến từ Houston, Texas. Cuối cùng anh ấy đã đặt Đại học Texas của mình tại Austin
bằng khoa học máy tính trong một khung. Anh ấy, tất nhiên, là một người vô thần. Anh ấy là một
người hướng nội thân thiện,
người mèo, và lo sợ rằng anh ta sẽ mất tế bào não theo thời gian. Anh cười lớn khi
xem sóc công viên, và mọi người nghĩ rằng anh ta là một người đơn giản.
"Invent with Python" là cuốn sách đầu tiên của anh ấy và sẽ không phải là cuốn sách cuối cùng
của anh ấy.
Trang web và blog của anh được đặt tại http://coffeeghost.net
421

Trang 436

You might also like