|
| 1 | +""" |
| 2 | +This example uses the Open Trivia Database API to display multiple choice trivia questions. |
| 3 | +Tap the screen to start, then a question will appear and a 30 second timer will start. |
| 4 | +The first player to hit their button will get 10 seconds to answer. |
| 5 | +Hit button again to reveal answer. Tap screen to move to next question. |
| 6 | +This program assumes two buttons are attached to D3 and D4 on the Adafruit PyPortal. |
| 7 | +""" |
| 8 | + |
| 9 | +import time |
| 10 | +import random |
| 11 | +import board |
| 12 | +from adafruit_pyportal import PyPortal |
| 13 | +from digitalio import DigitalInOut, Direction, Pull |
| 14 | +from adafruit_display_text import label |
| 15 | +from adafruit_bitmap_font import bitmap_font |
| 16 | + |
| 17 | +# initialize harware |
| 18 | +led = DigitalInOut(board.L) # For debugging |
| 19 | +led.direction = Direction.OUTPUT |
| 20 | +button1 = DigitalInOut(board.D4) |
| 21 | +button2 = DigitalInOut(board.D3) |
| 22 | +button1.direction = Direction.INPUT |
| 23 | +button2.direction = Direction.INPUT |
| 24 | +button1.pull = Pull.UP |
| 25 | +button2.pull = Pull.UP |
| 26 | +display = board.DISPLAY |
| 27 | + |
| 28 | +# determine the current working directory |
| 29 | +# needed so we know where to find files |
| 30 | +cwd = ("/"+__file__).rsplit('/', 1)[0] |
| 31 | + |
| 32 | +# Set up where we'll be fetching data from |
| 33 | +DATA_SOURCE = "https://opentdb.com/api.php?amount=1&type=multiple" |
| 34 | +Q_LOCATION = ['results', 0, 'question'] |
| 35 | +WA_LOCATION1 = ['results', 0, 'incorrect_answers', 0] |
| 36 | +WA_LOCATION2 = ['results', 0, 'incorrect_answers', 1] |
| 37 | +WA_LOCATION3 = ['results', 0, 'incorrect_answers', 2] |
| 38 | +CA_LOCATION = ['results', 0, 'correct_answer'] |
| 39 | + |
| 40 | +# Text info |
| 41 | +trivia_font = bitmap_font.load_font("/fonts/Arial-ItalicMT-17.bdf") |
| 42 | +answerChoices = ("A","B","C","D") |
| 43 | +a1f_text = '' |
| 44 | +a2f_text = '' |
| 45 | +a3f_text = '' |
| 46 | +a4f_text = '' |
| 47 | +player1_text = "Player 1!" |
| 48 | +player2_text = "Player 2!" |
| 49 | +timesup_text = "TIME'S UP!" |
| 50 | +loading_text = "loading question..." |
| 51 | +tapagain_text = "Tap again for \n next question." |
| 52 | +# Text locations (screen is 320 x 240) |
| 53 | +loading_position = tapagain_position = (100,120) |
| 54 | +player_position = timesup_position = answerReveal_position = (120, 75) |
| 55 | +answerReveal_position = (25, 75) |
| 56 | +q_position = (25, 70) |
| 57 | +a1_position = (25, 135) |
| 58 | +a2_position = (25, 155) |
| 59 | +a3_position = (25, 175) |
| 60 | +a4_position = (25, 195) |
| 61 | +loading_color = q_color = 0x8080FF |
| 62 | +a_color = 0xFFFFFF |
| 63 | + |
| 64 | +# Clear screen of all elements but background |
| 65 | +def clear_splash(): |
| 66 | + for _ in range(len(pyportal.splash) - 1): |
| 67 | + pyportal.splash.pop() |
| 68 | + |
| 69 | +# A function to shuffle trivia questions |
| 70 | +def shuffle(aList): |
| 71 | + for i in range(len(aList)-1, 0, -1): |
| 72 | + # Pick a random index from 0 to i |
| 73 | + j = random.randint(0, i + 1) |
| 74 | + # Swap arr[i] with the element at random index |
| 75 | + aList[i], aList[j] = aList[j], aList[i] |
| 76 | + return aList |
| 77 | + |
| 78 | +#convert html codes to normal text |
| 79 | +def unescape(s): |
| 80 | + s = s.replace(""", "''") |
| 81 | + s = s.replace("'", "'") |
| 82 | + s = s.replace("&", "&") |
| 83 | + return s |
| 84 | + |
| 85 | +# A function to handle the timer and determine which player answers first |
| 86 | +def faceOff(timerLength): |
| 87 | + timerStart = time.monotonic() |
| 88 | + print(time.monotonic() - timerStart) |
| 89 | + while time.monotonic() - timerStart < timerLength: |
| 90 | + if button1.value: |
| 91 | + led.value = False # For debugging |
| 92 | + else: # If button 1 pressed, print player 1 on screen and exit function |
| 93 | + pyportal.splash.pop() # make room for player that wins |
| 94 | + player1_text_area = label.Label(trivia_font, text=player1_text, color=loading_color) |
| 95 | + player1_text_area.x = player_position[0] |
| 96 | + player1_text_area.y = player_position[1] |
| 97 | + pyportal.splash.append(player1_text_area) |
| 98 | + led.value = True # For debugging |
| 99 | + break |
| 100 | + if button2.value: |
| 101 | + led.value = False # For debugging |
| 102 | + else: # If button 2 pressed, print player 1 on screen and exit function |
| 103 | + led.value = True # For debugging |
| 104 | + pyportal.splash.pop() # make room for player that wins |
| 105 | + player2_text_area = label.Label(trivia_font, text=player2_text, color=loading_color) |
| 106 | + player2_text_area.x = player_position[0] |
| 107 | + player2_text_area.y = player_position[1] |
| 108 | + pyportal.splash.append(player2_text_area) # push 5 |
| 109 | + break |
| 110 | + if time.monotonic() - timerStart > (timerLength - 0.5): # Timer runs out |
| 111 | + pyportal.splash.pop() # make room for player that wins |
| 112 | + timesup_text_area = label.Label(trivia_font, text=timesup_text, color=loading_color) |
| 113 | + timesup_text_area.x = timesup_position[0] |
| 114 | + timesup_text_area.y = timesup_position[1] |
| 115 | + pyportal.splash.append(timesup_text_area) |
| 116 | + print(time.monotonic() - timerStart) |
| 117 | + time.sleep(0.05) # debounce delay |
| 118 | + |
| 119 | +#function to format trivia question content |
| 120 | +def format_trivia(): |
| 121 | + pyportal.splash.pop() |
| 122 | + pyportal.splash.append(loading_text_area) |
| 123 | + # Formatting with display text library |
| 124 | + q_text = unescape(value[0]) |
| 125 | + q_text_formatted = pyportal.wrap_nicely(q_text, 35) |
| 126 | + qf_text = '' |
| 127 | + for wrp in range (len(q_text_formatted)): |
| 128 | + qf_text = qf_text + q_text_formatted[wrp] +"\n" |
| 129 | + q_text = qf_text |
| 130 | + q_text_area = label.Label(trivia_font, text=q_text, |
| 131 | + color=q_color, line_spacing = 1) |
| 132 | + q_text_area.x = q_position[0] |
| 133 | + q_text_area.y = q_position[1] |
| 134 | + a1_text = unescape(value[1]) |
| 135 | + a1_text_area = label.Label(trivia_font, text="A. "+a1_text, |
| 136 | + color=a_color, line_spacing = 1.5) |
| 137 | + a1_text_area.x = a1_position[0] |
| 138 | + a1_text_area.y = a1_position[1] |
| 139 | + a2_text = unescape(value[2]) |
| 140 | + a2_text_area = label.Label(trivia_font, text="B. "+a2_text, |
| 141 | + color=a_color, line_spacing = 1.5) |
| 142 | + a2_text_area.x = a2_position[0] |
| 143 | + a2_text_area.y = a2_position[1] |
| 144 | + a3_text = unescape(value[3]) |
| 145 | + a3_text_area = label.Label(trivia_font, text="C. "+a3_text, |
| 146 | + color=a_color, line_spacing = 1.5) |
| 147 | + a3_text_area.x = a3_position[0] |
| 148 | + a3_text_area.y = a3_position[1] |
| 149 | + a4_text = unescape(value[4]) |
| 150 | + a4_text_area = label.Label(trivia_font, text="D. "+a4_text, |
| 151 | + color=a_color, line_spacing = 1.5) |
| 152 | + a4_text_area.x = a4_position[0] |
| 153 | + a4_text_area.y = a4_position[1] |
| 154 | + pyportal.splash.pop() # take off loading... |
| 155 | + pyportal.splash.append(a1_text_area) |
| 156 | + pyportal.splash.append(a2_text_area) |
| 157 | + pyportal.splash.append(a3_text_area) |
| 158 | + pyportal.splash.append(a4_text_area) |
| 159 | + # append question last so it can be removed more easily later |
| 160 | + pyportal.splash.append(q_text_area) |
| 161 | + |
| 162 | +# PyPortal constructor |
| 163 | +pyportal = PyPortal(url=DATA_SOURCE, |
| 164 | + status_neopixel=board.NEOPIXEL, |
| 165 | + default_bg=cwd+"/trivia_title.bmp") |
| 166 | + |
| 167 | +pyportal.preload_font() # speed things up by preloading font |
| 168 | + |
| 169 | +while True: |
| 170 | + # Load new question when screen is touched |
| 171 | + if pyportal.touchscreen.touch_point: |
| 172 | + clear_splash() # clear all besides background screen |
| 173 | + pyportal.set_background(cwd+"/trivia.bmp") |
| 174 | + loading_text_area = label.Label(trivia_font, text=loading_text, color=loading_color) |
| 175 | + loading_text_area.x = loading_position[0] |
| 176 | + loading_text_area.y = loading_position[1] |
| 177 | + pyportal.splash.append(loading_text_area) #loading... |
| 178 | + answerList = [WA_LOCATION1, WA_LOCATION2, WA_LOCATION3, CA_LOCATION] |
| 179 | + # For catching potential index error when shuffling question |
| 180 | + try: |
| 181 | + shuffle(answerList) # Shuffle answers |
| 182 | + except IndexError: |
| 183 | + print("Index Error") |
| 184 | + tapagain_text_area = label.Label(trivia_font, text=tapagain_text, color=loading_color) |
| 185 | + tapagain_text_area.x = tapagain_position[0] |
| 186 | + tapagain_text_area.y = tapagain_position[1] |
| 187 | + pyportal.splash.pop() # take off loading... |
| 188 | + pyportal.splash.append(tapagain_text_area) |
| 189 | + continue |
| 190 | + try: |
| 191 | + # set the JSON path here, now that answers are shuffled |
| 192 | + # pylint: disable=protected-access |
| 193 | + pyportal._json_path=(Q_LOCATION, |
| 194 | + answerList[0], |
| 195 | + answerList[1], |
| 196 | + answerList[2], |
| 197 | + answerList[3],) |
| 198 | + value = pyportal.fetch() |
| 199 | + print("Response is", value) |
| 200 | + format_trivia() |
| 201 | + except RuntimeError as e: |
| 202 | + print("Some error occured, retrying! -", e) |
| 203 | + faceOff(30) # 30 seconds with question |
| 204 | + time.sleep(2) # pause for 2 seconds to show which player tapped first |
| 205 | + faceOff(10) # 10 seconds to answer |
| 206 | + # Show the correct answer |
| 207 | + for ansr in range(len(answerList)): |
| 208 | + if answerList[ansr] is CA_LOCATION: |
| 209 | + print(answerChoices[ansr]) |
| 210 | + correctAnswerChoice = answerChoices[ansr] |
| 211 | + break |
| 212 | + answerReveal_text="Correct Answer: "+str(correctAnswerChoice)+"\n(Tap for next question.)" |
| 213 | + answerReveal_text_area = label.Label(trivia_font, text=answerReveal_text, |
| 214 | + color=loading_color) |
| 215 | + answerReveal_text_area.x = answerReveal_position[0] |
| 216 | + answerReveal_text_area.y = answerReveal_position[1] |
| 217 | + pyportal.splash.pop() # Make room for answer |
| 218 | + pyportal.splash.append(answerReveal_text_area) |
0 commit comments