\begin{Verbatim}[commandchars=\\\{\}] // by gera // public domain package hello\PYZus{}life import \PYZdq{}core:slice\PYZdq{} import sdl \PYZdq{}vendor:sdl3\PYZdq{} ClearBoard :: proc(pixels : [][4]u8, color : [4]u8) \PYZob{} for i in 0..\PYZlt{}len(pixels) \PYZob{} pixels[i] = color \PYZcb{} \PYZcb{} DrawCells :: proc(cells : \PYZca{}map[[2]i32]bool, pixels : [][4]u8, color : [4]u8, w, h : i32) \PYZob{} for cell in cells \PYZob{} if cell.x \PYZgt{}= 0 \PYZam{}\PYZam{} cell.y \PYZgt{}= 0 \PYZam{}\PYZam{} cell.x \PYZlt{} w \PYZam{}\PYZam{} cell.y \PYZlt{} h \PYZob{} pixels[cell.x + cell.y * w] = color \PYZcb{} \PYZcb{} \PYZcb{} CountNeighbors :: proc(cells : \PYZca{}map[[2]i32]bool, pos : [2]i32) \PYZhy{}\PYZgt{} i32 \PYZob{} count : i32 count += i32(cells[\PYZob{}pos.x + 1, pos.y\PYZcb{}]) count += i32(cells[\PYZob{}pos.x + 1, pos.y + 1\PYZcb{}]) count += i32(cells[\PYZob{}pos.x, pos.y + 1\PYZcb{}]) count += i32(cells[\PYZob{}pos.x \PYZhy{} 1, pos.y + 1\PYZcb{}]) count += i32(cells[\PYZob{}pos.x \PYZhy{} 1, pos.y\PYZcb{}]) count += i32(cells[\PYZob{}pos.x \PYZhy{} 1, pos.y \PYZhy{} 1\PYZcb{}]) count += i32(cells[\PYZob{}pos.x, pos.y \PYZhy{} 1\PYZcb{}]) count += i32(cells[\PYZob{}pos.x + 1, pos.y \PYZhy{} 1\PYZcb{}]) return count \PYZcb{} ApplyRulesOnCell :: proc(cellscur, cellsnext : \PYZca{}map[[2]i32]bool, pos : [2]i32) \PYZob{} neighbors := CountNeighbors(cellscur, pos) alive := cellscur[pos] if (alive \PYZam{}\PYZam{} (neighbors \PYZlt{} 2 || neighbors \PYZgt{} 3)) do delete\PYZus{}key(cellsnext, pos) else if (!alive \PYZam{}\PYZam{} neighbors == 3) do cellsnext[pos] = true else if (alive \PYZam{}\PYZam{} (neighbors == 2 || neighbors == 3)) do cellsnext[pos] = true \PYZcb{} ComputeLife :: proc(cellscur, cellsnext : \PYZca{}map[[2]i32]bool) \PYZob{} clear(cellsnext) for cell in cellscur \PYZob{} x := cell[0] y := cell[1] ApplyRulesOnCell(cellscur, cellsnext, \PYZob{}x, y\PYZcb{}) ApplyRulesOnCell(cellscur, cellsnext, \PYZob{}x + 1, y\PYZcb{}) ApplyRulesOnCell(cellscur, cellsnext, \PYZob{}x + 1, y + 1\PYZcb{}) ApplyRulesOnCell(cellscur, cellsnext, \PYZob{}x, y + 1\PYZcb{}) ApplyRulesOnCell(cellscur, cellsnext, \PYZob{}x \PYZhy{} 1, y + 1\PYZcb{}) ApplyRulesOnCell(cellscur, cellsnext, \PYZob{}x \PYZhy{} 1, y\PYZcb{}) ApplyRulesOnCell(cellscur, cellsnext, \PYZob{}x \PYZhy{} 1, y \PYZhy{} 1\PYZcb{}) ApplyRulesOnCell(cellscur, cellsnext, \PYZob{}x, y \PYZhy{} 1\PYZcb{}) ApplyRulesOnCell(cellscur, cellsnext, \PYZob{}x + 1, y \PYZhy{} 1\PYZcb{}) \PYZcb{} \PYZcb{} main :: proc() \PYZob{} result := sdl.Init(\PYZob{}.VIDEO\PYZcb{}) window : \PYZca{}sdl.Window renderer : \PYZca{}sdl.Renderer result = sdl.CreateWindowAndRenderer(\PYZdq{}Game of Life\PYZdq{}, width = 640, height = 480, window\PYZus{}flags = \PYZob{}.RESIZABLE,\PYZcb{}, window = \PYZam{}window, renderer = \PYZam{}renderer) texture := sdl.CreateTexture(renderer, sdl.PixelFormat.ABGR8888, sdl.TextureAccess.STREAMING, 320, 240) sdl.SetRenderLogicalPresentation(renderer, texture.w, texture.h, sdl.RendererLogicalPresentation.LETTERBOX) sdl.SetTextureScaleMode(texture, sdl.ScaleMode.NEAREST) cells1 : map[[2]i32]bool cells2 : map[[2]i32]bool cellsfront := \PYZam{}cells1 cellsback := \PYZam{}cells2 /* initial state: **** * * * */ cellsfront[\PYZob{}0+texture.w/2, 0+texture.h/2\PYZcb{}] = true cellsfront[\PYZob{}1+texture.w/2, 0+texture.h/2\PYZcb{}] = true cellsfront[\PYZob{}2+texture.w/2, 0+texture.h/2\PYZcb{}] = true cellsfront[\PYZob{}3+texture.w/2, 0+texture.h/2\PYZcb{}] = true cellsfront[\PYZob{}1+texture.w/2, 1+texture.h/2\PYZcb{}] = true cellsfront[\PYZob{}2+texture.w/2, 2+texture.h/2\PYZcb{}] = true cellsfront[\PYZob{}2+texture.w/2, 3+texture.h/2\PYZcb{}] = true running : bool = true time\PYZus{}start : u64 loop: for running \PYZob{} time\PYZus{}start = sdl.GetTicksNS() event : sdl.Event result = sdl.PollEvent(\PYZam{}event) \PYZsh{}partial switch event.type \PYZob{} case .QUIT: running = false break loop \PYZcb{} pixels\PYZus{}rawptr : rawptr pitch : i32 sdl.LockTexture(texture, nil, \PYZam{}pixels\PYZus{}rawptr, \PYZam{}pitch) pixels := slice.from\PYZus{}ptr(([\PYZca{}][4]u8)(pixels\PYZus{}rawptr), int(texture.w) * int(texture.h)) ClearBoard(pixels, \PYZob{}200, 200, 200, 255\PYZcb{}) DrawCells(cellsfront, pixels, \PYZob{}0, 0, 255, 255\PYZcb{}, texture.w, texture.h) sdl.UnlockTexture(texture) sdl.RenderClear(renderer) sdl.RenderTexture(renderer, texture, nil, nil) sdl.RenderPresent(renderer) ComputeLife(cellsfront, cellsback) cellsfront, cellsback = cellsback, cellsfront time\PYZus{}end := sdl.GetTicksNS() // do more generations but maintain the target fps for time\PYZus{}end \PYZhy{} time\PYZus{}start \PYZlt{} 1e9/30 \PYZob{} ComputeLife(cellsfront, cellsback) cellsfront, cellsback = cellsback, cellsfront // also handle events so the window doesn\PYZsq{}t lag result = sdl.PollEvent(\PYZam{}event) \PYZsh{}partial switch event.type \PYZob{} case .QUIT: running = false break loop \PYZcb{} time\PYZus{}end = sdl.GetTicksNS() \PYZcb{} \PYZcb{} sdl.DestroyRenderer(renderer) sdl.DestroyWindow(window) sdl.Quit() \PYZcb{} \end{Verbatim}