Prototype switch circuitnode implementation

master
Louis Pearson 2022-08-08 14:39:33 -06:00
parent b4cc068efa
commit c179f234dd
4 changed files with 215 additions and 27 deletions

View File

@ -230,14 +230,16 @@ pub fn isEnabled(this: @This(), cell: Cell) bool {
return this.levels[i] >= 1; return this.levels[i] >= 1;
} }
pub fn toggle(this: *@This(), c: Cell) void { pub fn toggle(this: *@This(), c: Cell) ?u8 {
const cell = c; const cell = c;
if (this.get_cell(cell)) |tile| { if (this.get_cell(cell)) |tile| {
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 null;
} }
pub fn clearMap(this: *@This()) void { pub fn clearMap(this: *@This()) void {

View File

@ -651,7 +651,22 @@ fn manipulationProcess(pos: *Pos, control: *Control) !void {
}, },
.lever => { .lever => {
const cell = @divTrunc(i.pos, Map.tile_size); const cell = @divTrunc(i.pos, Map.tile_size);
circuit.toggle(cell); 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 x = level.world_x * 20 + @intCast(i16, cell[0]);
const y = level.world_y * 20 + @intCast(i16, cell[1]);
db.setSwitch(Coord.init(.{ x, y }), new_state);
}
try updateCircuit(); try updateCircuit();
}, },
} }
@ -728,7 +743,8 @@ fn updateCircuit() !void {
w4.tracef("[%d]: Socket [%d] <%d>", n, socket, e); w4.tracef("[%d]: Socket [%d] <%d>", n, socket, e);
}, },
.Plug => |Plug| w4.tracef("[%d]: Plug [%d] <%d>", n, Plug, e), .Plug => |Plug| w4.tracef("[%d]: Plug [%d] <%d>", n, Plug, e),
.Switch => |Switch| w4.tracef("[%d]: Switch [%s] <%d>", n, &@tagName(Switch), e), .Switch => |Switch| w4.tracef("[%d]: Switch %d [%d] <%d>", n, Switch.state, Switch.source, e),
.SwitchOutlet => |Switch| w4.tracef("[%d]: SwitchOutlet %d [%d] <%d>", n, Switch.which, Switch.source, e),
.Join => |Join| w4.tracef("[%d]: Join [%d] <%d>", n, Join, e), .Join => |Join| w4.tracef("[%d]: Join [%d] <%d>", n, Join, e),
.Outlet => |Outlet| w4.tracef("[%d]: Outlet [%d] <%d>", n, Outlet, e), .Outlet => |Outlet| w4.tracef("[%d]: Outlet [%d] <%d>", n, Outlet, e),
} }

View File

