extends GutTest const SimulationEventLogScript := preload("res://simulation/events/SimulationEventLog.gd") const VillageEconomyScript := preload("res://simulation/economy/VillageEconomy.gd") const RelationshipSystemScript := preload("res://simulation/relationships/RelationshipSystem.gd") const EventKnowledgeSystemScript := preload("res://simulation/knowledge/EventKnowledgeSystem.gd") const SimulationManagerScript := preload("res://simulation/SimulationManager.gd") func test_storage_never_accepts_more_than_its_capacity() -> void: var storage := StorageStateRecord.create(&"test_storage", {"food": 4.5}, 5.0) assert_eq(storage.deposit(SimulationIds.RESOURCE_FOOD, 2.0), 0.5) assert_eq(storage.get_amount(SimulationIds.RESOURCE_FOOD), 5.0) assert_eq(storage.get_available_capacity(), 0.0) func test_economy_moves_inventory_into_authoritative_storage() -> void: var village := SimVillage.new() village.food = 3.0 var economy := VillageEconomyScript.new() economy.configure(village, false) economy.initialize_storage() var npc := SimNPC.new(7, "Tester", SimulationIds.PROFESSION_FARMER, 5.0, 5.0) npc.add_inventory(SimulationIds.RESOURCE_FOOD, 2.0) assert_eq(economy.deposit_inventory(npc, SimulationIds.RESOURCE_FOOD), 2.0) assert_eq(npc.get_inventory_amount(SimulationIds.RESOURCE_FOOD), 0.0) assert_eq(economy.get_pantry().get_amount(SimulationIds.RESOURCE_FOOD), 5.0) assert_eq(village.food, 5.0) func test_event_log_preserves_order_and_derives_consumption_rate() -> void: var event_log := SimulationEventLogScript.new() event_log.record_narrative(10, SimulationIds.EVENT_TASK_STARTED, 4, &"", "Study") event_log.record_economic( 20, SimulationIds.EVENT_ITEM_CONSUMED, 4, SimulationIds.STORAGE_VILLAGE_PANTRY, &"consumed", SimulationIds.RESOURCE_FOOD, 1.0 ) assert_eq(event_log.events.size(), 2) assert_eq(int(event_log.events[0].data["event_id"]), 0) assert_eq(int(event_log.events[1].data["event_id"]), 1) assert_eq(event_log.get_for_actor(4).size(), 2) assert_eq(float(event_log.get_consumption_rates(200)["food_per_day"]), 1.0) var legacy_event_data := event_log.events[1].to_dictionary() legacy_event_data["schema_version"] = EconomicEventRecord.LEGACY_SCHEMA_VERSION legacy_event_data.erase("world_position") var migrated_event := EconomicEventRecord.from_dictionary(legacy_event_data) assert_not_null(migrated_event) assert_eq(migrated_event.get_world_position(), Vector3.ZERO) func test_food_aid_builds_directed_trust_once_per_event() -> void: var contributor := SimNPC.new(0, "Amina", SimulationIds.PROFESSION_FARMER, 5.0, 5.0) var observer := SimNPC.new(1, "Tarik", SimulationIds.PROFESSION_GUARD, 5.0, 5.0) contributor.home_position = Vector3.ZERO observer.home_position = Vector3(1.0, 0.0, 0.0) observer.hunger = 85.0 var villagers: Array[SimNPC] = [contributor, observer] var relationship_system := RelationshipSystemScript.new() relationship_system.initialize_households(villagers) var deposit_event := EconomicEventRecord.create( 7, SimulationIds.EVENT_STORAGE_DEPOSITED, 12, contributor.id, SimulationIds.npc_inventory_id(contributor.id), SimulationIds.STORAGE_VILLAGE_PANTRY, SimulationIds.RESOURCE_FOOD, 2.0 ) var later_deposit_event := EconomicEventRecord.create( 8, SimulationIds.EVENT_STORAGE_DEPOSITED, 13, contributor.id, SimulationIds.npc_inventory_id(contributor.id), SimulationIds.STORAGE_VILLAGE_PANTRY, SimulationIds.RESOURCE_FOOD, 1.0 ) var knower_ids: Array[int] = [contributor.id, observer.id] relationship_system.apply_event(deposit_event, villagers, knower_ids) relationship_system.apply_event(later_deposit_event, villagers, knower_ids) relationship_system.apply_event(deposit_event, villagers, knower_ids) var relationship: RelationshipStateRecord = relationship_system.get_relationship( observer.id, contributor.id ) assert_eq(relationship.get_trust(), 0.8) assert_eq(relationship.get_last_trust_cause_event_id(), 8) assert_eq( relationship_system.get_relationship(contributor.id, observer.id).get_trust(), RelationshipStateRecord.NEUTRAL_TRUST ) func test_event_knowledge_is_spatial_directed_and_idempotent() -> void: var actor := SimNPC.new(0, "Amina", SimulationIds.PROFESSION_FARMER, 5.0, 5.0) var nearby := SimNPC.new(1, "Tarik", SimulationIds.PROFESSION_GUARD, 5.0, 5.0) var distant := SimNPC.new(2, "Jasmin", SimulationIds.PROFESSION_GUARD, 5.0, 5.0) actor.position = Vector3.ZERO nearby.position = Vector3(4.0, 0.0, 0.0) distant.position = Vector3(20.0, 0.0, 0.0) var villagers: Array[SimNPC] = [actor, nearby, distant] var event := EconomicEventRecord.create( 12, SimulationIds.EVENT_STORAGE_DEPOSITED, 30, actor.id, SimulationIds.npc_inventory_id(actor.id), SimulationIds.STORAGE_VILLAGE_PANTRY, SimulationIds.RESOURCE_FOOD, 2.0, Vector3.ZERO ) actor.position = Vector3(100.0, 0.0, 0.0) var knowledge_system := EventKnowledgeSystemScript.new() assert_eq(knowledge_system.observe_event(event, villagers).size(), 2) assert_eq(knowledge_system.observe_event(event, villagers).size(), 0) assert_true(knowledge_system.knows_event(actor.id, 12)) assert_true(knowledge_system.knows_event(nearby.id, 12)) assert_false(knowledge_system.knows_event(distant.id, 12)) assert_eq(knowledge_system.get_knowers(12), [actor.id, nearby.id]) assert_eq( knowledge_system.get_record(actor.id, 12).get_acquisition_method(), SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED ) assert_eq( knowledge_system.get_record(nearby.id, 12).get_acquisition_method(), SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED ) func test_event_communication_preserves_first_provenance_and_stops_after_one_hop() -> void: var actor := SimNPC.new(0, "Amina", SimulationIds.PROFESSION_FARMER, 5.0, 5.0) var witness := SimNPC.new(1, "Tarik", SimulationIds.PROFESSION_GUARD, 5.0, 5.0) var listener := SimNPC.new(2, "Jasmin", SimulationIds.PROFESSION_GUARD, 5.0, 5.0) var relay_target := SimNPC.new(3, "Elma", SimulationIds.PROFESSION_GUARD, 5.0, 5.0) actor.position = Vector3.ZERO witness.position = Vector3(2.0, 0.0, 0.0) listener.position = Vector3(20.0, 0.0, 0.0) relay_target.position = Vector3(20.0, 0.0, 1.0) var villagers: Array[SimNPC] = [actor, witness, listener, relay_target] var event := EconomicEventRecord.create( 21, SimulationIds.EVENT_STORAGE_DEPOSITED, 40, actor.id, SimulationIds.npc_inventory_id(actor.id), SimulationIds.STORAGE_VILLAGE_PANTRY, SimulationIds.RESOURCE_FOOD, 2.0, Vector3.ZERO ) var knowledge_system := EventKnowledgeSystemScript.new() knowledge_system.observe_event(event, villagers) assert_null(knowledge_system.communicate_event(event, listener.id, relay_target.id, 50)) var communicated := knowledge_system.communicate_event(event, witness.id, listener.id, 50) assert_not_null(communicated) assert_eq( communicated.get_acquisition_method(), SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED ) assert_eq(communicated.get_source_npc_id(), witness.id) assert_eq(communicated.get_acquired_tick(), 50) assert_eq( communicated.get_source_acquisition_method(), SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED ) assert_null(knowledge_system.communicate_event(event, actor.id, listener.id, 50)) assert_eq(knowledge_system.get_record(listener.id, event.data["event_id"]), communicated) assert_null(knowledge_system.communicate_event(event, listener.id, relay_target.id, 50)) assert_false(knowledge_system.knows_event(relay_target.id, int(event.data["event_id"]))) var actor_gap_system := EventKnowledgeSystemScript.new() var direct_witness_records: Array[KnownEventStateRecord] = [ KnownEventStateRecord.create( witness.id, int(event.data["event_id"]), SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED ) ] actor_gap_system.restore(direct_witness_records) assert_null(actor_gap_system.communicate_event(event, witness.id, actor.id, 50)) func test_knowledge_retention_is_bounded_stable_and_importance_ranked() -> void: var knowledge_system := EventKnowledgeSystemScript.new() var records: Array[KnownEventStateRecord] = [ KnownEventStateRecord.create(0, 30, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED, -1, 1), KnownEventStateRecord.create(0, 20, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED, -1, 2), KnownEventStateRecord.create(0, 10, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED, -1, 3), KnownEventStateRecord.create(0, 5, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED, -1, 4), ] knowledge_system.restore(records) var no_lasting: Array[KnownEventStateRecord] = [] var capacity_forgotten := knowledge_system.maintain_retention(4, 100, no_lasting, false) assert_eq(capacity_forgotten.size(), 1) assert_eq(capacity_forgotten[0].get_event_id(), 30) assert_eq(knowledge_system.get_known_event_ids(0, 0), [20, 10, 5]) var lasting: Array[KnownEventStateRecord] = [knowledge_system.get_record(0, 20)] assert_eq(knowledge_system.maintain_retention(101, 100, lasting, true).size(), 0) assert_true(knowledge_system.knows_event(0, 20)) assert_eq(knowledge_system.maintain_retention(103, 100, lasting, true).size(), 1) assert_false(knowledge_system.knows_event(0, 10)) assert_true(knowledge_system.knows_event(0, 20)) var ranked_system := EventKnowledgeSystemScript.new() var ranked_records: Array[KnownEventStateRecord] = [ KnownEventStateRecord.create(1, 10, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED, -1, 5), KnownEventStateRecord.create(1, 20, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED, -1, 20), KnownEventStateRecord.create( 1, 30, SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED, 2, 20, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED ), KnownEventStateRecord.create(1, 40, SimulationIds.KNOWLEDGE_ACQUISITION_LEGACY, -1, 10), ] ranked_system.restore(ranked_records) var lasting_event_ids: Array[int] = [10] assert_eq(ranked_system.get_retained_event_ids(1, lasting_event_ids, 0), [10, 30, 20, 40]) assert_eq(ranked_system.get_retained_event_ids(1, lasting_event_ids, 2), [10, 30]) assert_eq(ranked_system.get_communicable_event_ids(1, lasting_event_ids), [10, 20]) var objective_events: Array[EconomicEventRecord] = [] for event_id in [10, 20, 30, 40]: objective_events.append( EconomicEventRecord.create( event_id, SimulationIds.EVENT_STORAGE_DEPOSITED, 0, 0, SimulationIds.npc_inventory_id(0), SimulationIds.STORAGE_VILLAGE_PANTRY, SimulationIds.RESOURCE_FOOD, 1.0 ) ) var manager := SimulationManagerScript.new() manager.event_log.restore(objective_events, 41) manager.event_knowledge_system.restore(ranked_records) var relationships: Array[RelationshipStateRecord] = [ RelationshipStateRecord.create(1, 0, 0.5, 0.65, 10) ] manager.relationship_system.restore(relationships) var retained_events: Array[EconomicEventRecord] = manager.get_retained_memory_events(1, 3) var retained_ids: Array[int] = [] for event in retained_events: retained_ids.append(int(event.data["event_id"])) assert_eq(retained_ids, [10, 30, 20]) manager.free()