Skip to content

Latest commit

 

History

History
250 lines (205 loc) · 8.81 KB

File metadata and controls

250 lines (205 loc) · 8.81 KB

#17 骰子数学

原文:http://inventwithpython.com/bigbookpython/project17.html

这个数学测验程序掷出两到六个骰子,你必须尽快把它们的边加起来。但是这个程序不仅仅是自动的闪存卡;它将骰子的正面画到屏幕上随机的地方。当你练习算术时,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 艺术画骰子面。
  • 添加七点、八点或九点的骰子点数。

探索程序

试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。

  1. 如果把 82 行改成ALL_DICE = [D1]会怎么样?
  2. 如果把 176 行的get((cx, cy), ' ')改成get((cx, cy), '.')会怎么样?
  3. 如果把 182 行的correctAnswers += 1改成correctAnswers += 0会怎么样?
  4. 如果删除或注释掉第 93 行的correctAnswers = 0,会得到什么错误信息?