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.
master
Louis Pearson 2022-08-08 15:50:05 -06:00
parent c179f234dd
commit a35d481b15
4 changed files with 88 additions and 82 deletions

View File

@ -236,7 +236,7 @@ pub fn toggle(this: *@This(), c: Cell) ?u8 {
if (T.is_switch(tile)) { if (T.is_switch(tile)) {
const toggled = toggle_switch(tile); const toggled = toggle_switch(tile);
this.set_cell(cell, toggled); this.set_cell(cell, toggled);
return tile; return toggled;
} }
} }
return null; return null;

View File

@ -654,17 +654,13 @@ fn manipulationProcess(pos: *Pos, control: *Control) !void {
const new_switch = circuit.toggle(cell); const new_switch = circuit.toggle(cell);
if (new_switch) |tile| { if (new_switch) |tile| {
const T = world.Tiles; const T = world.Tiles;
// TODO: make switch system better const new_state: u8 = switch(tile) {
const new_state: world.NodeKind.SwitchEnum = switch(tile) { T.SwitchTeeWestOn, T.SwitchTeeEastOn, T.SwitchVerticalOn => 1,
T.SwitchTeeWestOn => .North, else => 0,
T.SwitchTeeWestOff => .West,
T.SwitchTeeEastOn => .North,
T.SwitchTeeEastOff => .East,
T.SwitchVerticalOn => .South,
else => .Off,
}; };
const x = level.world_x * 20 + @intCast(i16, cell[0]); const x = level.world_x * 20 + @intCast(i16, cell[0]);
const y = level.world_y * 20 + @intCast(i16, cell[1]); 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); db.setSwitch(Coord.init(.{ x, y }), new_state);
} }
try updateCircuit(); try updateCircuit();
@ -729,7 +725,7 @@ fn updateCircuit() !void {
try map.set_cell(door, world.Tiles.Empty); try map.set_cell(door, world.Tiles.Empty);
} }
db.updateCircuit(); try db.updateCircuit(frame_alloc);
for (db.circuit_info) |node, n| { for (db.circuit_info) |node, n| {
const e = @boolToInt(node.energized); const e = @boolToInt(node.energized);

View File

@ -675,8 +675,7 @@ pub const Database = struct {
} }
} }
pub fn setSwitch(db: *Database, coord: Coord, new_state: u8) void {
pub fn setSwitch(db: *Database, coord: Coord, new_state: NodeKind.SwitchEnum) void {
const _switch = db.getNodeID(coord) orelse return; const _switch = db.getNodeID(coord) orelse return;
db.circuit_info[_switch].kind.Switch.state = new_state; db.circuit_info[_switch].kind.Switch.state = new_state;
} }
@ -689,53 +688,68 @@ pub const Database = struct {
return false; return false;
} }
pub fn updateCircuit(db: *Database) void { fn updateCircuitFragment(db: *Database, i: usize, visited: []bool) bool {
for (db.circuit_info) |node, i| { if (visited[i]) return db.circuit_info[i].energized;
switch (node.kind) { visited[i] = true;
.And => |And| { const node = db.circuit_info[i];
const input1 = db.circuit_info[And[0]].energized; switch (node.kind) {
const input2 = db.circuit_info[And[1]].energized; .And => |And| {
db.circuit_info[i].energized = (input1 and input2); const input1 = db.updateCircuitFragment(And[0], visited);
}, const input2 = db.updateCircuitFragment(And[1], visited);
.Xor => |Xor| { db.circuit_info[i].energized = (input1 and input2);
const input1 = db.circuit_info[Xor[0]].energized; },
const input2 = db.circuit_info[Xor[1]].energized; .Xor => |Xor| {
db.circuit_info[i].energized = (input1 and !input2) or (input2 and !input1); const input1 = db.updateCircuitFragment(Xor[0], visited);
}, const input2 = db.updateCircuitFragment(Xor[1], visited);
.Source => db.circuit_info[i].energized = true, db.circuit_info[i].energized = (input1 and !input2) or (input2 and !input1);
.Conduit => |Conduit| { },
const input1 = db.circuit_info[Conduit[0]].energized; .Source => db.circuit_info[i].energized = true,
const input2 = db.circuit_info[Conduit[1]].energized; .Conduit => |Conduit| {
db.circuit_info[i].energized = (input1 or input2); const input1 = db.updateCircuitFragment(Conduit[0], visited);
}, const input2 = db.updateCircuitFragment(Conduit[1], visited);
.Socket => |socket_opt| { db.circuit_info[i].energized = (input1 or input2);
if (socket_opt) |input| { },
db.circuit_info[i].energized = db.circuit_info[input].energized; // TODO: Sockets may come before the plug they are connected to
} else { .Socket => |socket_opt| {
db.circuit_info[i].energized = false; if (socket_opt) |input| {
} db.circuit_info[i].energized = db.updateCircuitFragment(input, visited);
}, } else {
.Plug => |Plug| { db.circuit_info[i].energized = false;
db.circuit_info[i].energized = db.circuit_info[Plug].energized; }
}, },
.Switch => |_Switch| { .Plug => |Plug| {
db.circuit_info[i].energized = db.circuit_info[_Switch.source].energized; db.circuit_info[i].energized = db.updateCircuitFragment(Plug, visited);
}, },
.SwitchOutlet => |_Switch| { .Switch => |_Switch| {
const _switch = db.circuit_info[_Switch.source]; db.circuit_info[i].energized = db.updateCircuitFragment(_Switch.source, visited);
const _outlet = db.circuit_info[i].kind.SwitchOutlet; },
// If the switch isn't energized, this outlet is not energized .SwitchOutlet => |_Switch| {
if (!_switch.energized) db.circuit_info[i].energized = false; const is_energized = db.updateCircuitFragment(_Switch.source, visited);
// If the switch is energized, check that it is outputting to this outlet const _switch = db.circuit_info[_Switch.source].kind.Switch;
db.circuit_info[i].energized = _outlet.which == _switch.kind.Switch.state; const _outlet = db.circuit_info[i].kind.SwitchOutlet;
}, // If the switch isn't energized, this outlet is not energized
.Join => |Join| { if (is_energized) db.circuit_info[i].energized = false;
db.circuit_info[i].energized = db.circuit_info[Join].energized; // If the switch is energized, check that it is outputting to this outlet
}, db.circuit_info[i].energized = _outlet.which == _switch.state;
.Outlet => |Outlet| { },
db.circuit_info[i].energized = db.circuit_info[Outlet].energized; .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 { pub const Switch = struct {
source: NodeID, source: NodeID,
state: SwitchEnum, state: u8,
}; };
pub const SwitchOutlet = struct { pub const SwitchOutlet = struct {
source: NodeID, source: NodeID,
which: SwitchEnum, which: u8,
}; };
pub const SwitchEnum = enum { Off, North, West, East, South };
pub fn read(reader: anytype) !NodeKind { pub fn read(reader: anytype) !NodeKind {
var kind: NodeKind = undefined; var kind: NodeKind = undefined;
@ -878,13 +891,13 @@ pub const NodeKind = union(NodeEnum) {
.Switch => { .Switch => {
kind = .{ .Switch = .{ kind = .{ .Switch = .{
.source = try reader.readInt(NodeID, .Little), .source = try reader.readInt(NodeID, .Little),
.state = @intToEnum(SwitchEnum, try reader.readInt(u8, .Little)), .state = try reader.readInt(u8, .Little),
} }; } };
}, },
.SwitchOutlet => { .SwitchOutlet => {
kind = .{ .SwitchOutlet = .{ kind = .{ .SwitchOutlet = .{
.source = try reader.readInt(NodeID, .Little), .source = try reader.readInt(NodeID, .Little),
.which = @intToEnum(SwitchEnum, try reader.readInt(u8, .Little)), .which = try reader.readInt(u8, .Little),
} }; } };
}, },
.Join => { .Join => {
@ -922,11 +935,11 @@ pub const NodeKind = union(NodeEnum) {
}, },
.Switch => |_Switch| { .Switch => |_Switch| {
try writer.writeInt(NodeID, _Switch.source, .Little); try writer.writeInt(NodeID, _Switch.source, .Little);
try writer.writeInt(u8, @enumToInt(_Switch.state), .Little); try writer.writeInt(u8, _Switch.state, .Little);
}, },
.SwitchOutlet => |_Switch| { .SwitchOutlet => |_Switch| {
try writer.writeInt(NodeID, _Switch.source, .Little); try writer.writeInt(NodeID, _Switch.source, .Little);
try writer.writeInt(u8, @enumToInt(_Switch.which), .Little); try writer.writeInt(u8, _Switch.which, .Little);
}, },
.Join => |Join| { .Join => |Join| {
try writer.writeInt(NodeID, Join, .Little); try writer.writeInt(NodeID, Join, .Little);
@ -948,8 +961,8 @@ pub const NodeKind = union(NodeEnum) {
.Source => std.fmt.format(writer, "{s}", .{name}), .Source => std.fmt.format(writer, "{s}", .{name}),
.Plug => |Plug| std.fmt.format(writer, "{s} [{}]", .{ name, Plug }), .Plug => |Plug| std.fmt.format(writer, "{s} [{}]", .{ name, Plug }),
.Socket => |Socket| std.fmt.format(writer, "{s} [{?}]", .{ name, Socket }), .Socket => |Socket| std.fmt.format(writer, "{s} [{?}]", .{ name, Socket }),
.Switch => |_Switch| std.fmt.format(writer, "{s} <{s}> [{}]", .{ name, @tagName(_Switch.state), _Switch.source }), .Switch => |_Switch| std.fmt.format(writer, "{s} <{}> [{}]", .{ name, _Switch.state, _Switch.source }),
.SwitchOutlet => |_Switch| std.fmt.format(writer, "{s} <{s}> [{}]", .{ name, @tagName(_Switch.which), _Switch.source }), .SwitchOutlet => |_Switch| std.fmt.format(writer, "{s} <{}> [{}]", .{ name, _Switch.which, _Switch.source }),
.Join => |Join| std.fmt.format(writer, "{s} [{}]", .{ name, Join }), .Join => |Join| std.fmt.format(writer, "{s} [{}]", .{ name, Join }),
.Outlet => |Outlet| std.fmt.format(writer, "{s} [{}]", .{ name, Outlet }), .Outlet => |Outlet| std.fmt.format(writer, "{s} [{}]", .{ name, Outlet }),
}; };

View File

@ -427,22 +427,19 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
std.debug.assert(north == true and south == true); std.debug.assert(north == true and south == true);
// Determine initial state of switch // Determine initial state of switch
const state: world.NodeKind.SwitchEnum = state: { const state: u8 = state: {
// Vertical switch // Vertical switch
if (!west and !east) { if (!west and !east) {
if (flags.circuit == .Switch_Off) break :state .Off; if (flags.circuit == .Switch_Off) break :state 0;
if (input_dir == .North) break :state .South; break :state 1;
break :state .North;
} }
if (east and !west) { if (east and !west) {
if (flags.circuit == .Switch_Off) break :state .East; if (flags.circuit == .Switch_Off) break :state 0;
if (input_dir == .North) break :state .South; break :state 1;
break :state .North;
} }
if (west and !east) { if (west and !east) {
if (flags.circuit == .Switch_Off) break :state .West; if (flags.circuit == .Switch_Off) break :state 0;
if (input_dir == .North) break :state .South; break :state 1;
break :state .North;
} }
return error.ImpossibleSwitchState; return error.ImpossibleSwitchState;
}; };
@ -461,7 +458,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
.which = .West, .which = 0,
} }, } },
.coord = coord, .coord = coord,
}); });
@ -479,7 +476,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
.which = .East, .which = 0,
} }, } },
.coord = coord, .coord = coord,
}); });
@ -497,7 +494,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
.which = .South, .which = 1,
} }, } },
.coord = coord, .coord = coord,
}); });
@ -515,7 +512,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
.which = .North, .which = 1,
} }, } },
.coord = coord, .coord = coord,
}); });