|  | ## Conway's Game of Life | 
						
						
							|  | ##	Rule 1 - Any live cell with fewer than two live neighbours dies (underpopulation) | 
						
						
							|  | ##	Rule 2 - Any live cell with two or three live neighbours lives | 
						
						
							|  | ##	Rule 3 - Any live cell with more than three live neighbours dies (overpopulation) | 
						
						
							|  | ##	Rule 4 - Any dead cell with epos.xactly three live neighbours becomes a live cell (reproduction) | 
						
						
							|  | extends Node2D | 
						
						
							|  | 
 | 
						
						
							|  | 
 | 
						
						
							|  | enum CellStates { | 
						
						
							|  | 	DEAD, | 
						
						
							|  | 	ALIVE, | 
						
						
							|  | } | 
						
						
							|  | 
 | 
						
						
							|  | @onready var generation_counter: Label = $CanvasLayer/Debug/VBoxContainer/GenerationCounter | 
						
						
							|  | @onready var generation_timer: Timer = $GenerationTimer | 
						
						
							|  | @onready var living_cells_counter: Label = $CanvasLayer/Debug/VBoxContainer/LivingCellsCounter | 
						
						
							|  | @onready var world_seed_label: Label = $CanvasLayer/Debug/VBoxContainer/WorldSeed | 
						
						
							|  | 
 | 
						
						
							|  | @export var world_seed: int | 
						
						
							|  | @export var cell_size: Vector2 = Vector2(16, 16) | 
						
						
							|  | @export var cell_texture: Texture2D | 
						
						
							|  | @export var world_size: Vector2 = Vector2(32, 32) | 
						
						
							|  | 
 | 
						
						
							|  | var cell_instance | 
						
						
							|  | var generation: int = 1 | 
						
						
							|  | var total_living: int = 0 | 
						
						
							|  | var world: Array = [] | 
						
						
							|  | 
 | 
						
						
							|  | 
 | 
						
						
							|  | func _ready() -> void: | 
						
						
							|  | 	if not world_seed: | 
						
						
							|  | 		world_seed = randi() | 
						
						
							|  | 	seed(world_seed) | 
						
						
							|  | 	world_seed_label.text = "World Seeed: %s" % world_seed | 
						
						
							|  | 
 | 
						
						
							|  | 	generate_world() | 
						
						
							|  | 
 | 
						
						
							|  | 
 | 
						
						
							|  | ## Check a cell against the Conway rules and return True if cell shoudl be alive | 
						
						
							|  | func cell_is_alive(pos: Vector2) -> bool: | 
						
						
							|  | 	var neighbors := count_living_neighbors(pos) | 
						
						
							|  | 	var currently_alive = world[pos.x][pos.y] is RID | 
						
						
							|  | 
 | 
						
						
							|  | 	# Rule 1 - Any live cell with fewer than two live neighbours dies (underpopulation) | 
						
						
							|  | 	if currently_alive and neighbors < 2: | 
						
						
							|  | 		return false | 
						
						
							|  | 	# Rule 2 - Any live cell with two or three live neighbours lives | 
						
						
							|  | 	elif currently_alive and (neighbors == 2 or neighbors == 3): | 
						
						
							|  | 		return true | 
						
						
							|  | 	# Rule 3 - Any live cell with more than three live neighbours dies (overpopulation) | 
						
						
							|  | 		return false | 
						
						
							|  | 	# Rule 4 - Any dead cell with epos.xactly three live neighbours becomes a live cell (reproduction) | 
						
						
							|  | 	elif not currently_alive and neighbors == 3: | 
						
						
							|  | 		return true | 
						
						
							|  | 
 | 
						
						
							|  | 	return false | 
						
						
							|  | 
 | 
						
						
							|  | ## Count all neighbors surrounding a cell | 
						
						
							|  | func count_living_neighbors(pos: Vector2) -> int: | 
						
						
							|  | 	var count := 0 | 
						
						
							|  | 
 | 
						
						
							|  | 	var x_min = 0 if pos.x - 1 < 0 else pos.x - 1 | 
						
						
							|  | 	var y_min = 0 if pos.y - 1 < 0 else pos.y - 1 | 
						
						
							|  | 	var x_max = world_size.x if pos.x + 2 > world_size.x else pos.x + 2 | 
						
						
							|  | 	var y_max = world_size.y if pos.y + 2 > world_size.y else pos.y + 2 | 
						
						
							|  | 
 | 
						
						
							|  | 	for x in range(x_min, x_max): | 
						
						
							|  | 		for y in range(y_min, y_max): | 
						
						
							|  | 			if x == pos.x and y == pos.y: continue # Current cell - Don't count | 
						
						
							|  | 			if world[x][y] is RID: | 
						
						
							|  | 				count += 1 | 
						
						
							|  | 	return count | 
						
						
							|  | 
 | 
						
						
							|  | ## Loop through the world and create or kill cells depending on rules | 
						
						
							|  | func process_generation() -> void: | 
						
						
							|  | 	generation += 1 | 
						
						
							|  | 	total_living = 0 | 
						
						
							|  | 
 | 
						
						
							|  | 	var new_world: Array= [] | 
						
						
							|  | 	for x in range(world_size.x): | 
						
						
							|  | 		new_world.append([]) | 
						
						
							|  | 		new_world[x].resize(world_size.x) | 
						
						
							|  | 		for y in range(world_size.y): | 
						
						
							|  | 			var pos = Vector2(x, y) | 
						
						
							|  | 			if cell_is_alive(pos): | 
						
						
							|  | 				total_living += 1 | 
						
						
							|  | 				new_world[x][y] = create_cell(pos) | 
						
						
							|  | 			else: | 
						
						
							|  | 				new_world[x][y] = kill_cell(pos) | 
						
						
							|  | 
 | 
						
						
							|  | 	world = new_world | 
						
						
							|  | 
 | 
						
						
							|  | 
 | 
						
						
							|  | ## Create the cell using the rendering server | 
						
						
							|  | func create_cell(pos: Vector2) -> RID: | 
						
						
							|  | 	var world_size = world.size() | 
						
						
							|  | 	if world[pos.x][pos.y] is RID: return world[pos.x][pos.y] | 
						
						
							|  | 
 | 
						
						
							|  | 	var rs = RenderingServer | 
						
						
							|  | 
 | 
						
						
							|  | 	cell_instance = rs.canvas_item_create() | 
						
						
							|  | 	rs.canvas_item_set_parent(cell_instance, get_canvas_item()) | 
						
						
							|  | 
 | 
						
						
							|  | 	var rect: Rect2 = Rect2(-cell_size.x/2, -cell_size.y/2, cell_size.x, cell_size.y) # Centered with cell size | 
						
						
							|  | 	rs.canvas_item_add_texture_rect(cell_instance, rect, cell_texture) | 
						
						
							|  | 
 | 
						
						
							|  | 	var pos_fixed = Vector2(pos.x * cell_size.x, pos.y * cell_size.y) | 
						
						
							|  | 	var trans = Transform2D(0, pos_fixed) | 
						
						
							|  | 	rs.canvas_item_set_transform(cell_instance, trans) | 
						
						
							|  | 
 | 
						
						
							|  | 	return cell_instance | 
						
						
							|  | 
 | 
						
						
							|  | func kill_cell(pos: Vector2) -> CellStates: | 
						
						
							|  | 	if world[pos.x][pos.y] is RID: | 
						
						
							|  | 		RenderingServer.free_rid(world[pos.x][pos.y]) | 
						
						
							|  | 	return CellStates.DEAD | 
						
						
							|  | 
 | 
						
						
							|  | 
 | 
						
						
							|  | ## Generate the world with the initial cells | 
						
						
							|  | func generate_world() -> void: | 
						
						
							|  | 	for x in range(world_size.x): | 
						
						
							|  | 		world.append([]) | 
						
						
							|  | 		world[x].resize(world_size.x) | 
						
						
							|  | 		for y in range(world_size.y): | 
						
						
							|  | 			if randi_range(0, 1): | 
						
						
							|  | 				total_living += 1 | 
						
						
							|  | 				world[x][y] = create_cell(Vector2(x, y)) | 
						
						
							|  | 			else: | 
						
						
							|  | 				world[x][y] = CellStates.DEAD | 
						
						
							|  | 
 | 
						
						
							|  | 
 | 
						
						
							|  | func _on_generation_timer_timeout() -> void: | 
						
						
							|  | 	process_generation() | 
						
						
							|  | 
 | 
						
						
							|  | 	generation_counter.text = "Generation: %s" % generation | 
						
						
							|  | 	living_cells_counter.text = "Living Cells: %s" % total_living | 
						
						
							|  | 
 | 
						
						
							|  | 	generation_timer.start()
 |