/ CircuitPython_Flying_Toasters / code.py
code.py
1 # SPDX-FileCopyrightText: 2019 Dave Astels for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 5 """ 6 Continuously scroll randomly generated After Dark style toasters. 7 Designed for an ItsyBitsy M4 Express and a 1.3" 240x240 TFT 8 9 Adafruit invests time and resources providing this open source code. 10 Please support Adafruit and open source hardware by purchasing 11 products from Adafruit! 12 13 Written by Dave Astels for Adafruit Industries 14 Copyright (c) 2019 Adafruit Industries 15 Licensed under the MIT license. 16 17 All text above must be included in any redistribution. 18 """ 19 20 import time 21 from random import seed, randint 22 import board 23 import displayio 24 from adafruit_st7789 import ST7789 25 import adafruit_imageload 26 27 28 # Sprite cell values 29 EMPTY = 0 30 CELL_1 = EMPTY + 1 31 CELL_2 = CELL_1 + 1 32 CELL_3 = CELL_2 + 1 33 CELL_4 = CELL_3 + 1 34 TOAST = CELL_4 + 1 35 36 NUMBER_OF_SPRITES = TOAST + 1 37 38 # Animation support 39 40 FIRST_CELL = CELL_1 41 LAST_CELL = CELL_4 42 NUMBER_OF_CELLS = (LAST_CELL - FIRST_CELL) + 1 43 44 # A boolean array corresponding to the sprites, True if it's part of the animation sequence. 45 ANIMATED = [FIRST_CELL <= _sprite <= LAST_CELL for _sprite in range(NUMBER_OF_SPRITES)] 46 47 # The chance (out of 10) that toast will enter 48 CHANCE_OF_NEW_TOAST = 2 49 50 # How many sprites to start with 51 INITIAL_NUMBER_OF_SPRITES = 4 52 53 seed(int(time.monotonic())) 54 55 56 def make_display(): 57 """Set up the display support. 58 Return the Display object. 59 """ 60 spi = board.SPI() 61 while not spi.try_lock(): 62 pass 63 spi.configure(baudrate=24000000) # Configure SPI for 24MHz 64 spi.unlock() 65 displayio.release_displays() 66 display_bus = displayio.FourWire(spi, command=board.D7, chip_select=board.D10, reset=board.D9) 67 68 return ST7789(display_bus, width=240, height=240, rowstart=80, auto_refresh=True) 69 70 71 def make_tilegrid(): 72 """Construct and return the tilegrid.""" 73 group = displayio.Group() 74 75 sprite_sheet, palette = adafruit_imageload.load("/spritesheet-2x.bmp", 76 bitmap=displayio.Bitmap, 77 palette=displayio.Palette) 78 grid = displayio.TileGrid(sprite_sheet, pixel_shader=palette, 79 width=5, height=5, 80 tile_height=64, tile_width=64, 81 x=0, y=-64, 82 default_tile=EMPTY) 83 group.append(grid) 84 display.show(group) 85 return grid 86 87 88 def random_cell(): 89 return randint(FIRST_CELL, LAST_CELL) 90 91 92 def evaluate_position(row, col): 93 """Return whether how long of a toaster is placeable at the given location. 94 :param row: the tile row (0-9) 95 :param col: the tile column (0-9) 96 """ 97 return tilegrid[col, row] == EMPTY 98 99 100 def seed_toasters(number_of_toasters): 101 """Create the initial toasters so it doesn't start empty""" 102 for _ in range(number_of_toasters): 103 while True: 104 row = randint(0, 4) 105 col = randint(0, 4) 106 if evaluate_position(row, col): 107 break 108 tilegrid[col, row] = random_cell() 109 110 111 def next_sprite(sprite): 112 if ANIMATED[sprite]: 113 return (((sprite - FIRST_CELL) + 1) % NUMBER_OF_CELLS) + FIRST_CELL 114 return sprite 115 116 117 def advance_animation(): 118 """Cycle through animation cells each time.""" 119 for tile_number in range(25): 120 tilegrid[tile_number] = next_sprite(tilegrid[tile_number]) 121 122 123 def slide_tiles(): 124 """Move the tilegrid one pixel to the bottom-left.""" 125 tilegrid.x -= 1 126 tilegrid.y += 1 127 128 129 def shift_tiles(): 130 """Move tiles one spot to the left, and reset the tilegrid's position""" 131 for row in range(4, 0, -1): 132 for col in range(4): 133 tilegrid[col, row] = tilegrid[col + 1, row - 1] 134 tilegrid[4, row] = EMPTY 135 for col in range(5): 136 tilegrid[col, 0] = EMPTY 137 tilegrid.x = 0 138 tilegrid.y = -64 139 140 141 def get_entry_row(): 142 while True: 143 row = randint(0, 4) 144 if tilegrid[4, row] == EMPTY and tilegrid[3, row] == EMPTY: 145 return row 146 147 148 def get_entry_column(): 149 while True: 150 col = randint(0, 3) 151 if tilegrid[col, 0] == EMPTY and tilegrid[col, 1] == EMPTY: 152 return col 153 154 155 def add_toaster_or_toast(): 156 """Maybe add a new toaster or toast on the right and/or top at a random open location""" 157 if randint(1, 10) <= CHANCE_OF_NEW_TOAST: 158 tile = TOAST 159 else: 160 tile = random_cell() 161 tilegrid[4, get_entry_row()] = tile 162 163 if randint(1, 10) <= CHANCE_OF_NEW_TOAST: 164 tile = TOAST 165 else: 166 tile = random_cell() 167 tilegrid[get_entry_column(), 0] = tile 168 169 170 display = make_display() 171 tilegrid = make_tilegrid() 172 seed_toasters(INITIAL_NUMBER_OF_SPRITES) 173 display.refresh() 174 175 while True: 176 for _ in range(64): 177 display.refresh(target_frames_per_second=80) 178 advance_animation() 179 slide_tiles() 180 shift_tiles() 181 add_toaster_or_toast() 182 display.refresh(target_frames_per_second=120)