fb8a3f5658
- 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.
91 lines
5.6 KiB
GDScript
91 lines
5.6 KiB
GDScript
extends RefCounted
|
||
const E = preload("res://world/owniverse/math/exact.gd")
|
||
|
||
const GREEN = "#40cc40"
|
||
const RED = "#ff6060"
|
||
const GREY = "#888888"
|
||
const Parameters = preload("res://world/owniverse/world_parameters.gd")
|
||
|
||
static func number(value, digits: int = 0) -> String:
|
||
if not E.finite(value): return "—"
|
||
return E.scientific(value, 4) if E.ge(E.absolute(value), 100000) else E.fixed(value, digits)
|
||
|
||
static func colored(text: String, color: String) -> String:
|
||
return E.add(E.add(E.add(E.add("[color=", color), "]"), text), "[/color]")
|
||
|
||
static func matches(world, id: String) -> Array:
|
||
var result: Array = []
|
||
for key in world.catalog.entities:
|
||
var entity = world.catalog.entities[key]
|
||
if ((E.eq(key, id) or E.eq(entity.count_as, id)) or E.eq(entity.group_as, id)):
|
||
if (not world.catalog.is_group(key)):
|
||
result.append(key)
|
||
return result
|
||
|
||
static func quantities(world, id: String) -> Dictionary:
|
||
var free: Variant = 0
|
||
var total: Variant = 0
|
||
var physical: Dictionary = world.ledger.physical_amounts(world.state)
|
||
for key in matches(world, id):
|
||
free = E.add(free, world.state.amount.get(key, 0))
|
||
total = E.add(total, physical.get(key, 0))
|
||
if E.eq(id, "SMBH"):
|
||
free = world.state.amount.get(id, 0)
|
||
total = free
|
||
return {"free": free, "total": total}
|
||
|
||
static func costs(world, global: Node, id: String) -> String:
|
||
if (not world.catalog.entities.has(id)):
|
||
return ""
|
||
var lines: PackedStringArray = []
|
||
for requirements in [world.catalog.entities[id].cost, world.catalog.entities[id].bind]:
|
||
for resource in requirements:
|
||
var required: Variant = requirements[resource]
|
||
if E.lt(required, 0):
|
||
continue
|
||
var available: Variant = E.floor_value(world.ledger.available(world.state, resource))
|
||
var name: String = str(global.get_template(resource, "Entity", "Name").get("text", resource))
|
||
lines.append(colored(E.add(E.add(E.add(E.add(E.add(name, ": "), number(required)), " ("), number(available)), ")"), (GREEN if E.ge(available, required) else RED)))
|
||
return "\n".join(lines)
|
||
|
||
static func variables(world, global: Node, id: String, rate: Variant = 0.0) -> Dictionary:
|
||
var counts: Dictionary = quantities(world, id)
|
||
var show_total: bool = false
|
||
for key in matches(world, id):
|
||
show_total = (show_total or E.eq(world.catalog.entities[key].group_as, "Planet"))
|
||
var amount: String = (number((counts.total if show_total else counts.free)) if world.catalog.entities.has(id) else "—")
|
||
if ((not show_total) and E.gt(counts.total, counts.free)):
|
||
amount = E.add(amount, E.add(" ", colored(E.add(E.add("(", number(counts.total)), ")"), GREY)))
|
||
if E.ne(rate, 0):
|
||
amount = E.add(amount, E.add(" | ", colored(E.add((("−" if E.lt(rate, 0) else "+")), number(E.absolute(rate), 1)), (RED if E.lt(rate, 0) else GREEN))))
|
||
var physical: Dictionary = world.ledger.physical_amounts(world.state)
|
||
var voids: Variant = 0
|
||
for key in physical:
|
||
voids = E.add(voids, E.multiply(physical[key], world.catalog.entities[key].voids))
|
||
var he: Variant = world.state.amount.get("He", 0)
|
||
var h: Variant = world.state.amount.get("H", 0)
|
||
var mixture: String = "—"
|
||
if E.gt(E.add(he, h), 0):
|
||
mixture = E.add(E.add(E.add(E.add("He: ", number(E.divide(E.multiply(100, he), (E.add(he, h))), 2)), "% / H: "), number(E.divide(E.multiply(100, h), (E.add(he, h))), 2)), "%")
|
||
var environment: Dictionary = world.environment.stellar_environment(world.state)
|
||
return {"amount": amount, "colored costs": costs(world, global, id), "lifespan": (number(world.catalog.entities[id].lifespan) if world.catalog.entities.has(id) else "—"), "voids": number(voids), "he / h": E.add("\n", mixture), "metallicity": E.add(number(E.multiply(environment.metallicity, 100), 2), "%"), "энергия": number(world.state.energy), "плотность": number(environment.density, 6), "лет в секунду": number(world.state.speed_in_year), "amount1": "—", "amount2": "—", "amount3": "—", "amount4": "—"}
|
||
|
||
static func render(world, global: Node, id: String, category: String = "Entity", rate: Variant = 0.0) -> String:
|
||
var values: Dictionary = variables(world, global, id, rate)
|
||
if E.eq(category, "Indicator"):
|
||
if E.eq(id, "Density"):
|
||
id = Parameters.density_display(world.environment.stellar_environment(world.state).density).mode
|
||
var scales: Dictionary = {"Burnability": "Bur", "Explosiveness": "Exp", "Aggression": "Agg"}
|
||
if scales.has(id):
|
||
var level: Variant = Parameters.value(world.state, scales[id])
|
||
values.amount = number(level, (1 if E.ne(level, E.floor_value(level)) else 0))
|
||
values.scale = global.render_template(global.get_template(E.add("Scale", str(clampi(E.integer(E.round_value(level)), 1, 19))), "Indicator", "Scale"), {})
|
||
if E.eq(id, "Trends"):
|
||
for trend in Parameters.TRENDS:
|
||
values[trend.to_lower()] = E.add(number(E.multiply(world.state.get("trends", {}).get(trend, 0), 100), 2), "%")
|
||
if E.eq(id, "Energy"):
|
||
values["энергия"] = E.add(E.add(E.add(number(world.state.energy), " ("), number(E.multiply(E.clamp_value(E.divide(world.state.energy, Parameters.INITIAL_ENERGY), 0, 1), 100), 2)), "%)")
|
||
var title: String = global.render_template(global.get_template(id, category, "Name"), values)
|
||
var body: String = global.render_template(global.get_template(id, category, "Description"), values)
|
||
return E.add(E.add(E.add("[b]", ((title if E.ne(title, "") else id))), "[/b]\n"), body)
|