diff --git a/src/app/pages/algorithms/conway-gol/conway-gol.html b/src/app/pages/algorithms/conway-gol/conway-gol.html
index 185995a..7352516 100644
--- a/src/app/pages/algorithms/conway-gol/conway-gol.html
+++ b/src/app/pages/algorithms/conway-gol/conway-gol.html
@@ -6,23 +6,33 @@
+
+
+
-
-
-
+ @if (gameStarted())
+ {
+
+ } @else {
+
+ }
@@ -33,7 +43,7 @@
[(ngModel)]="gridRows"
[min]="MIN_GRID_SIZE"
[max]="MAX_GRID_SIZE"
- (ngModelChange)="genericGridComponent.gridRows = gridRows; genericGridComponent.applyGridSize()"
+ (ngModelChange)="pauseGame(); genericGridComponent.gridRows = gridRows; genericGridComponent.applyGridSize()"
/>
@@ -44,7 +54,7 @@
[(ngModel)]="gridCols"
[min]="MIN_GRID_SIZE"
[max]="MAX_GRID_SIZE"
- (ngModelChange)="genericGridComponent.gridCols = gridCols; genericGridComponent.applyGridSize()"
+ (ngModelChange)="pauseGame(); genericGridComponent.gridCols = gridCols; genericGridComponent.applyGridSize()"
/>
diff --git a/src/app/pages/algorithms/conway-gol/conway-gol.models.ts b/src/app/pages/algorithms/conway-gol/conway-gol.models.ts
index f2854d6..c582cc2 100644
--- a/src/app/pages/algorithms/conway-gol/conway-gol.models.ts
+++ b/src/app/pages/algorithms/conway-gol/conway-gol.models.ts
@@ -6,15 +6,18 @@ export interface Node {
export enum Scenario {
RANDOM = 0,
- EMPTY
+ EMPTY = 1,
+ SIMPLE = 2,
+ PULSAR = 3,
+ GUN = 4
}
-export const DEFAULT_GRID_ROWS = 100;
-export const DEFAULT_GRID_COLS = 100;
+export const DEFAULT_GRID_ROWS = 40;
+export const DEFAULT_GRID_COLS = 40;
export const MIN_GRID_SIZE = 20;
-export const MAX_GRID_SIZE = 200;
-export const DEFAULT_TIME_PER_GENERATION = 50;
+export const MAX_GRID_SIZE = 100;
+export const DEFAULT_TIME_PER_GENERATION = 30;
export const MIN_TIME_PER_GENERATION = 20;
export const MAX_TIME_PER_GENERATION = 200;
diff --git a/src/app/pages/algorithms/conway-gol/conway-gol.ts b/src/app/pages/algorithms/conway-gol/conway-gol.ts
index 5d6d524..9190bed 100644
--- a/src/app/pages/algorithms/conway-gol/conway-gol.ts
+++ b/src/app/pages/algorithms/conway-gol/conway-gol.ts
@@ -1,4 +1,4 @@
-import {AfterViewInit, Component, ViewChild} from '@angular/core';
+import {AfterViewInit, Component, signal, ViewChild} from '@angular/core';
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from "@angular/material/card";
import {TranslatePipe} from "@ngx-translate/core";
import {UrlConstants} from '../../../constants/UrlConstants';
@@ -8,7 +8,7 @@ import {MatButton} from '@angular/material/button';
import {MatIcon} from '@angular/material/icon';
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
-import {DEFAULT_GRID_COLS, DEFAULT_GRID_ROWS, MAX_GRID_SIZE, MIN_GRID_SIZE, MAX_GRID_PX, Node, LIVE_SPAWN_PROBABILITY, Scenario, MAX_TIME_PER_GENERATION, MIN_TIME_PER_GENERATION, DEFAULT_TIME_PER_GENERATION} from './conway-gol.models';
+import {DEFAULT_GRID_COLS, DEFAULT_GRID_ROWS, DEFAULT_TIME_PER_GENERATION, LIVE_SPAWN_PROBABILITY, MAX_GRID_PX, MAX_GRID_SIZE, MAX_TIME_PER_GENERATION, MIN_GRID_SIZE, MIN_TIME_PER_GENERATION, Node, Scenario} from './conway-gol.models';
import {GenericGridComponent, GridPos} from '../../../shared/components/generic-grid/generic-grid';
@Component({
@@ -55,7 +55,8 @@ export class ConwayGol implements AfterViewInit {
protected readonly MAX_GRID_PX = MAX_GRID_PX;
grid: Node[][] = [];
- currentScenario: Scenario = 0;
+ currentScenario: Scenario = Scenario.SIMPLE;
+ readonly gameStarted = signal(false);
@ViewChild(GenericGridComponent) genericGridComponent!: GenericGridComponent;
@@ -72,6 +73,7 @@ export class ConwayGol implements AfterViewInit {
this.genericGridComponent.maxGridPx = this.MAX_GRID_PX;
this.genericGridComponent.initializeGrid();
}
+ this.gameStarted.set(false);
}
generate(scene: Scenario): void {
@@ -107,9 +109,14 @@ export class ConwayGol implements AfterViewInit {
};
initializeConwayGrid = (grid: Node[][]): void => {
+ this.gameStarted.set(false);
this.grid = grid;
- if (this.currentScenario === Scenario.RANDOM) {
- this.setupRandomLives();
+
+ switch(this.currentScenario) {
+ case Scenario.RANDOM: this.setupRandomLives(); break;
+ case Scenario.SIMPLE: this.setupSimpleLive(); break;
+ case Scenario.PULSAR: this.setupPulsar(); break;
+ case Scenario.GUN: this.setupGliderGun(); break;
}
};
@@ -122,8 +129,128 @@ export class ConwayGol implements AfterViewInit {
}
}
+ setupSimpleLive(): void {
+ this.grid[3][4].alive = true;
+ this.grid[4][5].alive = true;
+ this.grid[5][3].alive = true;
+ this.grid[5][4].alive = true;
+ this.grid[5][5].alive = true;
+ }
+
+ setupPulsar(): void {
+ const centerRow = Math.floor(this.gridRows / 2);
+ const centerCol = Math.floor(this.gridCols / 2);
+
+ const rows = [-6, -1, 1, 6];
+ const offsets = [2, 3, 4];
+
+ rows.forEach(r => {
+ offsets.forEach(c => {
+ this.setAlive(centerRow + r, centerCol + c);
+ this.setAlive(centerRow + r, centerCol - c);
+ this.setAlive(centerRow + c, centerCol + r);
+ this.setAlive(centerRow - c, centerCol + r);
+ });
+ });
+ }
+
+ setupGliderGun(): void {
+ const r = 5;
+ const c = 5;
+
+ const dots = [
+ [r+4, c], [r+4, c+1], [r+5, c], [r+5, c+1], // Block links
+ [r+4, c+10], [r+5, c+10], [r+6, c+10], [r+3, c+11], [r+7, c+11],
+ [r+2, c+12], [r+8, c+12], [r+2, c+13], [r+8, c+13], [r+5, c+14],
+ [r+3, c+15], [r+7, c+15], [r+4, c+16], [r+5, c+16], [r+6, c+16], [r+5, c+17],
+ [r+2, c+20], [r+3, c+20], [r+4, c+20], [r+2, c+21], [r+3, c+21], [r+4, c+21],
+ [r+1, c+22], [r+5, c+22], [r+0, c+24], [r+1, c+24], [r+5, c+24], [r+6, c+24],
+ [r+2, c+34], [r+3, c+34], [r+2, c+35], [r+3, c+35]
+ ];
+
+ dots.forEach(([row, col]) => this.setAlive(row, col));
+ }
+
+ // --- The rules of the game
+
+ pauseGame(): void {
+ this.gameStarted.set(false);
+ }
+
+ async startGame(): Promise {
+ this.gameStarted.set(true);
+ let lifeIsDead = false;
+ while (this.gameStarted()){
+ let gridClone = structuredClone(this.grid);
+ lifeIsDead = true;
+ for (let row = 0; row < this.gridRows; row++) {
+ for (let col = 0; col < this.gridCols; col++) {
+ lifeIsDead = this.checkLifeRules(row, col, gridClone, lifeIsDead);
+ }
+ }
+
+ this.swapGrid(gridClone);
+ if (lifeIsDead){
+ this.gameStarted.set(false);
+ }
+ await this.delay(this.lifeSpeed);
+ }
+ }
+
+ private checkLifeRules(row: number, col: number, gridClone: Node[][], lifeIsDead: boolean) {
+ const itsMe = this.grid[row][col];
+ let aliveNeighbors = this.howManyNeighborsAreLiving(row, col);
+ if (itsMe.alive && (aliveNeighbors < 2 || aliveNeighbors > 3)) {
+ gridClone[row][col].alive = false;
+ lifeIsDead = false;
+ } else if (!itsMe.alive && aliveNeighbors === 3) {
+ gridClone[row][col].alive = true;
+ lifeIsDead = false;
+ }
+ return lifeIsDead;
+ }
+
+ private swapGrid(gridClone: Node[][]) {
+ this.grid = gridClone;
+ if (this.genericGridComponent) {
+ this.genericGridComponent.grid = this.grid;
+ this.genericGridComponent.drawGrid();
+ }
+ }
+
+ private howManyNeighborsAreLiving(row: number, col: number): number {
+
+ let aliveNeighborCount = 0;
+ const minRow = Math.max(row - 1, 0);
+ const minCol = Math.max(col - 1, 0);
+ const maxRow = Math.min(row + 1, this.gridRows - 1);
+ const maxCol = Math.min(col + 1, this.gridCols - 1);
+
+ for (let nRow = minRow; nRow <= maxRow; nRow++) {
+ for (let nCol = minCol; nCol <= maxCol; nCol++) {
+ if (nRow == row && nCol == col) {
+ continue;
+ }
+ if (this.grid[nRow][nCol].alive) {
+ aliveNeighborCount++;
+ }
+ }
+ }
+ return aliveNeighborCount;
+ }
+
// --- Other methods ---
protected readonly Scenario = Scenario;
protected readonly MIN_TIME_PER_GENERATION = MIN_TIME_PER_GENERATION;
protected readonly MAX_TIME_PER_GENERATION = MAX_TIME_PER_GENERATION;
+
+ delay(ms: number) {
+ return new Promise( resolve => setTimeout(resolve, ms) );
+ }
+
+ private setAlive(r: number, c: number): void {
+ if (r >= 0 && r < this.gridRows && c >= 0 && c < this.gridCols) {
+ this.grid[r][c].alive = true;
+ }
+ }
}
diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json
index 7e03d1a..638d321 100644
--- a/src/assets/i18n/de.json
+++ b/src/assets/i18n/de.json
@@ -343,8 +343,12 @@
"GOL": {
"TITLE": "Conway's Spiel des Lebens",
"START": "Starten",
+ "PAUSE": "Pause",
"RANDOM_SCENE": "Zufällig",
"EMPTY_SCENE": "Leer",
+ "SIMPLE_SCENE": "Simpel",
+ "PULSAR_SCENE": "Pulsar",
+ "GUN_SCENE": "Pistole",
"ALIVE": "Lebend",
"DEAD": "Leer",
"SPEED": "Zeit pro Generation",
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 8754e04..30b4f67 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -342,8 +342,12 @@
"GOL": {
"TITLE": "Conway's Game of Life",
"START": "Start",
+ "PAUSE": "Pause",
"RANDOM_SCENE": "Random",
"EMPTY_SCENE": "Empty",
+ "SIMPLE_SCENE": "Simple",
+ "PULSAR_SCENE": "Pulsar",
+ "GUN_SCENE": "Gun",
"ALIVE": "Alive",
"DEAD": "Empty",
"SPEED": "Time per Generation",