// by gera // public domain package hello_life import sdl "vendor:sdl3" ClearBoard :: proc(pixels : [][4]u8, color : [4]u8) {  for i in 0..= 0 && cell.y >= 0 && cell.x < w && cell.y < h {  pixels[cell.x + cell.y * w] = color  }  } } CountNeighbors :: proc(cells : ^map[[2]i32]bool, pos : [2]i32) -> i32 {  count : i32  count += i32(cells[{pos.x + 1, pos.y}])  count += i32(cells[{pos.x + 1, pos.y + 1}])  count += i32(cells[{pos.x, pos.y + 1}])  count += i32(cells[{pos.x - 1, pos.y + 1}])  count += i32(cells[{pos.x - 1, pos.y}])  count += i32(cells[{pos.x - 1, pos.y - 1}])  count += i32(cells[{pos.x, pos.y - 1}])  count += i32(cells[{pos.x + 1, pos.y - 1}])  return count } ApplyRulesOnCell :: proc(cellscur, cellsnext : ^map[[2]i32]bool, pos : [2]i32) {  neighbors := CountNeighbors(cellscur, pos)  alive := cellscur[pos]  if (alive && (neighbors < 2 || neighbors > 3)) do delete_key(cellsnext, pos)  else if (!alive && neighbors == 3) do cellsnext[pos] = true  else if (alive && (neighbors == 2 || neighbors == 3)) do cellsnext[pos] = true } ComputeLife :: proc(cellscur, cellsnext : ^map[[2]i32]bool) {  clear(cellsnext)  for cell in cellscur {  x := cell[0]  y := cell[1]  ApplyRulesOnCell(cellscur, cellsnext, {x, y})  ApplyRulesOnCell(cellscur, cellsnext, {x + 1, y})  ApplyRulesOnCell(cellscur, cellsnext, {x + 1, y + 1})  ApplyRulesOnCell(cellscur, cellsnext, {x, y + 1})  ApplyRulesOnCell(cellscur, cellsnext, {x - 1, y + 1})  ApplyRulesOnCell(cellscur, cellsnext, {x - 1, y})  ApplyRulesOnCell(cellscur, cellsnext, {x - 1, y - 1})  ApplyRulesOnCell(cellscur, cellsnext, {x, y - 1})  ApplyRulesOnCell(cellscur, cellsnext, {x + 1, y - 1})  } } main :: proc() {  result := sdl.Init({.VIDEO})  window : ^sdl.Window  renderer : ^sdl.Renderer  result = sdl.CreateWindowAndRenderer("Game of Life", width = 640, height = 480,  window_flags = {.RESIZABLE,},  window = &window, renderer = &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.PIXELART)  cells1 : map[[2]i32]bool  cells2 : map[[2]i32]bool  cellsfront := &cells1  cellsback := &cells2 /* initial state:  ****  *  *  * */  cellsfront[{0+texture.w/2, 0+texture.h/2}] = true  cellsfront[{1+texture.w/2, 0+texture.h/2}] = true  cellsfront[{2+texture.w/2, 0+texture.h/2}] = true  cellsfront[{3+texture.w/2, 0+texture.h/2}] = true  cellsfront[{1+texture.w/2, 1+texture.h/2}] = true  cellsfront[{2+texture.w/2, 2+texture.h/2}] = true  cellsfront[{2+texture.w/2, 3+texture.h/2}] = true  running : bool = true  time_start : u64  loop: for running {  time_start = sdl.GetTicksNS()  event : sdl.Event  result = sdl.PollEvent(&event)  #partial switch event.type {  case .QUIT:  running = false  break loop  }  pixels_rawptr : rawptr  pitch : i32  sdl.LockTexture(texture, nil, &pixels_rawptr, &pitch)  pixels := ([^][4]u8)(pixels_rawptr)[:texture.w * texture.h]  ClearBoard(pixels, {200, 200, 200, 255})  DrawCells(cellsfront, pixels, {0, 0, 255, 255}, 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_end := sdl.GetTicksNS()  // do more generations but maintain the target fps  for time_end - time_start < 1e9/30 {  ComputeLife(cellsfront, cellsback)  cellsfront, cellsback = cellsback, cellsfront  // also handle events so the window doesn't lag  result = sdl.PollEvent(&event)  #partial switch event.type {  case .QUIT:  running = false  break loop  }  time_end = sdl.GetTicksNS()  }  }  sdl.DestroyRenderer(renderer)  sdl.DestroyWindow(window)  sdl.Quit() }