From a35d481b151e7c3eec29ab970b1a10657f5709b7 Mon Sep 17 00:00:00 2001 From: Louis Pearson Date: Mon, 8 Aug 2022 15:50:05 -0600 Subject: [PATCH] Improve switch node, recursive circuit eval - Switches now have two possible states, stored in a u8. This is either on/off in the case of vertical switches, or a toggle between the two SwitchOutlets for Tee switches - Circuit is now recursively evaluated to make sure every node was updated after their parent node. --- src/circuit.zig | 2 +- src/game.zig | 14 ++--- src/world.zig | 129 ++++++++++++++++++++++++------------------- tools/LDtkImport.zig | 25 ++++----- 4 files changed, 88 insertions(+), 82 deletions(-) diff --git a/src/circuit.zig b/src/circuit.zig index 846dbb2..f5d3d5a 100644 --- a/src/circuit.zig +++ b/src/circuit.zig @@ -236,7 +236,7 @@ pub fn toggle(this: *@This(), c: Cell) ?u8 { if (T.is_switch(tile)) { const toggled = toggle_switch(tile); this.set_cell(cell, toggled); - return tile; + return toggled; } } return null; diff --git a/src/game.zig b/src/game.zig index c5bbf7c..06ae28b 100644 --- a/src/game.zig +++ b/src/game.zig @@ -654,17 +654,13 @@ fn manipulationProcess(pos: *Pos, control: *Control) !void { const new_switch = circuit.toggle(cell); if (new_switch) |tile| { const T = world.Tiles; - // TODO: make switch system better - const new_state: world.NodeKind.SwitchEnum = switch(tile) { - T.SwitchTeeWestOn => .North, - T.SwitchTeeWestOff => .West, - T.SwitchTeeEastOn => .North, - T.SwitchTeeEastOff => .East, - T.SwitchVerticalOn => .South, - else => .Off, + const new_state: u8 = switch(tile) { + T.SwitchTeeWestOn, T.SwitchTeeEastOn, T.SwitchVerticalOn => 1, + else => 0, }; const x = level.world_x * 20 + @intCast(i16, cell[0]); const y = level.world_y * 20 + @intCast(i16, cell[1]); + w4.tracef("---- Updating switch (%d, %d)", x, y); db.setSwitch(Coord.init(.{ x, y }), new_state); } try updateCircuit(); @@ -729,7 +725,7 @@ fn updateCircuit() !void { try map.set_cell(door, world.Tiles.Empty); } - db.updateCircuit(); + try db.updateCircuit(frame_alloc); for (db.circuit_info) |node, n| { const e = @boolToInt(node.energized); diff --git a/src/world.zig b/src/world.zig index b4c6eb2..aa95cdb 100644 --- a/src/world.zig +++ b/src/world.zig @@ -675,8 +675,7 @@ pub const Database = struct { } } - - pub fn setSwitch(db: *Database, coord: Coord, new_state: NodeKind.SwitchEnum) void { + pub fn setSwitch(db: *Database, coord: Coord, new_state: u8) void { const _switch = db.getNodeID(coord) orelse return; db.circuit_info[_switch].kind.Switch.state = new_state; } @@ -689,53 +688,68 @@ pub const Database = struct { return false; } - pub fn updateCircuit(db: *Database) void { - for (db.circuit_info) |node, i| { - switch (node.kind) { - .And => |And| { - const input1 = db.circuit_info[And[0]].energized; - const input2 = db.circuit_info[And[1]].energized; - db.circuit_info[i].energized = (input1 and input2); - }, - .Xor => |Xor| { - const input1 = db.circuit_info[Xor[0]].energized; - const input2 = db.circuit_info[Xor[1]].energized; - db.circuit_info[i].energized = (input1 and !input2) or (input2 and !input1); - }, - .Source => db.circuit_info[i].energized = true, - .Conduit => |Conduit| { - const input1 = db.circuit_info[Conduit[0]].energized; - const input2 = db.circuit_info[Conduit[1]].energized; - db.circuit_info[i].energized = (input1 or input2); - }, - .Socket => |socket_opt| { - if (socket_opt) |input| { - db.circuit_info[i].energized = db.circuit_info[input].energized; - } else { - db.circuit_info[i].energized = false; - } - }, - .Plug => |Plug| { - db.circuit_info[i].energized = db.circuit_info[Plug].energized; - }, - .Switch => |_Switch| { - db.circuit_info[i].energized = db.circuit_info[_Switch.source].energized; - }, - .SwitchOutlet => |_Switch| { - const _switch = db.circuit_info[_Switch.source]; - const _outlet = db.circuit_info[i].kind.SwitchOutlet; - // If the switch isn't energized, this outlet is not energized - if (!_switch.energized) db.circuit_info[i].energized = false; - // If the switch is energized, check that it is outputting to this outlet - db.circuit_info[i].energized = _outlet.which == _switch.kind.Switch.state; - }, - .Join => |Join| { - db.circuit_info[i].energized = db.circuit_info[Join].energized; - }, - .Outlet => |Outlet| { - db.circuit_info[i].energized = db.circuit_info[Outlet].energized; - }, - } + fn updateCircuitFragment(db: *Database, i: usize, visited: []bool) bool { + if (visited[i]) return db.circuit_info[i].energized; + visited[i] = true; + const node = db.circuit_info[i]; + switch (node.kind) { + .And => |And| { + const input1 = db.updateCircuitFragment(And[0], visited); + const input2 = db.updateCircuitFragment(And[1], visited); + db.circuit_info[i].energized = (input1 and input2); + }, + .Xor => |Xor| { + const input1 = db.updateCircuitFragment(Xor[0], visited); + const input2 = db.updateCircuitFragment(Xor[1], visited); + db.circuit_info[i].energized = (input1 and !input2) or (input2 and !input1); + }, + .Source => db.circuit_info[i].energized = true, + .Conduit => |Conduit| { + const input1 = db.updateCircuitFragment(Conduit[0], visited); + const input2 = db.updateCircuitFragment(Conduit[1], visited); + db.circuit_info[i].energized = (input1 or input2); + }, + // TODO: Sockets may come before the plug they are connected to + .Socket => |socket_opt| { + if (socket_opt) |input| { + db.circuit_info[i].energized = db.updateCircuitFragment(input, visited); + } else { + db.circuit_info[i].energized = false; + } + }, + .Plug => |Plug| { + db.circuit_info[i].energized = db.updateCircuitFragment(Plug, visited); + }, + .Switch => |_Switch| { + db.circuit_info[i].energized = db.updateCircuitFragment(_Switch.source, visited); + }, + .SwitchOutlet => |_Switch| { + const is_energized = db.updateCircuitFragment(_Switch.source, visited); + const _switch = db.circuit_info[_Switch.source].kind.Switch; + const _outlet = db.circuit_info[i].kind.SwitchOutlet; + // If the switch isn't energized, this outlet is not energized + if (is_energized) db.circuit_info[i].energized = false; + // If the switch is energized, check that it is outputting to this outlet + db.circuit_info[i].energized = _outlet.which == _switch.state; + }, + .Join => |Join| { + db.circuit_info[i].energized = db.updateCircuitFragment(Join, visited); + }, + .Outlet => |Outlet| { + db.circuit_info[i].energized = db.updateCircuitFragment(Outlet, visited); + }, + } + return db.circuit_info[i].energized; + } + + pub fn updateCircuit(db: *Database, alloc: std.mem.Allocator) !void { + var visited = try alloc.alloc(bool, db.circuit_info.len); + defer alloc.free(visited); + std.mem.set(bool, visited, false); + var i: usize = db.circuit_info.len - 1; + while (i > 0) : (i -|= 1) { + _ = db.updateCircuitFragment(i, visited); + if (i == 0) break; } } }; @@ -833,13 +847,12 @@ pub const NodeKind = union(NodeEnum) { pub const Switch = struct { source: NodeID, - state: SwitchEnum, + state: u8, }; pub const SwitchOutlet = struct { source: NodeID, - which: SwitchEnum, + which: u8, }; - pub const SwitchEnum = enum { Off, North, West, East, South }; pub fn read(reader: anytype) !NodeKind { var kind: NodeKind = undefined; @@ -878,13 +891,13 @@ pub const NodeKind = union(NodeEnum) { .Switch => { kind = .{ .Switch = .{ .source = try reader.readInt(NodeID, .Little), - .state = @intToEnum(SwitchEnum, try reader.readInt(u8, .Little)), + .state = try reader.readInt(u8, .Little), } }; }, .SwitchOutlet => { kind = .{ .SwitchOutlet = .{ .source = try reader.readInt(NodeID, .Little), - .which = @intToEnum(SwitchEnum, try reader.readInt(u8, .Little)), + .which = try reader.readInt(u8, .Little), } }; }, .Join => { @@ -922,11 +935,11 @@ pub const NodeKind = union(NodeEnum) { }, .Switch => |_Switch| { try writer.writeInt(NodeID, _Switch.source, .Little); - try writer.writeInt(u8, @enumToInt(_Switch.state), .Little); + try writer.writeInt(u8, _Switch.state, .Little); }, .SwitchOutlet => |_Switch| { try writer.writeInt(NodeID, _Switch.source, .Little); - try writer.writeInt(u8, @enumToInt(_Switch.which), .Little); + try writer.writeInt(u8, _Switch.which, .Little); }, .Join => |Join| { try writer.writeInt(NodeID, Join, .Little); @@ -948,8 +961,8 @@ pub const NodeKind = union(NodeEnum) { .Source => std.fmt.format(writer, "{s}", .{name}), .Plug => |Plug| std.fmt.format(writer, "{s} [{}]", .{ name, Plug }), .Socket => |Socket| std.fmt.format(writer, "{s} [{?}]", .{ name, Socket }), - .Switch => |_Switch| std.fmt.format(writer, "{s} <{s}> [{}]", .{ name, @tagName(_Switch.state), _Switch.source }), - .SwitchOutlet => |_Switch| std.fmt.format(writer, "{s} <{s}> [{}]", .{ name, @tagName(_Switch.which), _Switch.source }), + .Switch => |_Switch| std.fmt.format(writer, "{s} <{}> [{}]", .{ name, _Switch.state, _Switch.source }), + .SwitchOutlet => |_Switch| std.fmt.format(writer, "{s} <{}> [{}]", .{ name, _Switch.which, _Switch.source }), .Join => |Join| std.fmt.format(writer, "{s} [{}]", .{ name, Join }), .Outlet => |Outlet| std.fmt.format(writer, "{s} [{}]", .{ name, Outlet }), }; diff --git a/tools/LDtkImport.zig b/tools/LDtkImport.zig index cd968b7..e173294 100644 --- a/tools/LDtkImport.zig +++ b/tools/LDtkImport.zig @@ -427,22 +427,19 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL std.debug.assert(north == true and south == true); // Determine initial state of switch - const state: world.NodeKind.SwitchEnum = state: { + const state: u8 = state: { // Vertical switch if (!west and !east) { - if (flags.circuit == .Switch_Off) break :state .Off; - if (input_dir == .North) break :state .South; - break :state .North; + if (flags.circuit == .Switch_Off) break :state 0; + break :state 1; } if (east and !west) { - if (flags.circuit == .Switch_Off) break :state .East; - if (input_dir == .North) break :state .South; - break :state .North; + if (flags.circuit == .Switch_Off) break :state 0; + break :state 1; } if (west and !east) { - if (flags.circuit == .Switch_Off) break :state .West; - if (input_dir == .North) break :state .South; - break :state .North; + if (flags.circuit == .Switch_Off) break :state 0; + break :state 1; } return error.ImpossibleSwitchState; }; @@ -461,7 +458,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL try nodes.append(.{ .kind = .{ .SwitchOutlet = .{ .source = next_node, - .which = .West, + .which = 0, } }, .coord = coord, }); @@ -479,7 +476,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL try nodes.append(.{ .kind = .{ .SwitchOutlet = .{ .source = next_node, - .which = .East, + .which = 0, } }, .coord = coord, }); @@ -497,7 +494,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL try nodes.append(.{ .kind = .{ .SwitchOutlet = .{ .source = next_node, - .which = .South, + .which = 1, } }, .coord = coord, }); @@ -515,7 +512,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL try nodes.append(.{ .kind = .{ .SwitchOutlet = .{ .source = next_node, - .which = .North, + .which = 1, } }, .coord = coord, });