Skip to main content
added 896 characters in body
Source Link
André
  • 2k
  • 14
  • 28

The creation is based on a game of life on a hexagonal grid. Instead of birth and death, the neighbor count determines whether to freeze or thaw the current cell. This way symmetric and hexagonal structures are created that for many seeds resemble natural snowflakes. The random seed determines the number of iterations, the freezing rules, and the ASCII symbol (out of a small list of symmetric characters) to use for visualization.

The grid is composed to hold a hexagonal grid, thus every other row (row % 2) has to be shifted to the left and right, respectively. This is especially necessary when counting the number of frozen neighbors in get_frozen_neighbors(), where the col_offset picks the correct neighbors for each cell. TheSimilarly, upon printing the grid in grid2ascii(), we have to add a space in front of even rows, in order to shift them to the correct position.

The actual growing process starts with an initial frozen cell (set to 1) in the center of a large enough grid (initialized unfrozen, i.e. with zeros), which then updates in every iteration via update_grid(). To not saw off the branch we are sitting on, we have to work with a copy of the grid, thus updated_grid will be populated according to the rules and finally returned. In each iteration we count the number of frozen neighbors, check whether to freeze or unfreeze the current cell based on the freeze-rule and repeat iterating the grid.

# This script generates a snowflake using a hexagonal grid and cell rules for freezing/thawing cells.
import sys
import numpy as np

ROWS, COLS = 30, 30  # Size of the grid

def get_frozen_neighbors(grid, row, col):
    # Return -1 for border cells so that they are not processed by any rules
    if row <= 0 or row >= grid.shape[0] - 1 or col <= 0 or col >= grid.shape[1] - 1:
        return -1

    col_offset = 1 if ((row % 2) == 0) else -1  # Adjust column offset for hexagonal grid

    return (
        grid[row - 1, col]
        + grid[row - 1, col + col_offset]
        + grid[row, col - 1]
        + grid[row, col + 1]
        + grid[row + 1, col]
        + grid[row + 1, col + col_offset]
    )


def update_grid(grid, freeze):
    updated_grid = grid.copy()
    for row in range(grid.shape[0]):
        for col in range(grid.shape[1]):
            if get_frozen_neighbors(grid, row, col) in freeze:
                updated_grid[row, col] = 1
            else:
                updated_grid[row, col] = 0

    return updated_grid


def grid2ascii(grid, ascii_symbol):
    for row_idx, row in enumerate(grid):
        if np.all(row == 0):
            continue  # Skip fully empty rows
        line = ''
        if row_idx % 2 == 0:
            line += ' '  # Offset even rows for hexagonal appearance
        line += ' '.join((ascii_symbol if cell == 1 else ' ') for cell in row)
        print(line)
    print("\n")


if __name__ == "__main__":
    if len(sys.argv) > 1:
        seed = int(sys.argv[1])
    else:
        seed = 40

    np.random.seed(seed)

    # Select symbol and number of iterations based on random seed
    ascii_symbol = np.random.choice(['+', 'X', '*', '#'])
    iterations   = np.random.randint(5, 15)

    # Randomly generate rulesetfreezing rule based on the seed
    freeze = [1]  # Has to be included, otherwise, we would never start growing
    for i in range(2, 7):
        if np.random.choice([True, False]):
            freeze.append(i)

    grid = np.zeros((ROWS, COLS), dtype=int)

    # Freeze the center cell (We have to start somewhere...)
    center_row, center_col       = ROWS // 2, COLS // 2
    grid[center_row, center_col] = 1

    for _ in range(iterations):
        grid = update_grid(grid, freeze)

    # Finally, print the grid using the selected ASCII symbol
    grid2ascii(grid, ascii_symbol)

The creation is based on a game of life on a hexagonal grid. Instead of birth and death, the neighbor count determines whether to freeze or thaw the current cell. This way symmetric and hexagonal structures are created that for many seeds resemble natural snowflakes. The random seed determines the number of iterations, the freezing rules, and the ASCII symbol (out of a small list of symmetric characters) to use for visualization. The process starts with an initial frozen cell (set to 1) in the center of a large enough grid (initialized unfrozen, i.e. with zeros), which then updates in every iteration.

# This script generates a snowflake using a hexagonal grid and cell rules for freezing/thawing cells.
import sys
import numpy as np

ROWS, COLS = 30, 30  # Size of the grid

def get_frozen_neighbors(grid, row, col):
    # Return -1 for border cells so that they are not processed by any rules
    if row <= 0 or row >= grid.shape[0] - 1 or col <= 0 or col >= grid.shape[1] - 1:
        return -1

    col_offset = 1 if ((row % 2) == 0) else -1  # Adjust column offset for hexagonal grid

    return (
        grid[row - 1, col]
        + grid[row - 1, col + col_offset]
        + grid[row, col - 1]
        + grid[row, col + 1]
        + grid[row + 1, col]
        + grid[row + 1, col + col_offset]
    )


