Made with Godot 4 in GDScript. Click and drag to move, scrollwheel to zoom in/out. Most of the relevant code:

class_name Grid extends Node2D
    
signal rule_changed(rule)
    
# Width in cells
@export_range(3, 1000) var width : int
    
# Number of rows / generations
@export_range(1, 1000) var height : int
    
# Width of cell in pixels
@export var cell_width : int
    
# The rule number from 0 to 255
@export var rule : int :
    set(value):
        rule = value
        rule_changed.emit(rule)
    
@export var scale_sensitivity := 0.03
   
var _grid : Array[Array]
var _ruleset : Dictionary
var _initial_conditions : Array[int]
var _dragging := false
    
func bin_string(n):
    var ret_str = ""
    while n > 0:
        ret_str = str(n & 1) + ret_str
        n = n >> 1
    while len(ret_str) < 8:
        ret_str = '0' + ret_str
    return ret_str
    
func _ready():
    setup_ruleset()
    calculate_grid()
    
func _draw():
    for n in range(height):
        for i in range(width):
            var pos := Vector2(i * cell_width, n * cell_width)
            if _grid[n][i] == 1:
                var color = Color(
                    n / float(height),
                    i / float(width),
                    0
                )
                draw_rect(Rect2(pos, Vector2(cell_width, cell_width)), color)
    
# Based on the rule, update ruleset dictionary
func setup_ruleset():
    _ruleset.clear()
    var rule_str = bin_string(rule)
    var i = len(rule_str) - 1
    for b in rule_str:
        _ruleset[i] = int(b)
        i -= 1
    
func calculate_grid():
    _grid.clear()
    _initial_conditions.clear()
    
    # Put a 1 in the middle of the first generation
    for i in range(0, width):
        if i == int(width / 2):
            _initial_conditions.append(1)
        else:
            _initial_conditions.append(0)
    
    for n in range(height):
        # First row, so use initial conditions
        if n == 0:
            _grid.append(_initial_conditions)
        else:
            var previous_row := _grid[n - 1]
            var row := []
            
            # Apply the current rule
            for x in range(0, width):
                var value
                if x == 0:
                    value = str(previous_row[width - 1]) + str(previous_row[x]) + str(previous_row[x + 1])
                elif x == width - 1:
                    value = str(previous_row[x - 1]) + str(previous_row[x]) + str(previous_row[0])
                else:
                    value = str(previous_row[x - 1]) + str(previous_row[x]) + str(previous_row[x + 1])
                value = value.bin_to_int()
                row.append(_ruleset[value])
            _grid.append(row)
    
func update():
    if is_node_ready():
        setup_ruleset()
        calculate_grid()
        queue_redraw()
    
func _on_width_input_value_changed(value):
    width = value
    update()
    
func _on_height_input_value_changed(value):
    height = value
    update()
    
func _on_rule_input_value_changed(value):
    rule = value
    update()
    
func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_LEFT:
            _dragging = event.pressed
        elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
            scale = Vector2(scale.x - scale_sensitivity, scale.y - scale_sensitivity)
        elif event.button_index == MOUSE_BUTTON_WHEEL_UP:
            scale = Vector2(scale.x + scale_sensitivity, scale.y + scale_sensitivity)
        if scale.x > 4.0:
            scale.x = 1.0
        elif scale.x <= 0.1:
            scale.x = 0.1
        if scale.y > 4.0:
            scale.y = 1.0
        elif scale.y <= 0.1:
            scale.y = 0.1
    if event is InputEventMouseMotion and _dragging:
        position += event.relative
    

StatusPrototype
CategoryOther
PlatformsHTML5
AuthorDoodleDonut
Made withGodot

Leave a comment

Log in with itch.io to leave a comment.