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)) {
const toggled = toggle_switch(tile);
this.set_cell(cell, toggled);
return tile;
return toggled;
}
}
return null;

View File

@ -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);

View File

@ -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| {
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.circuit_info[And[0]].energized;
const input2 = db.circuit_info[And[1]].energized;
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.circuit_info[Xor[0]].energized;
const input2 = db.circuit_info[Xor[1]].energized;
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.circuit_info[Conduit[0]].energized;
const input2 = db.circuit_info[Conduit[1]].energized;
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.circuit_info[input].energized;
db.circuit_info[i].energized = db.updateCircuitFragment(input, visited);
} else {
db.circuit_info[i].energized = false;
}
},
.Plug => |Plug| {
db.circuit_info[i].energized = db.circuit_info[Plug].energized;
db.circuit_info[i].energized = db.updateCircuitFragment(Plug, visited);
},
.Switch => |_Switch| {
db.circuit_info[i].energized = db.circuit_info[_Switch.source].energized;
db.circuit_info[i].energized = db.updateCircuitFragment(_Switch.source, visited);
},
.SwitchOutlet => |_Switch| {
const _switch = db.circuit_info[_Switch.source];
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 (!_switch.energized) db.circuit_info[i].energized = false;
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.kind.Switch.state;
db.circuit_info[i].energized = _outlet.which == _switch.state;
},
.Join => |Join| {
db.circuit_info[i].energized = db.circuit_info[Join].energized;
db.circuit_info[i].energized = db.updateCircuitFragment(Join, visited);
},
.Outlet => |Outlet| {
db.circuit_info[i].energized = db.circuit_info[Outlet].energized;
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 }),
};

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);
// 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,
});