def update_grid(grid, freeze):
    updated_grid = grid.copy()
    for row in range(grid.shape[0]):
        for col in range(grid.shape[1]):
            if get_frozen_neighbors(grid, row, col) in freeze:
                updated_grid[row, col] = 1
            else:
                updated_grid[row, col] = 0

    return updated_grid


def grid2ascii(grid, ascii_symbol):
    for row_idx, row in enumerate(grid):
        if np.all(row == 0):
            continue  # Skip fully empty rows
        line = ''
        if row_idx % 2 == 0:
            line += ' '  # Offset even rows for hexagonal appearance
        line += ' '.join((ascii_symbol if cell == 1 else ' ') for cell in row)
        print(line)
    print("\n")


if __name__ == "__main__":
    if len(sys.argv) > 1:
        seed = int(sys.argv[1])
    else:
        seed = 40

    np.random.seed(seed)

    # Select symbol and number of iterations based on random seed
    ascii_symbol = np.random.choice(['+', 'X', '*', '#'])
    iterations   = np.random.randint(5, 15)

    # Randomly generate ruleset based on the seed
    freeze = [1]
    for i in range(2, 7):
        if np.random.choice([True, False]):
            freeze.append(i)

    grid = np.zeros((ROWS, COLS), dtype=int)

    # Freeze the center cell (We have to start somewhere...)
    center_row, center_col       = ROWS // 2, COLS // 2
    grid[center_row, center_col] = 1

    for _ in range(iterations):
        grid = update_grid(grid, freeze)

    # Finally, print the grid using the selected ASCII symbol
    grid2ascii(grid, ascii_symbol)

The creation is based on a game of life on a hexagonal grid. Instead of birth and death, the neighbor count determines whether to freeze or thaw the current cell. This way symmetric and hexagonal structures are created that for many seeds resemble natural snowflakes. The random seed determines the number of iterations, the freezing rules, and the ASCII symbol (out of a small list of symmetric characters) to use for visualization.

The grid is composed to hold a hexagonal grid, thus every other row (row % 2) has to be shifted to the left and right, respectively. This is especially necessary when counting the number of frozen neighbors in get_frozen_neighbors(), where the col_offset picks the correct neighbors for each cell. Similarly, upon printing the grid in grid2ascii(), we have to add a space in front of even rows, in order to shift them to the correct position.

The actual growing process starts with an initial frozen cell (set to 1) in the center of a large enough grid (initialized unfrozen, i.e. with zeros), which then updates in every iteration via update_grid(). To not saw off the branch we are sitting on, we have to work with a copy of the grid, thus updated_grid will be populated according to the rules and finally returned. In each iteration we count the number of frozen neighbors, check whether to freeze or unfreeze the current cell based on the freeze-rule and repeat iterating the grid.

# This script generates a snowflake using a hexagonal grid and cell rules for freezing/thawing cells.
import sys
import numpy as np

ROWS, COLS = 30, 30  # Size of the grid

def get_frozen_neighbors(grid, row, col):
    # Return -1 for border cells so that they are not processed by any rules
    if row <= 0 or row >= grid.shape[0] - 1 or col <= 0 or col >= grid.shape[1] - 1:
        return -1

    col_offset = 1 if ((row % 2) == 0) else -1  # Adjust column offset for hexagonal grid

    return (
        grid[row - 1, col]
        + grid[row - 1, col + col_offset]
        + grid[row, col - 1]
        + grid[row, col + 1]
        + grid[row + 1, col]
        + grid[row + 1, col + col_offset]
    )


def update_grid(grid, freeze):
    updated_grid = grid.copy()
    for row in range(grid.shape[0]):
        for col in range(grid.shape[1]):
            if get_frozen_neighbors(grid, row, col) in freeze:
                updated_grid[row, col] = 1
            else:
                updated_grid[row, col] = 0

    return updated_grid


def grid2ascii(grid, ascii_symbol):
    for row_idx, row in enumerate(grid):
        if np.all(row == 0):
            continue  # Skip fully empty rows
        line = ''
        if row_idx % 2 == 0:
            line += ' '  # Offset even rows for hexagonal appearance
        line += ' '.join((ascii_symbol if cell == 1 else ' ') for cell in row)
        print(line)
    print("\n")


