254 lines
9.9 KiB
Lua
254 lines
9.9 KiB
Lua
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")
|