package.path = "./?.lua;" .. package.path class = require "libs/middleclass" local Wire = require "wire" require "world" gridWidth, gridHeight = 2, 2 local passed = 0 local function test(name, fn) local ok, err = pcall(fn) if not ok then io.stderr:write("FAIL: " .. name .. "\n" .. err .. "\n") os.exit(1) end passed = passed + 1 end local function assertTrue(value, message) if not value then error(message or "expected true", 2) end end local function assertFalse(value, message) if value then error(message or "expected false", 2) end end local function switch(color, x, y) local entity = {x = x, y = y, activated = false} entity.color = {getname = function() return color end} function entity:getGridPos() return {x = self.x, y = self.y} end function entity:activate() self.activated = true end function entity:deactivate() self.activated = false end return entity end local function door(circuitId, x, y) local entity = {circuitId = circuitId, x = x, y = y, powered = false} function entity:getGridPos() return {x = self.x, y = self.y} end function entity:setPowered(powered) self.powered = powered end return entity end local function makeWorld(secondSwitch) local blueSwitch = switch("blue", 2, 1) local wiredDoor = door("circuit_a", 1, 1) local roomA = { name = "a", player = {}, wiredSwitches = {}, wiredDoors = {wiredDoor}, wires = {[1] = { [1] = {circuit_a = Wire.EAST}, [2] = {circuit_a = Wire.WEST + Wire.EAST}, }}, } local roomB = { name = "b", player = {}, wiredSwitches = {blueSwitch}, wiredDoors = {}, wires = {[1] = { [1] = {circuit_a = Wire.WEST + Wire.EAST}, [2] = {circuit_a = Wire.WEST}, }}, } local function wireCircuitAt(room, cell) local tracks = room.wires[cell.y] and room.wires[cell.y][cell.x] for circuitId in pairs(tracks or {}) do return circuitId end end roomA.wireCircuitAt = wireCircuitAt roomB.wireCircuitAt = wireCircuitAt if secondSwitch then table.insert(roomB.wiredSwitches, secondSwitch) end local instance = setmetatable({ levels = {a = roomA, b = roomB}, roomByName = {a = {name = "a", x = 1, y = 1}, b = {name = "b", x = 2, y = 1}}, collidableMatrices = {red = {}, green = {}, blue = {}}, }, {__index = World}) function instance:roomCell(cell) if cell.x <= gridWidth then return roomA, {x = cell.x, y = cell.y} end return roomB, {x = cell.x - gridWidth, y = cell.y} end local function wireTracksAt(room, cell, create) local row = room.wires[cell.y] if not row and create then row = {}; room.wires[cell.y] = row end local tracks = row and row[cell.x] if not tracks and create then tracks = {}; row[cell.x] = tracks end return tracks end roomA.wireTracksAt = wireTracksAt roomB.wireTracksAt = wireTracksAt return instance, roomA, roomB, wiredDoor, blueSwitch end test("direction masks add and remove without duplicating bits", function() local mask = Wire.add(0, Wire.EAST) mask = Wire.add(mask, Wire.EAST) assertTrue(mask == Wire.EAST) assertTrue(Wire.has(mask, Wire.EAST)) assertFalse(Wire.has(mask, Wire.WEST)) assertTrue(Wire.remove(mask, Wire.EAST) == 0) end) test("orphaned and overlapping wire cells remain selectable without a door", function() local world, roomA = makeWorld() roomA.wires[1][1].orphan_b = Wire.SOUTH local top = world:circuitAt({x = 1, y = 1}) assertTrue(top == "orphan_b", "top orphaned circuit was not selectable") local preferred = world:circuitAt({x = 1, y = 1}, "circuit_a") assertTrue(preferred == "circuit_a", "selected overlapping circuit was not preferred") end) test("a white switch powers every layer of a white door", function() local world, roomA, roomB = makeWorld() roomA.wiredDoors = { door("circuit_a", 1, 1), door("old_green_id", 1, 1), door("old_blue_id", 1, 1), } roomB.wiredSwitches = { switch("red", 2, 1), switch("green", 2, 1), switch("blue", 2, 1), } world.collidableMatrices.red[1] = {[4] = {}} world.collidableMatrices.green[1] = {[4] = {}} world.collidableMatrices.blue[1] = {[4] = {}} world:resolveCircuits() for _, wiredDoor in ipairs(roomA.wiredDoors) do assertTrue(wiredDoor.circuitId == "circuit_a", "door layer did not adopt terminal circuit") assertTrue(world.circuitState.doorPowered[wiredDoor], "white door layer remained locked") end local color = world:wireColorAt(roomA, {x = 1, y = 1}, "circuit_a") assertTrue(color[1] == 1 and color[2] == 1 and color[3] == 1, "white switch did not produce white wire") local redGreenOnly = world:wireColorAt(roomA, {x = 1, y = 1}, "circuit_a", {red = true, green = true}) assertTrue(redGreenOnly[1] == 1 and redGreenOnly[2] == 1 and redGreenOnly[3] == 0, "white wire did not obey the room's visible player channels") end) test("world matrices join a circuit across a room boundary", function() local world, _, _, wiredDoor, blueSwitch = makeWorld() world.collidableMatrices.blue[1] = {[4] = {}} world:resolveCircuits() assertTrue(world.wireMatrix[1][2].circuit_a ~= nil) assertTrue(world.wireMatrix[1][3].circuit_a ~= nil) assertTrue(world.wiredDoorMatrix[1][1][1].entity == wiredDoor) assertTrue(world.wiredSwitchMatrix[1][4][1].entity == blueSwitch) assertTrue(world.circuitState.switchPressed[blueSwitch]) assertTrue(world.circuitState.doorPowered[wiredDoor]) assertTrue(wiredDoor.powered) assertTrue(world:wireColorAt(world.levels.a, {x = 1, y = 1}, "circuit_a", {red = true, green = true}) == nil, "blue wire leaked into a room without a blue player channel") local visibleBlue = world:wireColorAt(world.levels.a, {x = 1, y = 1}, "circuit_a", {blue = true}) assertTrue(visibleBlue[1] == 0 and visibleBlue[2] == 0 and visibleBlue[3] == 1, "blue wire did not render in a room with a blue player channel") end) test("wire color is re-derived when its terminal switch changes", function() local world, roomA, roomB = makeWorld() world:resolveCircuits() local colors = world:wireSwitchColorsAt(roomA, {x = 1, y = 1}, "circuit_a") assertTrue(colors.blue and not colors.red) roomB.wiredSwitches = {switch("red", 2, 1)} world:resolveCircuits() colors = world:wireSwitchColorsAt(roomA, {x = 1, y = 1}, "circuit_a") assertTrue(colors.red and not colors.blue, "wire retained the deleted terminal switch color") end) test("a switchless wire component has no gameplay color", function() local world, roomA, roomB = makeWorld() roomB.wiredSwitches = {} world:resolveCircuits() assertTrue(world:wireSwitchColorsAt(roomA, {x = 1, y = 1}, "circuit_a") == nil) assertTrue(world:wireColorAt(roomA, {x = 1, y = 1}, "circuit_a") == nil) end) test("a door requires every independent switch run at its terminal", function() local greenSwitch = switch("green", 1, 2) local world, roomA, _, wiredDoor = makeWorld() table.insert(roomA.wiredSwitches, greenSwitch) roomA.wires[1][1].circuit_b = Wire.SOUTH roomA.wires[2] = {[1] = {circuit_b = Wire.NORTH}} world.collidableMatrices.blue[1] = {[4] = {}} world:resolveCircuits() assertFalse(world.circuitState.doorPowered[wiredDoor]) world.collidableMatrices.green[2] = {[1] = {}} world:resolveCircuits() assertTrue(world.circuitState.doorPowered[wiredDoor]) end) test("an orphaned stub at a door does not block a valid switch run", function() local world, roomA, _, wiredDoor = makeWorld() roomA.wires[1][1].abandoned_stroke = 0 world.collidableMatrices.blue[1] = {[4] = {}} world:resolveCircuits() assertTrue(world.circuitState.doorPowered[wiredDoor]) assertTrue(wiredDoor.powered) end) test("selecting a parked colour changes to its room", function() local function player() local entity = {switched = false} function entity:playSwitchSound(success) self.switched = success end return entity end local function room(name, players, active) local entity = {name = name, player = players, activeColors = active, colorsPlayerHas = {}} for color in pairs(players) do table.insert(entity.colorsPlayerHas, color) end function entity:getPossibleColors() return self.colorsPlayerHas end function entity:setActiveColor(color, value) self.activeColors[color] = value end function entity:getActiveColors() local colors = {} for color, value in pairs(self.activeColors) do if value then table.insert(colors, color) end end return colors end return entity end local roomA = room("a", {red = player()}, {red = true, green = false, blue = false}) local roomB = room("b", {blue = player()}, {red = false, green = false, blue = false}) local world = setmetatable({ levels = {a = roomA, b = roomB}, roomByName = {a = {name = "a", x = 1, y = 1}, b = {name = "b", x = 2, y = 1}}, }, {__index = World}) world.flushRuntime = function(self) self.flushed = true end local oldCurrentRoom, oldSetEditorRoomName = currentRoom, setEditorRoomName currentRoom = roomA setEditorRoomName = function() end assertTrue(world:switchPlayerColor(roomA, "blue")) assertTrue(currentRoom == roomB) assertFalse(roomA.activeColors.red) assertTrue(roomB.activeColors.blue) assertTrue(roomB.player.blue.switched) assertTrue(world.flushed) currentRoom, setEditorRoomName = oldCurrentRoom, oldSetEditorRoomName end) test("one run cannot terminate at two different switches", function() local second = switch("red", 1, 2) local world, _, roomB = makeWorld(second) world:resolveCircuits() local rooms, err = world:addWireConnection({x = 3, y = 1}, {x = 3, y = 2}, "circuit_a") assertTrue(#rooms == 0 and err ~= nil, "second switch terminal was accepted") assertTrue(roomB.wires[2] == nil or roomB.wires[2][1] == nil, "rejected second terminal left a connected wire cell") local colors = world:wireSwitchColorsAt(world.levels.a, {x = 1, y = 1}, "circuit_a") assertTrue(colors.blue and not colors.red, "run stopped deriving color from its sole terminal switch") end) test("matching IDs do not bridge a gap or one-sided room edge", function() local world, _, roomB, wiredDoor = makeWorld() world.collidableMatrices.blue[1] = {[4] = {}} roomB.wires[1][1].circuit_a = Wire.EAST -- no reciprocal WEST edge world:resolveCircuits() assertFalse(world.circuitState.doorPowered[wiredDoor]) end) io.write("PASS: " .. passed .. " wire tests\n")