if __name__ == "__main__":
    if len(sys.argv) > 1:
        seed = int(sys.argv[1])
    else:
        seed = 40

    np.random.seed(seed)

    # Select symbol and number of iterations based on random seed
    ascii_symbol = np.random.choice(['+', 'X', '*', '#'])
    iterations   = np.random.randint(5, 15)

    # Randomly generate freezing rule based on the seed
    freeze = [1]  # Has to be included, otherwise, we would never start growing
    for i in range(2, 7):
        if np.random.choice([True, False]):
            freeze.append(i)

    grid = np.zeros((ROWS, COLS), dtype=int)

    # Freeze the center cell (We have to start somewhere...)
    center_row, center_col       = ROWS // 2, COLS // 2
    grid[center_row, center_col] = 1

    for _ in range(iterations):
        grid = update_grid(grid, freeze)

    # Finally, print the grid using the selected ASCII symbol
    grid2ascii(grid, ascii_symbol)
added 69 characters in body; deleted 28 characters in body; deleted 3 characters in body
Source Link
André
  • 2k
  • 14
  • 28

Snowflake creation approach

Approach to create ❄

The creation is based on a game of life on a hexagonal grid. Instead of birth and death, the neighbor count determines whether to freeze or thaw the current cell. This way symmetric and hexagonal structures are created that for many seeds resemble natural snowflakes. The random seed determines the number of iterations, the freezing rules, and the ASCII symbol (out of a small list of symmetric characters) to use for visualization. The process starts with an initial frozen cell (set to 1) in the center of a large enough grid (initialzedinitialized unfrozen, i.e. with zeros), which then updates toin every iteration.

Code to create the snowflake(s)


Code to create ❄

snowflakes.py contaisncontains the following:

Examples of some snowflakes that my code has created


Examples of some ❄ that my code has created

AI usage disclosure


AI usage disclosure

Instructions for running my code


Instructions for running my code

Lessons learned


Lessons learned

I learned a lot about the beauty and symmetry of natural patterns, especially about the phsysicsphysics of snowflakes. I did not dare to even try to mimic the physical process but rather came up with an art-driven approach.

Snowflake creation approach

The creation is based on a game of life on a hexagonal grid. Instead of birth and death, the neighbor count determines whether to freeze or thaw the current cell. This way symmetric and hexagonal structures are created that for many seeds resemble natural snowflakes. The random seed determines the number of iterations, the freezing rules, and the ASCII symbol (out of a small list of symmetric characters) to use for visualization. The process starts with an initial frozen cell (set to 1) in the center of a large enough grid (initialzed unfrozen, i.e. with zeros), which then updates to

Code to create the snowflake(s)

snowflakes.py contaisn the following:

Examples of some snowflakes that my code has created

AI usage disclosure

Instructions for running my code

Lessons learned

I learned a lot about the beauty and symmetry of natural patterns, especially about the phsysics of snowflakes. I did not dare to even try to mimic the physical process but rather came up with an art-driven approach.

Approach to create ❄

The creation is based on a game of life on a hexagonal grid. Instead of birth and death, the neighbor count determines whether to freeze or thaw the current cell. This way symmetric and hexagonal structures are created that for many seeds resemble natural snowflakes. The random seed determines the number of iterations, the freezing rules, and the ASCII symbol (out of a small list of symmetric characters) to use for visualization. The process starts with an initial frozen cell (set to 1) in the center of a large enough grid (initialized unfrozen, i.e. with zeros), which then updates in every iteration.


Code to create ❄

snowflakes.py contains the following:


Examples of some ❄ that my code has created


AI usage disclosure


Instructions for running my code


Lessons learned

I learned a lot about the beauty and symmetry of natural patterns, especially about the physics of snowflakes. I did not dare to even try to mimic the physical process but rather came up with an art-driven approach.

Source Link
André
  • 2k
  • 14
  • 28

Snowflake creation approach

The creation is based on a game of life on a hexagonal grid. Instead of birth and death, the neighbor count determines whether to freeze or thaw the current cell. This way symmetric and hexagonal structures are created that for many seeds resemble natural snowflakes. The random seed determines the number of iterations, the freezing rules, and the ASCII symbol (out of a small list of symmetric characters) to use for visualization. The process starts with an initial frozen cell (set to 1) in the center of a large enough grid (initialzed unfrozen, i.e. with zeros), which then updates to

Code to create the snowflake(s)

snowflakes.py contaisn the following:

# This script generates a snowflake using a hexagonal grid and cell rules for freezing/thawing cells.
import sys
import numpy as np

ROWS, COLS = 30, 30  # Size of the grid

def get_frozen_neighbors(grid, row, col):
    # Return -1 for border cells so that they are not processed by any rules
    if row <= 0 or row >= grid.shape[0] - 1 or col <= 0 or col >= grid.shape[1] - 1:
        return -1

    col_offset = 1 if ((row % 2) == 0) else -1  # Adjust column offset for hexagonal grid

    return (
        grid[row - 1, col]
        + grid[row - 1, col + col_offset]
        + grid[row, col - 1]
        + grid[row, col + 1]
        + grid[row + 1, col]
        + grid[row + 1, col + col_offset]
    )


