Begin adding directed conduit pieces

master
Louis Pearson 2022-08-10 17:17:25 -06:00
parent e36cffb8c6
commit 4ccc7e9a2e
6 changed files with 1065 additions and 414 deletions

File diff suppressed because it is too large Load Diff

2
deps/zig-ldtk vendored

@ -1 +1 @@
Subproject commit 1c85773b680b25d690b95e5c88b2e5bfe9ba534a Subproject commit d1ace5b48eaf1caa3bce44add0d14e4fb2f10061

View File

@ -32,11 +32,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1660017629, "lastModified": 1660137652,
"narHash": "sha256-Koz6/k7c6hx4qVz/bboxdR2QsBdkxjRWpNmsOWJtXZE=", "narHash": "sha256-L92gcG6Ya4bqjJmStl/HTENhc0PR9lmVgTmeVpk13Os=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "9f15d6c3a74d2778c6e1af67947c95f100dc6fd2", "rev": "a7f89ddd6313ef88fc9c6534ac2511ba9da85fdb",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -70,11 +70,11 @@
}, },
"unstable": { "unstable": {
"locked": { "locked": {
"lastModified": 1659981942, "lastModified": 1660071133,
"narHash": "sha256-uCFiP/B/NXOWzhN6TKfMbSxtVMk1bVnCrnJRjCF6RmU=", "narHash": "sha256-XX6T9wcvEZIVWY4TO5O1d2MgFyFrF2v4TpCFs7fjdn8=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "39d7f929fbcb1446ad7aa7441b04fb30625a4190", "rev": "36cc29d837e7232e3176e4651e8e117a6f231793",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -89,11 +89,11 @@
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 1659919434, "lastModified": 1660096418,
"narHash": "sha256-U6QsM5FbFpEkqPfXwe/QvNdCWwBIXW9A3fhXo3hFps8=", "narHash": "sha256-+NewKwnerjOe5e65y0xwlRzoQ6JoKP2EaZLR+72d8wU=",
"owner": "arqv", "owner": "arqv",
"repo": "zig-overlay", "repo": "zig-overlay",
"rev": "db9604e7fff0a41f06302d61dd4f320c4e23b81e", "rev": "f7f0dd5bc40290187c70245d11b61c2305e4f114",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -46,7 +46,7 @@ pub fn extractLevel(opt: Options) !void {
circuit.map_size = .{ level.width, height }; circuit.map_size = .{ level.width, height };
w4.tracef("%d", @src().line); w4.tracef("%d", @src().line);
var auto_map = try alloc.alloc(bool, size); var auto_map = try alloc.alloc(world.SolidType, size);
defer alloc.free(auto_map); defer alloc.free(auto_map);
var circuit_map = try alloc.alloc(CircuitType, size); var circuit_map = try alloc.alloc(CircuitType, size);
@ -56,7 +56,7 @@ pub fn extractLevel(opt: Options) !void {
for (tiles) |data, i| { for (tiles) |data, i| {
switch (data) { switch (data) {
.tile => |tile| { .tile => |tile| {
auto_map[i] = false; auto_map[i] = .Empty;
map.tiles[i] = tile; map.tiles[i] = tile;
circuit_map[i] = .None; circuit_map[i] = .None;
}, },
@ -79,7 +79,7 @@ pub fn extractLevel(opt: Options) !void {
const y = @divTrunc(i, width); const y = @divTrunc(i, width);
const stride = width; const stride = width;
if (!auto_map[i]) { if (auto_map[i] == .Empty) {
autotiles[i] = null; autotiles[i] = null;
continue; continue;
} }
@ -93,25 +93,25 @@ pub fn extractLevel(opt: Options) !void {
// Check horizontal neighbors // Check horizontal neighbors
if (x == 0) { if (x == 0) {
west = out_of_bounds; west = out_of_bounds;
east = auto_map[i + 1]; east = auto_map[i + 1] == .Solid;
} else if (x == width - 1) { } else if (x == width - 1) {
west = auto_map[i - 1]; west = auto_map[i - 1] == .Solid;
east = out_of_bounds; east = out_of_bounds;
} else { } else {
west = auto_map[i - 1]; west = auto_map[i - 1] == .Solid;
east = auto_map[i + 1]; east = auto_map[i + 1] == .Solid;
} }
// Check vertical neighbours // Check vertical neighbours
if (y == 0) { if (y == 0) {
north = out_of_bounds; north = out_of_bounds;
south = auto_map[i + stride]; south = auto_map[i + stride] == .Solid;
} else if (y == height - 1) { } else if (y == height - 1) {
north = auto_map[i - stride]; north = auto_map[i - stride] == .Solid;
south = out_of_bounds; south = out_of_bounds;
} else { } else {
north = auto_map[i - stride]; north = auto_map[i - stride] == .Solid;
south = auto_map[i + stride]; south = auto_map[i + stride] == .Solid;
} }
autotiles[i] = AutoTile{ autotiles[i] = AutoTile{
@ -203,12 +203,18 @@ pub fn extractLevel(opt: Options) !void {
for (autotiles) |autotile_opt, i| { for (autotiles) |autotile_opt, i| {
if (autotile_opt) |autotile| { if (autotile_opt) |autotile| {
const tile = switch (circuit_map[i]) { const tile = switch (circuit_map[i]) {
.Conduit, .Source, .Join => opt.conduit.find(autotile), .Conduit,
.Conduit_Vertical,
.Conduit_Horizontal,
.Source,
.Join,
=> opt.conduit.find(autotile),
.Switch_On => opt.switch_on.find(autotile), .Switch_On => opt.switch_on.find(autotile),
.Switch_Off => opt.switch_off.find(autotile), .Switch_Off => opt.switch_off.find(autotile),
.Plug, .Socket => opt.plug.find(autotile), .Plug, .Socket => opt.plug.find(autotile),
.And => world.Tiles.LogicAnd, .And => world.Tiles.LogicAnd,
.Xor => world.Tiles.LogicXor, .Xor => world.Tiles.LogicXor,
.Diode => world.Tiles.LogicDiode,
.None, .Outlet => 0, .None, .Outlet => 0,
}; };
circuit.map[i] = tile; circuit.map[i] = tile;

View File

@ -2,22 +2,6 @@
const std = @import("std"); const std = @import("std");
/// The CircuitType of a tile modifies how the tile responds to
/// electricity
pub const CircuitType = enum(u4) {
None = 0,
Conduit = 1,
Plug = 2,
Switch_Off = 3,
Switch_On = 4,
Join = 5,
And = 6,
Xor = 7,
Outlet = 8,
Source = 9,
Socket = 10,
};
/// This lists the most important tiles so I don't have to keep rewriting things /// This lists the most important tiles so I don't have to keep rewriting things
pub const Tiles = struct { pub const Tiles = struct {
// Switches // Switches
@ -50,9 +34,10 @@ pub const Tiles = struct {
pub const LogicAnd = 20; pub const LogicAnd = 20;
pub const LogicNot = 21; pub const LogicNot = 21;
pub const LogicXor = 22; pub const LogicXor = 22;
pub const LogicDiode = 23;
pub fn is_logic(tile: u8) bool { pub fn is_logic(tile: u8) bool {
return tile >= 21 and tile <= 24; return tile >= LogicAnd and tile <= LogicDiode;
} }
pub const ConduitCross = 96; pub const ConduitCross = 96;
@ -109,29 +94,52 @@ pub const Tiles = struct {
}, 2); }, 2);
}; };
pub const SolidType = enum(u2) {
Empty = 0,
Solid = 1,
Oneway = 2,
};
/// The CircuitType of a tile modifies how the tile responds to
/// electricity
pub const CircuitType = enum(u5) {
None = 0,
Conduit = 1,
Plug = 2,
Switch_Off = 3,
Switch_On = 4,
Join = 5,
And = 6,
Xor = 7,
Outlet = 8,
Source = 9,
Socket = 10,
Diode = 11,
Conduit_Vertical = 12,
Conduit_Horizontal = 13,
};
pub const TileData = union(enum) { pub const TileData = union(enum) {
tile: u7, tile: u7,
flags: struct { flags: struct {
solid: bool, solid: SolidType,
circuit: CircuitType, circuit: CircuitType,
}, },
pub fn getCircuit(data: TileData) ?CircuitType { pub fn getCircuit(data: TileData) ?CircuitType {
switch (data) { if (data == .flags) {
.tile => |_| return null, return data.flags.circuit;
.flags => |flags| {
if (flags.circuit == .None) return null;
return flags.circuit;
},
} }
return null;
} }
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),
.flags => |flags| { .flags => |flags| {
const solid = @enumToInt(flags.solid);
const circuit = @enumToInt(flags.circuit); const circuit = @enumToInt(flags.circuit);
return (@intCast(u7, @boolToInt(flags.solid))) | (@intCast(u7, circuit) << 1); return 0b0111_1111 & ((@intCast(u7, solid)) | (@intCast(u7, circuit) << 2));
}, },
} }
} }
@ -139,13 +147,13 @@ pub const TileData = union(enum) {
pub fn fromByte(byte: u8) TileData { pub fn fromByte(byte: u8) TileData {
const is_tile = (0b1000_0000 & byte) > 0; const is_tile = (0b1000_0000 & byte) > 0;
if (is_tile) { if (is_tile) {
const tile = @intCast(u7, (0b0111_1111 & byte)); const tile = @intCast(u7, (0b0000_0011 & byte));
return TileData{ .tile = tile }; return TileData{ .tile = tile };
} else { } else {
const is_solid = (0b0000_0001 & byte) > 0; const solid = (0b0000_0011 & byte);
const circuit = @intCast(u4, (0b0001_1110 & byte) >> 1); const circuit = @intCast(u5, (0b0111_1100 & byte) >> 2);
return TileData{ .flags = .{ return TileData{ .flags = .{
.solid = is_solid, .solid = @intToEnum(SolidType, solid),
.circuit = @intToEnum(CircuitType, circuit), .circuit = @intToEnum(CircuitType, circuit),
} }; } };
} }
@ -257,12 +265,12 @@ pub const Level = struct {
size: u16, size: u16,
tiles: ?[]TileData, tiles: ?[]TileData,
pub fn init(x: u8, y: u8, width: u16, buf: []TileData) Level { pub fn init(x: i8, y: i8, width: u16, buf: []TileData) Level {
return Level{ return Level{
.world_x = x, .world_x = x,
.world_y = y, .world_y = y,
.width = width, .width = width,
.size = buf.len, .size = @intCast(u16, buf.len),
.tiles = buf, .tiles = buf,
}; };
} }
@ -310,9 +318,11 @@ pub const Level = struct {
pub fn getTile(level: Level, globalc: Coord) ?TileData { pub fn getTile(level: Level, globalc: Coord) ?TileData {
const tiles = level.tiles orelse return null; const tiles = level.tiles orelse return null;
const worldc = globalc.toLevelTopLeft(); const worldc = globalc.toLevelTopLeft();
const se = worldc.add(.{ 20, 20 });
if (!globalc.within(worldc, se)) return null;
const x = globalc.val[0] - worldc.val[0]; const x = globalc.val[0] - worldc.val[0];
const y = globalc.val[1] - worldc.val[1]; const y = globalc.val[1] - worldc.val[1];
const w = @intCast(i16, level.width); const w = @intCast(i32, level.width);
const i = @intCast(usize, x + y * w); const i = @intCast(usize, x + y * w);
return tiles[i]; return tiles[i];
} }
@ -817,25 +827,30 @@ pub const Database = struct {
if (visited[i]) return db.circuit_info[i].energized; if (visited[i]) return db.circuit_info[i].energized;
visited[i] = true; visited[i] = true;
const node = db.circuit_info[i]; const node = db.circuit_info[i];
const w4 = @import("wasm4.zig");
switch (node.kind) { switch (node.kind) {
.And => |And| { .And => |And| {
w4.tracef("[updateCircuitFragment] %d And %d %d", i, And[0], And[1]);
const input1 = db.updateCircuitFragment(And[0], visited); const input1 = db.updateCircuitFragment(And[0], visited);
const input2 = db.updateCircuitFragment(And[1], visited); const input2 = db.updateCircuitFragment(And[1], visited);
db.circuit_info[i].energized = (input1 and input2); db.circuit_info[i].energized = (input1 and input2);
}, },
.Xor => |Xor| { .Xor => |Xor| {
w4.tracef("[updateCircuitFragment] %d Xor", i);
const input1 = db.updateCircuitFragment(Xor[0], visited); const input1 = db.updateCircuitFragment(Xor[0], visited);
const input2 = db.updateCircuitFragment(Xor[1], visited); const input2 = db.updateCircuitFragment(Xor[1], visited);
db.circuit_info[i].energized = (input1 and !input2) or (input2 and !input1); db.circuit_info[i].energized = (input1 and !input2) or (input2 and !input1);
}, },
.Source => db.circuit_info[i].energized = true, .Source => db.circuit_info[i].energized = true,
.Conduit => |Conduit| { .Conduit => |Conduit| {
w4.tracef("[updateCircuitFragment] %d Conduit", i);
const input1 = db.updateCircuitFragment(Conduit[0], visited); const input1 = db.updateCircuitFragment(Conduit[0], visited);
const input2 = db.updateCircuitFragment(Conduit[1], visited); const input2 = db.updateCircuitFragment(Conduit[1], visited);
db.circuit_info[i].energized = (input1 or input2); db.circuit_info[i].energized = (input1 or input2);
}, },
// TODO: Sockets may come before the plug they are connected to // TODO: Sockets may come before the plug they are connected to
.Socket => |socket_opt| { .Socket => |socket_opt| {
w4.tracef("[updateCircuitFragment] %d Socket", i);
if (socket_opt) |input| { if (socket_opt) |input| {
db.circuit_info[i].energized = db.updateCircuitFragment(input, visited); db.circuit_info[i].energized = db.updateCircuitFragment(input, visited);
} else { } else {
@ -843,12 +858,15 @@ pub const Database = struct {
} }
}, },
.Plug => |Plug| { .Plug => |Plug| {
w4.tracef("[updateCircuitFragment] %d Plug", i);
db.circuit_info[i].energized = db.updateCircuitFragment(Plug, visited); db.circuit_info[i].energized = db.updateCircuitFragment(Plug, visited);
}, },
.Switch => |_Switch| { .Switch => |_Switch| {
w4.tracef("[updateCircuitFragment] %d Switch %d", i, _Switch.source);
db.circuit_info[i].energized = db.updateCircuitFragment(_Switch.source, visited); db.circuit_info[i].energized = db.updateCircuitFragment(_Switch.source, visited);
}, },
.SwitchOutlet => |_Switch| { .SwitchOutlet => |_Switch| {
w4.tracef("[updateCircuitFragment] %d Switch Outlet", i);
const is_energized = db.updateCircuitFragment(_Switch.source, visited); const is_energized = db.updateCircuitFragment(_Switch.source, visited);
const _switch = db.circuit_info[_Switch.source].kind.Switch; const _switch = db.circuit_info[_Switch.source].kind.Switch;
const _outlet = db.circuit_info[i].kind.SwitchOutlet; const _outlet = db.circuit_info[i].kind.SwitchOutlet;
@ -858,12 +876,15 @@ pub const Database = struct {
db.circuit_info[i].energized = _outlet.which == _switch.state; db.circuit_info[i].energized = _outlet.which == _switch.state;
}, },
.Join => |Join| { .Join => |Join| {
w4.tracef("[updateCircuitFragment] %d Join", i);
db.circuit_info[i].energized = db.updateCircuitFragment(Join, visited); db.circuit_info[i].energized = db.updateCircuitFragment(Join, visited);
}, },
.Outlet => |Outlet| { .Outlet => |Outlet| {
w4.tracef("[updateCircuitFragment] %d Outlet", i);
db.circuit_info[i].energized = db.updateCircuitFragment(Outlet, visited); db.circuit_info[i].energized = db.updateCircuitFragment(Outlet, visited);
}, },
} }
w4.tracef("[updateCircuitFragment] %d end", i);
return db.circuit_info[i].energized; return db.circuit_info[i].energized;
} }
@ -872,6 +893,8 @@ pub const Database = struct {
defer alloc.free(visited); defer alloc.free(visited);
std.mem.set(bool, visited, false); std.mem.set(bool, visited, false);
var i: usize = db.circuit_info.len - 1; var i: usize = db.circuit_info.len - 1;
const w4 = @import("wasm4.zig");
w4.tracef("[updateCircuit] circuit info len %d", db.circuit_info.len);
while (i > 0) : (i -|= 1) { while (i > 0) : (i -|= 1) {
_ = db.updateCircuitFragment(i, visited); _ = db.updateCircuitFragment(i, visited);
if (i == 0) break; if (i == 0) break;

View File

@ -152,8 +152,8 @@ fn parseLevel(opt: struct {
const layers = level.layerInstances orelse return error.NoLayers; const layers = level.layerInstances orelse return error.NoLayers;
const world_x: i8 = @intCast(i8, @divExact(level.worldX, (ldtk.worldGridWidth orelse 160))); const world_x: i8 = @intCast(i8, @divFloor(level.worldX, (ldtk.worldGridWidth orelse 160)));
const world_y: i8 = @intCast(i8, @divExact(level.worldY, (ldtk.worldGridHeight orelse 160))); const world_y: i8 = @intCast(i8, @divFloor(level.worldY, (ldtk.worldGridHeight orelse 160)));
var circuit_layer: ?LDtk.LayerInstance = null; var circuit_layer: ?LDtk.LayerInstance = null;
var collision_layer: ?LDtk.LayerInstance = null; var collision_layer: ?LDtk.LayerInstance = null;
@ -285,14 +285,22 @@ fn parseLevel(opt: struct {
// Add circuit tiles // Add circuit tiles
for (circuit.intGridCsv) |cir64, i| { for (circuit.intGridCsv) |cir64, i| {
const cir = @intCast(u4, cir64); const cir = @intToEnum(world.CircuitType, @intCast(u5, cir64));
const col = collision.intGridCsv[i]; const col = collision.intGridCsv[i];
if (col == 0 or col == 1) { if (cir != .None and col == 2) return error.DebrisAndCircuitOverlapped;
tiles[i] = world.TileData{ .flags = .{ if (cir == .None) continue;
.solid = col == 1, const solid: world.SolidType = switch (col) {
.circuit = @intToEnum(world.CircuitType, cir), 0 => .Empty,
} }; 1 => .Solid,
} 3 => .Oneway,
else => return error.DebrisAndCircuitOverlapped,
};
if (cir == .Socket)
std.log.warn("[parseLevel] {}: {}", .{ i, cir });
tiles[i] = world.TileData{ .flags = .{
.solid = solid,
.circuit = cir,
} };
} }
return parsed_level; return parsed_level;
@ -313,22 +321,15 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
var sources = Queue{}; var sources = Queue{};
var sockets = Queue{}; var sockets = Queue{};
var level_hashmap = std.AutoHashMap(u16, world.Level).init(alloc);
defer level_hashmap.deinit();
for (levels) |level| { for (levels) |level| {
const id: u16 = @bitCast(u8, level.world_x) | @intCast(u16, @bitCast(u8, level.world_y)) << 8;
// So we can quickly find levels
try level_hashmap.put(id, level);
// Use a global coordinate system for our algorithm // Use a global coordinate system for our algorithm
const global_x = @intCast(i16, level.world_x) * 20; const global_x = @intCast(i16, level.world_x) * 20;
const global_y = @intCast(i16, level.world_y) * 20; const global_y = @intCast(i16, level.world_y) * 20;
for (level.tiles orelse continue) |tileData, i| { for (level.tiles orelse continue) |tileData, i| {
const x = global_x + @intCast(i16, @mod(i, level.width)); const x = global_x + @intCast(i16, @mod(i, level.width));
const y = global_y + @intCast(i16, @divTrunc(i, level.width)); const y = global_y + @intCast(i16, @divTrunc(i, level.width));
const coordinate = try alloc.create(Node); const search_item = try alloc.create(Node);
coordinate.* = .{ .data = .{ search_item.* = .{ .data = .{
.last_node = @intCast(world.NodeID, nodes.items.len), .last_node = @intCast(world.NodeID, nodes.items.len),
.coord = Coord.init(.{ x, y }), .coord = Coord.init(.{ x, y }),
} }; } };
@ -340,12 +341,11 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
switch (flags.circuit) { switch (flags.circuit) {
.Source => { .Source => {
try nodes.append(.{ .kind = .Source, .coord = Coord.init(.{ x, y }) }); try nodes.append(.{ .kind = .Source, .coord = Coord.init(.{ x, y }) });
sources.append(coordinate); sources.append(search_item);
}, },
.Socket => { .Socket => {
// try nodes.append(.{ .kind = .{ .Plug = null } }); search_item.data.last_node = std.math.maxInt(world.NodeID);
coordinate.data.last_node = std.math.maxInt(world.NodeID); sockets.append(search_item);
sockets.append(coordinate);
}, },
else => { else => {
// Do nothing // Do nothing
@ -367,24 +367,22 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
while (run < 2) : (run += 1) { while (run < 2) : (run += 1) {
if (run == 0) bfs_queue.concatByMoving(&sources); if (run == 0) bfs_queue.concatByMoving(&sources);
if (run == 1) bfs_queue.concatByMoving(&sockets); if (run == 1) bfs_queue.concatByMoving(&sockets);
// bfs_queue.concatByMoving(&outlets);
while (bfs_queue.popFirst()) |node| { while (bfs_queue.popFirst()) |node| {
// Make sure we clean up the node's memory // Make sure we clean up the node's memory
defer alloc.destroy(node); defer alloc.destroy(node);
const coord = node.data.coord; const coord = node.data.coord;
if (visited.contains(coord)) continue; if (visited.contains(coord)) continue;
try visited.put(coord, .{}); try visited.put(coord, {});
// TODO remove magic numbers
const worldc = coord.toWorld(); const worldc = coord.toWorld();
const id: u16 = @bitCast(u8, worldc[0]) | @intCast(u16, @bitCast(u8, worldc[1])) << 8; // const level = getLevel(levels, worldc[0], worldc[1]);
// const level_opt: ?world.Level = level_hashmap.get(.{ world_x, world_y }); if (getLevel(levels, worldc[0], worldc[1])) |level| {
if (level_hashmap.getPtr(id) != null) {
const level = level_hashmap.getPtr(id);
const last_node = node.data.last_node; const last_node = node.data.last_node;
var next_node = last_node; var next_node = last_node;
const tile = level.?.getTile(coord).?; const tile = level.getTile(coord) orelse continue;
std.log.warn("[buildCircuit] {} [{}] {}", .{ coord, node.data.last_node, tile });
if (tile != .flags) continue; if (tile != .flags) continue;
const flags = tile.flags; const flags = tile.flags;
@ -397,6 +395,56 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
// previous nodes to point to the conduit // previous nodes to point to the conduit
// TODO // TODO
}, },
.Conduit_Horizontal => {
// Skip vertical inputs
const last_coord = node.data.last_coord.?;
const input_dir: Dir = getInputDirection(coord, last_coord);
if (input_dir == .North or input_dir == .South) {
_ = visited.remove(coord);
continue;
}
const left = try alloc.create(Node);
left.* = Node{ .data = .{
.last_node = last_node,
.coord = coord.add(.{ -1, 0 }),
.last_coord = coord,
} };
bfs_queue.append(left);
const right = try alloc.create(Node);
right.* = Node{ .data = .{
.last_node = last_node,
.coord = coord.add(.{ 1, 0 }),
.last_coord = coord,
} };
bfs_queue.append(right);
continue;
},
.Conduit_Vertical => {
// Skip horizontal inputs
const last_coord = node.data.last_coord.?;
const input_dir: Dir = getInputDirection(coord, last_coord);
if (input_dir == .West or input_dir == .East) {
_ = visited.remove(coord);
continue;
}
const up = try alloc.create(Node);
up.* = Node{ .data = .{
.last_node = last_node,
.coord = coord.add(.{ 0, -1 }),
.last_coord = coord,
} };
bfs_queue.append(up);
const down = try alloc.create(Node);
down.* = Node{ .data = .{
.last_node = last_node,
.coord = coord.add(.{ 0, 1 }),
.last_coord = coord,
} };
bfs_queue.append(down);
continue;
},
.Socket => { .Socket => {
next_node = @intCast(world.NodeID, nodes.items.len); next_node = @intCast(world.NodeID, nodes.items.len);
try nodes.append(.{ try nodes.append(.{
@ -423,28 +471,39 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
.Switch_Off, .Switch_On => { .Switch_Off, .Switch_On => {
// Identify input side // Identify input side
const last_coord = node.data.last_coord.?; const last_coord = node.data.last_coord.?;
const Dir = enum { North, West, East, South }; const input_dir: Dir = getInputDirection(coord, last_coord);
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 // Find outlets
const north_opt = level.?.getTile(coord.add(.{ 0, -1 })).?.getCircuit(); const ncoord = coord.add(.{ 0, -1 });
const west_opt = level.?.getTile(coord.add(.{ -1, 0 })).?.getCircuit(); const wcoord = coord.add(.{ -1, 0 });
const east_opt = level.?.getTile(coord.add(.{ 1, 0 })).?.getCircuit(); const ecoord = coord.add(.{ 1, 0 });
const south_opt = level.?.getTile(coord.add(.{ 0, 1 })).?.getCircuit(); const scoord = coord.add(.{ 0, 1 });
const north = (north_opt orelse world.CircuitType.None) != .None; const north_opt = if (level.getTile(ncoord)) |t| t.getCircuit() else @panic("AAAAA");
const west = (west_opt orelse world.CircuitType.None) != .None; const west_opt = if (level.getTile(wcoord)) |t| t.getCircuit() else @panic("AAAAA");
const east = (east_opt orelse world.CircuitType.None) != .None; const east_opt = if (level.getTile(ecoord)) |t| t.getCircuit() else @panic("AAAAA");
const south = (south_opt orelse world.CircuitType.None) != .None; const south_opt = if (level.getTile(scoord)) |t| t.getCircuit() else @panic("AAAAA");
const north_tile = north_opt orelse world.CircuitType.None;
const west_tile = west_opt orelse world.CircuitType.None;
const east_tile = east_opt orelse world.CircuitType.None;
const south_tile = south_opt orelse world.CircuitType.None;
const north = north_tile != .None and north_tile != .Conduit_Horizontal;
const west = west_tile != .None and west_tile != .Conduit_Vertical;
const east = east_tile != .None and east_tile != .Conduit_Vertical;
const south = south_tile != .None and south_tile != .Conduit_Horizontal;
std.log.warn("[buildCircuit] {}: {} {},\n\t{} {},\n\t{} {},\n\t{} {}", .{
coord,
north_tile,
ncoord,
west_tile,
wcoord,
east_tile,
ecoord,
south_tile,
scoord,
});
// We don't have four way switches, don't allow them // We don't have four way switches, don't allow them
std.debug.assert(west != true or east != true); std.debug.assert(west != true or east != true);
@ -480,7 +539,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
// Add switch outlets // Add switch outlets
if (input_dir != .West and west) { if (input_dir != .West and west) {
const out_node = @intCast(world.NodeID, nodes.items.len); const out_node = @intCast(world.NodeID, nodes.items.len);
const new_coord = coord.add(.{-1, 0}); const new_coord = coord.add(.{ -1, 0 });
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
@ -499,7 +558,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
if (input_dir != .East and east) { if (input_dir != .East and east) {
const out_node = @intCast(world.NodeID, nodes.items.len); const out_node = @intCast(world.NodeID, nodes.items.len);
const new_coord = coord.add(.{1, 0}); const new_coord = coord.add(.{ 1, 0 });
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
@ -518,7 +577,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
if (input_dir != .South and south) { if (input_dir != .South and south) {
const out_node = @intCast(world.NodeID, nodes.items.len); const out_node = @intCast(world.NodeID, nodes.items.len);
const new_coord = coord.add(.{0, 1}); const new_coord = coord.add(.{ 0, 1 });
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
@ -537,7 +596,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
if (input_dir != .North and north) { if (input_dir != .North and north) {
const out_node = @intCast(world.NodeID, nodes.items.len); const out_node = @intCast(world.NodeID, nodes.items.len);
const new_coord = coord.add(.{0, -1}); const new_coord = coord.add(.{ 0, -1 });
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
@ -596,10 +655,34 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
} }
} else { } else {
_ = visited.remove(coord); _ = visited.remove(coord);
if (side == .O) { if (side != .O) {
// TODO: reverse the path, since the search path // TODO: reverse the path, since the search path
// may have come from a plug // may have come from a plug
return error.OutputToSource; next_node = @intCast(world.NodeID, nodes.items.len);
try nodes.append(.{
.kind = .{ .And = .{ last_node, std.math.maxInt(world.NodeID) } },
.coord = coord,
});
std.log.warn("{}", .{nodes.items[last_node]});
switch (nodes.items[last_node].kind) {
.And => |_and| {
if (_and[0] == std.math.maxInt(world.NodeID)) {
nodes.items[last_node].kind.And[0] = next_node;
} else if (_and[1] == std.math.maxInt(world.NodeID)) {
nodes.items[last_node].kind.And[1] = next_node;
} else {
return error.AndGateFilled;
}
},
.SwitchOutlet => |_switch| {
_ = _switch;
std.log.warn("{}", .{nodes.items[last_node].coord});
},
.Socket => |socket| {
_ = socket;
},
else => return error.Unimplemented,
}
} else if (side == .L) { } else if (side == .L) {
next_node = @intCast(world.NodeID, nodes.items.len); next_node = @intCast(world.NodeID, nodes.items.len);
try nodes.append(.{ try nodes.append(.{
@ -625,7 +708,7 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
} }
}, },
.Xor => { .Xor => {
std.log.warn("XOR XOR XOR",.{}); std.log.warn("XOR XOR XOR", .{});
// TODO: verify Xor gate is properly connected // TODO: verify Xor gate is properly connected
const last_coord = node.data.last_coord.?; const last_coord = node.data.last_coord.?;
const Side = enum { O, L, R }; const Side = enum { O, L, R };
@ -679,6 +762,9 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
continue; continue;
} }
}, },
.Diode => {
// TODO
},
.None => continue, .None => continue,
} }
@ -712,10 +798,30 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
bfs_queue.append(left); bfs_queue.append(left);
bfs_queue.append(down); bfs_queue.append(down);
bfs_queue.append(up); bfs_queue.append(up);
} }
} }
} }
return nodes; return nodes;
} }
const Dir = enum { North, West, East, South };
fn getInputDirection(coord: world.Coordinate, last_coord: world.Coordinate) Dir {
if (last_coord.eq(coord.add(.{ 0, -1 }))) {
return .North;
} else if (last_coord.eq(coord.add(.{ -1, 0 }))) {
return .West;
} else if (last_coord.eq(coord.add(.{ 1, 0 }))) {
return .East;
} else {
return .South;
}
}
fn getLevel(levels: []world.Level, x: i8, y: i8) ?world.Level {
for (levels) |level| {
if (level.world_x == x and level.world_y == y) return level;
}
return null;
}