Cellular Automata
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
| Status | Prototype |
| Category | Other |
| Platforms | HTML5 |
| Author | DoodleDonut |
| Made with | Godot |

Leave a comment
Log in with itch.io to leave a comment.