Game nào cũng đầy phép toán: trừ máu, cộng vàng, tính sát thương chí mạng, chia phần thưởng cho cả đội. Bài này đi qua các phép toán trên int và float trong Python, và một cái bẫy về số thực mà ai cũng dính ít nhất một lần.
Các phép toán cơ bản
| Toán tử | Ý nghĩa | Ví dụ | Kết quả |
|---|---|---|---|
+ | cộng | 100 + 25 | 125 |
- | trừ | 100 - 30 | 70 |
* | nhân | 12 * 3 | 36 |
/ | chia | 10 / 4 | 2.5 |
// | chia lấy phần nguyên | 17 // 5 | 3 |
% | chia lấy dư | 17 % 5 | 2 |
** | lũy thừa | 2 ** 10 | 1024 |
base_damage = 40
crit_multiplier = 2
damage = base_damage * crit_multiplier
print(damage) # 80
Thứ tự tính giống toán ở trường: ** trước, rồi *, /, //, %, cuối cùng mới +, -. Dùng ngoặc khi muốn tính khác đi.
print(10 + 5 * 2) # 20
print((10 + 5) * 2) # 30
Phép chia / luôn ra float
Người học C# hay bất ngờ ở chỗ này. Trong C#, 10 / 2 ra 5 kiểu int. Trong Python, / luôn trả về float:
print(10 / 2) # 5.0
print(7 / 2) # 3.5
Khi cần kết quả là số nguyên, ví dụ số món đồ mua được, dùng //.
Chia lấy nguyên // và chia lấy dư %
Có 17 đồng vàng, mỗi bình máu giá 5 đồng. Mua được mấy bình, còn thừa bao nhiêu?
gold = 17
potion_price = 5
potions = gold // potion_price
change = gold % potion_price
print(potions) # 3
print(change) # 2
% còn hay dùng để kiểm tra chia hết. Ví dụ cứ mỗi 5 level thì gặp boss:
level = 15
print(level % 5 == 0) # True, level này có boss
Hay đổi giây ra phút và giây để hiện thời gian chơi:
play_time = 257
minutes = play_time // 60
seconds = play_time % 60
print(minutes, "phút", seconds, "giây") # 4 phút 17 giây
Lỗi hay gặp: chia cho 0.
hp / 0hayhp // 0hayhp % 0đều báoZeroDivisionError: division by zero. Hay gặp khi tính trung bình điểm mà danh sách người chơi đang rỗng. Kiểm tra số chia khác 0 trước khi chia.
Lũy thừa **
Nhiều game tính exp cần để lên cấp theo lũy thừa:
level = 5
exp_needed = 100 * 2 ** (level - 1)
print(exp_needed) # 1600
** với số mũ 0.5 là căn bậc hai: 16 ** 0.5 ra 4.0.
Lưu ý: ^ trong Python không phải lũy thừa. 2 ^ 3 ra 1, vì ^ là phép XOR trên bit. Muốn 2 mũ 3 thì viết 2 ** 3.
Làm tròn với round
round(số, số_chữ_thập_phân) làm tròn. Bỏ tham số thứ hai thì làm tròn thành int.
crit_rate = 0.156789
print(round(crit_rate, 2)) # 0.16
print(round(7.8)) # 8
Có một điểm lạ: round làm tròn số ở chính giữa về số chẵn gần nhất.
print(round(2.5)) # 2
print(round(3.5)) # 4
Cách này gọi là làm tròn kiểu ngân hàng, giúp tổng nhiều lần làm tròn không bị lệch về một phía. Nếu game cần luôn làm tròn lên hay xuống, dùng module math:
import math
print(math.ceil(7.1)) # 8, luôn làm tròn lên
print(math.floor(7.9)) # 7, luôn làm tròn xuống
Vì sao 0.1 + 0.2 không bằng 0.3
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False
Không phải Python tính sai. Máy tính lưu float ở dạng nhị phân, và 0.1 không biểu diễn chính xác được trong hệ nhị phân, giống như 1/3 không viết hết được trong hệ thập phân. C#, JavaScript cũng y hệt.
Hậu quả trong game: so sánh float bằng == có thể sai. Đừng làm vậy. Dùng math.isclose:
import math
speed = 0.1 + 0.2
print(math.isclose(speed, 0.3)) # True
Với tiền tệ trong game, cách an toàn nhất là lưu bằng int. Lưu 150 xu thay vì 1.5 vàng.
Bài tập
Nhân vật có 1000 vàng. Kiếm giá 275. Tính số kiếm mua được, số vàng thừa, và nếu mỗi kiếm được giảm 10% thì giá mới là bao nhiêu (làm tròn 1 chữ số).
Xem đáp án
gold = 1000
sword_price = 275
swords = gold // sword_price
left_gold = gold % sword_price
sale_price = round(sword_price * 0.9, 1)
print(swords) # 3
print(left_gold) # 175
print(sale_price) # 247.5