Dice Math

Tags: large, artistic, game, math

This math quiz program rolls two to six dice whose sides you must add up as quickly as possible. But this program operates as more than just automated flash cards; it draws the faces of the dice to random places on the screen. The ASCII-art aspect adds a fun twist while you practice arithmetic.

dice_math.py
  1
  2"""Dice Math, by Al Sweigart al@inventwithpython.com
  3A flash card addition game where you sum the total on random dice rolls.
  4View this code at https://nostarch.com/big-book-small-python-projects
  5Tags: large, artistic, game, math"""
  6
  7import random, time
  8
  9# Set up the constants:
 10DICE_WIDTH = 9
 11DICE_HEIGHT = 5
 12CANVAS_WIDTH = 79
 13CANVAS_HEIGHT = 24 - 3  # -3 for room to enter the sum at the bottom.
 14
 15# The duration is in seconds:
 16QUIZ_DURATION = 30  # (!) Try changing this to 10 or 60.
 17MIN_DICE = 2  # (!) Try changing this to 1 or 5.
 18MAX_DICE = 6  # (!) Try changing this to 14.
 19
 20# (!) Try changing these to different numbers:
 21REWARD = 4  # (!) Points awarded for correct answers.
 22PENALTY = 1  # (!) Points removed for incorrect answers.
 23# (!) Try setting PENALTY to a negative number to give points for
 24# wrong answers!
 25
 26# The program hangs if all of the dice can't fit on the screen:
 27assert MAX_DICE <= 14
 28
 29D1 = (['+-------+',
 30       '|       |',
 31       '|   O   |',
 32       '|       |',
 33       '+-------+'], 1)
 34
 35D2a = (['+-------+',
 36        '| O     |',
 37        '|       |',
 38        '|     O |',
 39        '+-------+'], 2)
 40
 41D2b = (['+-------+',
 42        '|     O |',
 43        '|       |',
 44        '| O     |',
 45        '+-------+'], 2)
 46
 47D3a = (['+-------+',
 48        '| O     |',
 49        '|   O   |',
 50        '|     O |',
 51        '+-------+'], 3)
 52
 53D3b = (['+-------+',
 54        '|     O |',
 55        '|   O   |',
 56        '| O     |',
 57        '+-------+'], 3)
 58
 59D4 = (['+-------+',
 60       '| O   O |',
 61       '|       |',
 62       '| O   O |',
 63       '+-------+'], 4)
 64
 65D5 = (['+-------+',
 66       '| O   O |',
 67       '|   O   |',
 68       '| O   O |',
 69       '+-------+'], 5)
 70
 71D6a = (['+-------+',
 72        '| O   O |',
 73        '| O   O |',
 74        '| O   O |',
 75        '+-------+'], 6)
 76
 77D6b = (['+-------+',
 78        '| O O O |',
 79        '|       |',
 80        '| O O O |',
 81        '+-------+'], 6)
 82
 83ALL_DICE = [D1, D2a, D2b, D3a, D3b, D4, D5, D6a, D6b]
 84
 85print('''Dice Math, by Al Sweigart al@inventwithpython.com
 86
 87Add up the sides of all the dice displayed on the screen. You have
 88{} seconds to answer as many as possible. You get {} points for each
 89correct answer and lose {} point for each incorrect answer.
 90'''.format(QUIZ_DURATION, REWARD, PENALTY))
 91input('Press Enter to begin...')
 92
 93# Keep track of how many answers were correct and incorrect:
 94correctAnswers = 0
 95incorrectAnswers = 0
 96startTime = time.time()
 97while time.time() < startTime + QUIZ_DURATION:  # Main game loop.
 98    # Come up with the dice to display:
 99    sumAnswer = 0
100    diceFaces = []
101    for i in range(random.randint(MIN_DICE, MAX_DICE)):
102        die = random.choice(ALL_DICE)
103        # die[0] contains the list of strings of the die face:
104        diceFaces.append(die[0])
105        # die[1] contains the integer number of pips on the face:
106        sumAnswer += die[1]
107
108    # Contains (x, y) tuples of the top-left corner of each die.
109    topLeftDiceCorners = []
110
111    # Figure out where dice should go:
112    for i in range(len(diceFaces)):
113        while True:
114            # Find a random place on the canvas to put the die:
115            left = random.randint(0, CANVAS_WIDTH  - 1 - DICE_WIDTH)
116            top  = random.randint(0, CANVAS_HEIGHT - 1 - DICE_HEIGHT)
117
118            # Get the x, y coordinates for all four corners:
119            #      left
120            #      v
121            #top > +-------+ ^
122            #      | O     | |
123            #      |   O   | DICE_HEIGHT (5)
124            #      |     O | |
125            #      +-------+ v
126            #      <------->
127            #      DICE_WIDTH (9)
128            topLeftX = left
129            topLeftY = top
130            topRightX = left + DICE_WIDTH
131            topRightY = top
132            bottomLeftX = left
133            bottomLeftY = top + DICE_HEIGHT
134            bottomRightX = left + DICE_WIDTH
135            bottomRightY = top + DICE_HEIGHT
136
137            # Check if this die overlaps with previous dice.
138            overlaps = False
139            for prevDieLeft, prevDieTop in topLeftDiceCorners:
140                prevDieRight = prevDieLeft + DICE_WIDTH
141                prevDieBottom = prevDieTop + DICE_HEIGHT
142                # Check each corner of this die to see if it is inside
143                # of the area the previous die:
144                for cornerX, cornerY in ((topLeftX, topLeftY),
145                                         (topRightX, topRightY),
146                                         (bottomLeftX, bottomLeftY),
147                                         (bottomRightX, bottomRightY)):
148                    if (prevDieLeft <= cornerX < prevDieRight
149                        and prevDieTop <= cornerY < prevDieBottom):
150                            overlaps = True
151            if not overlaps:
152                # It doesn't overlap, so we can put it here:
153                topLeftDiceCorners.append((left, top))
154                break
155
156    # Draw the dice on the canvas:
157
158    # Keys are (x, y) tuples of ints, values the character at that
159    # position on the canvas:
160    canvas = {}
161    # Loop over each die:
162    for i, (dieLeft, dieTop) in enumerate(topLeftDiceCorners):
163        # Loop over each character in the die's face:
164        dieFace = diceFaces[i]
165        for dx in range(DICE_WIDTH):
166            for dy in range(DICE_HEIGHT):
167                # Copy this character to the correct place on the canvas:
168                canvasX = dieLeft + dx
169                canvasY = dieTop + dy
170                # Note that in dieFace, a list of strings, the x and y
171                # are swapped:
172                canvas[(canvasX, canvasY)] = dieFace[dy][dx]
173
174    # Display the canvas on the screen:
175    for cy in range(CANVAS_HEIGHT):
176        for cx in range(CANVAS_WIDTH):
177            print(canvas.get((cx, cy), ' '), end='')
178        print()  # Print a newline.
179
180    # Let the player enter their answer:
181    response = input('Enter the sum: ').strip()
182    if response.isdecimal() and int(response) == sumAnswer:
183        correctAnswers += 1
184    else:
185        print('Incorrect, the answer is', sumAnswer)
186        time.sleep(2)
187        incorrectAnswers += 1
188
189# Display the final score:
190score = (correctAnswers * REWARD) - (incorrectAnswers * PENALTY)
191print('Correct:  ', correctAnswers)
192print('Incorrect:', incorrectAnswers)
193print('Score:    ', score)

https://inventwithpython.com/bigbookpython/project17.html