KO

Slave Rift

tueb2024 제작 · 슈팅
5.0 (1)
▶ 플레이 💬 후기 소스 🕘 버전 편집 GitHub
games/slave-rift / src / Game / ScoreNumber.php · commit 9ea6094
<?phpnamespace App\Game;/** * Score value stored as mantissa × 10^exponent. * * Arithmetic never reconstructs the complete value, so scores can keep growing * far beyond PHP's floating-point range while staying deterministic. */final class ScoreNumber{    public float $mantissa = 0.0;    public int $exponent = 0;    public function __construct(float $value = 0.0)    {        $this->setFloat($value);    }    public static function fromParts(float $mantissa, int $exponent): self    {        if (!is_finite($mantissa)) {            throw new \InvalidArgumentException('Score mantissa must be finite.');        }        $score = new self();        $score->mantissa = $mantissa;        $score->exponent = $exponent;        $score->normalize();        return $score;    }    public function copy(): self    {        return self::fromParts($this->mantissa, $this->exponent);    }    public function setFloat(float $value): self    {        if (!is_finite($value)) {            throw new \InvalidArgumentException('Score value must be finite.');        }        if ($value === 0.0) {            $this->mantissa = 0.0;            $this->exponent = 0;            return $this;        }        $this->exponent = (int)floor(log10(abs($value)));        $this->mantissa = $value / pow(10.0, $this->exponent);        $this->normalize();        return $this;    }    public function multiply(float $factor): self    {        if (!is_finite($factor)) {            throw new \InvalidArgumentException('Score factor must be finite.');        }        if ($factor === 0.0 || $this->mantissa === 0.0) {            $this->mantissa = 0.0;            $this->exponent = 0;            return $this;        }        $this->mantissa *= $factor;        $this->normalize();        return $this;    }    public function multiplyScore(self $factor): self    {        if ($factor->mantissa === 0.0 || $this->mantissa === 0.0) {            $this->mantissa = 0.0;            $this->exponent = 0;            return $this;        }        $this->mantissa *= $factor->mantissa;        $this->exponent += $factor->exponent;        $this->normalize();        return $this;    }    public function add(self $other): self    {        if ($other->mantissa === 0.0) return $this;        if ($this->mantissa === 0.0) {            $this->mantissa = $other->mantissa;            $this->exponent = $other->exponent;            return $this;        }        $diff = $this->exponent - $other->exponent;        if ($diff > 15) return $this;        if ($diff < -15) {            $this->mantissa = $other->mantissa;            $this->exponent = $other->exponent;            return $this;        }        if ($diff >= 0) {            $this->mantissa += $other->mantissa / pow(10.0, $diff);        } else {            $this->mantissa = $this->mantissa / pow(10.0, -$diff) + $other->mantissa;            $this->exponent = $other->exponent;        }        $this->normalize();        return $this;    }    public function format(int $decimals = 2): string    {        if ($this->mantissa === 0.0) return '0';        $decimals = max(0, min(4, $decimals));        $suffixes = ['', 'K', 'M', 'B', 'T', 'Qa', 'Qi', 'Sx', 'Sp', 'Oc', 'No', 'Dc'];        if ($this->exponent >= 0) {            $group = intdiv($this->exponent, 3);            if ($group < count($suffixes)) {                $withinGroup = $this->exponent - $group * 3;                $value = $this->mantissa * pow(10.0, $withinGroup);                return number_format($value, $decimals, '.', '') . $suffixes[$group];            }        }        return sprintf('%.' . $decimals . 'fe%d', $this->mantissa, $this->exponent);    }    public function __toString(): string    {        return $this->format();    }    private function normalize(): void    {        if ($this->mantissa === 0.0) {            $this->exponent = 0;            return;        }        if (!is_finite($this->mantissa)) {            throw new \OverflowException('Score mantissa overflowed before normalization.');        }        $shift = (int)floor(log10(abs($this->mantissa)));        if ($shift !== 0) {            $this->mantissa /= pow(10.0, $shift);            $this->exponent += $shift;        }        while (abs($this->mantissa) >= 10.0) {            $this->mantissa /= 10.0;            $this->exponent++;        }        while (abs($this->mantissa) < 1.0) {            $this->mantissa *= 10.0;            $this->exponent--;        }    }}