mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 05:32:07 -04:00
221 lines
8.9 KiB
Lua
221 lines
8.9 KiB
Lua
#!/usr/bin/env luajit
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local H = require("tests.link3_harness")
|
|
local Cache = require("tests.game3_cache")
|
|
|
|
local failed = 0
|
|
local function check(cond, msg)
|
|
if cond then
|
|
print("[ok] " .. msg)
|
|
else
|
|
failed = failed + 1
|
|
print("[FAIL] " .. msg)
|
|
end
|
|
end
|
|
local function eq(a, b, msg)
|
|
check(a == b, string.format("%s (%s == %s)", msg, tostring(a), tostring(b)))
|
|
end
|
|
|
|
if not Cache.root("meta.json") then
|
|
print("[skip] the Gen 3 arena state runs a real link battle on ROM data: " .. tostring(Cache.reason))
|
|
os.exit(0)
|
|
end
|
|
H.mountCache()
|
|
|
|
local function input()
|
|
local i = { pressed = {} }
|
|
function i:wasPressed(b) return self.pressed[b] == true end
|
|
function i:isDown() return false end
|
|
return i
|
|
end
|
|
|
|
local function arenaGame(w)
|
|
local g = w.game
|
|
g.input = input()
|
|
g.returns = {}
|
|
g.left = 0
|
|
g.returnToLauncher = function(o) g.returns[#g.returns + 1] = o end
|
|
g.leaveArena = function() g.left = g.left + 1 end
|
|
return g
|
|
end
|
|
|
|
local function mon(species, moves)
|
|
return H.legal({ species = species, level = 40, moves = moves,
|
|
ivs = { hp = 15, atk = 15, def = 15, spe = 15, spa = 15, spd = 15 },
|
|
evs = { hp = 0, atk = 0, def = 0, spe = 0, spa = 0, spd = 0 }, item = 0 })
|
|
end
|
|
|
|
local PROFILE = { engine = 3, version = "firered", kind = "vanilla", rulesetId = "g3_single", rule = { partySize = 6 } }
|
|
|
|
local function arenaSpec(relay, seat, party, done)
|
|
return {
|
|
profile = PROFILE, role = seat == nil and "spectator" or (seat == 0 and "host" or "guest"),
|
|
seat = seat, seats = 2, seed = relay.seed, match = relay.match, room = relay.room,
|
|
players = relay.players, myParty = party and H.pack(party) or nil,
|
|
session = relay:session(seat), client = relay:client(), mode = "single",
|
|
headless = true, autoFight = true, onDone = done,
|
|
}
|
|
end
|
|
|
|
local function newArena(w, spec)
|
|
return H.run(w, function()
|
|
local ArenaState = require("src.ui.game3.arena_state")
|
|
return ArenaState.new(arenaGame(w), spec)
|
|
end)
|
|
end
|
|
|
|
local function step(w, arena)
|
|
H.run(w, function() arena:update(1 / 60) end)
|
|
end
|
|
|
|
print("[test] 1. two arena states and a spectator play one match to the end over the relay")
|
|
do
|
|
local relay = H.relay({ seed = 0xABCDE, seats = 2 })
|
|
local w0 = H.newWorld("seat0", { name = "RED", trainerId = 1, gender = 0, party = {}, bag = {} })
|
|
local w1 = H.newWorld("seat1", { name = "LEAF", trainerId = 2, gender = 1, party = {}, bag = {} })
|
|
local ws = H.newWorld("spectator", { name = "WATCH", trainerId = 3, gender = 0, party = {}, bag = {} })
|
|
local done = { [0] = {}, [1] = {}, s = {} }
|
|
local s0 = arenaSpec(relay, 0, { mon(6, { 10 }), mon(25, { 84 }) }, function(r) table.insert(done[0], r) end)
|
|
local s1 = arenaSpec(relay, 1, { mon(9, { 33 }), mon(1, { 33 }) }, function(r) table.insert(done[1], r) end)
|
|
local a0 = newArena(w0, s0)
|
|
local a1 = newArena(w1, s1)
|
|
local ss = arenaSpec(relay, nil, nil, function(r) table.insert(done.s, r) end)
|
|
local as = newArena(ws, ss)
|
|
eq(a0.stage, "linking", "seat 0 starts linking")
|
|
eq(as.spectator, true, "the third state is a spectator")
|
|
for _ = 1, 20000 do
|
|
step(w0, a0)
|
|
step(w1, a1)
|
|
step(ws, as)
|
|
relay:tick()
|
|
if a0.done and a1.done and as.done then break end
|
|
end
|
|
check(a0.done and a1.done and as.done, "all three arena states left")
|
|
eq(#done[0], 1, "seat 0 onDone ran exactly once")
|
|
eq(#done[1], 1, "seat 1 onDone ran exactly once")
|
|
local mirror = { win = "lose", lose = "win", draw = "draw" }
|
|
eq(mirror[done[0][1]], done[1][1], "their results mirror")
|
|
eq(done.s[1], "ended", "the spectator ended with ended")
|
|
eq(#w0.reports, 1, "seat 0 reported once")
|
|
eq(w0.reports[1], done[0][1], "with its own result")
|
|
eq(#w1.reports, 1, "seat 1 reported once")
|
|
eq(#ws.reports, 0, "the spectator never reports")
|
|
eq(#w0.game.returns, 1, "seat 0 returned to the launcher once")
|
|
eq(w0.game.returns[1] and w0.game.returns[1].tab, "online", "on the online tab")
|
|
eq(w0.game.left, 1, "and left the arena phase once")
|
|
check(s0.session.left and s1.session.left and ss.session.left, "every room session was left")
|
|
local byes = 0
|
|
for _, row in ipairs(relay.log) do if row.msg.type == "game3_bye" then byes = byes + 1 end end
|
|
check(byes >= 1, "the link closed with game3_bye before the room was left")
|
|
end
|
|
|
|
print("[test] 2. an arena with no seed from the relay shows the failure and leaves with error")
|
|
do
|
|
local relay = H.relay({ seed = 5, seats = 2 })
|
|
relay.seed = nil
|
|
local w0 = H.newWorld("seat0", { name = "RED", trainerId = 1, gender = 0, party = {}, bag = {} })
|
|
local results = {}
|
|
local spec = arenaSpec(relay, 0, { mon(6, { 10 }) }, function(r) results[#results + 1] = r end)
|
|
spec.seed = nil
|
|
local a0 = newArena(w0, spec)
|
|
eq(a0.stage, "failed", "the arena shows the failure")
|
|
check(type(a0.message) == "string" and a0.message ~= "", "with a message")
|
|
for _ = 1, 5 do step(w0, a0) end
|
|
eq(#results, 0, "nothing is reported while the failure is on screen")
|
|
w0.game.input.pressed.a = true
|
|
step(w0, a0)
|
|
step(w0, a0)
|
|
eq(results[1], "error", "A dismisses it with error")
|
|
eq(#results, 1, "exactly once")
|
|
eq(#w0.game.returns, 1, "back to the launcher")
|
|
end
|
|
|
|
print("[test] 3. a peer that never arrives can be cancelled with B once the wait is long")
|
|
do
|
|
local relay = H.relay({ seed = 9, seats = 2 })
|
|
local w0 = H.newWorld("seat0", { name = "RED", trainerId = 1, gender = 0, party = {}, bag = {} })
|
|
local results = {}
|
|
local a0 = newArena(w0, arenaSpec(relay, 0, { mon(6, { 10 }) }, function(r) results[#results + 1] = r end))
|
|
local ArenaStateCancel = H.run(w0, function() return require("src.ui.game3.arena_state").CANCEL_FRAMES end)
|
|
for _ = 1, ArenaStateCancel do step(w0, a0) end
|
|
eq(a0.stage, "linking", "still waiting for the other seat")
|
|
w0.game.input.pressed.b = true
|
|
step(w0, a0)
|
|
step(w0, a0)
|
|
eq(results[1], "ended", "B leaves with ended")
|
|
check(a0.done, "and the state is gone")
|
|
end
|
|
|
|
local function peek(w)
|
|
return H.run(w, function()
|
|
local LB = require("src.core.game3.link.battle")
|
|
return LB.endReason
|
|
end)
|
|
end
|
|
|
|
print("[test] 4. a seat still on the last turn keeps its result when the winner leaves the room")
|
|
do
|
|
local relay = H.relay({ seed = 0xABCDE, seats = 2, lag = { [1] = 2 } })
|
|
local w0 = H.newWorld("seat0", { name = "RED", trainerId = 1, gender = 0, party = {}, bag = {} })
|
|
local w1 = H.newWorld("seat1", { name = "LEAF", trainerId = 2, gender = 1, party = {}, bag = {} })
|
|
local done = { [0] = {}, [1] = {} }
|
|
local s0 = arenaSpec(relay, 0, { mon(6, { 10 }), mon(25, { 84 }) }, function(r) table.insert(done[0], r) end)
|
|
local s1 = arenaSpec(relay, 1, { mon(9, { 33 }), mon(1, { 33 }) }, function(r) table.insert(done[1], r) end)
|
|
s1.headless = false
|
|
local a0 = newArena(w0, s0)
|
|
local a1 = newArena(w1, s1)
|
|
local lagged = false
|
|
for _ = 1, 30000 do
|
|
step(w0, a0)
|
|
step(w1, a1)
|
|
if a0.done and not a1.done then lagged = true end
|
|
relay:tick()
|
|
if a0.done and a1.done then break end
|
|
end
|
|
check(a0.done and a1.done, "both arena states left")
|
|
check(lagged, "seat 0 left the room while seat 1 was still finishing")
|
|
local mirror = { win = "lose", lose = "win", draw = "draw" }
|
|
check(done[0][1] == "win" or done[0][1] == "lose", "seat 0 has a winner (" .. tostring(done[0][1]) .. ")")
|
|
eq(done[1][1], mirror[done[0][1]], "seat 1 keeps the mirrored result")
|
|
eq(w1.reports[1], mirror[w0.reports[1]], "and reports it")
|
|
check(peek(w1) ~= "peer_dropped", "seat 1 is not ended as a dropped peer")
|
|
end
|
|
|
|
print("[test] 5. a rendered spectator behind the seats replays to the end after both seats leave")
|
|
do
|
|
local relay = H.relay({ seed = 0xABCDE, seats = 2, lag = { spectator = 2 } })
|
|
local w0 = H.newWorld("seat0", { name = "RED", trainerId = 1, gender = 0, party = {}, bag = {} })
|
|
local w1 = H.newWorld("seat1", { name = "LEAF", trainerId = 2, gender = 1, party = {}, bag = {} })
|
|
local ws = H.newWorld("spectator", { name = "WATCH", trainerId = 3, gender = 0, party = {}, bag = {} })
|
|
local done = { [0] = {}, [1] = {}, s = {} }
|
|
local a0 = newArena(w0, arenaSpec(relay, 0, { mon(6, { 10 }), mon(25, { 84 }) }, function(r) table.insert(done[0], r) end))
|
|
local a1 = newArena(w1, arenaSpec(relay, 1, { mon(9, { 33 }), mon(1, { 33 }) }, function(r) table.insert(done[1], r) end))
|
|
local ss = arenaSpec(relay, nil, nil, function(r) table.insert(done.s, r) end)
|
|
ss.headless = false
|
|
local as = newArena(ws, ss)
|
|
local reason, lagged
|
|
for _ = 1, 30000 do
|
|
step(w0, a0)
|
|
step(w1, a1)
|
|
if not as.done then
|
|
step(ws, as)
|
|
reason = peek(ws) or reason
|
|
if a0.done and a1.done then lagged = true end
|
|
end
|
|
relay:tick()
|
|
if a0.done and a1.done and as.done then break end
|
|
end
|
|
check(a0.done and a1.done and as.done, "all three arena states left")
|
|
check(lagged, "both seats left while the spectator was still replaying")
|
|
eq(reason, nil, "the spectator is not cut off")
|
|
eq(done.s[1], "ended", "the spectator ends with ended")
|
|
end
|
|
|
|
if failed == 0 then
|
|
print("[pass] link3 arena state")
|
|
os.exit(0)
|
|
end
|
|
print("[fail] link3 arena state: " .. failed)
|
|
os.exit(1)
|