Add exact arithmetic implementation and tests
- Introduced exact arithmetic with support for large integers and fractions in `exact.gd`, `big_integer.gd`, and `exact_value.gd`. - Implemented a comprehensive test suite in `test_exact_math.gd` to validate arithmetic operations against expected results. - Added a README in `math` directory to document the exact arithmetic system and its usage. - Ensured that all arithmetic operations maintain precision without floating-point errors, using a custom representation for large numbers. - Included methods for encoding and decoding exact values for serialization.
This commit is contained in:
+39
-38
@@ -1,4 +1,5 @@
|
||||
extends SceneTree
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
const Clock = preload("res://world/owniverse/simulation_time.gd")
|
||||
var checks: int = 0
|
||||
var failures: int = 0
|
||||
@@ -7,9 +8,9 @@ func _initialize() -> void:
|
||||
call_deferred("run")
|
||||
|
||||
func check(condition: bool, message: String) -> void:
|
||||
checks += 1
|
||||
if not condition:
|
||||
failures += 1
|
||||
checks = E.add(checks, 1)
|
||||
if (not condition):
|
||||
failures = E.add(failures, 1)
|
||||
push_error(message)
|
||||
|
||||
func fixture():
|
||||
@@ -18,16 +19,16 @@ func fixture():
|
||||
for id in global.OwniverseEntities:
|
||||
var entity = load("res://world/owniverse/owniverse_entity.gd").new(global.OwniverseEntities[id])
|
||||
world.catalog.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
|
||||
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
|
||||
world.catalog.entities["N"].life_product = {}
|
||||
world.catalog.entities["N"].product_per_year_type = "H"
|
||||
world.catalog.entities["N"].product_per_year_amount = 10.0
|
||||
world.catalog.entities["N"].auto = 0.0
|
||||
world.state.amount.N = 1.0
|
||||
world.catalog.entities["N"].product_per_year_amount = 10
|
||||
world.catalog.entities["N"].auto = 0
|
||||
world.state.amount.N = 1
|
||||
return world
|
||||
|
||||
func dispose(world) -> void:
|
||||
@@ -39,39 +40,39 @@ func run() -> void:
|
||||
var global: Node = root.get_node("Global")
|
||||
var description = load("res://world/owniverse/item_description.gd")
|
||||
var world = fixture()
|
||||
world.state.amount.H = 8.0
|
||||
world.state.amount.He = 2.0
|
||||
world.state.amount.H = 8
|
||||
world.state.amount.He = 2
|
||||
for locale in ["en", "ru"]:
|
||||
global.Locale = locale
|
||||
var text: String = description.render(world, global, "RD")
|
||||
check(text.contains("[b]") and not text.contains("%value"), "Localized template resolved: " + locale)
|
||||
check(text.contains("#40cc40"), "Affordable cost green: " + locale)
|
||||
check(text.contains("4.0000E+10"), "Lifespan substituted: " + locale)
|
||||
check((text.contains("[b]") and (not text.contains("%value"))), E.add("Localized template resolved: ", locale))
|
||||
check(text.contains("#40cc40"), E.add("Affordable cost green: ", locale))
|
||||
check(text.contains("4.0000E+10"), E.add("Lifespan substituted: ", locale))
|
||||
text = description.render(world, global, "He")
|
||||
check(text.contains("He: 20.00% / H: 80.00%") and not text.contains("%value"), "Helium metadata and mixture: " + locale)
|
||||
check((text.contains("He: 20.00% / H: 80.00%") and (not text.contains("%value"))), E.add("Helium metadata and mixture: ", locale))
|
||||
text = description.render(world, global, "Shovelin", "Force")
|
||||
check(text.count("]—[/color]") == 4, "Missing force levels use dash: " + locale)
|
||||
check(E.eq(text.count("]—[/color]"), 4), E.add("Missing force levels use dash: ", locale))
|
||||
text = description.render(world, global, "YD")
|
||||
check(text.contains("#ff6060"), "Insufficient cost red: " + locale)
|
||||
check(text.contains("#ff6060"), E.add("Insufficient cost red: ", locale))
|
||||
var reordered: Dictionary = {"text": "%value0% / %value1%", "value0": "lifespan", "value1": "amount"}
|
||||
check(global.render_template(reordered, {"amount": "12", "lifespan": "99"}) == "99 / 12", "Metadata chooses variables, not fixed argument positions")
|
||||
world.state.amount.RD = 2.0
|
||||
world.state.binds.G = {"RD": 3.0, "HP": 7.0}
|
||||
var values: Dictionary = description.variables(world, global, "RD", -2.0)
|
||||
check(values.amount.contains("2") and values.amount.contains("(5)") and values.amount.contains("#888888"), "Free and total counts with gray binding total")
|
||||
check(values.amount.contains("#ff6060") and values.amount.contains("−2.0"), "Real-time decrease red")
|
||||
check(E.eq(global.render_template(reordered, {"amount": "12", "lifespan": "99"}), "99 / 12"), "Metadata chooses variables, not fixed argument positions")
|
||||
world.state.amount.RD = 2
|
||||
world.state.binds.G = {"RD": 3, "HP": 7}
|
||||
var values: Dictionary = description.variables(world, global, "RD", E.neg(2))
|
||||
check(((values.amount.contains("2") and values.amount.contains("(5)")) and values.amount.contains("#888888")), "Free and total counts with gray binding total")
|
||||
check((values.amount.contains("#ff6060") and values.amount.contains("−2.0")), "Real-time decrease red")
|
||||
values = description.variables(world, global, "HP")
|
||||
check(values.amount == "7", "Planet description includes bound planets")
|
||||
world.state.amount.EON1 = 2.0
|
||||
world.state.binds.G.EON2 = 3.0
|
||||
check(E.eq(values.amount, "7"), "Planet description includes bound planets")
|
||||
world.state.amount.EON1 = 2
|
||||
world.state.binds.G.EON2 = 3
|
||||
var saved_state: Dictionary = world.state.duplicate(true)
|
||||
var counters: Dictionary = world.presentation.amounts(world.state, world.catalog)
|
||||
check(counters.HP == 7.0 and counters.LP == 5.0 and counters.Planet == 12.0, "Planet counters include bound objects and aggregate eons")
|
||||
check(counters.RD == 2.0, "Star counters retain available quantity")
|
||||
check(world.state == saved_state, "Reading counters does not mutate simulation")
|
||||
check(world.presentation.amounts(world.state, world.catalog, true).HP == 0.0, "Produced counters remain independent of current population")
|
||||
values = description.variables(world, global, "RD", 2.0)
|
||||
check(values.amount.contains("#40cc40") and values.amount.contains("+2.0"), "Increase green")
|
||||
check(((E.eq(counters.HP, 7) and E.eq(counters.LP, 5)) and E.eq(counters.Planet, 12)), "Planet counters include bound objects and aggregate eons")
|
||||
check(E.eq(counters.RD, 2), "Star counters retain available quantity")
|
||||
check(E.eq(world.state, saved_state), "Reading counters does not mutate simulation")
|
||||
check(E.eq(world.presentation.amounts(world.state, world.catalog, true).HP, 0), "Produced counters remain independent of current population")
|
||||
values = description.variables(world, global, "RD", 2)
|
||||
check((values.amount.contains("#40cc40") and values.amount.contains("+2.0")), "Increase green")
|
||||
var stage_script = load("res://stage/stage.gd")
|
||||
check(stage_script.can_instantiate(), "Stage integration compiles")
|
||||
var stage = stage_script.new()
|
||||
@@ -81,9 +82,9 @@ func run() -> void:
|
||||
stage.g_item_descriptions = [label]
|
||||
stage.description_targets[0] = {"id": "RD", "category": "Entity"}
|
||||
stage._refresh_descriptions()
|
||||
check(label.bbcode_enabled and label.text.contains("Красный карлик"), "Stage displays localized rich text")
|
||||
check((label.bbcode_enabled and label.text.contains("Красный карлик")), "Stage displays localized rich text")
|
||||
stage._update_descriptions(0.25)
|
||||
world.state.amount.RD += 1.0
|
||||
world.state.amount.RD = E.add(world.state.amount.RD, 1)
|
||||
stage._update_descriptions(0.25)
|
||||
check(label.text.contains("+4.0"), "Selected description refreshes with rate per real second")
|
||||
global.Locale = "en"
|
||||
@@ -92,5 +93,5 @@ func run() -> void:
|
||||
label.free()
|
||||
stage.free()
|
||||
dispose(world)
|
||||
print("Descriptions: %d checks, %d failures" % [checks, failures])
|
||||
quit(0 if failures == 0 else 1)
|
||||
print(("Descriptions: %d checks, %d failures" % [checks, failures]))
|
||||
quit((0 if E.eq(failures, 0) else 1))
|
||||
|
||||
Reference in New Issue
Block a user