text: """
  Perlin Noise 1
  
  If Python and Arcade are installed, this example can be run from the command line with:
  python -m arcade.examples.perlin_noise_1
  
  TODO: This code doesn't work properly, and isn't currently listed in the examples.
  """
  import arcade
  import numpy as np
  from PIL import Image
  
  # Set how many rows and columns we will have
  ROW_COUNT = 30
  COLUMN_COUNT = 30
  
  # This sets the WIDTH and HEIGHT of each grid location
  WIDTH = 10
  HEIGHT = 10
  
  # This sets the margin between each cell
  # and on the edges of the screen.
  MARGIN = 2
  
  # Do the math to figure out our screen dimensions
  SCREEN_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
  SCREEN_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
  SCREEN_TITLE = "Perlin Noise 1 Example"
  
  
  # Perlin noise generator from:
  # https://stackoverflow.com/questions/42147776/producing-2d-perlin-noise-with-numpy
  
  def perlin(x, y, seed=0):
      # permutation table
      np.random.seed(seed)
      p = np.arange(256, dtype=int)
      np.random.shuffle(p)
      p = np.stack([p, p]).flatten()
      # coordinates of the top-left
      xi = x.astype(int)
      yi = y.astype(int)
      # internal coordinates
      xf = x - xi
      yf = y - yi
      # fade factors
      u = fade(xf)
      v = fade(yf)
      # noise components
      n00 = gradient(p[p[xi] + yi], xf, yf)
      n01 = gradient(p[p[xi] + yi + 1], xf, yf - 1)
      n11 = gradient(p[p[xi + 1] + yi + 1], xf - 1, yf - 1)
      n10 = gradient(p[p[xi + 1] + yi], xf - 1, yf)
      # combine noises
      x1 = lerp(n00, n10, u)
      x2 = lerp(n01, n11, u)  # FIX1: I was using n10 instead of n01
      return lerp(x1, x2, v)  # FIX2: I also had to reverse x1 and x2 here
  
  
  def lerp(a, b, x):
      """linear interpolation"""
      return a + x * (b - a)
  
  
  def fade(t):
      """6t^5 - 15t^4 + 10t^3"""
      return 6 * t ** 5 - 15 * t ** 4 + 10 * t ** 3
  
  
  def gradient(h, x, y):
      """grad converts h to the right gradient vector and return the dot product with (x,y)"""
      vectors = np.array([[0, 1], [0, -1], [1, 0], [-1, 0]])
      g = vectors[h % 4]
      return g[:, :, 0] * x + g[:, :, 1] * y
  
  
  class MyGame(arcade.Window):
      """
      Main application class.
      """
  
      def __init__(self, width, height, title):
          """
          Set up the application.
          """
          super().__init__(width, height, title)
  
          self.shape_list = None
  
          arcade.set_background_color(arcade.color.BLACK)
  
          self.grid = None
          self.recreate_grid()
  
      def recreate_grid(self):
          lin = np.linspace(0, 5, ROW_COUNT, endpoint=False)
          y, x = np.meshgrid(lin, lin)
          self.grid = (perlin(x, y, seed=0))
  
          self.grid *= 255
          self.grid += 128
  
          # for row in range(ROW_COUNT):
          #     for column in range(COLUMN_COUNT):
          #         print(f"{self.grid[row][column]:5.2f} ", end="")
          #     print()
  
          self.shape_list = arcade.ShapeElementList()
          for row in range(ROW_COUNT):
              for column in range(COLUMN_COUNT):
                  color = self.grid[row][column], 0, 0
  
                  x = (MARGIN + WIDTH) * column + MARGIN + WIDTH // 2
                  y = (MARGIN + HEIGHT) * row + MARGIN + HEIGHT // 2
  
                  current_rect = arcade.create_rectangle_filled(x, y, WIDTH, HEIGHT, color)
                  self.shape_list.append(current_rect)
  
          im = Image.fromarray(np.uint8(self.grid), "L")
          im.save("test.png")
  
      def on_draw(self):
          """
          Render the screen.
          """
  
          # This command has to happen before we start drawing
          arcade.start_render()
  
          self.shape_list.draw()
  
      def on_mouse_press(self, x, y, button, modifiers):
          """
          Called when the user presses a mouse button.
          """
  
          # Change the x/y screen coordinates to grid coordinates
          column = x // (WIDTH + MARGIN)
          row = y // (HEIGHT + MARGIN)
  
          print(f"Click coordinates: ({x}, {y}). Grid coordinates: ({row}, {column})")
  
          # Make sure we are on-grid. It is possible to click in the upper right
          # corner in the margin and go to a grid location that doesn't exist
          if row < ROW_COUNT and column < COLUMN_COUNT:
  
              # Flip the location between 1 and 0.
              if self.grid[row][column] == 0:
                  self.grid[row][column] = 1
              else:
                  self.grid[row][column] = 0
  
          self.recreate_grid()
  
  
  def main():
      MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
      arcade.run()
  
  
  if __name__ == "__main__":
      main()
  