Circuit simulation online
parent
878665056c
commit
dbf10e0aaa
|
@ -1,5 +1,4 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const assets = @import("assets");
|
|
||||||
|
|
||||||
fn is_circuit(tile: u8) bool {
|
fn is_circuit(tile: u8) bool {
|
||||||
return is_plug(tile) or is_conduit(tile) or is_switch(tile);
|
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);
|
(tile >= 176 and tile < 180);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_switch(tile: u8) bool {
|
pub fn is_switch(tile: u8) bool {
|
||||||
return tile >= 134 and tile < 136;
|
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 };
|
const Side = enum(u2) { up, right, down, left };
|
||||||
fn side(s: Side) u2 {
|
fn side(s: Side) u2 {
|
||||||
return @enumToInt(s);
|
return @enumToInt(s);
|
||||||
|
@ -51,10 +54,10 @@ fn get_inputs(tile: u8) Current {
|
||||||
178 => .{ true, false, true, true },
|
178 => .{ true, false, true, true },
|
||||||
179 => .{ true, true, true, false },
|
179 => .{ true, true, true, false },
|
||||||
// Plugs
|
// Plugs
|
||||||
149 => .{ false, false, true, false },
|
150 => .{ false, false, true, false },
|
||||||
150 => .{ true, false, false, false },
|
151 => .{ true, false, false, false },
|
||||||
151 => .{ false, false, false, true },
|
152 => .{ false, false, false, true },
|
||||||
152 => .{ false, true, false, false },
|
153 => .{ false, true, false, false },
|
||||||
// Closed switch
|
// Closed switch
|
||||||
134 => .{ true, false, true, false },
|
134 => .{ true, false, true, false },
|
||||||
else => .{ false, false, false, false },
|
else => .{ false, false, false, false },
|
||||||
|
@ -66,10 +69,10 @@ const Plugs = [4]bool;
|
||||||
fn get_plugs(tile: u8) Plugs {
|
fn get_plugs(tile: u8) Plugs {
|
||||||
return switch (tile) {
|
return switch (tile) {
|
||||||
// Plugs
|
// Plugs
|
||||||
149 => .{ true, false, false, false },
|
150 => .{ true, false, false, false },
|
||||||
150 => .{ false, false, true, false },
|
151 => .{ false, false, true, false },
|
||||||
151 => .{ false, true, false, false },
|
152 => .{ false, true, false, false },
|
||||||
152 => .{ false, false, false, true },
|
153 => .{ false, false, false, true },
|
||||||
// Cross
|
// Cross
|
||||||
146 => .{ true, true, true, true },
|
146 => .{ true, true, true, true },
|
||||||
else => .{ false, false, false, false },
|
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;
|
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));
|
const i = @intCast(usize, @mod(c[0], 20) + (c[1] * 20));
|
||||||
return if (assets.conduit[i] != 0) assets.conduit[i] - 1 else null;
|
return if (this.cells[i].tile != 0) this.cells[i].tile - 1 else null;
|
||||||
// return assets.conduit[i];
|
}
|
||||||
|
|
||||||
|
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 {
|
fn index2cell(i: usize) Cell {
|
||||||
|
@ -119,18 +127,26 @@ fn cell2index(c: Cell) ?usize {
|
||||||
return @intCast(usize, @mod(c[0], 20) + (c[1] * 20));
|
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 MAXCELLS = 400;
|
||||||
const CellMap = [MAXCELLS]CellState; // std.AutoHashMap(Cell, CellState);
|
const CellMap = [MAXCELLS]CellState; // std.AutoHashMap(Cell, CellState);
|
||||||
|
|
||||||
offset: Cell,
|
offset: Cell,
|
||||||
cells: CellMap,
|
cells: CellMap,
|
||||||
|
bridges: std.BoundedArray([2]Cell, 10),
|
||||||
|
|
||||||
pub fn init() @This() {
|
pub fn init(offset: Cell, map: []const u8) @This() {
|
||||||
return @This(){
|
var this = @This(){
|
||||||
.offset = Cell{ 0, 0 },
|
.offset = offset,
|
||||||
.cells = [1]CellState{false} ** 400,
|
.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 {
|
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 {
|
pub fn enable(this: *@This(), cell: Cell) void {
|
||||||
if (this.indexOf(cell)) |c| {
|
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);
|
q.insert(root);
|
||||||
while (q.remove()) |cell| {
|
while (q.remove()) |cell| {
|
||||||
const index = this.indexOf(cell) orelse continue;
|
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;
|
if (visited.isSet(index)) continue;
|
||||||
visited.set(index);
|
visited.set(index);
|
||||||
this.enable(cell);
|
this.enable(cell);
|
||||||
|
@ -173,8 +212,23 @@ pub fn fill(this: *@This(), root: Cell) void {
|
||||||
if (!conductor) continue;
|
if (!conductor) continue;
|
||||||
const s = @intToEnum(Side, i);
|
const s = @intToEnum(Side, i);
|
||||||
const delta = dir(s);
|
const delta = dir(s);
|
||||||
// w4.trace("side {} ({}), delta {}", .{ s, i, delta });
|
|
||||||
q.insert(cell + 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;
|
||||||
|
}
|
||||||
|
|
51
src/main.zig
51
src/main.zig
|
@ -115,7 +115,7 @@ const KB = 1024;
|
||||||
var heap: [8 * KB]u8 = undefined;
|
var heap: [8 * KB]u8 = undefined;
|
||||||
var fba = std.heap.FixedBufferAllocator.init(&heap);
|
var fba = std.heap.FixedBufferAllocator.init(&heap);
|
||||||
var world: World = World.init(fba.allocator());
|
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 anim_store = struct {
|
||||||
const stand = Anim.frame(0);
|
const stand = Anim.frame(0);
|
||||||
|
@ -191,6 +191,7 @@ export fn update() void {
|
||||||
world.process(1, &.{.pos}, velocityProcess);
|
world.process(1, &.{.pos}, velocityProcess);
|
||||||
world.process(1, &.{ .pos, .physics }, physicsProcess);
|
world.process(1, &.{ .pos, .physics }, physicsProcess);
|
||||||
world.processWithID(1, &.{ .pos, .control }, wireManipulationProcess);
|
world.processWithID(1, &.{ .pos, .control }, wireManipulationProcess);
|
||||||
|
world.processWithID(1, &.{ .pos, .control }, circuitManipulationProcess);
|
||||||
world.process(1, &.{.wire}, wirePhysicsProcess);
|
world.process(1, &.{.wire}, wirePhysicsProcess);
|
||||||
world.process(1, &.{ .pos, .control, .physics, .kinematic }, controlProcess);
|
world.process(1, &.{ .pos, .control, .physics, .kinematic }, controlProcess);
|
||||||
world.process(1, &.{ .pos, .kinematic }, kinematicProcess);
|
world.process(1, &.{ .pos, .kinematic }, kinematicProcess);
|
||||||
|
@ -198,6 +199,21 @@ export fn update() void {
|
||||||
world.process(1, &.{ .sprite, .controlAnim, .control }, controlAnimProcess);
|
world.process(1, &.{ .sprite, .controlAnim, .control }, controlAnimProcess);
|
||||||
world.process(1, &.{ .pos, .sprite }, drawProcess);
|
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;
|
w4.DRAW_COLORS.* = 0x0210;
|
||||||
for (assets.solid) |tilePlus, i| {
|
for (assets.solid) |tilePlus, i| {
|
||||||
const tile = tilePlus - 1;
|
const tile = tilePlus - 1;
|
||||||
|
@ -206,9 +222,10 @@ export fn update() void {
|
||||||
w4.blitSub(&assets.tiles, pos, .{ 8, 8 }, t, 128, .{ .bpp = .b2 });
|
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 (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 tile = tilePlus - 1;
|
||||||
const t = w4.Vec2{ @intCast(i32, (tile % 16) * 8), @intCast(i32, (tile / 16) * 8) };
|
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) };
|
const pos = w4.Vec2{ @intCast(i32, (i % 20) * 8), @intCast(i32, (i / 20) * 8) };
|
||||||
|
@ -241,6 +258,10 @@ export fn update() void {
|
||||||
time += 1;
|
time += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn world2cell(vec: Vec2f) Vec2 {
|
||||||
|
return vec2ftovec2(vec / @splat(2, @as(f32, 8)));
|
||||||
|
}
|
||||||
|
|
||||||
/// pos should be in tile coordinates, not world coordinates
|
/// pos should be in tile coordinates, not world coordinates
|
||||||
fn get_conduit(vec: Vec2) ?u8 {
|
fn get_conduit(vec: Vec2) ?u8 {
|
||||||
const x = vec[0];
|
const x = vec[0];
|
||||||
|
@ -250,6 +271,28 @@ fn get_conduit(vec: Vec2) ?u8 {
|
||||||
return assets.conduit[@intCast(u32, i)];
|
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 {
|
fn wireManipulationProcess(_: f32, id: usize, pos: *Pos, control: *Control) void {
|
||||||
var offset = switch (control.facing) {
|
var offset = switch (control.facing) {
|
||||||
.left => Vec2f{ -6, -4 },
|
.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)));
|
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 } };
|
indicator = .{ .t = .plug, .pos = mapPos * @splat(2, @as(i32, 8)) + Vec2{ 4, 4 } };
|
||||||
if (input.btnp(.one, .two)) {
|
if (input.btnp(.one, .two)) {
|
||||||
e.wire.?.nodes.slice()[details.which].pinned = true;
|
e.wire.?.nodes.slice()[details.which].pinned = true;
|
||||||
|
|
Loading…
Reference in New Issue