Created
May 25, 2026 01:19
-
-
Save paigeadelethompson/bb33089de4250d62c7d2821dbf034181 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ;;; conway.ss - Conway's Game of Life | |
| ;;; Renders the classic cellular automaton on an infinite toroidal grid using ANSI terminal codes. | |
| (export main) | |
| ;; Import standard utility modules | |
| (import :std/iter ; For imperative loops (for, in-range) | |
| :std/sugar ; Syntactic sugar additions | |
| :std/srfi/1 ; List processing library | |
| :std/misc/threads) ; For frame-rate limiting (thread-sleep!) | |
| ;; --- 1. CONFIGURATION --- | |
| ;; Grid size variables for the display window | |
| (def *rows* 20) | |
| (def *cols* 40) | |
| ;; --- 2. GRID CREATION & CORE ENGINE UTILITIES --- | |
| (def (make-grid rows cols) | |
| ;; Allocates a fresh 2D array matrix: a top-level vector containing child vectors | |
| (let (grid (make-vector rows)) | |
| (for (i (in-range rows)) | |
| ;; Initialize every individual cell position to 0 (dead) | |
| (vector-set! grid i (make-vector cols 0))) | |
| grid)) | |
| (def (random-grid rows cols) | |
| ;; Generates a seed matrix where each cell has a 20% statistical chance of living | |
| (let (grid (make-grid rows cols)) | |
| (for (r (in-range rows)) | |
| (for (c (in-range cols)) | |
| ;; random-integer rolls a number from 0 to 99 | |
| (when (< (random-integer 100) 20) | |
| (vector-set! (vector-ref grid r) c 1)))) | |
| grid)) | |
| (def (grid-ref grid r c) | |
| ;; Retrieves cell state with boundary wrapping. | |
| ;; The 'modulo' logic binds edges together so cells wrap like a donut (torus). | |
| (let ((wrapped-r (modulo r *rows*)) | |
| (wrapped-c (modulo c *cols*))) | |
| (vector-ref (vector-ref grid wrapped-r) wrapped-c))) | |
| ;; --- 3. GAME OF LIFE STATE LAWS --- | |
| (def (count-neighbors grid r c) | |
| ;; Aggregates the living integer states (0 or 1) of all 8 bordering locations. | |
| ;; Uses (1- x) for subtraction and (1+ x) for addition to shift index locations. | |
| (+ (grid-ref grid (1- r) (1- c)) (grid-ref grid (1- r) c) (grid-ref grid (1- r) (1+ c)) ; Top row offset | |
| (grid-ref grid r (1- c)) (grid-ref grid r (1+ c)) ; Center row offset | |
| (grid-ref grid (1+ r) (1- c)) (grid-ref grid (1+ r) c) (grid-ref grid (1+ r) (1+ c)))) ; Bottom row offset | |
| (def (next-generation current-grid) | |
| ;; Processes and isolates the next full grid state based on the active canvas | |
| (let (new-grid (make-grid *rows* *cols*)) | |
| (for (r (in-range *rows*)) | |
| (for (c (in-range *cols*)) | |
| (let ((neighbors (count-neighbors current-grid r c)) | |
| (alive? (= (grid-ref current-grid r c) 1))) | |
| ;; Evaluate Conway's rules using Scheme conditional branches | |
| (cond | |
| ;; Rule 1 & 3: Overpopulation or Underpopulation causes cell failure | |
| ((and alive? (or (< neighbors 2) (> neighbors 3))) | |
| (vector-set! (vector-ref new-grid r) c 0)) | |
| ;; Rule 2: Optimal cell clustering allows life to sustain | |
| ((and alive? (or (= neighbors 2) (= neighbors 3))) | |
| (vector-set! (vector-ref new-grid r) c 1)) | |
| ;; Rule 4: Dead zone with exactly 3 neighbors sparks cell birth | |
| ((and (not alive?) (= neighbors 3)) | |
| (vector-set! (vector-ref new-grid r) c 1)) | |
| ;; Baseline: Maintain existing death states if conditions aren't met | |
| (else | |
| (vector-set! (vector-ref new-grid r) c 0)))))) | |
| new-grid)) | |
| ;; --- 4. RENDER DRIVER & RUNTIME LOOP --- | |
| (def (display-grid grid) | |
| ;; Updates terminal window frame by shifting the cursor back home instead of scrolling | |
| (write-vector #u8(27 91 72)) ; ANSI Escape code: Home cursor positioning | |
| (for (r (in-range *rows*)) | |
| (for (c (in-range *cols*)) | |
| ;; Render visual blocks for alive cells, blank strings for dead ones | |
| (if (= (grid-ref grid r c) 1) | |
| (write-vector #u8(226 150 136)) | |
| (write-byte 32))) | |
| (newline))) | |
| (def (game-loop grid) | |
| ;; Infinite driver cycle processing the evolution timeline step by step | |
| (display-grid grid) | |
| (thread-sleep! 0.1) ; Rest cycle of 100 milliseconds to control simulation speed | |
| (game-loop (next-generation grid))) | |
| ;; Global execution endpoint compiled down | |
| (def (main . args) | |
| (write-vector #u8(27 91 50 74)) ; ANSI Escape code: Initial hard screen clear | |
| (game-loop (random-grid *rows* *cols*))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment