From c88f4184eaa48efbdc69e76eb3c8a4a206434ad3 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Sat, 16 Aug 2025 14:52:24 -0400 Subject: Started writing a data validator Currently, it can check whether identifiers point to non-existent objects, or whether multiple objects share the same identifier. It can also determine whether an identifier is underspecified (e.g. a door doesn't specify a room, or a global connection doesn't specify a map). --- tools/validator/CMakeLists.txt | 11 + tools/validator/human_processor.cpp | 425 ++++++++++++++++++++++++++++++++++++ tools/validator/human_processor.h | 14 ++ tools/validator/main.cpp | 31 +++ tools/validator/structs.h | 97 ++++++++ tools/validator/validator.cpp | 250 +++++++++++++++++++++ tools/validator/validator.h | 12 + 7 files changed, 840 insertions(+) create mode 100644 tools/validator/CMakeLists.txt create mode 100644 tools/validator/human_processor.cpp create mode 100644 tools/validator/human_processor.h create mode 100644 tools/validator/main.cpp create mode 100644 tools/validator/structs.h create mode 100644 tools/validator/validator.cpp create mode 100644 tools/validator/validator.h (limited to 'tools/validator') diff --git a/tools/validator/CMakeLists.txt b/tools/validator/CMakeLists.txt new file mode 100644 index 0000000..0ad58c2 --- /dev/null +++ b/tools/validator/CMakeLists.txt @@ -0,0 +1,11 @@ +find_package(Protobuf REQUIRED) + +add_executable(validator + human_processor.cpp + main.cpp + validator.cpp +) +set_property(TARGET validator PROPERTY CXX_STANDARD 20) +set_property(TARGET validator PROPERTY CXX_STANDARD_REQUIRED ON) +target_include_directories(validator PUBLIC ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/tools) +target_link_libraries(validator PUBLIC protos util protobuf::libprotobuf) diff --git a/tools/validator/human_processor.cpp b/tools/validator/human_processor.cpp new file mode 100644 index 0000000..5382443 --- /dev/null +++ b/tools/validator/human_processor.cpp @@ -0,0 +1,425 @@ +#include "human_processor.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "structs.h" + +namespace com::fourisland::lingo2_archipelago { +namespace { + +template +T ReadMessageFromFile(const std::string& path) { + std::cout << "Processing " << path << std::endl; + + std::ifstream file(path); + std::stringstream buffer; + buffer << file.rdbuf(); + + T message; + google::protobuf::TextFormat::ParseFromString(buffer.str(), &message); + + return message; +} + +class HumanProcessor { + public: + HumanProcessor(const std::string& mapdir, CollectedInfo& info) + : mapdir_(mapdir), info_(info) {} + + void Run() { + std::filesystem::path datadir_path = mapdir_; + + ProcessConnectionsFile(datadir_path / "connections.txtpb", std::nullopt); + ProcessMaps(datadir_path); + ProcessIdsFile(datadir_path / "ids.txtpb"); + } + + private: + void ProcessMaps(std::filesystem::path path) { + std::filesystem::path maps_dir = path / "maps"; + for (auto const& dir_entry : + std::filesystem::directory_iterator(maps_dir)) { + ProcessMap(dir_entry.path()); + } + } + + void ProcessMap(std::filesystem::path path) { + std::string map_name = path.filename(); + + ProcessConnectionsFile(path / "connections.txtpb", map_name); + ProcessDoorsFile(path / "doors.txtpb", map_name); + ProcessRooms(path / "rooms", map_name); + } + + void ProcessRooms(std::filesystem::path path, + const std::string& current_map_name) { + for (auto const& dir_entry : std::filesystem::directory_iterator(path)) { + auto room = ReadMessageFromFile(dir_entry.path().string()); + ProcessRoom(room, current_map_name); + } + } + + void ProcessRoom(const HumanRoom& h_room, + const std::string& current_map_name) { + RoomIdentifier room_identifier; + room_identifier.set_map(current_map_name); + room_identifier.set_name(h_room.name()); + + RoomInfo& room_info = info_.rooms[room_identifier]; + room_info.definitions.push_back(h_room); + + for (const HumanPanel& h_panel : h_room.panels()) { + ProcessPanel(h_panel, current_map_name, h_room.name()); + } + + for (const HumanPainting& h_painting : h_room.paintings()) { + ProcessPainting(h_painting, current_map_name, h_room.name()); + } + + for (const HumanPort& h_port : h_room.ports()) { + ProcessPort(h_port, current_map_name, h_room.name()); + } + + for (const HumanLetter& h_letter : h_room.letters()) { + ProcessLetter(h_letter, current_map_name, h_room.name()); + } + + for (const HumanMastery& h_mastery : h_room.masteries()) { + ProcessMastery(h_mastery, current_map_name, h_room.name()); + } + + for (const HumanKeyholder& h_keyholder : h_room.keyholders()) { + ProcessKeyholder(h_keyholder, current_map_name, h_room.name()); + } + } + + void ProcessPanel(const HumanPanel& h_panel, + const std::string& current_map_name, + const std::string& current_room_name) { + PanelIdentifier panel_identifier; + panel_identifier.set_map(current_map_name); + panel_identifier.set_room(current_room_name); + panel_identifier.set_name(h_panel.name()); + + PanelInfo& panel_info = info_.panels[panel_identifier]; + panel_info.definitions.push_back(h_panel); + panel_info.proxies[h_panel.answer()].definitions.push_back(Proxy()); + + for (const Proxy& h_proxy : h_panel.proxies()) { + ProxyInfo& proxy_info = panel_info.proxies[h_proxy.answer()]; + proxy_info.definitions.push_back(h_proxy); + } + + if (h_panel.has_required_door()) { + DoorIdentifier required_door_identifier = + *GetCompleteDoorIdentifier(h_panel.required_door(), current_map_name); + DoorInfo& required_door_info = info_.doors[required_door_identifier]; + required_door_info.panels_referenced_by.push_back(panel_identifier); + } + + if (h_panel.has_required_room()) { + RoomIdentifier required_room_identifier = + *GetCompleteRoomIdentifier(h_panel.required_room(), current_map_name); + RoomInfo& required_room_info = info_.rooms[required_room_identifier]; + required_room_info.panels_referenced_by.push_back(panel_identifier); + } + } + + void ProcessPainting(const HumanPainting& h_painting, + const std::string& current_map_name, + const std::string& current_room_name) { + PaintingIdentifier painting_identifier; + painting_identifier.set_map(current_map_name); + painting_identifier.set_room(current_room_name); + painting_identifier.set_name(h_painting.name()); + + PaintingInfo& painting_info = info_.paintings[painting_identifier]; + painting_info.definitions.push_back(h_painting); + + if (h_painting.has_required_door()) { + DoorIdentifier required_door_identifier = *GetCompleteDoorIdentifier( + h_painting.required_door(), current_map_name); + DoorInfo& required_door_info = info_.doors[required_door_identifier]; + required_door_info.paintings_referenced_by.push_back(painting_identifier); + } + } + + void ProcessPort(const HumanPort& h_port, const std::string& current_map_name, + const std::string& current_room_name) { + PortIdentifier port_identifier; + port_identifier.set_map(current_map_name); + port_identifier.set_room(current_room_name); + port_identifier.set_name(h_port.name()); + + PortInfo& port_info = info_.ports[port_identifier]; + port_info.definitions.push_back(h_port); + + if (h_port.has_required_door()) { + DoorIdentifier required_door_identifier = + *GetCompleteDoorIdentifier(h_port.required_door(), current_map_name); + DoorInfo& required_door_info = info_.doors[required_door_identifier]; + required_door_info.ports_referenced_by.push_back(port_identifier); + } + } + + void ProcessLetter(const HumanLetter& h_letter, + const std::string& current_map_name, + const std::string& current_room_name) { + LetterIdentifier letter_identifier = + std::make_tuple(h_letter.key()[0], h_letter.level2()); + LetterInfo& letter_info = info_.letters[letter_identifier]; + + RoomIdentifier room_identifier; + room_identifier.set_map(current_map_name); + room_identifier.set_name(current_room_name); + letter_info.defined_in.push_back(room_identifier); + } + + void ProcessMastery(const HumanMastery& h_mastery, + const std::string& current_map_name, + const std::string& current_room_name) { + // Nothing really to validate about masteries yet. + } + + void ProcessKeyholder(const HumanKeyholder& h_keyholder, + const std::string& current_map_name, + const std::string& current_room_name) { + KeyholderIdentifier keyholder_identifier; + keyholder_identifier.set_map(current_map_name); + keyholder_identifier.set_room(current_room_name); + keyholder_identifier.set_name(h_keyholder.name()); + + KeyholderInfo& keyholder_info = info_.keyholders[keyholder_identifier]; + keyholder_info.definitions.push_back(h_keyholder); + } + + void ProcessDoorsFile(std::filesystem::path path, + const std::string& current_map_name) { + if (!std::filesystem::exists(path)) { + return; + } + + auto doors = ReadMessageFromFile(path.string()); + + for (const HumanDoor& door : doors.doors()) { + ProcessDoor(door, current_map_name); + } + } + + void ProcessDoor(const HumanDoor& h_door, + const std::string& current_map_name) { + DoorIdentifier door_identifier; + door_identifier.set_map(current_map_name); + door_identifier.set_name(h_door.name()); + + DoorInfo& door_info = info_.doors[door_identifier]; + door_info.definitions.push_back(h_door); + + if (h_door.has_location_room()) { + RoomIdentifier location_room_identifier; + location_room_identifier.set_map(current_map_name); + location_room_identifier.set_name(h_door.location_room()); + info_.rooms[location_room_identifier].doors_referenced_by.push_back( + door_identifier); + } + + for (const PaintingIdentifier& pi : h_door.move_paintings()) { + auto complete_painting_identifier = + GetCompletePaintingIdentifier(pi, current_map_name, std::nullopt); + if (complete_painting_identifier) { + PaintingInfo& move_painting_info = + info_.paintings[*complete_painting_identifier]; + move_painting_info.doors_referenced_by.push_back(door_identifier); + } else { + door_info.malformed_identifiers.paintings.push_back(pi); + } + } + + for (const PanelIdentifier& pi : h_door.panels()) { + auto complete_panel_identifier = GetCompletePanelIdentifierWithoutAnswer( + pi, current_map_name, std::nullopt); + if (complete_panel_identifier) { + PanelInfo& panel_info = info_.panels[*complete_panel_identifier]; + panel_info.doors_referenced_by.push_back(door_identifier); + + if (pi.has_answer()) { + panel_info.proxies[pi.answer()].doors_referenced_by.push_back( + door_identifier); + } + } else { + door_info.malformed_identifiers.panels.push_back(pi); + } + } + + for (const KeyholderIdentifier& ki : h_door.keyholders()) { + auto complete_keyholder_identifier = + GetCompleteKeyholderIdentifierWithoutKey(ki, current_map_name, + std::nullopt); + if (complete_keyholder_identifier) { + KeyholderInfo& keyholder_info = + info_.keyholders[*complete_keyholder_identifier]; + keyholder_info.doors_referenced_by.push_back(door_identifier); + } else { + door_info.malformed_identifiers.keyholders.push_back(ki); + } + } + + for (const RoomIdentifier& ri : h_door.rooms()) { + RoomIdentifier complete_room_identifier = + *GetCompleteRoomIdentifier(ri, current_map_name); + RoomInfo& room_info = info_.rooms[complete_room_identifier]; + room_info.doors_referenced_by.push_back(door_identifier); + } + + for (const DoorIdentifier& di : h_door.doors()) { + DoorIdentifier complete_door_identifier = + *GetCompleteDoorIdentifier(di, current_map_name); + DoorInfo& other_door_info = info_.doors[complete_door_identifier]; + other_door_info.doors_referenced_by.push_back(door_identifier); + } + } + + void ProcessConnectionsFile(std::filesystem::path path, + std::optional current_map_name) { + if (!std::filesystem::exists(path)) { + return; + } + + auto connections = ReadMessageFromFile(path.string()); + + for (const HumanConnection& connection : connections.connections()) { + ProcessConnection(connection, current_map_name); + } + } + + void ProcessConnection(const HumanConnection& human_connection, + const std::optional& current_map_name) { + if (human_connection.has_from_room()) { + if (current_map_name) { + RoomIdentifier room_identifier; + room_identifier.set_map(*current_map_name); + room_identifier.set_name(human_connection.from_room()); + + RoomInfo& room_info = info_.rooms[room_identifier]; + room_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout << "A global connection used from_room." << std::endl; + } + } else if (human_connection.has_from()) { + ProcessSingleConnection(human_connection, human_connection.from(), + current_map_name); + } + + if (human_connection.has_to_room()) { + if (current_map_name) { + RoomIdentifier room_identifier; + room_identifier.set_map(*current_map_name); + room_identifier.set_name(human_connection.to_room()); + + RoomInfo& room_info = info_.rooms[room_identifier]; + room_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout << "A global connection used to_room." << std::endl; + } + } else if (human_connection.has_to()) { + ProcessSingleConnection(human_connection, human_connection.to(), + current_map_name); + } + + if (human_connection.has_door()) { + auto door_identifier = + GetCompleteDoorIdentifier(human_connection.door(), current_map_name); + if (door_identifier) { + DoorInfo& door_info = info_.doors[*door_identifier]; + door_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout + << "A connection used the following malformed door identifier: " + << human_connection.door().ShortDebugString() << std::endl; + } + } + } + + void ProcessSingleConnection( + const HumanConnection& human_connection, + const HumanConnection::Endpoint& endpoint, + const std::optional& current_map_name) { + if (endpoint.has_room()) { + auto room_identifier = + GetCompleteRoomIdentifier(endpoint.room(), current_map_name); + if (room_identifier) { + RoomInfo& room_info = info_.rooms[*room_identifier]; + room_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout + << "A connection used the following malformed room identifier: " + << endpoint.room().ShortDebugString() << std::endl; + } + } else if (endpoint.has_painting()) { + auto painting_identifier = GetCompletePaintingIdentifier( + endpoint.painting(), current_map_name, std::nullopt); + if (painting_identifier) { + PaintingInfo& painting_info = info_.paintings[*painting_identifier]; + painting_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout + << "A connection used the following malformed painting identifier: " + << endpoint.painting().ShortDebugString() << std::endl; + } + } else if (endpoint.has_port()) { + auto port_identifier = GetCompletePortIdentifier( + endpoint.port(), current_map_name, std::nullopt); + if (port_identifier) { + PortInfo& port_info = info_.ports[*port_identifier]; + port_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout + << "A connection used the following malformed port identifier: " + << endpoint.port().ShortDebugString() << std::endl; + } + } else if (endpoint.has_panel()) { + auto panel_identifier = GetCompletePanelIdentifierWithoutAnswer( + endpoint.panel(), current_map_name, std::nullopt); + if (panel_identifier) { + PanelInfo& panel_info = info_.panels[*panel_identifier]; + panel_info.connections_referenced_by.push_back(human_connection); + + if (endpoint.panel().has_answer()) { + panel_info.proxies[endpoint.panel().answer()] + .connections_referenced_by.push_back(human_connection); + } + } + } + } + + void ProcessIdsFile(std::filesystem::path path) { + // Ignore this for now. + } + + std::string mapdir_; + CollectedInfo& info_; +}; + +} // namespace + +void ProcessHumanData(const std::string& mapdir, CollectedInfo& info) { + HumanProcessor human_processor(mapdir, info); + human_processor.Run(); +} + +} // namespace com::fourisland::lingo2_archipelago diff --git a/tools/validator/human_processor.h b/tools/validator/human_processor.h new file mode 100644 index 0000000..52f174f --- /dev/null +++ b/tools/validator/human_processor.h @@ -0,0 +1,14 @@ +#ifndef TOOLS_VALIDATOR_HUMAN_PROCESSOR_H_ +#define TOOLS_VALIDATOR_HUMAN_PROCESSOR_H_ + +#include + +namespace com::fourisland::lingo2_archipelago { + +struct CollectedInfo; + +void ProcessHumanData(const std::string& mapdir, CollectedInfo& info); + +} // namespace com::fourisland::lingo2_archipelago + +#endif /* TOOLS_VALIDATOR_HUMAN_PROCESSOR_H_ */ diff --git a/tools/validator/main.cpp b/tools/validator/main.cpp new file mode 100644 index 0000000..af9842b --- /dev/null +++ b/tools/validator/main.cpp @@ -0,0 +1,31 @@ +#include "human_processor.h" +#include "structs.h" +#include "validator.h" + +namespace com::fourisland::lingo2_archipelago { +namespace { + +void Run(const std::string& mapdir) { + CollectedInfo info; + + ProcessHumanData(mapdir, info); + + ValidateCollectedInfo(info); +} + +} // namespace +} // namespace com::fourisland::lingo2_archipelago + +int main(int argc, char** argv) { + if (argc != 2) { + std::cout << "Incorrect argument count." << std::endl; + std::cout << "Usage: validator [path to map directory]" << std::endl; + return 1; + } + + std::string mapdir = argv[1]; + + com::fourisland::lingo2_archipelago::Run(mapdir); + + return 0; +} diff --git a/tools/validator/structs.h b/tools/validator/structs.h new file mode 100644 index 0000000..c3427f4 --- /dev/null +++ b/tools/validator/structs.h @@ -0,0 +1,97 @@ +#ifndef TOOLS_VALIDATOR_STRUCTS_H_ +#define TOOLS_VALIDATOR_STRUCTS_H_ + +#include +#include +#include + +#include "proto/human.pb.h" +#include "util/identifiers.h" + +namespace com::fourisland::lingo2_archipelago { + +struct MalformedIdentifiers { + std::vector paintings; + std::vector panels; + std::vector keyholders; + + bool HasAny() const { + return !paintings.empty() || !panels.empty() || !keyholders.empty(); + } +}; + +struct RoomInfo { + std::vector definitions; + + std::vector doors_referenced_by; + std::vector panels_referenced_by; + std::vector connections_referenced_by; +}; + +struct DoorInfo { + std::vector definitions; + + std::vector connections_referenced_by; + std::vector doors_referenced_by; + std::vector panels_referenced_by; + std::vector paintings_referenced_by; + std::vector ports_referenced_by; + + MalformedIdentifiers malformed_identifiers; +}; + +struct PortInfo { + std::vector definitions; + + std::vector connections_referenced_by; +}; + +struct PaintingInfo { + std::vector definitions; + + std::vector connections_referenced_by; + std::vector doors_referenced_by; +}; + +struct ProxyInfo { + std::vector definitions; + + std::vector connections_referenced_by; + std::vector doors_referenced_by; +}; + +struct PanelInfo { + std::vector definitions; + + std::vector connections_referenced_by; + std::vector doors_referenced_by; + + std::map proxies; +}; + +struct KeyholderInfo { + std::vector definitions; + + std::vector doors_referenced_by; +}; + +using LetterIdentifier = std::tuple; + +struct LetterInfo { + std::vector defined_in; +}; + +struct CollectedInfo { + std::map rooms; + std::map doors; + std::map ports; + std::map paintings; + std::map panels; + std::map + keyholders; + std::map letters; +}; + +} // namespace com::fourisland::lingo2_archipelago + +#endif /* TOOLS_VALIDATOR_STRUCTS_H_ */ diff --git a/tools/validator/validator.cpp b/tools/validator/validator.cpp new file mode 100644 index 0000000..3381ed2 --- /dev/null +++ b/tools/validator/validator.cpp @@ -0,0 +1,250 @@ +#include "validator.h" + +#include + +#include "proto/human.pb.h" +#include "structs.h" +#include "util/identifiers.h" + +namespace com::fourisland::lingo2_archipelago { +namespace { + +void ValidateRoom(const RoomIdentifier& room_identifier, + const RoomInfo& room_info) { + if (room_info.definitions.empty()) { + std::cout << "Room " << room_identifier.ShortDebugString() + << " has no definition, but was referenced:" << std::endl; + + for (const DoorIdentifier& door_identifier : + room_info.doors_referenced_by) { + std::cout << " DOOR " << door_identifier.ShortDebugString() + << std::endl; + } + + for (const PanelIdentifier& panel_identifier : + room_info.panels_referenced_by) { + std::cout << " PANEL " << panel_identifier.ShortDebugString() + << std::endl; + } + + for (const HumanConnection& connection : + room_info.connections_referenced_by) { + std::cout << " CONNECTION " << connection.ShortDebugString() + << std::endl; + } + } else if (room_info.definitions.size() > 1) { + std::cout << "Room " << room_identifier.ShortDebugString() + << " was defined multiple times." << std::endl; + } +} + +void ValidateDoor(const DoorIdentifier& door_identifier, + const DoorInfo& door_info) { + if (door_info.definitions.empty()) { + std::cout << "Door " << door_identifier.ShortDebugString() + << " has no definition, but was referenced:" << std::endl; + + for (const DoorIdentifier& other_door_identifier : + door_info.doors_referenced_by) { + std::cout << " DOOR " << other_door_identifier.ShortDebugString() + << std::endl; + } + + for (const PanelIdentifier& panel_identifier : + door_info.panels_referenced_by) { + std::cout << " PANEL " << panel_identifier.ShortDebugString() + << std::endl; + } + + for (const PaintingIdentifier& painting_identifier : + door_info.paintings_referenced_by) { + std::cout << " PAINTING " << painting_identifier.ShortDebugString() + << std::endl; + } + + for (const PortIdentifier& port_identifier : + door_info.ports_referenced_by) { + std::cout << " PORT " << port_identifier.ShortDebugString() + << std::endl; + } + + for (const HumanConnection& connection : + door_info.connections_referenced_by) { + std::cout << " CONNECTION " << connection.ShortDebugString() + << std::endl; + } + } else if (door_info.definitions.size() > 1) { + std::cout << "Door " << door_identifier.ShortDebugString() + << " was defined multiple times." << std::endl; + } + + if (door_info.malformed_identifiers.HasAny()) { + std::cout << "Door " << door_identifier.ShortDebugString() + << " has malformed identifiers:" << std::endl; + + for (const PaintingIdentifier& painting_identifier : + door_info.malformed_identifiers.paintings) { + std::cout << " PAINTING " << painting_identifier.ShortDebugString() + << std::endl; + } + + for (const PanelIdentifier& panel_identifier : + door_info.malformed_identifiers.panels) { + std::cout << " PANEL " << panel_identifier.ShortDebugString() + << std::endl; + } + + for (const KeyholderIdentifier& keyholder_identifier : + door_info.malformed_identifiers.keyholders) { + std::cout << " KEYHOLDER " << keyholder_identifier.ShortDebugString() + << std::endl; + } + } +} + +void ValidatePort(const PortIdentifier& port_identifier, + const PortInfo& port_info) { + if (port_info.definitions.empty()) { + std::cout << "Port " << port_identifier.ShortDebugString() + << " has no definition, but was referenced:" << std::endl; + + for (const HumanConnection& connection : + port_info.connections_referenced_by) { + std::cout << " CONNECTION " << connection.ShortDebugString() + << std::endl; + } + } else if (port_info.definitions.size() > 1) { + std::cout << "Port " << port_identifier.ShortDebugString() + << " was defined multiple times." << std::endl; + } +} + +void ValidatePainting(const PaintingIdentifier& painting_identifier, + const PaintingInfo& painting_info) { + if (painting_info.definitions.empty()) { + std::cout << "Painting " << painting_identifier.ShortDebugString() + << " has no definition, but was referenced:" << std::endl; + + for (const DoorIdentifier& door_identifier : + painting_info.doors_referenced_by) { + std::cout << " DOOR " << door_identifier.ShortDebugString() + << std::endl; + } + + for (const HumanConnection& connection : + painting_info.connections_referenced_by) { + std::cout << " CONNECTION " << connection.ShortDebugString() + << std::endl; + } + } else if (painting_info.definitions.size() > 1) { + std::cout << "Painting " << painting_identifier.ShortDebugString() + << " was defined multiple times." << std::endl; + } +} + +void ValidatePanel(const PanelIdentifier& panel_identifier, + const PanelInfo& panel_info) { + if (panel_info.definitions.empty()) { + std::cout << "Panel " << panel_identifier.ShortDebugString() + << " has no definition, but was referenced:" << std::endl; + + for (const DoorIdentifier& door_identifier : + panel_info.doors_referenced_by) { + std::cout << " DOOR " << door_identifier.ShortDebugString() + << std::endl; + } + + for (const HumanConnection& connection : + panel_info.connections_referenced_by) { + std::cout << " CONNECTION " << connection.ShortDebugString() + << std::endl; + } + } else if (panel_info.definitions.size() > 1) { + std::cout << "Panel " << panel_identifier.ShortDebugString() + << " was defined multiple times." << std::endl; + } + + for (const auto& [answer, proxy_info] : panel_info.proxies) { + if (proxy_info.definitions.empty()) { + std::cout << "Panel " << panel_identifier.ShortDebugString() + << " with answer \"" << answer + << "\" has no definition, but was referenced:" << std::endl; + + for (const DoorIdentifier& door_identifier : + proxy_info.doors_referenced_by) { + std::cout << " DOOR " << door_identifier.ShortDebugString() + << std::endl; + } + + for (const HumanConnection& connection : + proxy_info.connections_referenced_by) { + std::cout << " CONNECTION " << connection.ShortDebugString() + << std::endl; + } + } else if (proxy_info.definitions.size() > 1) { + std::cout << "Panel " << panel_identifier.ShortDebugString() + << " with answer \"" << answer + << "\" was defined multiple times." << std::endl; + } + } +} + +void ValidateKeyholder(const KeyholderIdentifier& keyholder_identifier, + const KeyholderInfo& keyholder_info) { + if (keyholder_info.definitions.empty()) { + std::cout << "Keyholder " << keyholder_identifier.ShortDebugString() + << " has no definition, but was referenced:" << std::endl; + + for (const DoorIdentifier& door_identifier : + keyholder_info.doors_referenced_by) { + std::cout << " DOOR " << door_identifier.ShortDebugString() + << std::endl; + } + } else if (keyholder_info.definitions.size() > 1) { + std::cout << "Keyholder " << keyholder_identifier.ShortDebugString() + << " was defined multiple times." << std::endl; + } +} + +void ValidateLetter(const LetterIdentifier& letter_identifier, + const LetterInfo& letter_info) { + std::string letter_name = std::string(1, std::get<0>(letter_identifier)) + + (std::get<1>(letter_identifier) ? "2" : "1"); + + if (letter_info.defined_in.size() > 1) { + std::cout << "Letter " << letter_name + << " was defined in multiple places:" << std::endl; + + for (const RoomIdentifier& room_identifier : letter_info.defined_in) { + std::cout << " " << room_identifier.ShortDebugString() << std::endl; + } + } +} + +} // namespace + +void ValidateCollectedInfo(const CollectedInfo& info) { + for (const auto& [room_identifier, room_info] : info.rooms) { + ValidateRoom(room_identifier, room_info); + } + for (const auto& [door_identifier, door_info] : info.doors) { + ValidateDoor(door_identifier, door_info); + } + for (const auto& [port_identifier, port_info] : info.ports) { + ValidatePort(port_identifier, port_info); + } + for (const auto& [painting_identifier, painting_info] : info.paintings) { + ValidatePainting(painting_identifier, painting_info); + } + for (const auto& [panel_identifier, panel_info] : info.panels) { + ValidatePanel(panel_identifier, panel_info); + } + for (const auto& [keyholder_identifier, keyholder_info] : info.keyholders) { + ValidateKeyholder(keyholder_identifier, keyholder_info); + } + for (const auto& [letter_identifier, letter_info] : info.letters) { + ValidateLetter(letter_identifier, letter_info); + } +} + +} // namespace com::fourisland::lingo2_archipelago diff --git a/tools/validator/validator.h b/tools/validator/validator.h new file mode 100644 index 0000000..b710429 --- /dev/null +++ b/tools/validator/validator.h @@ -0,0 +1,12 @@ +#ifndef TOOLS_VALIDATOR_VALIDATOR_H_ +#define TOOLS_VALIDATOR_VALIDATOR_H + +namespace com::fourisland::lingo2_archipelago { + +struct CollectedInfo; + +void ValidateCollectedInfo(const CollectedInfo& info); + +} // namespace com::fourisland::lingo2_archipelago + +#endif /* TOOLS_VALIDATOR_VALIDATOR_H */ -- cgit 1.4.1