# SPDX-FileCopyrightText: 2018 Dave Astels for Adafruit Industries # # SPDX-License-Identifier: MIT """ Dice roller for the NeoTrellisM4 Adafruit invests time and resources providing this open source code. Please support Adafruit and open source hardware by purchasing products from Adafruit! Written by Dave Astels for Adafruit Industries Copyright (c) 2018 Adafruit Industries Licensed under the MIT license. All text above must be included in any redistribution. """ # pylint: disable=global-statement import math import time import random import board import adafruit_trellism4 import adafruit_adxl34x import busio # Set up the trellis and accelerometer trellis = adafruit_trellism4.TrellisM4Express() i2c = busio.I2C(board.ACCELEROMETER_SCL, board.ACCELEROMETER_SDA) accelerometer = adafruit_adxl34x.ADXL345(i2c) # a 3-wide, 4-tall fonts for 0-9 # Each string is a row with a space being unlit, and anything else being lit. number_patterns = [ [" * ", "* *", "* *", "***"], # 0 [" * ", "** ", " * ", "***"], # 1 ["** ", " *", " * ", "***"], # 2 ["** ", " *", " **", "***"], # 3 ["* *", "* *", "***", " *"], # 4 ["***", "* ", " **", "***"], # 5 [" **", "* ", "***", "***"], # 6 ["***", " *", " * ", " * "], # 7 [" * ", "* *", "***", "***"], # 8 [" * " ,"* *", " **", " *"] # 9 ] sides_per_die = [4, 6, 8, 10, 12, 20] die_colors = [ (255, 0, 0), # d4 (0, 255, 0), # d6 (0, 0, 255), # d8 (255, 255, 0), # d10 (0, 255, 255), # d12 (255, 0, 255)] # d20 def display_digit(number, offset, color, force_zero): """Display a digit. number -- the number (0-9) to display offset -- the left-most column of the displayed digit color -- the RGB color to use to display the digit force_zero -- whether to leave a 0 blank (False) or display it """ bits = number_patterns[number] for row in range(4): for col in range(3): if bits[row][col] == " " or (number == 0 and not force_zero): trellis.pixels[col + offset, row] = (0, 0, 0) else: trellis.pixels[col + offset, row] = color def display_number(number, color): """Display a multi-digit number. If the number is > 99, the hundreds digit (1-4) is indicated on the far left column: 100 is the top pixel, 400 is the bottom. number -- the number to display color -- the RGB color to use to display the digit force_zeros -- whether to leave a 0 in the tens place blank (False) or display it """ if number >= 500 or number < 0: return False display_digit(number % 10, 5, color, True) display_digit((number // 10) % 10, 1, color, number >= 100) hundreds = number // 100 for h in range(4): if h + 1 == hundreds: trellis.pixels[0, h] = (255, 255, 255) else: trellis.pixels[0, h] = (0, 0, 0) return True def animate_to(number, color): """Perform an animation (displaying random numbers) before displaying the requested number. number -- the number to eventually display color -- the color to use (indicates the type of dice used) """ for _ in range(10): trellis.pixels.fill((0, 0, 0)) display_number(random.randint(10, 99), color) time.sleep(0.1) trellis.pixels.fill((0, 0, 0)) display_number(number, color) def roll(number, sides): """Generate a random dice roll. Returns the total of the roll. number -- the number of dice to roll sides -- the number of side on dice to roll (4, 6, 8, 10, 12, 20) """ total = 0 for _ in range(number): total += random.randint(1, sides + 1) return total previous_reading = [None, None, None] bound = 4.0 def shaken(): """Detect when the Trellis is shaken. See http://www.profoundlogic.com/docs/display/PUI/Accelerometer+Test+for+Shaking TL;DR one or more axis experiences a significant (set by bound) change very quickly Returns whether a shake was detected. """ global previous_reading result = False x, y, z = accelerometer.acceleration if previous_reading[0] is not None: result = (math.fabs(previous_reading[0] - x) > bound and math.fabs(previous_reading[1] - y) > bound and math.fabs(previous_reading[2] - z) > bound) previous_reading = (x, y, z) return result selected_count = -1 selected_die = -1 while True: trellis.pixels.fill((0, 0, 0)) previous_reading = accelerometer.acceleration while not shaken(): # update selected count/die if a respective selection has been made pressed = trellis.pressed_keys # Get the pressed buttons count_selector = [key for key in pressed if key[1] == 0] # first row presses die_selector = [key for key in pressed if key[1] == 1] # second row presses if len(count_selector) > 0: selected_count = count_selector[0][0] + 1 if len(die_selector) > 0 and die_selector[0][0] < 6: # only 6 types of dice selected_die = die_selector[0][0] # update the pixels to reflect the selections for i in range(8): if i < selected_count: # Display a bar for the count trellis.pixels[i, 0] = (128, 64, 16) else: trellis.pixels[i, 0] = (0, 0, 0) if i == selected_die: # Just the selected dice, in the appropriate color trellis.pixels[i, 1] = die_colors[selected_die] else: trellis.pixels[i, 1] = (0, 0, 0) # Only do the roll if both count and die have been selected if (selected_count > -1) and (selected_die > -1): animate_to(roll(selected_count, sides_per_die[selected_die]), die_colors[selected_die]) timeout = time.monotonic() + 5.0 while len(trellis.pressed_keys) == 0 and time.monotonic() < timeout: pass