const std = @import("std"); const Rng = std.rand.DefaultPrng; const grid_scale = 2; const grid_width = 128; const grid_height = 128; const num_cells = grid_width * grid_height; var grid1 = std.mem.zeroes([num_cells]u8); var grid2 = std.mem.zeroes([num_cells]u8); // this is just a namespace const Grid = struct { const xpos = 0; const ypos = 24; var old: *[num_cells]u8 = &grid1; var new: *[num_cells]u8 = &grid2; fn get(x: i32, y: i32) u8 { // note x,y are signed const nx = x; const ny = y; if (nx < 0) return 0; if (nx >= grid_width) return 0; if (ny < 0) return 0; if (ny >= grid_height) return 0; return Grid.old.*[@intCast(ny * grid_width + nx)]; } fn set(x: u32, y: u32, val: u8) void { Grid.new.*[@intCast(y*grid_width + x)] = val; } fn flip() void { if (Grid.old == &grid1) { Grid.old = &grid2; Grid.new = &grid1; } else { Grid.old = &grid1; Grid.new = &grid2; } } }; const Mouse = struct { x: u32 = 0, y: u32 = 0, down: bool = false, click: bool = false }; var mouse = Mouse{}; const RGBA = extern struct { r: u8 = 0, g: u8 = 0, b: u8 = 0, a: u8 = 0 }; const img_width = Grid.xpos + grid_width*grid_scale; const img_height = Grid.ypos + grid_height*grid_scale; export var img_data = std.mem.zeroes([img_height][img_width]RGBA); export fn img_xsize() usize { return img_width; } export fn img_ysize() usize { return img_height; } const bgcol = RGBA{.r=0xff, .g=0xf8, .b=0xf0}; const col_black = RGBA{.r=0, .g=0, .b=0}; const col_grey = RGBA{.r=0xd0, .g=0xd0, .b=0xd0}; var neighbours_live_to_live: [9]bool = .{false} ** 9; var neighbours_dead_to_live: [9]bool = .{false} ** 9; var tock = false; fn set_pixel(x: u32, y: u32, r: u8, g: u8, b: u8) void { const pixel = &img_data[y][x]; pixel.r = r; pixel.g = g; pixel.b = b; pixel.a = 255; } fn set(x: u32, y: u32, val: u8) void { if (x >= grid_width) return; if (y >= grid_height) return; Grid.set(x, y, val); var r: u8 = bgcol.r; var g: u8 = bgcol.g; var b: u8 = bgcol.b; if (val == 1) { r = 128; g = 160; b = 128; } for (0..grid_scale) |gx| { for (0..grid_scale) |gy| { set_pixel(Grid.xpos + grid_scale*x + gx, Grid.ypos + grid_scale*y + gy, r, g, b); } } } fn initGrid() void { var rnd = Rng.init(0); for (0..num_cells) |i| { const x = i % grid_width; const y = i / grid_width; const rand = rnd.random().int(u8) < 128; Grid.old.*[i] = if (rand and (x > 16) and (y > 16) and (x < 112) and (y < 112)) 1 else 0; } } export fn init() void { for (0..img_width) |x| { for (0..img_height) |y| { img_data[y][x].r = 240; img_data[y][x].g = 240; img_data[y][x].b = 240; img_data[y][x].a = 255; } } initGrid(); neighbours_live_to_live[4] = true; neighbours_live_to_live[5] = true; neighbours_live_to_live[6] = true; neighbours_live_to_live[7] = true; neighbours_live_to_live[8] = true; neighbours_dead_to_live[5] = true; neighbours_dead_to_live[6] = true; neighbours_dead_to_live[7] = true; neighbours_dead_to_live[8] = true; } export fn tick(mouse_x: u32, mouse_y: u32, mouse_down: bool) void { mouse.x = mouse_x; mouse.y = mouse_y; if (mouse_down) { mouse.click = !mouse.down; } else { mouse.click = false; } mouse.down = mouse_down; tock = !tock; if (tock) { // Update cells for (0..grid_width) |cx| { for (0..grid_height) |cy| { // have to do this cast as the type of cx/cy is fixed as usize, issue #17039 const x: i32 = @intCast(cx); const y: i32 = @intCast(cy); // Count neighbours var n: u32 = 0; n += Grid.get(x-1,y-1) + Grid.get(x, y-1) + Grid.get(x+1,y-1); n += Grid.get(x-1,y) + Grid.get(x+1,y); n += Grid.get(x-1,y+1) + Grid.get(x, y+1) + Grid.get(x+1,y+1); var live: u8 = Grid.get(x, y); if (live == 1) { live = 0; if (neighbours_live_to_live[n]) live = 1; } else { if (neighbours_dead_to_live[n]) live = 1; } set(@intCast(x), @intCast(y), live); } } Grid.flip(); } // Handle mouse const mx_grid = (mouse.x - Grid.xpos)/grid_scale; const my_grid = (mouse.y - Grid.ypos)/grid_scale; if (mouse.down) set(mx_grid, my_grid, 1); draw_text(0, 0, " birth:", col_black); draw_text(0, 14, "survive:", col_black); var changed = false; for (0..9) |n| { var buf: [32]u8 = undefined; const str = std.fmt.bufPrint(&buf, "{}", .{n}) catch unreachable; changed = changed or toggle_box(&neighbours_dead_to_live[n], 72+12*n, 0, str); changed = changed or toggle_box(&neighbours_live_to_live[n], 72+12*n, 14, str); } if (changed) initGrid(); } const font = [_]u64{ 0x0000000000000000, 0x0004000404040404, 0x000000000024486c, 0x0024247e247e2424, 0x00103c5038147810, 0x00609468102c520c, 0x005c22520c042418, 0x0000000000081018, 0x0020100808081020, 0x0004081010100804, 0x0000105438541000, 0x000010107c101000, 0x04080c0000000000, 0x000000007e000000, 0x0006000000000000, 0x0002040810204080, 0x0018244242422418, 0x0010101010101418, 0x007e020c3040423c, 0x003c42403840423c, 0x00207e2224081020, 0x003c4240423e027e, 0x003c42423e020438, 0x000808101020407e, 0x003c42423c42423c, 0x001820407c42423c, 0x0006000000060000, 0x0204060000060000, 0x0020100804081020, 0x0000007e007e0000, 0x0004081020100804, 0x000800081020221c, 0x007804f2aab24438, 0x0042423c24241818, 0x003e42423e42423e, 0x0078040202020478, 0x001e22424242221e, 0x007e02023e02027e, 0x000202023e02027e, 0x0078444272020478, 0x004242427e424242, 0x0010101010101010, 0x1820202020202020, 0x004222120e122242, 0x007e020202020202, 0x00829292aaaac6c6, 0x00626252524a4646, 0x0018244242422418, 0x000202023e42423e, 0x4038244242422418, 0x004222123e42423e, 0x003c42403c02423c, 0x00101010101010fe, 0x003c424242424242, 0x0018182424244242, 0x00c6c6aaaa929282, 0x0082442810284482, 0x0010101010284482, 0x007e02041820407e, 0x003c04040404043c, 0x0080402010080402, 0x003c20202020203c, 0x00101010107c3810, 0xff00000000000000, 0x007e04041e044438, 0x007c427c403c0000, 0x003e4242463a0202, 0x0078040404780000, 0x005c6242427c4040, 0x007c027e423c0000, 0x00080808083e0870, 0x3c407c42625c0000, 0x00424242463a0202, 0x0010101010180010, 0x1820202020200020, 0x0044241c24440404, 0x0010101010101018, 0x00929292926e0000, 0x00424242463a0000, 0x003c4242423c0000, 0x02023e42463a0000, 0x40405c62427c0000, 0x00020202463a0000, 0x003e403c027c0000, 0x00700808083e0808, 0x005c624242420000, 0x0018182424420000, 0x0028285454820000, 0x0042241824420000, 0x3c407c4242420000, 0x007e0418207e0000, 0x007010100c101070, 0x0010101010101010, 0x000e08083008080e, 0x000000000000324c, 0x3c4299858599423c, }; const font_width = 8; const font_height = 8; fn draw_text(x: u32, y: u32, text: []const u8, col: RGBA) void { var mx = x; for (text) |char| { const bitmap: u64 = font[char-32]; var mask: u64 = 1; for (0..font_height) |cy| { for (0..font_width) |cx| { if (bitmap & mask != 0) { img_data[y+cy][mx+cx].r = col.r; img_data[y+cy][mx+cx].g = col.g; img_data[y+cy][mx+cx].b = col.b; } mask = mask << 1; } } mx += font_width; } } fn toggle_box(val: *bool, x: u32, y: u32, text: []const u8) bool { const clicked = (mouse.click and mouse.x >= x and mouse.x < x+font_width*text.len and mouse.y >= y and mouse.y < y+font_height); if (clicked) { val.* = !val.*; } draw_text(x, y, text, if (val.*) col_black else col_grey); return clicked; }