def update_grid(grid, freeze):
    updated_grid = grid.copy()
    for row in range(grid.shape[0]):
        for col in range(grid.shape[1]):
            if get_frozen_neighbors(grid, row, col) in freeze:
                updated_grid[row, col] = 1
            else:
                updated_grid[row, col] = 0

    return updated_grid


def grid2ascii(grid, ascii_symbol):
    for row_idx, row in enumerate(grid):
        if np.all(row == 0):
            continue  # Skip fully empty rows
        line = ''
        if row_idx % 2 == 0:
            line += ' '  # Offset even rows for hexagonal appearance
        line += ' '.join((ascii_symbol if cell == 1 else ' ') for cell in row)
        print(line)
    print("\n")


if __name__ == "__main__":
    if len(sys.argv) > 1:
        seed = int(sys.argv[1])
    else:
        seed = 40

    np.random.seed(seed)

    # Select symbol and number of iterations based on random seed
    ascii_symbol = np.random.choice(['+', 'X', '*', '#'])
    iterations   = np.random.randint(5, 15)

    # Randomly generate ruleset based on the seed
    freeze = [1]
    for i in range(2, 7):
        if np.random.choice([True, False]):
            freeze.append(i)

    grid = np.zeros((ROWS, COLS), dtype=int)

    # Freeze the center cell (We have to start somewhere...)
    center_row, center_col       = ROWS // 2, COLS // 2
    grid[center_row, center_col] = 1

    for _ in range(iterations):
        grid = update_grid(grid, freeze)

    # Finally, print the grid using the selected ASCII symbol
    grid2ascii(grid, ascii_symbol)

Examples of some snowflakes that my code has created

  • seed = 40:
                  *       *       *       *
                   * * * *         * * * *
                  *       * *   * *       *
                 *   * * * *     * * * *   *
              * *   *   * * *   * * *   *   * *
                 * * * * * * * * * * * * * *
                * * * * * * *   * * * * * * *
                   * * *   * * * *   * * *
          *   *     * * *           * * *     *   *
           * * * * *   *   * * * *   *   * * * * *
          *   * * * * *   * *   * *   * * * * *   *
         *   * * * * *   *   * *   *   * * * * *   *
      * *   *   * *     * * *   * * *     * *   *   * *
         *   * * * * *   *   * *   *   * * * * *   *
          *   * * * * *   * *   * *   * * * * *   *
           * * * * *   *   * * * *   *   * * * * *
          *   *     * * *           * * *     *   *
                   * * *   * * * *   * * *
                * * * * * * *   * * * * * * *
                 * * * * * * * * * * * * * *
              * *   *   * * *   * * *   *   * *
                 *   * * * *     * * * *   *
                  *       * *   * *       *
                   * * * *         * * * *
                  *       *       *       *
  • seed = 256:
                   +       +       +       +
                    +   +           +   +
              +   +       +       +       +   +
                    +       +   +       +
          +           +               +           +
            +   +   +                   +   +   +
      +   +                   +                   +   +
            +   +   +                   +   +   +
          +           +               +           +
                    +       +   +       +
              +   +       +       +       +   +
                    +   +           +   +
                  +       +       +       +
  • seed = 9876:
                   # # # #         # # # #
                  #       #       #       #
                 #           # #           #
                #         # #   # #         #
                 #         #     #         #
                    #         #         #
                 # # #       # #       # # #
                #         #   #   #         #
           # #   #   # #             # #   #   # #
          #     # #   # #   #   #   # #   # #     #
         #                                         #
        #             #   #       #   #             #
         #                                         #
          #     # #   # #   #   #   # #   # #     #
           # #   #   # #             # #   #   # #
                #         #   #   #         #
                 # # #       # #       # # #
                    #         #         #
                 #         #     #         #
                #         # #   # #         #
                 #           # #           #
                  #       #       #       #
                   # # # #         # # # #

AI usage disclosure

No AI has been used to generate the code.

Instructions for running my code

Just run python snowflakes.py <your seed> via command line and observe the action.

Lessons learned

I learned a lot about the beauty and symmetry of natural patterns, especially about the phsysics of snowflakes. I did not dare to even try to mimic the physical process but rather came up with an art-driven approach.

Finally, because of the hexagonal pattern formed by the water molecules, I had to come up with a way to represent a hexagonal grid in an easy way, both for computing the updates, but also for displaying the result.

Upon some research I found that - of course - my solution is far from being unique. There is a GitHub repo that creates colorful snowflakes in a "Game of Hex" fashion and a keen adventurer even dared to use Scratch to simulate snowflakes.