extends Sprite2D var n: int = 64 var arr := [] var data_img: Image var data_tex: ImageTexture var gol := GoL.new() @onready var mat: ShaderMaterial = material as ShaderMaterial # sim. consts var T := 0.01 var t := 0.0 # player (2x2 block) var player_alive := true var player_col := 10 var player_row := 10 var PLAYER_SHAPE := [Vector2i(0,0), Vector2i(1,0), Vector2i(0,1), Vector2i(1,1)] # Called when the node enters the scene tree for the first time. func _ready() -> void: data_img = Image.create(n, n, false, Image.FORMAT_R8) data_img.fill(Color8(0, 0, 0, 255)) data_tex = ImageTexture.create_from_image(data_img) mat.set_shader_parameter("binDataTex", data_tex) mat.set_shader_parameter("n", n) # player _seed_clear() _fill_example() _spawn_player(20, 10) _upload_arr() func _upload_arr() -> void: for y in n: for x in n: var v := int(arr[y][x]) * 255 data_img.set_pixel(x, y, Color8(v, 0, 0, 255)) data_tex.update(data_img) # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(_delta: float) -> void: if Input.is_action_just_pressed("move_right"): _move_player(1, 0) if Input.is_action_just_pressed("move_left"): _move_player(-1, 0) if Input.is_action_just_pressed("move_up"): _move_player(0, -1) if Input.is_action_just_pressed("move_down"): _move_player(0, 1) t += _delta if t >= T: t = 0.0 _game_of_life_step() # arr = gol.step_once(arr, n) # cpp step (no player) _track_player_after_step() _upload_arr() func _game_of_life_step() -> void: var next := [] for y in n: var row := [] for x in n: var alive: bool = arr[y][x] == 1 var cn := _count_neighs(x, y) var newv := 0 if alive: newv = int(cn == 2 or cn == 3) else: newv = int(cn == 3) row.append(newv) next.append(row) arr = next func _count_neighs(x:int, y:int) -> int: var ans := 0 for _dy in 3: var dy := _dy-1 for _dx in 3: var dx := _dx-1 if dx == 0 and dy == 0: continue var nx := int((x + dx + n)%n) var ny := int((y + dy + n)%n) ans += arr[ny][nx] return ans func _track_player_after_step() -> void: if not player_alive: return var best_count := -1 var best_shift := Vector2i(0, 0) # search a 5x5 neighborhood for where the 2x2 footprint moved for dy in range(-2, 3): for dx in range(-2, 3): var cnt := 0 for off in PLAYER_SHAPE: var x:int = (player_col + off.x + dx + n) % n var y:int = (player_row + off.y + dy + n) % n cnt += arr[y][x] if cnt > best_count: best_count = cnt best_shift = Vector2i(dx, dy) # if nothing from the footprint survived, the player is dead if best_count <= 0: player_alive = false print("Player dead!") return player_col = (player_col + best_shift.x + n) % n player_row = (player_row + best_shift.y + n) % n func _fill_example() -> void: #_checkerboard() _ship() # inits func _checkerboard() -> void: for y in n: var row := [] for x in n: row.append((x + y) % 2) # checkerboard arr.append(row) func _ship() -> void: var i: int = 0 var j: int = 0 while (i < n): var row := [] j = 0 while (j < n): row.append(0) j+=1 arr.append(row) i+=1 arr[0][1] = 1 arr[1][2] = 1 arr[2][0] = 1 arr[2][1] = 1 arr[2][2] = 1 # Player func _write_player(val:int) -> void: for off in PLAYER_SHAPE: var x:int = (player_col + off.x + n) % n var y:int = (player_row + off.y + n) % n arr[y][x] = val func _spawn_player(col:int, row:int) -> void: player_col = (col + n) % n player_row = (row + n) % n player_alive = true _write_player(1) _upload_arr() func _despawn_player() -> void: if player_alive: _write_player(0) player_alive = false _upload_arr() func _move_player(dx:int, dy:int) -> void: if not player_alive: return # erase old footprint _write_player(0) # move player_col = (player_col + dx + n) % n player_row = (player_row + dy + n) % n # write new footprint _write_player(1) _upload_arr() # Clear func _seed_clear() -> void: arr.clear() for _y in n: var row := [] for _x in n: row.append(0) arr.append(row)