@ -116,6 +116,16 @@ pub const TileData = union(enum) {
circuit: CircuitType, circuit: CircuitType,
}, },
pub fn getCircuit(data: TileData) ?CircuitType {
switch (data) {
.tile => |_| return null,
.flags => |flags| {
if (flags.circuit == .None) return null;
return flags.circuit;
},
}
}
pub fn toByte(data: TileData) u8 { pub fn toByte(data: TileData) u8 {
switch (data) { switch (data) {
.tile => |int| return 0b1000_0000 | @intCast(u8, int), .tile => |int| return 0b1000_0000 | @intCast(u8, int),
@ -665,6 +675,12 @@ pub const Database = struct {
} }
} }
pub fn setSwitch(db: *Database, coord: Coord, new_state: NodeKind.SwitchEnum) void {
const _switch = db.getNodeID(coord) orelse return;
db.circuit_info[_switch].kind.Switch.state = new_state;
}
pub fn isEnergized(db: *Database, coord: Coord) bool { pub fn isEnergized(db: *Database, coord: Coord) bool {
for (db.circuit_info) |node, i| { for (db.circuit_info) |node, i| {
if (!coord.eq(node.coord)) continue; if (!coord.eq(node.coord)) continue;
@ -702,10 +718,16 @@ pub const Database = struct {
.Plug => |Plug| { .Plug => |Plug| {
db.circuit_info[i].energized = db.circuit_info[Plug].energized; db.circuit_info[i].energized = db.circuit_info[Plug].energized;
}, },
.Switch => |state| { .Switch => |_Switch| {
// TODO Rework switch to make sense db.circuit_info[i].energized = db.circuit_info[_Switch.source].energized;
db.circuit_info[i].energized = false; },
_ = state; .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| { .Join => |Join| {
db.circuit_info[i].energized = db.circuit_info[Join].energized; db.circuit_info[i].energized = db.circuit_info[Join].energized;
@ -772,6 +794,7 @@ const NodeEnum = enum(u4) {
Plug, Plug,
Socket, Socket,
Switch, Switch,
SwitchOutlet,
Join, Join,
Outlet, Outlet,
}; };
@ -798,11 +821,25 @@ pub const NodeKind = union(NodeEnum) {
/// Vertical = Off or Top/Bottom, depending on flow /// Vertical = Off or Top/Bottom, depending on flow
/// Horizontal = Off or Left/Right, depending on flow /// Horizontal = Off or Left/Right, depending on flow
/// Tee = Top/Bottom or Left/Right, depending on flow /// Tee = Top/Bottom or Left/Right, depending on flow
Switch: SwitchEnum, Switch: Switch,
/// Interface between a switch and other components. Each one
/// of these represents a possible outlet on a switch, and reads
/// the state of the switch to determine if it is powered or not
SwitchOutlet: SwitchOutlet,
/// Connection between levels
Join: NodeID, Join: NodeID,
/// Used to identify entities that recieve power, like doors
Outlet: NodeID, Outlet: NodeID,
const SwitchEnum = enum { Off, Bottom, Top, Left, Right }; pub const Switch = struct {
source: NodeID,
state: SwitchEnum,
};
pub const SwitchOutlet = struct {
source: NodeID,
which: SwitchEnum,
};
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;
@ -839,9 +876,16 @@ pub const NodeKind = union(NodeEnum) {
kind = .{ .Plug = try reader.readInt(NodeID, .Little) }; kind = .{ .Plug = try reader.readInt(NodeID, .Little) };
}, },
.Switch => { .Switch => {
kind = .{ kind = .{ .Switch = .{
.Switch = @intToEnum(SwitchEnum, try reader.readInt(NodeID, .Little)), .source = try reader.readInt(NodeID, .Little),
}; .state = @intToEnum(SwitchEnum, try reader.readInt(u8, .Little)),
} };
},
.SwitchOutlet => {
kind = .{ .SwitchOutlet = .{
.source = try reader.readInt(NodeID, .Little),
.which = @intToEnum(SwitchEnum, try reader.readInt(u8, .Little)),
} };
}, },
.Join => { .Join => {
kind = .{ .Join = try reader.readInt(NodeID, .Little) }; kind = .{ .Join = try reader.readInt(NodeID, .Little) };
@ -876,8 +920,13 @@ pub const NodeKind = union(NodeEnum) {
const socket = Socket orelse std.math.maxInt(NodeID); const socket = Socket orelse std.math.maxInt(NodeID);
try writer.writeInt(NodeID, socket, .Little); try writer.writeInt(NodeID, socket, .Little);
}, },
.Switch => |Switch| { .Switch => |_Switch| {
try writer.writeInt(NodeID, @enumToInt(Switch), .Little); try writer.writeInt(NodeID, _Switch.source, .Little);
try writer.writeInt(u8, @enumToInt(_Switch.state), .Little);
},
.SwitchOutlet => |_Switch| {
try writer.writeInt(NodeID, _Switch.source, .Little);
try writer.writeInt(u8, @enumToInt(_Switch.which), .Little);
}, },
.Join => |Join| { .Join => |Join| {
try writer.writeInt(NodeID, Join, .Little); try writer.writeInt(NodeID, Join, .Little);
@ -899,7 +948,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) }), .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 }),
.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

@ -367,7 +367,9 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
switch (flags.circuit) { switch (flags.circuit) {
.Source => {}, // Do nothing, but add everything around the source .Source => {}, // Do nothing, but add everything around the source
.Conduit => { .Conduit => {
// Collects from two other nodes. Needs to store more info in coordinate queue // Collects from two other nodes. Intersections will need to be stored so when
// we find out we have to outputs, we can add the conduit and possible rewrite
// previous nodes to point to the conduit
// TODO // TODO
}, },
.Socket => { .Socket => {
@ -393,21 +395,139 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
.coord = coord, .coord = coord,
}); });
}, },
.Switch_Off => { .Switch_Off, .Switch_On => {
// TODO: Find last coordinate of search and determine flow // Identify input side
const last_coord = node.data.last_coord.?;
const Dir = enum { North, West, East, South };
const input_dir: Dir = dir: {
if (last_coord.eq(coord.add(.{ 0, -1 }))) {
break :dir .North;
} else if (last_coord.eq(coord.add(.{ -1, 0 }))) {
break :dir .West;
} else if (last_coord.eq(coord.add(.{ 1, 0 }))) {
break :dir .East;
} else {
break :dir .South;
}
};
// Find outlets
const north_opt = level.?.getTile(coord.add(.{ 0, -1 })).?.getCircuit();
const west_opt = level.?.getTile(coord.add(.{ -1, 0 })).?.getCircuit();
const east_opt = level.?.getTile(coord.add(.{ 1, 0 })).?.getCircuit();
const south_opt = level.?.getTile(coord.add(.{ 0, 1 })).?.getCircuit();
const north = (north_opt orelse world.CircuitType.None) != .None;
const west = (west_opt orelse world.CircuitType.None) != .None;
const east = (east_opt orelse world.CircuitType.None) != .None;
const south = (south_opt orelse world.CircuitType.None) != .None;
// We don't have four way switches, don't allow them
std.debug.assert(west != true or east != true);
// We only have vertically oriented switches ATM
std.debug.assert(north == true and south == true);
// Determine initial state of switch
const state: world.NodeKind.SwitchEnum = 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 (east and !west) {
if (flags.circuit == .Switch_Off) break :state .East;
if (input_dir == .North) break :state .South;
break :state .North;
}
if (west and !east) {
if (flags.circuit == .Switch_Off) break :state .West;
if (input_dir == .North) break :state .South;
break :state .North;
}
return error.ImpossibleSwitchState;
};
// Add switch
next_node = @intCast(world.NodeID, nodes.items.len); next_node = @intCast(world.NodeID, nodes.items.len);
try nodes.append(.{ try nodes.append(.{
.kind = .{ .Switch = .Off }, .kind = .{ .Switch = .{
.coord = coord, .source = last_node,
}); .state = state,
}, } },
.Switch_On => {
// TODO: Find last coordinate of search and determine flow
next_node = @intCast(world.NodeID, nodes.items.len);
try nodes.append(.{
.kind = .{ .Switch = .Off },
.coord = coord, .coord = coord,
}); });
// Add switch outlets
if (input_dir != .West and west) {
const out_node = @intCast(world.NodeID, nodes.items.len);
try nodes.append(.{
.kind = .{ .SwitchOutlet = .{
.source = next_node,
.which = .West,
} },
.coord = coord,
});
const right = try alloc.create(Node);
right.* = Node{ .data = .{
.last_node = out_node,
.coord = coord.add(.{ 1, 0 }),
.last_coord = coord,
} };
bfs_queue.append(right);
}
if (input_dir != .East and east) {
const out_node = @intCast(world.NodeID, nodes.items.len);
try nodes.append(.{
.kind = .{ .SwitchOutlet = .{
.source = next_node,
.which = .East,
} },
.coord = coord,
});
const left = try alloc.create(Node);
left.* = Node{ .data = .{
.last_node = out_node,
.coord = coord.add(.{ -1, 0 }),
.last_coord = coord,
} };
bfs_queue.append(left);
}
if (input_dir != .South and south) {
const out_node = @intCast(world.NodeID, nodes.items.len);
try nodes.append(.{
.kind = .{ .SwitchOutlet = .{
.source = next_node,
.which = .South,
} },
.coord = coord,
});
const down = try alloc.create(Node);
down.* = Node{ .data = .{
.last_node = out_node,
.coord = coord.add(.{ 0, 1 }),
.last_coord = coord,
} };
bfs_queue.append(down);
}
if (input_dir != .North and north) {
const out_node = @intCast(world.NodeID, nodes.items.len);
try nodes.append(.{
.kind = .{ .SwitchOutlet = .{
.source = next_node,
.which = .North,
} },
.coord = coord,
});
const up = try alloc.create(Node);
up.* = Node{ .data = .{
.last_node = out_node,
.coord = coord.add(.{ 0, -1 }),
.last_coord = coord,
} };
bfs_queue.append(up);
}
continue;
}, },
.Join => { .Join => {
const last_coord = node.data.last_coord.?; const last_coord = node.data.last_coord.?;