feat(gameoflife): allow for variations to game of life (closes #1023) (#1041)

## What are the changes about?

Allows the game of life animation to be ran with configurable rules, specifically: the neighbors required for birth and survival.
### Example: [B3/S12345 (Maze)](https://conwaylife.com/wiki/OCA:Maze)
(where numbers after B are neighbors required for birth, and S for survival)
<video src="/attachments/49120c5e-ce21-4025-852e-924befbc2621" title="Screencast_20260814_233338" controls></video>

Introduces two keys to the config:
```ini
# Sets the multiple amounts of neighbors needed for a cell to survive.
# Each numerical digit from 0 to 8 inclusive in this string becomes
# one of the targets required for this cell to survive.
# Example: a string of "23" makes the cell survive on 2 or 3 neighbors
gameoflife_param_survival = "23"

# Sets the multiple amounts of neighbors needed for a cell to be born.
# Each numerical digit from 0 to 8 inclusive in this string becomes
# one of the targets required for this cell to be born.
# Example: a string of "3" makes the cell be born on 3 neighbors
gameoflife_param_birth = "3"
```

## What existing issue(s) does this resolve?

Solves !1023

## Pre-requisites

- [x] I have tested & confirmed the changes work locally
- [x] I have read and fully adhere to the rules set in the contributing guidelines found in `CONTRIBUTING.md`

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/1041
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
This commit is contained in:
RadsammyT
2026-08-24 15:32:16 +02:00
committed by AnErrupTion
parent bd28c0e679
commit 0a29dc2fef
4 changed files with 37 additions and 5 deletions

View File

@@ -39,6 +39,11 @@ animation_frame_delay: u16,
dead_cell: Cell,
width: usize,
height: usize,
// Where N is the amount of neighbors, determines if the cell can survive/be born
// by checking if the Nth bit (where if N == 0, it is the least significant bit,
// or 2 to the power of 0) is set to 1.
cell_survival: u9,
cell_birth: u9,
pub fn init(
allocator: Allocator,
@@ -50,6 +55,8 @@ pub fn init(
animate: *bool,
timeout_sec: u12,
animation_frame_delay: u16,
string_cell_survival: []const u8,
string_cell_birth: []const u8,
) !GameOfLife {
const width = terminal_buffer.width;
const height = terminal_buffer.height;
@@ -77,6 +84,16 @@ pub fn init(
.dead_cell = .{ .ch = DEAD_CHAR, .fg = @intCast(TerminalBuffer.Color.DEFAULT), .bg = terminal_buffer.bg },
.width = width,
.height = height,
.cell_birth = 0,
.cell_survival = 0,
};
for (string_cell_survival) |c| switch (c) {
'0'...'8' => game.cell_survival |= (@as(u9, 1) << @truncate(@as(u9, c - '0'))),
else => {},
};
for (string_cell_birth) |c| switch (c) {
'0'...'8' => game.cell_birth |= (@as(u9, 1) << @truncate(@as(u9, c - '0'))),
else => {},
};
// Initialize grid
@@ -173,11 +190,10 @@ fn updateGeneration(self: *GameOfLife) void {
const is_alive = self.current_grid[index];
// Optimized rule application
self.next_grid[index] = switch (neighbors) {
2 => is_alive,
3 => true,
else => false,
};
self.next_grid[index] = if (is_alive)
self.cell_survival & (@as(u9, 1) << @truncate(neighbors)) != 0
else
self.cell_birth & (@as(u9, 1) << @truncate(neighbors)) != 0;
}
}