mirror of
https://github.com/fairyglade/ly.git
synced 2025-12-20 19:24:53 +00:00
Merge branch 'master' of codeberg.org:fairyglade/ly
This commit is contained in:
@@ -23,6 +23,7 @@ allow_empty_password = true
|
|||||||
# doom -> PSX DOOM fire
|
# doom -> PSX DOOM fire
|
||||||
# matrix -> CMatrix
|
# matrix -> CMatrix
|
||||||
# colormix -> Color mixing shader
|
# colormix -> Color mixing shader
|
||||||
|
# gameoflife -> John Conway's Game of Life
|
||||||
animation = none
|
animation = none
|
||||||
|
|
||||||
# Stop the animation after some time
|
# Stop the animation after some time
|
||||||
@@ -125,6 +126,27 @@ error_fg = 0x01FF0000
|
|||||||
# Foreground color id
|
# Foreground color id
|
||||||
fg = 0x00FFFFFF
|
fg = 0x00FFFFFF
|
||||||
|
|
||||||
|
# Game of Life entropy interval (0 = disabled, >0 = add entropy every N generations)
|
||||||
|
# 0 -> Pure Conway's Game of Life (will eventually stabilize)
|
||||||
|
# 10 -> Add entropy every 10 generations (recommended for continuous activity)
|
||||||
|
# 50+ -> Less frequent entropy for more natural evolution
|
||||||
|
gameoflife_entropy_interval = 10
|
||||||
|
|
||||||
|
# Game of Life animation foreground color id
|
||||||
|
gameoflife_fg = 0x0000FF00
|
||||||
|
|
||||||
|
# Game of Life frame delay (lower = faster animation, higher = slower)
|
||||||
|
# 1-3 -> Very fast animation
|
||||||
|
# 6 -> Default smooth animation speed
|
||||||
|
# 10+ -> Slower, more contemplative speed
|
||||||
|
gameoflife_frame_delay = 6
|
||||||
|
|
||||||
|
# Game of Life initial cell density (0.0 to 1.0)
|
||||||
|
# 0.1 -> Sparse, minimal activity
|
||||||
|
# 0.4 -> Balanced activity (recommended)
|
||||||
|
# 0.7+ -> Dense, chaotic patterns
|
||||||
|
gameoflife_initial_density = 0.4
|
||||||
|
|
||||||
# Remove main box borders
|
# Remove main box borders
|
||||||
hide_borders = false
|
hide_borders = false
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +1,63 @@
|
|||||||
|
authenticating = authentifizieren...
|
||||||
|
brightness_down = Helligkeit-
|
||||||
|
brightness_up = Helligkeit+
|
||||||
capslock = Feststelltaste
|
capslock = Feststelltaste
|
||||||
err_alloc = Speicherzuweisung fehlgeschlagen
|
err_alloc = Speicherzuweisung fehlgeschlagen
|
||||||
err_bounds = Listenindex ist außerhalb des Bereichs
|
err_bounds = Index ausserhalb des Bereichs
|
||||||
|
err_brightness_change = Helligkeitsänderung fehlgeschlagen
|
||||||
err_chdir = Fehler beim oeffnen des home-ordners
|
err_chdir = Fehler beim Oeffnen des Home-Ordners
|
||||||
|
err_config = Fehler beim Verarbeiten der Konfigurationsdatei
|
||||||
err_console_dev = Zugriff auf die Konsole fehlgeschlagen
|
err_console_dev = Zugriff auf die Konsole fehlgeschlagen
|
||||||
err_dgn_oob = Protokoll Nachricht
|
err_dgn_oob = Diagnose-Nachricht
|
||||||
err_domain = Unzulaessige domain
|
err_domain = Ungueltige Domain
|
||||||
|
err_empty_password = Leeres Passwort nicht zugelassen
|
||||||
|
err_envlist = Fehler beim Abrufen der Umgebungs-Variablen
|
||||||
err_hostname = Holen des Hostnames fehlgeschlagen
|
err_hostname = Abrufen des Hostnames fehlgeschlagen
|
||||||
err_mlock = Abschließen des Passwortspeichers fehlgeschlagen
|
err_mlock = Sperren des Passwortspeichers fehlgeschlagen
|
||||||
err_null = Null Zeiger
|
err_null = Null Pointer
|
||||||
|
err_numlock = Numlock konnte nicht aktiviert werden
|
||||||
err_pam = pam Transaktion fehlgeschlagen
|
err_pam = PAM-Transaktion fehlgeschlagen
|
||||||
err_pam_abort = pam Transaktion abgebrochen
|
err_pam_abort = PAM-Transaktion abgebrochen
|
||||||
err_pam_acct_expired = Benutzerkonto abgelaufen
|
err_pam_acct_expired = Benutzerkonto abgelaufen
|
||||||
err_pam_auth = Authentifizierungs Fehler
|
err_pam_auth = Authentifizierungsfehler
|
||||||
err_pam_authinfo_unavail = holen der Benutzerinformationen fehlgeschlagen
|
err_pam_authinfo_unavail = Abrufen der Benutzerinformationen fehlgeschlagen
|
||||||
err_pam_authok_reqd = Schluessel abgelaufen
|
err_pam_authok_reqd = Passwort abgelaufen
|
||||||
err_pam_buf = Speicherpufferfehler
|
err_pam_buf = Speicherpufferfehler
|
||||||
err_pam_cred_err = Fehler beim setzen der Anmeldedaten
|
err_pam_cred_err = Fehler beim Setzen der Anmeldedaten
|
||||||
err_pam_cred_expired = Anmeldedaten abgelaufen
|
err_pam_cred_expired = Anmeldedaten abgelaufen
|
||||||
err_pam_cred_insufficient = Anmeldedaten unzureichend
|
err_pam_cred_insufficient = Anmeldedaten unzureichend
|
||||||
err_pam_cred_unavail = Fehler beim holen der Anmeldedaten
|
err_pam_cred_unavail = Fehler beim Abrufen der Anmeldedaten
|
||||||
err_pam_maxtries = Maximale Versuche erreicht
|
err_pam_maxtries = Maximale Versuchsanzahl erreicht
|
||||||
err_pam_perm_denied = Zugriff Verweigert
|
err_pam_perm_denied = Zugriff verweigert
|
||||||
err_pam_session = Sitzungsfehler
|
err_pam_session = Sitzungsfehler
|
||||||
err_pam_sys = Systemfehler
|
err_pam_sys = Systemfehler
|
||||||
err_pam_user_unknown = Unbekannter Nutzer
|
err_pam_user_unknown = Unbekannter Nutzer
|
||||||
err_path = Fehler beim setzen des Pfades
|
err_path = Fehler beim Setzen des Pfades
|
||||||
err_perm_dir = Fehler beim wechseln des Ordners
|
err_perm_dir = Ordnerwechsel fehlgeschlagen
|
||||||
err_perm_group = Fehler beim heruntersetzen der Gruppen Berechtigungen
|
err_perm_group = Fehler beim Heruntersetzen der Gruppenberechtigungen
|
||||||
err_perm_user = Fehler beim heruntersetzen der Nutzer Berechtigungen
|
err_perm_user = Fehler beim Heruntersetzen der Nutzerberechtigungen
|
||||||
err_pwnam = Holen der Benutzerinformationen fehlgeschlagen
|
err_pwnam = Abrufen der Benutzerinformationen fehlgeschlagen
|
||||||
|
err_sleep = Sleep-Befehl fehlgeschlagen
|
||||||
|
err_tty_ctrl = Fehler bei der TTY-Uebergabe
|
||||||
err_user_gid = Fehler beim setzen der Gruppen Id des Nutzers
|
err_user_gid = Fehler beim Setzen der Gruppen-ID
|
||||||
err_user_init = Initialisierung des Nutzers fehlgeschlagen
|
err_user_init = Nutzer-Initialisierung fehlgeschlagen
|
||||||
err_user_uid = Setzen der Benutzer Id fehlgeschlagen
|
err_user_uid = Setzen der Benutzer-ID fehlgeschlagen
|
||||||
|
err_xauth = Xauth-Befehl fehlgeschlagen
|
||||||
|
err_xcb_conn = xcb-Verbindung fehlgeschlagen
|
||||||
err_xsessions_dir = Fehler beim finden des Sitzungsordners
|
err_xsessions_dir = Fehler beim Finden des Sitzungsordners
|
||||||
err_xsessions_open = Fehler beim öffnen des Sitzungsordners
|
err_xsessions_open = Fehler beim Oeffnen des Sitzungsordners
|
||||||
|
insert = Einfügen
|
||||||
login = Anmelden
|
login = Nutzer
|
||||||
logout = Abgemeldet
|
logout = Abmelden
|
||||||
|
no_x11_support = X11-Support bei Kompilierung deaktiviert
|
||||||
|
normal = Normal
|
||||||
numlock = Numtaste
|
numlock = Numlock
|
||||||
|
other = Andere
|
||||||
password = Passwort
|
password = Passwort
|
||||||
restart = Neustarten
|
restart = Neustarten
|
||||||
shell = shell
|
shell = Shell
|
||||||
shutdown = Herunterfahren
|
shutdown = Herunterfahren
|
||||||
|
sleep = Sleep
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
|
x11 = X11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
189
src/animations/GameOfLife.zig
Normal file
189
src/animations/GameOfLife.zig
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Animation = @import("../tui/Animation.zig");
|
||||||
|
const Cell = @import("../tui/Cell.zig");
|
||||||
|
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const GameOfLife = @This();
|
||||||
|
|
||||||
|
// Visual styles - using block characters like other animations
|
||||||
|
const ALIVE_CHAR: u21 = 0x2588; // Full block █
|
||||||
|
const DEAD_CHAR: u21 = ' ';
|
||||||
|
const NEIGHBOR_DIRS = [_][2]i8{
|
||||||
|
.{ -1, -1 }, .{ -1, 0 }, .{ -1, 1 },
|
||||||
|
.{ 0, -1 }, .{ 0, 1 }, .{ 1, -1 },
|
||||||
|
.{ 1, 0 }, .{ 1, 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
allocator: Allocator,
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
current_grid: []bool,
|
||||||
|
next_grid: []bool,
|
||||||
|
frame_counter: usize,
|
||||||
|
generation: u64,
|
||||||
|
fg_color: u32,
|
||||||
|
entropy_interval: usize,
|
||||||
|
frame_delay: usize,
|
||||||
|
initial_density: f32,
|
||||||
|
dead_cell: Cell,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg_color: u32, entropy_interval: usize, frame_delay: usize, initial_density: f32) !GameOfLife {
|
||||||
|
const width = terminal_buffer.width;
|
||||||
|
const height = terminal_buffer.height;
|
||||||
|
const grid_size = width * height;
|
||||||
|
|
||||||
|
const current_grid = try allocator.alloc(bool, grid_size);
|
||||||
|
const next_grid = try allocator.alloc(bool, grid_size);
|
||||||
|
|
||||||
|
var game = GameOfLife{
|
||||||
|
.allocator = allocator,
|
||||||
|
.terminal_buffer = terminal_buffer,
|
||||||
|
.current_grid = current_grid,
|
||||||
|
.next_grid = next_grid,
|
||||||
|
.frame_counter = 0,
|
||||||
|
.generation = 0,
|
||||||
|
.fg_color = fg_color,
|
||||||
|
.entropy_interval = entropy_interval,
|
||||||
|
.frame_delay = frame_delay,
|
||||||
|
.initial_density = initial_density,
|
||||||
|
.dead_cell = .{ .ch = DEAD_CHAR, .fg = @intCast(TerminalBuffer.Color.DEFAULT), .bg = terminal_buffer.bg },
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize grid
|
||||||
|
game.initializeGrid();
|
||||||
|
|
||||||
|
return game;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn animation(self: *GameOfLife) Animation {
|
||||||
|
return Animation.init(self, deinit, realloc, draw);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: *GameOfLife) void {
|
||||||
|
self.allocator.free(self.current_grid);
|
||||||
|
self.allocator.free(self.next_grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn realloc(self: *GameOfLife) anyerror!void {
|
||||||
|
const new_width = self.terminal_buffer.width;
|
||||||
|
const new_height = self.terminal_buffer.height;
|
||||||
|
const new_size = new_width * new_height;
|
||||||
|
|
||||||
|
const current_grid = try self.allocator.realloc(self.current_grid, new_size);
|
||||||
|
const next_grid = try self.allocator.realloc(self.next_grid, new_size);
|
||||||
|
|
||||||
|
self.current_grid = current_grid;
|
||||||
|
self.next_grid = next_grid;
|
||||||
|
self.width = new_width;
|
||||||
|
self.height = new_height;
|
||||||
|
|
||||||
|
self.initializeGrid();
|
||||||
|
self.generation = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *GameOfLife) void {
|
||||||
|
// Update game state at controlled frame rate
|
||||||
|
self.frame_counter += 1;
|
||||||
|
if (self.frame_counter >= self.frame_delay) {
|
||||||
|
self.frame_counter = 0;
|
||||||
|
self.updateGeneration();
|
||||||
|
self.generation += 1;
|
||||||
|
|
||||||
|
// Add entropy based on configuration (0 = disabled, >0 = interval)
|
||||||
|
if (self.entropy_interval > 0 and self.generation % self.entropy_interval == 0) {
|
||||||
|
self.addEntropy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render with the configured color
|
||||||
|
const alive_cell = Cell{ .ch = ALIVE_CHAR, .fg = self.fg_color, .bg = self.terminal_buffer.bg };
|
||||||
|
|
||||||
|
for (0..self.height) |y| {
|
||||||
|
const row_offset = y * self.width;
|
||||||
|
for (0..self.width) |x| {
|
||||||
|
const cell = if (self.current_grid[row_offset + x]) alive_cell else self.dead_cell;
|
||||||
|
cell.put(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn updateGeneration(self: *GameOfLife) void {
|
||||||
|
// Conway's Game of Life rules with optimized neighbor counting
|
||||||
|
for (0..self.height) |y| {
|
||||||
|
const row_offset = y * self.width;
|
||||||
|
for (0..self.width) |x| {
|
||||||
|
const index = row_offset + x;
|
||||||
|
const neighbors = self.countNeighborsOptimized(x, y);
|
||||||
|
const is_alive = self.current_grid[index];
|
||||||
|
|
||||||
|
// Optimized rule application
|
||||||
|
self.next_grid[index] = switch (neighbors) {
|
||||||
|
2 => is_alive,
|
||||||
|
3 => true,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Efficient grid swap
|
||||||
|
std.mem.swap([]bool, &self.current_grid, &self.next_grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn countNeighborsOptimized(self: *GameOfLife, x: usize, y: usize) u8 {
|
||||||
|
var count: u8 = 0;
|
||||||
|
|
||||||
|
for (NEIGHBOR_DIRS) |dir| {
|
||||||
|
const neighbor_x = @as(i32, @intCast(x)) + dir[0];
|
||||||
|
const neighbor_y = @as(i32, @intCast(y)) + dir[1];
|
||||||
|
const width_i32: i32 = @intCast(self.width);
|
||||||
|
const height_i32: i32 = @intCast(self.height);
|
||||||
|
|
||||||
|
// Toroidal wrapping with modular arithmetic
|
||||||
|
const wx: usize = @intCast(@mod(neighbor_x + width_i32, width_i32));
|
||||||
|
const wy: usize = @intCast(@mod(neighbor_y + height_i32, height_i32));
|
||||||
|
|
||||||
|
if (self.current_grid[wy * self.width + wx]) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initializeGrid(self: *GameOfLife) void {
|
||||||
|
const total_cells = self.width * self.height;
|
||||||
|
|
||||||
|
// Clear grid
|
||||||
|
@memset(self.current_grid, false);
|
||||||
|
@memset(self.next_grid, false);
|
||||||
|
|
||||||
|
// Random initialization with configurable density
|
||||||
|
for (0..total_cells) |i| {
|
||||||
|
self.current_grid[i] = self.terminal_buffer.random.float(f32) < self.initial_density;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addEntropy(self: *GameOfLife) void {
|
||||||
|
// Add fewer random cells but in clusters for more interesting patterns
|
||||||
|
const clusters = 2;
|
||||||
|
for (0..clusters) |_| {
|
||||||
|
const cx = self.terminal_buffer.random.intRangeAtMost(usize, 1, self.width - 2);
|
||||||
|
const cy = self.terminal_buffer.random.intRangeAtMost(usize, 1, self.height - 2);
|
||||||
|
|
||||||
|
// Small cluster around center point
|
||||||
|
for (0..3) |dy| {
|
||||||
|
for (0..3) |dx| {
|
||||||
|
if (self.terminal_buffer.random.float(f32) < 0.4) {
|
||||||
|
const x = (cx + dx) % self.width;
|
||||||
|
const y = (cy + dy) % self.height;
|
||||||
|
self.current_grid[y * self.width + x] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,10 @@ doom_bottom_color: u32 = 0x00FFFFFF,
|
|||||||
error_bg: u32 = 0x00000000,
|
error_bg: u32 = 0x00000000,
|
||||||
error_fg: u32 = 0x01FF0000,
|
error_fg: u32 = 0x01FF0000,
|
||||||
fg: u32 = 0x00FFFFFF,
|
fg: u32 = 0x00FFFFFF,
|
||||||
|
gameoflife_fg: u32 = 0x0000FF00,
|
||||||
|
gameoflife_entropy_interval: usize = 10,
|
||||||
|
gameoflife_frame_delay: usize = 6,
|
||||||
|
gameoflife_initial_density: f32 = 0.4,
|
||||||
hide_borders: bool = false,
|
hide_borders: bool = false,
|
||||||
hide_version_string: bool = false,
|
hide_version_string: bool = false,
|
||||||
hide_key_hints: bool = false,
|
hide_key_hints: bool = false,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ pub const Animation = enum {
|
|||||||
doom,
|
doom,
|
||||||
matrix,
|
matrix,
|
||||||
colormix,
|
colormix,
|
||||||
|
gameoflife,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const DisplayServer = enum {
|
pub const DisplayServer = enum {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const ColorMix = @import("animations/ColorMix.zig");
|
|||||||
const Doom = @import("animations/Doom.zig");
|
const Doom = @import("animations/Doom.zig");
|
||||||
const Dummy = @import("animations/Dummy.zig");
|
const Dummy = @import("animations/Dummy.zig");
|
||||||
const Matrix = @import("animations/Matrix.zig");
|
const Matrix = @import("animations/Matrix.zig");
|
||||||
|
const GameOfLife = @import("animations/GameOfLife.zig");
|
||||||
const Animation = @import("tui/Animation.zig");
|
const Animation = @import("tui/Animation.zig");
|
||||||
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
||||||
const Session = @import("tui/components/Session.zig");
|
const Session = @import("tui/components/Session.zig");
|
||||||
@@ -364,6 +365,10 @@ pub fn main() !void {
|
|||||||
var color_mix = ColorMix.init(&buffer, config.colormix_col1, config.colormix_col2, config.colormix_col3);
|
var color_mix = ColorMix.init(&buffer, config.colormix_col1, config.colormix_col2, config.colormix_col3);
|
||||||
animation = color_mix.animation();
|
animation = color_mix.animation();
|
||||||
},
|
},
|
||||||
|
.gameoflife => {
|
||||||
|
var game_of_life = try GameOfLife.init(allocator, &buffer, config.gameoflife_fg, config.gameoflife_entropy_interval, config.gameoflife_frame_delay, config.gameoflife_initial_density);
|
||||||
|
animation = game_of_life.animation();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
defer animation.deinit();
|
defer animation.deinit();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user