Files

232 lines
9.1 KiB
GDScript

extends SceneTree
const WorldSimulation = preload("res://src/simulation/world_simulation.gd")
func _initialize() -> void:
var failures: Array[String] = []
var reports: Dictionary = {}
reports["no_mana"] = _capture_no_mana()
reports["missing_input"] = _capture_missing_input()
reports["waiting_input"] = _capture_waiting_input()
reports["working"] = _capture_working()
reports["outputting"] = _capture_outputting()
reports["blocked_output"] = _capture_blocked_output()
reports["charger_no_mana"] = _capture_charger_no_mana()
for status in [
&"no_mana",
&"missing_input",
&"waiting_input",
&"working",
&"outputting",
&"blocked_output",
]:
if not reports.has(status):
failures.append("Missing inspection capture for %s." % String(status))
continue
var capture: Dictionary = reports[status]
if not bool(capture.get("ok", false)):
failures.append(
"Could not reach status %s (got %s)." % [
String(status),
String(capture.get("status", &"")),
]
)
continue
failures.append_array(_validate_payload(status, capture["report"], capture["unchanged"]))
var charger_capture: Dictionary = reports["charger_no_mana"]
if not bool(charger_capture.get("ok", false)):
failures.append("Charger did not emit a second-stage no_mana status.")
else:
var charger_report: Dictionary = charger_capture["report"]
var recipe: Dictionary = charger_report.get("recipe", {})
if recipe.get("id", &"") != WorldSimulation.RECIPE_CHARGE:
failures.append("Charger inspection did not expose the charge recipe.")
if recipe.get("input_kind", &"") != WorldSimulation.ITEM_REFINED:
failures.append("Charger recipe input was not refined crystal.")
if recipe.get("output_kind", &"") != WorldSimulation.ITEM_CHARGED:
failures.append("Charger recipe output was not charged crystal.")
failures.append_array(
_validate_payload(&"no_mana", charger_report, charger_capture["unchanged"])
)
if failures.is_empty():
print("INSPECTION_SMOKE_OK statuses=%s" % ",".join([
"no_mana",
"missing_input",
"waiting_input",
"working",
"outputting",
"blocked_output",
"charger_no_mana",
]))
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func _validate_payload(status: StringName, report: Dictionary, unchanged: bool) -> Array[String]:
var failures: Array[String] = []
if report.is_empty():
failures.append("Inspection returned an empty payload for %s." % String(status))
return failures
for key in ["recipe", "progress", "inventory", "mana", "reason"]:
if not report.has(key):
failures.append("Inspection for %s missing field %s." % [String(status), key])
var recipe: Dictionary = report.get("recipe", {})
if recipe.is_empty() or not recipe.has("id"):
failures.append("Inspection for %s missing recipe id." % String(status))
var progress: Dictionary = report.get("progress", {})
if not progress.has("ratio") or not progress.has("remaining_ticks"):
failures.append("Inspection for %s missing progress fields." % String(status))
var inventory: Dictionary = report.get("inventory", {})
if not inventory.has("input_count") or not inventory.has("output_buffer"):
failures.append("Inspection for %s missing inventory fields." % String(status))
var mana: Dictionary = report.get("mana", {})
if not mana.has("powered") or not mana.has("network"):
failures.append("Inspection for %s missing mana fields." % String(status))
if status != &"working" and String(report.get("reason", "")).is_empty():
failures.append("Inspection for %s had an empty stop reason." % String(status))
if not unchanged:
failures.append("Inspection for %s mutated tick, inventory, or fingerprint." % String(status))
return failures
func _inspect_without_mutation(simulation: WorldSimulation, building_id: int) -> Dictionary:
var before := {
"tick": simulation.tick_index,
"fingerprint": simulation.state_fingerprint(),
"sink": simulation.sink_inventory.duplicate(),
}
var input_count := -1
if simulation.buildings.has(building_id):
input_count = int(simulation.buildings[building_id].get("input_count", -1))
var report := simulation.inspect_building(building_id)
var unchanged: bool = (
int(before["tick"]) == simulation.tick_index
and String(before["fingerprint"]) == simulation.state_fingerprint()
and before["sink"] == simulation.sink_inventory
and (
input_count < 0
or input_count == int(simulation.buildings[building_id].get("input_count", -2))
)
)
return {
"report": report,
"unchanged": unchanged,
"status": report.get("status", &""),
"ok": not report.is_empty(),
}
func _capture_no_mana() -> Dictionary:
var simulation := WorldSimulation.new()
var machine_id := simulation.place_building(WorldSimulation.KIND_MACHINE, Vector2i(0, 0), 0)
simulation.step()
var capture := _inspect_without_mutation(simulation, machine_id)
capture["ok"] = String(capture["status"]) == "no_mana"
return capture
func _capture_missing_input() -> Dictionary:
var simulation := WorldSimulation.new()
var machine_id := simulation.place_building(WorldSimulation.KIND_MACHINE, Vector2i(0, 0), 0)
simulation.place_building(WorldSimulation.KIND_MANA_SOURCE, Vector2i(0, 1), 0)
simulation.step()
var capture := _inspect_without_mutation(simulation, machine_id)
capture["ok"] = String(capture["status"]) == "missing_input"
return capture
func _capture_waiting_input() -> Dictionary:
var simulation := WorldSimulation.new()
var machine_id := simulation.place_building(WorldSimulation.KIND_MACHINE, Vector2i(0, 0), 0)
simulation.place_building(WorldSimulation.KIND_MANA_SOURCE, Vector2i(0, 1), 0)
simulation.place_building(WorldSimulation.KIND_BELT, Vector2i(-1, 0), 0)
simulation.step()
var capture := _inspect_without_mutation(simulation, machine_id)
capture["ok"] = String(capture["status"]) == "waiting_input"
return capture
func _capture_working() -> Dictionary:
var simulation := _powered_line()
var machine_id := _find_kind(simulation, WorldSimulation.KIND_MACHINE)
for _tick in range(400):
simulation.step()
if String(simulation.buildings[machine_id]["status"]) == "working":
var capture := _inspect_without_mutation(simulation, machine_id)
capture["ok"] = String(capture["status"]) == "working"
return capture
return {"ok": false, "status": simulation.buildings[machine_id]["status"], "report": {}, "unchanged": true}
func _capture_outputting() -> Dictionary:
var simulation := _powered_line()
var machine_id := _find_kind(simulation, WorldSimulation.KIND_MACHINE)
var source_cell := Vector2i(-3, 0)
for _tick in range(400):
simulation.step()
if int(simulation.buildings[_find_kind(simulation, WorldSimulation.KIND_SOURCE)].get("produced", 0)) >= 1:
break
simulation.remove_building(source_cell)
for _tick in range(400):
simulation.step()
if String(simulation.buildings[machine_id]["status"]) == "outputting":
var capture := _inspect_without_mutation(simulation, machine_id)
capture["ok"] = String(capture["status"]) == "outputting"
return capture
return {"ok": false, "status": simulation.buildings[machine_id]["status"], "report": {}, "unchanged": true}
func _capture_blocked_output() -> Dictionary:
var simulation := WorldSimulation.new()
simulation.place_building(WorldSimulation.KIND_SOURCE, Vector2i(-3, 0), 0)
simulation.place_building(WorldSimulation.KIND_BELT, Vector2i(-2, 0), 0)
simulation.place_building(WorldSimulation.KIND_BELT, Vector2i(-1, 0), 0)
var machine_id := simulation.place_building(WorldSimulation.KIND_MACHINE, Vector2i(0, 0), 0)
simulation.place_building(WorldSimulation.KIND_MANA_SOURCE, Vector2i(0, 1), 0)
for _tick in range(400):
simulation.step()
if String(simulation.buildings[machine_id]["status"]) == "blocked_output":
var capture := _inspect_without_mutation(simulation, machine_id)
capture["ok"] = String(capture["status"]) == "blocked_output"
return capture
return {"ok": false, "status": simulation.buildings[machine_id]["status"], "report": {}, "unchanged": true}
func _capture_charger_no_mana() -> Dictionary:
var simulation := WorldSimulation.new()
var charger_id := simulation.place_building(WorldSimulation.KIND_CHARGER, Vector2i(2, 0), 0)
simulation.step()
var capture := _inspect_without_mutation(simulation, charger_id)
capture["ok"] = String(capture["status"]) == "no_mana"
return capture
func _powered_line() -> WorldSimulation:
var simulation := WorldSimulation.new()
simulation.place_building(WorldSimulation.KIND_SOURCE, Vector2i(-3, 0), 0)
simulation.place_building(WorldSimulation.KIND_BELT, Vector2i(-2, 0), 0)
simulation.place_building(WorldSimulation.KIND_BELT, Vector2i(-1, 0), 0)
simulation.place_building(WorldSimulation.KIND_MACHINE, Vector2i(0, 0), 0)
simulation.place_building(WorldSimulation.KIND_MANA_SOURCE, Vector2i(0, 2), 0)
simulation.place_building(WorldSimulation.KIND_MANA_PIPE, Vector2i(0, 1), 0)
simulation.place_building(WorldSimulation.KIND_BELT, Vector2i(1, 0), 0)
simulation.place_building(WorldSimulation.KIND_BELT, Vector2i(2, 0), 0)
simulation.place_building(WorldSimulation.KIND_SINK, Vector2i(3, 0), 0)
return simulation
func _find_kind(simulation: WorldSimulation, kind: StringName) -> int:
for id_variant in simulation.get_building_ids():
var id := int(id_variant)
if simulation.buildings[id]["kind"] == kind:
return id
return -1