2026-09-15 08:27:04 +03:00
extends SceneTree
2026-09-30 08:08:27 +03:00
const E = preload ( "res://world/owniverse/math/exact.gd" )
2026-09-15 08:27:04 +03:00
var checks : int = 0
var failures : int = 0
func _initialize () -> void :
call_deferred ( "run" )
func check ( condition : bool , message : String ) -> void :
2026-09-30 08:08:27 +03:00
checks = E . add ( checks , 1 )
if ( not condition ):
failures = E . add ( failures , 1 )
2026-09-15 08:27:04 +03:00
push_error ( message )
func fixture ():
var world = load ( "res://world/owniverse/owniverse.gd" ) . new ()
var global : Node = root . get_node ( "Global" )
for id in global . OwniverseEntities :
var entity = load ( "res://world/owniverse/owniverse_entity.gd" ) . new ( global . OwniverseEntities [ id ])
2026-09-29 20:23:20 +03:00
world . catalog . entities [ id ] = entity
2026-09-30 08:08:27 +03:00
world . state . amount [ id ] = 0
world . state . stats . total_amount [ id ] = 0
if E . ne ( entity . group_as , "" ):
world . state . amount [ entity . group_as ] = 0
world . state . stats . total_amount [ entity . group_as ] = 0
2026-09-29 20:23:20 +03:00
world . catalog . entities [ "N" ] . life_product = {}
world . catalog . entities [ "N" ] . product_per_year_type = "H"
2026-09-30 08:08:27 +03:00
world . catalog . entities [ "N" ] . product_per_year_amount = 10
world . catalog . entities [ "N" ] . auto = 0
world . state . amount . N = 1
2026-09-15 08:27:04 +03:00
return world
func dispose ( world ) -> void :
2026-09-29 20:23:20 +03:00
for entity in world . catalog . entities . values ():
2026-09-15 08:27:04 +03:00
entity . free ()
world . free ()
func run () -> void :
var world = fixture ()
2026-09-22 22:50:54 +03:00
for id in [ "DSN" , "SN" , "BSN" , "HN" , "SGRB" , "GRB" , "LGRB" , "HGRB" ]:
2026-09-30 08:08:27 +03:00
check ( E . eq ( world . stellar . evolution_products ( world . state , world . catalog . entities [ id ], 3 ) . blast , E . multiply ( 3 , world . catalog . entities [ id ] . blast )), E . add ( "Entity blast determines power: " , id ))
world . catalog . entities . SN . output_and . blast = 999
2026-09-29 20:23:20 +03:00
world . catalog . entities . SN . blast = 4
2026-09-30 08:08:27 +03:00
check ( E . eq ( world . stellar . evolution_products ( world . state , world . catalog . entities . SN , 2 ) . blast , 8 ), "Imported output cannot override or double blast power" )
2026-09-29 20:23:20 +03:00
world . catalog . entities . SN . blast = 0
2026-09-30 08:08:27 +03:00
check (( not world . stellar . evolution_products ( world . state , world . catalog . entities . SN , 2 ) . has ( "blast" )), "Zero entity blast disables external event" )
world . catalog . entities . SN . output_or = { "positive" : "blast:999" , "negative" : "blast:999" , "chance" : 50 }
check (( not world . stellar . evolution_products ( world . state , world . catalog . entities . SN , 2 ) . has ( "blast" )), "Alternative output cannot restore disabled blast" )
2026-09-22 22:50:54 +03:00
dispose ( world )
world = fixture ()
2026-09-29 20:23:20 +03:00
var rd = world . catalog . entities . RD
2026-09-30 08:08:27 +03:00
check ( E . eq ( rd . planets . HP . chance , E . of ( "0.01" )), "Preserve fractional planet probability" )
check (( E . eq ( rd . planets . GG . amount , 2 ) and E . eq ( rd . planets . GG . chance , E . of ( "0.08" ))), "Read count and chance separately" )
check ( E . eq ( rd . _parse_planet_string ( "HP:1:0.01" ), { "HP" : { "amount" : 1 , "chance" : E . of ( "0.01" )}}), "Accept a single planet term" )
2026-09-15 08:27:04 +03:00
check ( rd . _parse_planet_string ( "" ) . is_empty (), "Empty planet definitions stay empty" )
for id in [ "HP" , "EON1" , "C0.9" , "C1.2" ]:
2026-09-30 08:08:27 +03:00
check ( world . catalog . entities [ id ] . blastability , E . add ( "Blast vulnerable: " , id ))
2026-09-15 08:27:04 +03:00
for id in [ "RP" , "GG" , "AB" , "C1.3" , "C1.4" , "C1.5" ]:
2026-09-30 08:08:27 +03:00
check (( not world . catalog . entities [ id ] . blastability ), E . add ( "Blast protected: " , id ))
2026-09-15 08:27:04 +03:00
# Actual GI table: HRG -> SN, with 1% GRB; no random rounding.
var totals : Dictionary = {}
for i in 100 :
2026-09-30 08:08:27 +03:00
var products : Dictionary = world . stellar . evolution_products ( world . state , world . catalog . entities . HRG , 1 )
2026-09-15 08:27:04 +03:00
for id in products :
2026-09-30 08:08:27 +03:00
totals [ id ] = E . add ( totals . get ( id , 0 ), products [ id ])
check (( E . eq ( totals . get ( "SN" ), 99 ) and E . eq ( totals . get ( "GRB" ), 1 )), "GI rare outcomes accumulate deterministically" )
2026-09-15 08:27:04 +03:00
var restored = fixture ()
2026-09-30 08:08:27 +03:00
restored . state = E . decode ( bytes_to_var ( var_to_bytes ( E . encode ( world . state ))))
check ( E . eq ( world . stellar . evolution_products ( world . state , world . catalog . entities . HRG , 37 ), restored . stellar . evolution_products ( restored . state , restored . catalog . entities . HRG , 37 )), "Evolution carry survives serialization" )
2026-09-15 08:27:04 +03:00
dispose ( restored )
# Real RD lifespan, four stars spread over the existing lifespan/40 window.
2026-09-30 08:08:27 +03:00
world . state . amount . N = 0
world . state . amount . RD = 4
world . batches . append_batch ( world . state , "RD" , 4 , 0 , 0 )
var lifespan : Variant = rd . lifespan
var width : Variant = rd . lifespan_delta
2026-09-29 20:23:20 +03:00
world . evolution . evolve_batches ( world . state , lifespan )
2026-09-30 08:08:27 +03:00
check ( E . eq ( world . state . amount . RD , 4 ), "Window start does not kill the whole batch" )
world . evolution . evolve_batches ( world . state , E . add ( lifespan , E . divide ( width , 4 )))
check (( E . eq ( world . state . amount . RD , 3 ) and E . eq ( world . state . amount . TWD , 1 )), "One quarter of a batch evolves" )
world . evolution . evolve_batches ( world . state , E . add ( lifespan , E . divide ( width , 2 )))
check (( E . eq ( world . state . amount . RD , 2 ) and E . eq ( world . state . amount . TWD , 2 )), "Half the batch evolves" )
world . evolution . evolve_batches ( world . state , E . add ( lifespan , width ))
check (( E . eq ( world . state . amount . RD , 0 ) and E . eq ( world . state . amount . TWD , 4 )), "Window end completes evolution" )
2026-09-15 08:27:04 +03:00
check ( world . state . batches . RD . is_empty (), "Completed batch removed" )
dispose ( world )
# Creation accumulates independently for each star/planet pair.
world = fixture ()
2026-09-30 08:08:27 +03:00
world . state . amount . RD = 100
2026-09-15 08:27:04 +03:00
for i in 100 :
2026-09-30 08:08:27 +03:00
world . stellar . create_planets ( world . state , "RD" , 1 , 0 , 0 , 1 )
check ( E . eq ( world . state . binds . RD . HP , 1 ), "100 RD at metallicity 1 produce one HP" )
check ( E . eq ( world . state . binds . RD . RP , 21 ), "Rocky planet probability retains fractions" )
check (( E . eq ( world . state . binds . RD . GG , 16 ) and E . eq ( world . state . binds . RD . AB , 120 )), "Planet multiplicity applied" )
check (( E . eq ( world . state . amount . HP , 0 ) and E . eq ( world . state . stats . total_amount . HP , 1 )), "Created planets are bound, counted once" )
2026-09-15 08:27:04 +03:00
var saves : Node = root . get_node ( "SaveManager" )
2026-10-01 07:48:52 +03:00
check ( E . eq ( saves . validate_data ({ "format_version" : 4 , "app_version" : "" , "ui" : { "current_page" : 0 }, "state" : world . state }), "" ), "Planet carry save validates" )
2026-09-15 08:27:04 +03:00
restored = fixture ()
2026-09-30 08:08:27 +03:00
restored . state = E . decode ( bytes_to_var ( var_to_bytes ( E . encode ( world . state ))))
world . stellar . create_planets ( world . state , "RD" , 37 , 0 , 0 , E . of ( "0.7" ))
restored . stellar . create_planets ( restored . state , "RD" , 37 , 0 , 0 , E . of ( "0.7" ))
check ( E . eq ( world . state , restored . state ), "Planet carry continues after save" )
2026-09-15 08:27:04 +03:00
dispose ( restored )
dispose ( world )
# A partially dying dwarf sterilizes HP; survivors and RP move to its heir.
world = fixture ()
2026-09-30 08:08:27 +03:00
world . state . amount . RD = 4
world . state . binds . RD = { "HP" : 8 , "RP" : 4 , "C1.2" : 4 , "C1.3" : 4 , "GG" : 4 }
world . batches . append_batch ( world . state , "RD" , 4 , 0 , 0 )
world . evolution . evolve_batches ( world . state , E . add ( lifespan , E . divide ( width , 2 )))
check (( E . eq ( world . state . amount . RD , 2 ) and E . eq ( world . state . amount . TWD , 2 )), "Partial star transition with planets" )
check ( E . eq ( E . add ( world . state . binds . RD . HP , world . state . binds . TWD . HP ), 4 ), "Half of HP sterilized" )
check (( E . eq ( world . state . binds . RD [ "C1.3" ], 2 ) and E . eq ( world . state . binds . TWD [ "C1.3" ], 2 )), "Protected civilization splits between parent and heir" )
check ( E . eq ( E . add ( world . state . binds . RD . RP , world . state . binds . TWD . RP ), 10 ), "Sterilized planets become RP without loss" )
check (( E . eq ( world . state . amount . RP , 0 ) and E . eq ( world . state . amount [ "C1.3" ], 0 )), "Inherited planets never become free" )
world . evolution . evolve_batches ( world . state , E . add ( lifespan , width ))
check (( E . eq ( world . state . binds . RD [ "C1.3" ], 0 ) and E . eq ( world . state . binds . TWD [ "C1.3" ], 4 )), "No planets left at extinct parent type" )
2026-10-01 07:48:52 +03:00
check ( E . eq ( saves . validate_data ({ "format_version" : 4 , "app_version" : "" , "ui" : { "current_page" : 0 }, "state" : world . state }), "" ), "Inherited state validates" )
2026-09-15 08:27:04 +03:00
dispose ( world )
world = fixture ()
2026-09-30 08:08:27 +03:00
world . state . amount . WS = 2
world . state . binds . WS = { "HP" : 4 , "C1.3" : 4 , "RP" : 4 , "GG" : 4 }
world . batches . append_batch ( world . state , "WS" , 2 , 0 , 0 )
world . evolution . evolve_batches ( world . state , E . add ( world . catalog . entities . WS . lifespan , world . catalog . entities . WS . lifespan_delta ))
2026-09-29 20:23:20 +03:00
var physical : Dictionary = world . ledger . physical_amounts ( world . state )
2026-09-30 08:08:27 +03:00
check (((( E . eq ( physical . HP , 0 ) and E . eq ( physical [ "C1.3" ], 0 )) and E . eq ( physical . RP , 0 )) and E . eq ( physical . GG , 0 )), "Level 2 removes all planets, including protected civilization" )
check ( E . eq ( world . state . amount . LRG , 2 ), "Giant evolves after local destruction" )
2026-09-15 08:27:04 +03:00
dispose ( world )
world = fixture ()
2026-09-30 08:08:27 +03:00
world . state . amount . RD = 10
world . state . binds . RD = { "HP" : 10 , "C1.2" : 10 , "C1.3" : 10 , "RP" : 0 }
world . stellar . apply_blast ( world . state , 5 , { "blast" : 1 , "density" : 1 })
check (( E . eq ( world . state . binds . RD . HP , 5 ) and E . eq ( world . state . binds . RD [ "C1.2" ], 5 )), "Blast damages vulnerable worlds proportionally" )
check (( E . eq ( world . state . binds . RD [ "C1.3" ], 10 ) and E . eq ( world . state . binds . RD . RP , 10 )), "Blast protects C1.3 and creates RP" )
world . stellar . apply_blast ( world . state , 1000000 , { "blast" : 1 , "density" : 1 })
check (( E . eq ( world . state . binds . RD . HP , 0 ) and E . eq ( world . state . binds . RD . RP , 20 )), "Overwhelming blast is capped at available planets" )
check (( not world . state . amount . has ( "blast" )), "Blast is not a resource" )
2026-09-15 08:27:04 +03:00
dispose ( world )
world = fixture ()
2026-09-30 08:08:27 +03:00
world . state . amount . RD = 1
world . state . binds . RD = { "HP" : 1 }
2026-09-15 08:27:04 +03:00
for i in 10 :
2026-09-30 08:08:27 +03:00
world . stellar . apply_blast ( world . state , 1 , { "blast" : 1 , "density" : E . of ( "0.1" )})
check (( E . eq ( world . state . binds . RD . HP , 0 ) and E . eq ( world . state . binds . RD . RP , 1 )), "Small blast effects accumulate across cycles" )
2026-09-15 08:27:04 +03:00
dispose ( world )
# Actual supernova output: one SN gives a neutron star, resources, and an event.
world = fixture ()
2026-09-30 08:08:27 +03:00
world . state . amount . SN = 1
world . batches . append_batch ( world . state , "SN" , 1 , 0 , 0 )
world . evolution . evolve_batches ( world . state , 2 )
check (( E . eq ( world . state . amount . SN , 0 ) and E . eq ( world . state . amount . LNS , 1 )), "Supernova produces a remnant" )
check ((( E . eq ( world . state . amount . H , 8 ) and E . eq ( world . state . amount . He , 50 )) and E . eq ( world . state . amount . Met , 11 )), "Supernova returns table resources" )
check (( not world . state . amount . has ( "blast" )), "Supernova dispatches blast output" )
2026-10-01 07:48:52 +03:00
check ( E . eq ( saves . validate_data ({ "format_version" : 4 , "app_version" : "" , "ui" : { "current_page" : 0 }, "state" : world . state }), "" ), "Blast state validates" )
2026-09-15 08:27:04 +03:00
dispose ( world )
world = fixture ()
2026-09-30 08:08:27 +03:00
world . state . amount . H = 1000
world . state . amount . Met = 100
check ( E . eq ( world . production . produce ( "RD" , 100 ), 100 ), "Manual star creation succeeds" )
check ( E . ge ( world . state . binds . RD . HP , 1 ), "Manual creation dispatches planet formation" )
check ( E . eq ( world . state . amount . H , 900 ), "Planet creation does not duplicate or spend stellar fuel" )
2026-09-15 08:27:04 +03:00
dispose ( world )
world = fixture ()
2026-09-30 08:08:27 +03:00
world . state . amount . N = 0
world . state . amount . RD = 1
world . state . amount . H = 100
world . state . amount . Met = 100
world . state . auto_resources . H = 100
world . evolute ( E . of ( "0.1" ))
check (( E . eq ( world . state . amount . RD , 101 ) and E . eq ( world . state . amount . H , 0 )), "Automatic formation uses its resource budget" )
check ( E . ge ( world . state . binds . RD . HP , 1 ), "Automatic formation also creates planets" )
2026-09-15 08:27:04 +03:00
dispose ( world )
2026-09-21 20:42:40 +03:00
world = fixture ()
2026-09-30 08:08:27 +03:00
world . catalog . entities . N . auto = 1
2026-09-21 20:42:40 +03:00
# 1000 newly produced automatic H reserve 1 H, enough for 10 HBD.
2026-09-30 08:08:27 +03:00
world . evolute ( 100 )
check ( E . eq ( world . state . amount . HBD , 10 ), "Brown dwarfs form without existing ordinary stars" )
check (( E . eq ( world . state . amount . H , 999 ) and E . eq ( world . state . auto_resources . H , 999 )), "Brown dwarfs consume exactly their reserved hydrogen" )
check (( E . eq ( world . presentation . amounts ( world . state , world . catalog ) . BD , 10 ) and E . gt ( world . state . batches . HBD . size (), 0 )), "Brown dwarfs appear in counter and evolution batches" )
world . catalog . entities . N . product_per_year_amount = 0
world . evolute ( 100 )
check ( E . eq ( world . state . amount . HBD , 10 ), "Stored automatic budget is not taxed repeatedly" )
2026-09-21 20:42:40 +03:00
dispose ( world )
world = fixture ()
2026-09-30 08:08:27 +03:00
world . catalog . entities . N . auto = 1
world . state . amount . RD = 1
world . evolute ( 50 )
check (( E . eq ( world . state . amount . HBD , 5 ) and E . near ( world . state . auto_star_deficits . HBD_fuel , E . of ( "0.5" ))), "Individual dwarfs retain prepaid fractional fuel" )
check ( E . eq ( world . state . amount . H , 0 ), "Prepaid fuel is not available to ordinary stars" )
2026-09-21 20:42:40 +03:00
var resumed = fixture ()
2026-09-30 08:08:27 +03:00
resumed . catalog . entities . N . auto = 1
resumed . state = E . decode ( bytes_to_var ( var_to_bytes ( E . encode ( world . state ))))
world . evolute ( 50 )
resumed . evolute ( 50 )
check ( E . eq ( world . state . amount . HBD , 10 ), "Accumulated quota eventually creates a whole batch" )
check ( E . eq ( world . state , resumed . state ), "Brown dwarf quota survives serialization deterministically" )
check (( E . eq ( world . state . amount . RD , 1000 ) and E . eq ( world . state . amount . H , 0 )), "Shared automatic hydrogen is spent once" )
world . state . batches . HBD = [{ "amount" : 10 , "start_year" : 101 , "end_year" : 101 }]
world . evolution . evolve_batches ( world . state , 101 )
check ((( E . eq ( world . state . amount . HBD , 0 ) and E . eq ( world . state . amount . CBD , 10 )) and E . eq ( world . presentation . amounts ( world . state , world . catalog ) . BD , 10 )), "Cooling preserves aggregate brown dwarf count" )
2026-09-21 20:42:40 +03:00
dispose ( resumed )
dispose ( world )
world = fixture ()
var saw_individual : bool = false
for i in range ( 250 ):
2026-09-30 08:08:27 +03:00
world . state . amount . H = E . add ( world . state . amount . H , 1 )
world . state . auto_resources . H = E . add ( world . state . auto_resources . get ( "H" , 0 ), 1 )
var before : Variant = world . state . amount . HBD
world . automatic . produce_brown_dwarfs ( world . state , 1 , E . of ( i ), E . of ( E . add ( i , 1 )))
var born : Variant = E . subtract ( world . state . amount . HBD , before )
check (( E . eq ( born , 0 ) or E . eq ( born , 1 )), "Small fuel increments produce individual dwarfs" )
saw_individual = ( saw_individual or E . eq ( born , 1 ))
2026-09-21 20:42:40 +03:00
check ( saw_individual , "Random rounding does not wait for ten dwarfs" )
resumed = fixture ()
2026-09-30 08:08:27 +03:00
resumed . state = E . decode ( bytes_to_var ( var_to_bytes ( E . encode ( world . state ))))
2026-09-21 20:42:40 +03:00
for i in range ( 250 , 500 ):
for simulation in [ world , resumed ]:
2026-09-30 08:08:27 +03:00
simulation . state . amount . H = E . add ( simulation . state . amount . H , 1 )
simulation . state . auto_resources . H = E . add ( simulation . state . auto_resources . H , 1 )
simulation . automatic . produce_brown_dwarfs ( simulation . state , 1 , E . of ( i ), E . of ( E . add ( i , 1 )))
check ( E . eq ( world . state , resumed . state ), "Random sequence and prepaid fuel survive saving" )
check ( E . near ( E . add ( E . add ( world . state . amount . H , E . multiply ( world . state . amount . HBD , E . of ( "0.1" ))), world . state . auto_star_deficits . HBD_fuel ), 500 ), "Individual production conserves fuel including prepaid reserve" )
check ( E . near ( E . add ( world . state . amount . HBD , world . state . auto_star_deficits . HBD ), 5 ), "Signed remainder preserves the 0.1 percent allocation" )
var paused_count : Variant = world . state . amount . HBD
for i in 10 : world . automatic . produce_brown_dwarfs ( world . state , 0 , 500 , 501 )
check ( E . eq ( world . state . amount . HBD , paused_count ), "No repeated random attempts without new fuel" )
2026-09-21 20:42:40 +03:00
dispose ( resumed )
dispose ( world )
2026-09-15 08:27:04 +03:00
# Two simultaneous successors share surviving planets in the same 99:1 ratio.
world = fixture ()
2026-09-29 20:23:20 +03:00
world . catalog . entities . HRG . destroy_level = 0
2026-09-30 08:08:27 +03:00
world . state . amount . HRG = 100
world . state . binds . HRG = { "C1.3" : 100 }
world . state . batches . HRG = [{ "start_year" : E . of ( "0.1" ), "end_year" : E . of ( "0.1" ), "amount" : 100 }]
world . evolution . evolve_batches ( world . state , E . of ( "0.1" ))
check (( E . eq ( world . state . binds . SN [ "C1.3" ], 99 ) and E . eq ( world . state . binds . GRB [ "C1.3" ], 1 )), "Planets split among actual alternative successors" )
check ( E . eq ( world . state . binds . HRG [ "C1.3" ], 0 ), "Alternative evolution leaves no orphan bindings" )
2026-09-15 08:27:04 +03:00
dispose ( world )
world = fixture ()
2026-09-30 08:08:27 +03:00
world . state . amount . N = 0
world . state . amount . HRG = 1
world . state . batches . HRG = [{ "start_year" : E . of ( "0.1" ), "end_year" : E . of ( "0.1" ), "amount" : 1 }]
world . evolute ( 3 )
check (( E . eq ( world . state . amount . LNS , 1 ) and E . eq ( world . state . amount . SN , 0 )), "Long step follows multiple stellar generations" )
check ( E . ge ( world . state . amount . S , 99 ), "Short-lived newborn supernova emits S before disappearing" )
2026-09-15 08:27:04 +03:00
dispose ( world )
2026-09-30 08:08:27 +03:00
print (( "Stellar rules: %d checks, %d failures" % [ checks , failures ]))
quit (( 0 if E . eq ( failures , 0 ) else 1 ))