Add world indicators and parameters to enhance universe simulation
- new universe creation function.
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
extends SceneTree
|
||||
|
||||
const Parameters = preload("res://world/owniverse/world_parameters.gd")
|
||||
const Descriptions = preload("res://world/owniverse/item_description.gd")
|
||||
var checks: int = 0
|
||||
var failures: int = 0
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("run")
|
||||
|
||||
func check(condition: bool, message: String) -> void:
|
||||
checks += 1
|
||||
if not condition:
|
||||
failures += 1
|
||||
push_error(message)
|
||||
|
||||
func fixture():
|
||||
var world = load("res://world/owniverse/owniverse.gd").new()
|
||||
for id in root.get_node("Global").OwniverseEntities:
|
||||
var entity = load("res://world/owniverse/owniverse_entity.gd").new(root.get_node("Global").OwniverseEntities[id])
|
||||
world.entities[id] = entity
|
||||
world.state.amount[id] = 0.0
|
||||
world.state.stats.total_amount[id] = 0.0
|
||||
if entity.group_as != "":
|
||||
world.state.amount[entity.group_as] = 0.0
|
||||
world.state.stats.total_amount[entity.group_as] = 0.0
|
||||
return world
|
||||
|
||||
func dispose(world) -> void:
|
||||
for entity in world.entities.values(): entity.free()
|
||||
world.free()
|
||||
|
||||
func run() -> void:
|
||||
var global: Node = root.get_node("Global")
|
||||
var world = fixture()
|
||||
world.state.amount.RD = 400.0
|
||||
world._append_batch(world.state, "RD", 400.0, 0.0, 0.0)
|
||||
var end: float = world.state.batches.RD[0].end_year
|
||||
world.modify_world_parameter("Bur", 9.0)
|
||||
check(is_equal_approx(world.state.batches.RD[0].end_year, end / 10.0), "Burnability accelerates existing cohorts")
|
||||
var count: float = world.state.batches.RD[0].amount
|
||||
world.modify_world_parameter("Bur", -9.0)
|
||||
check(is_equal_approx(world.state.batches.RD[0].end_year, end) and world.state.batches.RD[0].amount == count, "Changing burnability back preserves population and lifetime")
|
||||
world.modify_world_parameter("Bur", 9.0)
|
||||
world._append_batch(world.state, "YD", 10.0, 0.0, 0.0)
|
||||
check(is_equal_approx(world.state.batches.YD[0].start_year, world.entities.YD.lifespan / 10.0), "New stars use the same burnability")
|
||||
world.state.time = {"datetime": end / 10.0 - 0.1, "year": end / 10.0 - 0.1}
|
||||
world._withdraw_batches(world.state, "RD", world.state.time.year)
|
||||
world.modify_world_parameter("Bur", -18.0)
|
||||
check(world.state.batches.RD[0].end_year >= world.state.time.year, "Slowing a partially evolved cohort keeps its remaining window in the future")
|
||||
world.modify_world_parameter("Exp", 9.0)
|
||||
check(world._evolution_products(world.state, world.entities.SN, 2.0).blast == 180.0, "Explosion power includes world explosiveness")
|
||||
var products: Dictionary = world._evolution_products(world.state, world.entities.HRG, 1000.0)
|
||||
check(products.get("GRB", 0.0) == 100.0 and products.get("SN", 0.0) == 900.0, "Explosiveness raises rare-event probability without duplicating stars")
|
||||
world.entities.HRG.output_or.chance = 50.0
|
||||
products = world._evolution_products(world.state, world.entities.HRG, 100.0)
|
||||
check(products.get("GRB", 0.0) == 100.0 and products.get("SN", 0.0) == 0.0, "Rare-event probability is bounded at one")
|
||||
dispose(world)
|
||||
|
||||
world = fixture()
|
||||
var ordinary: Dictionary = world._life_products(world.state, world.entities.HP, 1000000.0)
|
||||
world.state.life_remainders.clear()
|
||||
world.modify_world_parameter("Agg", 9.0)
|
||||
var aggressive: Dictionary = world._life_products(world.state, world.entities.HP, 1000000.0)
|
||||
check(aggressive.EON1 > ordinary.EON1, "Aggression accelerates primitive evolution")
|
||||
world.state.life_remainders.clear()
|
||||
world.state.world_parameters.Order = 10.0
|
||||
var orderly: Dictionary = world._life_products(world.state, world.entities.HP, 1000000.0)
|
||||
check(orderly.RP < aggressive.RP, "Order reduces degradation")
|
||||
world.state.life_remainders.clear()
|
||||
world.state.world_parameters.Enlightenment = 10.0
|
||||
var enlightened: Dictionary = world._life_products(world.state, world.entities.HP, 1000000.0)
|
||||
check(enlightened.EON1 > orderly.EON1, "Enlightenment increases primitive progress")
|
||||
dispose(world)
|
||||
|
||||
world = fixture()
|
||||
var hotter = fixture()
|
||||
for simulation in [world, hotter]:
|
||||
simulation.state.amount.RD = 1000000.0
|
||||
simulation._append_batch(simulation.state, "RD", 1000000.0, 0.0, 0.0)
|
||||
hotter.modify_world_parameter("Bur", 9.0)
|
||||
world.evolute(1000.0)
|
||||
hotter.evolute(1000.0)
|
||||
check(hotter.state.amount.S > world.state.amount.S, "Burnability changes actual space production")
|
||||
check(hotter.state.energy + hotter.state.amount.S == Parameters.INITIAL_ENERGY, "Enhanced production still spends the GI energy budget")
|
||||
dispose(world)
|
||||
dispose(hotter)
|
||||
|
||||
world = fixture()
|
||||
world.state.amount["C0.2"] = 1000000.0
|
||||
world.state.ethics["C0.2"] = {"m": 1000000.0}
|
||||
var baseline: Dictionary = world.state.duplicate(true)
|
||||
var advanced: Dictionary = baseline.duplicate(true)
|
||||
advanced.world_parameters.Agg = 19.0
|
||||
var next_stage: String = global.CivilizationRules["C0.2"].next
|
||||
var outcomes: Array = []
|
||||
for target in [baseline, advanced]:
|
||||
var expired: Dictionary = {"C0.2": [{"amount": 1000000.0, "from": 0.0, "to": 1.0}]}
|
||||
var budget: Dictionary = world.CivilizationState.stellar_budget(world, target, global.CivilizationRules)
|
||||
world.CivilizationEvolution.prepare(world, target, expired, 1.0, budget, global.CivilizationRules)
|
||||
outcomes.append(expired["C0.2"][0].products.get(next_stage, 0.0))
|
||||
check(outcomes[1] > outcomes[0], "Aggression changes actual ethical civilization transitions")
|
||||
dispose(world)
|
||||
|
||||
# Trends use ethical proportions and stage weights, not population magnitude.
|
||||
world = fixture()
|
||||
var rules: Dictionary = {"C2.0": {"home": "DwStar"}, "C2.1": {"home": "DwStar"}}
|
||||
world.state.ethics = {"C2.0": {"d": 100.0}, "C2.1": {"d": 100.0}}
|
||||
var source: Dictionary = world.state.duplicate(true)
|
||||
var single: Dictionary = source.duplicate(true)
|
||||
var split: Dictionary = source.duplicate(true)
|
||||
Parameters.advance(source, single, rules, 1.0e6)
|
||||
for i in 10: Parameters.advance(source, split, rules, 1.0e5)
|
||||
check(is_equal_approx(single.trends.Integration, 0.018), "Trend integrates elapsed years and doubled stage weights")
|
||||
for key in Parameters.TRENDS:
|
||||
check(is_equal_approx(single.trends[key], split.trends[key]), "Trend is independent of frame partition: " + key)
|
||||
source.ethics["C2.0"].d = 1000000.0
|
||||
var larger: Dictionary = world.state.duplicate(true)
|
||||
Parameters.advance(source, larger, rules, 1.0e6)
|
||||
check(is_equal_approx(larger.trends.Integration, single.trends.Integration), "Population proportions determine trend influence")
|
||||
world.state.trends.Integration = 0.99
|
||||
Parameters.advance(source, world.state, rules, 1.0e8)
|
||||
check(world.state.civilization_stats["trend:Integration"] == 2.0, "Long steps process every crossed threshold")
|
||||
check(is_equal_approx(world.state.trends.Integration, 0.79), "Threshold crossing preserves the fractional remainder")
|
||||
check(world.state.world_parameters.Federation > 1.0 or world.state.world_parameters.Enlightenment > 1.0 or world.state.world_parameters.Order > 1.0, "Integration events change the world")
|
||||
var restored: Dictionary = bytes_to_var(var_to_bytes(world.state))
|
||||
Parameters.advance(source, world.state, rules, 1.0e8)
|
||||
Parameters.advance(source, restored, rules, 1.0e8)
|
||||
check(world.state == restored, "Trend events resume identically after serialization")
|
||||
Parameters.advance(source, world.state, rules, 1.0e12)
|
||||
check(world.state.trends.Integration >= 0.0 and world.state.trends.Integration < 1.0, "Very large intervals finish with a valid trend fraction")
|
||||
check(world.state.world_parameters.Agg >= 1.0, "Repeated integration events cannot reduce aggression below the scale")
|
||||
var payload: Dictionary = {"format_version": 2, "app_version": "test", "ui": {"current_page": 1}, "state": world.state}
|
||||
var manager: Node = root.get_node("SaveManager")
|
||||
check(manager.validate_data(payload) == "", "New parameters and trends pass save validation")
|
||||
var old: Dictionary = payload.duplicate(true)
|
||||
old.state.erase("world_parameters")
|
||||
old.state.erase("trends")
|
||||
check(manager.validate_data(old) == "", "Existing current-format saves remain readable")
|
||||
payload.state.world_parameters.Agg = 20.0
|
||||
check(manager.validate_data(payload) != "", "Out-of-range scales are rejected")
|
||||
dispose(world)
|
||||
|
||||
for sample in [[1e-7, 0.0], [1e-6, 0.5], [1e-5, 1.0], [0.9, 0.0], [0.95, 0.5], [1.0, 1.0]]:
|
||||
check(is_equal_approx(Parameters.density_display(sample[0]).position, sample[1]), "Density boundary " + str(sample[0]))
|
||||
# The fixture overrides saving/loading; the player's save is never accessed.
|
||||
var scene = load("res://world/world.tscn").instantiate()
|
||||
var stage = scene.get_node("Stage")
|
||||
stage.owniverse.free()
|
||||
stage.set_script(load("res://tests/fixtures/ui_stage.gd"))
|
||||
root.add_child(scene)
|
||||
await process_frame
|
||||
await process_frame
|
||||
stage.owniverse.state.energy = Parameters.INITIAL_ENERGY / 4.0
|
||||
stage.owniverse.state.world_parameters.Bur = 19.0
|
||||
stage.owniverse.state.trends.Integration = 0.75
|
||||
stage.world_indicators.update(stage.owniverse)
|
||||
for bar in stage.world_indicators.energy_bars:
|
||||
check(bar.scale.x == 0.25, "Energy fill is synchronized on both pages")
|
||||
var pointer: Control = stage.world_indicators.pointers.Bur
|
||||
check(is_equal_approx(pointer.position.x + pointer.size.x / 2.0, 304.0), "Burnability pointer reaches the correct scale endpoint")
|
||||
check(stage.world_indicators.trend_bars[1].scale.y == 0.75, "Trend bar displays its stored level")
|
||||
for locale in ["en", "ru"]:
|
||||
global.Locale = locale
|
||||
for id in ["Energy", "Density", "Burnability", "Explosiveness", "Aggression", "Trends"]:
|
||||
var description: String = Descriptions.render(stage.owniverse, global, id, "Indicator")
|
||||
check(not description.contains("—") and not description.contains("%value"), "Resolved indicator description: " + locale + "/" + id)
|
||||
dispose(stage.owniverse)
|
||||
scene.free()
|
||||
print("World indicators: %d checks, %d failures" % [checks, failures])
|
||||
quit(0 if failures == 0 else 1)
|
||||
Reference in New Issue
Block a user