From dbf10e0aaa99aede73990ce81bbba41119e41ad3 Mon Sep 17 00:00:00 2001 From: Louis Pearson Date: Wed, 19 Jan 2022 22:05:30 -0700 Subject: [PATCH] Circuit simulation online --- src/circuit.zig | 96 ++++++++++++++++++++++++++++++++++++++----------- src/main.zig | 51 +++++++++++++++++++++++--- 2 files changed, 122 insertions(+), 25 deletions(-) diff --git a/src/circuit.zig b/src/circuit.zig index 2a0680e..b610f7c 100644 --- a/src/circuit.zig +++ b/src/circuit.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const assets = @import("assets"); fn is_circuit(tile: u8) bool { return is_plug(tile) or is_conduit(tile) or is_switch(tile); @@ -16,10 +15,14 @@ fn is_conduit(tile: u8) bool { (tile >= 176 and tile < 180); } -fn is_switch(tile: u8) bool { +pub fn is_switch(tile: u8) bool { return tile >= 134 and tile < 136; } +fn toggle_switch(tile: u8) u8 { + return if (tile == 134) 135 else 134; +} + const Side = enum(u2) { up, right, down, left }; fn side(s: Side) u2 { return @enumToInt(s); @@ -51,10 +54,10 @@ fn get_inputs(tile: u8) Current { 178 => .{ true, false, true, true }, 179 => .{ true, true, true, false }, // Plugs - 149 => .{ false, false, true, false }, - 150 => .{ true, false, false, false }, - 151 => .{ false, false, false, true }, - 152 => .{ false, true, false, false }, + 150 => .{ false, false, true, false }, + 151 => .{ true, false, false, false }, + 152 => .{ false, false, false, true }, + 153 => .{ false, true, false, false }, // Closed switch 134 => .{ true, false, true, false }, else => .{ false, false, false, false }, @@ -66,10 +69,10 @@ const Plugs = [4]bool; fn get_plugs(tile: u8) Plugs { return switch (tile) { // Plugs - 149 => .{ true, false, false, false }, - 150 => .{ false, false, true, false }, - 151 => .{ false, true, false, false }, - 152 => .{ false, false, false, true }, + 150 => .{ true, false, false, false }, + 151 => .{ false, false, true, false }, + 152 => .{ false, true, false, false }, + 153 => .{ false, false, false, true }, // Cross 146 => .{ true, true, true, true }, else => .{ false, false, false, false }, @@ -103,11 +106,16 @@ fn dir(s: Side) Cell { }; } -pub fn get_cell(c: Cell) ?u8 { +pub fn get_cell(this: @This(), c: Cell) ?u8 { if (c[0] < 0 or c[0] > 19 or c[1] > 19 or c[1] < 0) return null; const i = @intCast(usize, @mod(c[0], 20) + (c[1] * 20)); - return if (assets.conduit[i] != 0) assets.conduit[i] - 1 else null; - // return assets.conduit[i]; + return if (this.cells[i].tile != 0) this.cells[i].tile - 1 else null; +} + +pub fn set_cell(this: *@This(), c: Cell, tile: u8) void { + if (c[0] < 0 or c[0] > 19 or c[1] > 19 or c[1] < 0) return; + const i = @intCast(usize, @mod(c[0], 20) + (c[1] * 20)); + this.cells[i].tile = tile + 1; } fn index2cell(i: usize) Cell { @@ -119,18 +127,26 @@ fn cell2index(c: Cell) ?usize { return @intCast(usize, @mod(c[0], 20) + (c[1] * 20)); } -const CellState = bool; +const CellState = struct { enabled: bool = false, tile: u8 }; const MAXCELLS = 400; const CellMap = [MAXCELLS]CellState; // std.AutoHashMap(Cell, CellState); offset: Cell, cells: CellMap, +bridges: std.BoundedArray([2]Cell, 10), -pub fn init() @This() { - return @This(){ - .offset = Cell{ 0, 0 }, - .cells = [1]CellState{false} ** 400, +pub fn init(offset: Cell, map: []const u8) @This() { + var this = @This(){ + .offset = offset, + .cells = undefined, + .bridges = std.BoundedArray([2]Cell, 10).init(0) catch unreachable, }; + // TODO: copy only part of a map + for (map) |tile, i| { + this.cells[i].enabled = false; + this.cells[i].tile = tile; + } + return this; } pub fn indexOf(this: @This(), cell: Cell) ?usize { @@ -139,7 +155,30 @@ pub fn indexOf(this: @This(), cell: Cell) ?usize { pub fn enable(this: *@This(), cell: Cell) void { if (this.indexOf(cell)) |c| { - this.cells[c] = true; + this.cells[c].enabled = true; + } +} + +pub fn bridge(this: *@This(), cells: [2]Cell) void { + if (this.indexOf(cells[0])) |_| { + if (this.indexOf(cells[1])) |_| { + this.bridges.append(cells) catch unreachable; + } + } +} + +pub fn enabled(this: @This(), cell: Cell) bool { + if (this.indexOf(cell)) |c| { + return this.cells[c].enabled; + } + return false; +} + +pub fn toggle(this: *@This(), cell: Cell) void { + if (this.get_cell(cell)) |tile| { + if (is_switch(tile)) { + this.set_cell(cell, toggle_switch(tile)); + } } } @@ -165,7 +204,7 @@ pub fn fill(this: *@This(), root: Cell) void { q.insert(root); while (q.remove()) |cell| { const index = this.indexOf(cell) orelse continue; - const tile = get_cell(cell) orelse continue; + const tile = this.get_cell(cell) orelse continue; if (visited.isSet(index)) continue; visited.set(index); this.enable(cell); @@ -173,8 +212,23 @@ pub fn fill(this: *@This(), root: Cell) void { if (!conductor) continue; const s = @intToEnum(Side, i); const delta = dir(s); - // w4.trace("side {} ({}), delta {}", .{ s, i, delta }); q.insert(cell + delta); } + if (is_plug(tile)) { + for (this.bridges.constSlice()) |b| { + if (@reduce(.And, b[0] == cell)) { + q.insert(b[1]); + } else if (@reduce(.And, b[1] == cell)) { + q.insert(b[0]); + } + } + } } } + +pub fn clear(this: *@This()) void { + for (this.cells) |*cell| { + cell.enabled = false; + } + this.bridges.resize(0) catch unreachable; +} diff --git a/src/main.zig b/src/main.zig index 1298860..58dbad5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -115,7 +115,7 @@ const KB = 1024; var heap: [8 * KB]u8 = undefined; var fba = std.heap.FixedBufferAllocator.init(&heap); var world: World = World.init(fba.allocator()); -var circuit = Circuit.init(); +var circuit = Circuit.init(Vec2{ 0, 0 }, &assets.conduit); const anim_store = struct { const stand = Anim.frame(0); @@ -191,6 +191,7 @@ export fn update() void { world.process(1, &.{.pos}, velocityProcess); world.process(1, &.{ .pos, .physics }, physicsProcess); world.processWithID(1, &.{ .pos, .control }, wireManipulationProcess); + world.processWithID(1, &.{ .pos, .control }, circuitManipulationProcess); world.process(1, &.{.wire}, wirePhysicsProcess); world.process(1, &.{ .pos, .control, .physics, .kinematic }, controlProcess); world.process(1, &.{ .pos, .kinematic }, kinematicProcess); @@ -198,6 +199,21 @@ export fn update() void { world.process(1, &.{ .sprite, .controlAnim, .control }, controlAnimProcess); world.process(1, &.{ .pos, .sprite }, drawProcess); + circuit.clear(); + const q = World.Query.require(&.{.wire}); + var wireIter = world.iter(q); + while (wireIter.next()) |wireID| { + const e = world.get(wireID); + const nodes = e.wire.?.nodes.constSlice(); + const cellBegin = world2cell(nodes[0].pos); + const cellEnd = world2cell(nodes[nodes.len - 1].pos); + + circuit.bridge(.{ cellBegin, cellEnd }); + } + for (assets.sources) |source| { + circuit.fill(source); + } + w4.DRAW_COLORS.* = 0x0210; for (assets.solid) |tilePlus, i| { const tile = tilePlus - 1; @@ -206,9 +222,10 @@ export fn update() void { w4.blitSub(&assets.tiles, pos, .{ 8, 8 }, t, 128, .{ .bpp = .b2 }); } - for (assets.conduit) |tilePlus, i| { + for (circuit.cells) |cell, i| { + const tilePlus = cell.tile; if (tilePlus == 0) continue; - if (circuit.cells[i]) w4.DRAW_COLORS.* = 0x0210 else w4.DRAW_COLORS.* = 0x0310; + if (circuit.cells[i].enabled) w4.DRAW_COLORS.* = 0x0210 else w4.DRAW_COLORS.* = 0x0310; const tile = tilePlus - 1; const t = w4.Vec2{ @intCast(i32, (tile % 16) * 8), @intCast(i32, (tile / 16) * 8) }; const pos = w4.Vec2{ @intCast(i32, (i % 20) * 8), @intCast(i32, (i / 20) * 8) }; @@ -241,6 +258,10 @@ export fn update() void { time += 1; } +fn world2cell(vec: Vec2f) Vec2 { + return vec2ftovec2(vec / @splat(2, @as(f32, 8))); +} + /// pos should be in tile coordinates, not world coordinates fn get_conduit(vec: Vec2) ?u8 { const x = vec[0]; @@ -250,6 +271,28 @@ fn get_conduit(vec: Vec2) ?u8 { return assets.conduit[@intCast(u32, i)]; } +fn circuitManipulationProcess(_: f32, id: usize, pos: *Pos, control: *Control) void { + _ = id; + var offset = switch (control.facing) { + .left => Vec2f{ -6, -4 }, + .right => Vec2f{ 6, -4 }, + .up => Vec2f{ 0, -12 }, + .down => Vec2f{ 0, 4 }, + }; + if (control.grabbing == null) { + const mapPos = vec2ftovec2(pos.pos + offset); + const cell = @divTrunc(mapPos, @splat(2, @as(i32, 8))); + if (circuit.get_cell(cell)) |tile| { + if (Circuit.is_switch(tile)) { + indicator = .{ .t = .plug, .pos = cell * @splat(2, @as(i32, 8)) + Vec2{ 4, 4 } }; + if (input.btnp(.one, .two)) { + circuit.toggle(cell); + } + } + } + } +} + fn wireManipulationProcess(_: f32, id: usize, pos: *Pos, control: *Control) void { var offset = switch (control.facing) { .left => Vec2f{ -6, -4 }, @@ -277,7 +320,7 @@ fn wireManipulationProcess(_: f32, id: usize, pos: *Pos, control: *Control) void } var mapPos = vec2ftovec2((pos.pos + offset) / @splat(2, @as(f32, 8))); - if (Circuit.is_plug(Circuit.get_cell(mapPos) orelse 0)) { + if (Circuit.is_plug(circuit.get_cell(mapPos) orelse 0)) { indicator = .{ .t = .plug, .pos = mapPos * @splat(2, @as(i32, 8)) + Vec2{ 4, 4 } }; if (input.btnp(.one, .two)) { e.wire.?.nodes.slice()[details.which].pinned = true;