code.py
  1  # SPDX-FileCopyrightText: 2019 Dave Astels for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  """
  6  Continuously scroll randomly generated Mario style clouds.
  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  LEFT = 1
 31  MIDDLE = 2
 32  RIGHT = 3
 33  
 34  # These constants determine what happens when tiles are shifted.
 35  # if randint(1, 10) > the value, the thing happens
 36  
 37  # The chance a new cloud will enter
 38  CHANCE_OF_NEW_CLOUD = 4
 39  
 40  # The chance an existing cloud gets extended
 41  CHANCE_OF_EXTENDING_A_CLOUD = 5
 42  
 43  seed(int(time.monotonic()))
 44  
 45  
 46  def make_display():
 47      """Set up the display support.
 48      Return the Display object.
 49      """
 50      spi = board.SPI()
 51      while not spi.try_lock():
 52          pass
 53      spi.configure(baudrate=24000000)  # Configure SPI for 24MHz
 54      spi.unlock()
 55  
 56      displayio.release_displays()
 57      display_bus = displayio.FourWire(spi, command=board.D7, chip_select=board.D10, reset=board.D9)
 58  
 59      return ST7789(display_bus, width=240, height=240, rowstart=80, auto_refresh=True)
 60  
 61  
 62  def make_tilegrid():
 63      """Construct and return the tilegrid."""
 64      group = displayio.Group()
 65  
 66      sprite_sheet, palette = adafruit_imageload.load("/tilesheet-2x.bmp",
 67                                                      bitmap=displayio.Bitmap,
 68                                                      palette=displayio.Palette)
 69      grid = displayio.TileGrid(sprite_sheet, pixel_shader=palette,
 70                                width=9, height=5,
 71                                tile_height=48, tile_width=32,
 72                                default_tile=EMPTY)
 73      group.append(grid)
 74      display.show(group)
 75      return grid
 76  
 77  
 78  def evaluate_position(row, col):
 79      """Return how long of a cloud is placeable at the given location.
 80      :param row: the tile row (0-4)
 81      :param col: the tile column (0-8)
 82      """
 83      if tilegrid[col, row] != EMPTY or tilegrid[col + 1, row] != EMPTY:
 84          return 0
 85      end_col = col + 1
 86      while end_col < 9 and tilegrid[end_col, row] == EMPTY:
 87          end_col += 1
 88      return min([4, end_col - col])
 89  
 90  
 91  def seed_clouds(number_of_clouds):
 92      """Create the initial clouds so it doesn't start empty"""
 93      for _ in range(number_of_clouds):
 94          while True:
 95              row = randint(0, 4)
 96              col = randint(0, 7)
 97              cloud_length = evaluate_position(row, col)
 98              if cloud_length > 0:
 99                  break
100          l = randint(1, cloud_length)
101          tilegrid[col, row] = LEFT
102          for _ in range(l - 2):
103              col += 1
104              tilegrid[col, row] = MIDDLE
105          tilegrid[col + 1, row] = RIGHT
106  
107  
108  def slide_tiles():
109      """Move the tilegrid to the left, one pixel at a time, a full time width"""
110      for _ in range(32):
111          tilegrid.x -= 1
112          display.refresh(target_frames_per_second=60)
113  
114  
115  def shift_tiles():
116      """Move tiles one spot to the left, and reset the tilegrid's position"""
117      for row in range(5):
118          for col in range(8):
119              tilegrid[col, row] = tilegrid[col + 1, row]
120          tilegrid[8, row] = EMPTY
121      tilegrid.x = 0
122  
123  
124  def extend_clouds():
125      """Extend any clouds on the right edge, either finishing them with a right
126      end or continuing them with a middle piece
127      """
128      for row in range(5):
129          if tilegrid[7, row] == LEFT or tilegrid[7, row] == MIDDLE:
130              if randint(1, 10) > CHANCE_OF_EXTENDING_A_CLOUD:
131                  tilegrid[8, row] = MIDDLE
132              else:
133                  tilegrid[8, row] = RIGHT
134  
135  
136  def add_cloud():
137      """Maybe add a new cloud on the right at a random open row"""
138      if randint(1, 10) > CHANCE_OF_NEW_CLOUD:
139          count = 0
140          while True:
141              count += 1
142              if count == 50:
143                  return
144              row = randint(0, 4)
145              if tilegrid[7, row] == EMPTY and tilegrid[8, row] == EMPTY:
146                  break
147          tilegrid[8, row] = LEFT
148  
149  
150  display = make_display()
151  tilegrid = make_tilegrid()
152  seed_clouds(5)
153  
154  while True:
155      slide_tiles()
156      shift_tiles()
157      extend_clouds()
158      add_cloud()