这个数学测验程序掷出两到六个骰子,你必须尽快把它们的边加起来。但是这个程序不仅仅是自动的闪存卡;它将骰子的正面画到屏幕上随机的地方。当你练习算术时,ASCII 艺术画方面增加了一个有趣的转折。
当您运行dicemath.py时,输出如下:
Dice Math, by Al Sweigart email@protected
Add up the sides of all the dice displayed on the screen. You have
30 seconds to answer as many as possible. You get 4 points for each
correct answer and lose 1 point for each incorrect answer.
Press Enter to begin...
+-------+
| O O |
| O |
| O O |
+-------+
+-------+
+-------+ | O O | +-------+
| O | | | | O |
| | | O O | | |
| O | +-------+ | O |
+-------+ +-------+
Enter the sum: 13
`--snip--`屏幕上的骰子由存储在canvas变量中的字典表示。在 Python 中,元组类似于列表,但是它们的内容不能改变。该字典的关键字是标记骰子左上角位置的(x, y)元组,而值是ALL_DICE中的“骰子元组”之一。您可以在第 28 到 80 行中看到,每个骰子元组包含一个字符串列表,它以图形方式表示一个可能的骰子面,以及骰子面上有多少点数的整数。该程序使用这些信息来显示骰子并计算它们的总和。
第 174 到 177 行将canvas字典中的数据呈现在屏幕上,其方式类似于项目 13“康威的生命游戏”在屏幕上呈现单元格的方式。
"""Dice Math, by Al Sweigart email@protected
A flash card addition game where you sum the total on random dice rolls.
View this code at https://nostarch.com/big-book-small-python-projects
Tags: large, artistic, game, math"""
import random, time
# Set up the constants:
DICE_WIDTH = 9
DICE_HEIGHT = 5
CANVAS_WIDTH = 79
CANVAS_HEIGHT = 24 - 3 # -3 for room to enter the sum at the bottom.
# The duration is in seconds:
QUIZ_DURATION = 30 # (!) Try changing this to 10 or 60.
MIN_DICE = 2 # (!) Try changing this to 1 or 5.
MAX_DICE = 6 # (!) Try changing this to 14.
# (!) Try changing these to different numbers:
REWARD = 4 # (!) Points awarded for correct answers.
PENALTY = 1 # (!) Points removed for incorrect answers.
# (!) Try setting PENALTY to a negative number to give points for
# wrong answers!
# The program hangs if all of the dice can't fit on the screen:
assert MAX_DICE <= 14
D1 = (['+-------+',
'| |',
'| O |',
'| |',
'+-------+'], 1)
D2a = (['+-------+',
'| O |',
'| |',
'| O |',
'+-------+'], 2)
D2b = (['+-------+',
'| O |',
'| |',
'| O |',
'+-------+'], 2)
D3a = (['+-------+',
'| O |',
'| O |',
'| O |',
'+-------+'], 3)
D3b = (['+-------+',
'| O |',
'| O |',
'| O |',
'+-------+'], 3)
D4 = (['+-------+',
'| O O |',
'| |',
'| O O |',
'+-------+'], 4)
D5 = (['+-------+',
'| O O |',
'| O |',
'| O O |',
'+-------+'], 5)
D6a = (['+-------+',
'| O O |',
'| O O |',
'| O O |',
'+-------+'], 6)
D6b = (['+-------+',
'| O O O |',
'| |',
'| O O O |',
'+-------+'], 6)
ALL_DICE = [D1, D2a, D2b, D3a, D3b, D4, D5, D6a, D6b]
print('''Dice Math, by Al Sweigart email@protected
Add up the sides of all the dice displayed on the screen. You have
{} seconds to answer as many as possible. You get {} points for each
correct answer and lose {} point for each incorrect answer.
'''.format(QUIZ_DURATION, REWARD, PENALTY))
input('Press Enter to begin...')
# Keep track of how many answers were correct and incorrect:
correctAnswers = 0
incorrectAnswers = 0
startTime = time.time()
while time.time() < startTime + QUIZ_DURATION: # Main game loop.
# Come up with the dice to display:
sumAnswer = 0
diceFaces = []
for i in range(random.randint(MIN_DICE, MAX_DICE)):
die = random.choice(ALL_DICE)
# die[0] contains the list of strings of the die face:
diceFaces.append(die[0])
# die[1] contains the integer number of pips on the face:
sumAnswer += die[1]
# Contains (x, y) tuples of the top-left corner of each die.
topLeftDiceCorners = []
# Figure out where dice should go:
for i in range(len(diceFaces)):
while True:
# Find a random place on the canvas to put the die:
left = random.randint(0, CANVAS_WIDTH - 1 - DICE_WIDTH)
top = random.randint(0, CANVAS_HEIGHT - 1 - DICE_HEIGHT)
# Get the x, y coordinates for all four corners:
# left
# v
#top > +-------+ ^
# | O | |
# | O | DICE_HEIGHT (5)
# | O | |
# +-------+ v
# <------->
# DICE_WIDTH (9)
topLeftX = left
topLeftY = top
topRightX = left + DICE_WIDTH
topRightY = top
bottomLeftX = left
bottomLeftY = top + DICE_HEIGHT
bottomRightX = left + DICE_WIDTH
bottomRightY = top + DICE_HEIGHT
# Check if this die overlaps with previous dice.
overlaps = False
for prevDieLeft, prevDieTop in topLeftDiceCorners:
prevDieRight = prevDieLeft + DICE_WIDTH
prevDieBottom = prevDieTop + DICE_HEIGHT
# Check each corner of this die to see if it is inside
# of the area the previous die:
for cornerX, cornerY in ((topLeftX, topLeftY),
(topRightX, topRightY),
(bottomLeftX, bottomLeftY),
(bottomRightX, bottomRightY)):
if (prevDieLeft <= cornerX < prevDieRight
and prevDieTop <= cornerY < prevDieBottom):
overlaps = True
if not overlaps:
# It doesn't overlap, so we can put it here:
topLeftDiceCorners.append((left, top))
break
# Draw the dice on the canvas:
# Keys are (x, y) tuples of ints, values the character at that
# position on the canvas:
canvas = {}
# Loop over each die:
for i, (dieLeft, dieTop) in enumerate(topLeftDiceCorners):
# Loop over each character in the die's face:
dieFace = diceFaces[i]
for dx in range(DICE_WIDTH):
for dy in range(DICE_HEIGHT):
# Copy this character to the correct place on the canvas:
canvasX = dieLeft + dx
canvasY = dieTop + dy
# Note that in dieFace, a list of strings, the x and y
# are swapped:
canvas[(canvasX, canvasY)] = dieFace[dy][dx]
# Display the canvas on the screen:
for cy in range(CANVAS_HEIGHT):
for cx in range(CANVAS_WIDTH):
print(canvas.get((cx, cy), ' '), end='')
print() # Print a newline.
# Let the player enter their answer:
response = input('Enter the sum: ').strip()
if response.isdecimal() and int(response) == sumAnswer:
correctAnswers += 1
else:
print('Incorrect, the answer is', sumAnswer)
time.sleep(2)
incorrectAnswers += 1
# Display the final score:
score = (correctAnswers * REWARD) - (incorrectAnswers * PENALTY)
print('Correct: ', correctAnswers)
print('Incorrect:', incorrectAnswers)
print('Score: ', score) 在输入源代码并运行几次之后,尝试对其进行实验性的修改。标有(!)的注释对你可以做的小改变有建议。你也可以自己想办法做到以下几点:
- 重新设计 ASCII 艺术画骰子面。
- 添加七点、八点或九点的骰子点数。
试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。
- 如果把 82 行改成
ALL_DICE = [D1]会怎么样? - 如果把 176 行的
get((cx, cy), ' ')改成get((cx, cy), '.')会怎么样? - 如果把 182 行的
correctAnswers += 1改成correctAnswers += 0会怎么样? - 如果删除或注释掉第 93 行的
correctAnswers = 0,会得到什么错误信息?
