Last active
April 27, 2024 10:52
-
-
Save rzuf79/9e2e027b72f93184e416b5906506e7f4 to your computer and use it in GitHub Desktop.
Hnum - a barbaric large number module for games.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
Hnum - a barbaric large number module for games. | |
Usage: | |
local a_hundred = HNum:new(100) | |
print(a_hundred:get_formatted()) -- 100 | |
local a_thousand = HNum:new(1000) | |
print(a_thousand:get_formatted()) -- 1k | |
local also_a_thousand = HNum:new(1, 1) | |
print(also_a_thousand:get_formatted()) -- 1k | |
a_thousand = a_thousand + 1000 | |
print(a_thousand:get_formatted()) -- 2k | |
local a_million = HNum:new(1, 2) | |
print(a_million:get_formatted()) -- 1m | |
local two_billion_from_string = HNum:new("2573684736") | |
print(two_billion_from_string:get_formatted()) -- 2.57b | |
print(two_billion_from_string:to_string()) -- 2573684000 | |
Note, that some precission is lost in the last example. It can be tweaked by increasing the | |
'precision' variable in HNum:from_string(). | |
The methods for converting the number to and from a string are here to make storing the | |
value easily when saving the game. Simply store the :to_string-ed value in your save | |
data and pass it when creating a new instance of HNum during load. | |
]]-- | |
local HNum = {} | |
-- Convert a number to a HNum table if needed | |
local function convert(num) | |
if type(num) == "number" then | |
num = HNum:new(num, 0) | |
end | |
return num | |
end | |
function HNum:new(value, thousands) | |
local newObj = { | |
__type = "hnum", | |
value = value or 0.0, | |
thousands = thousands or 0 | |
} | |
self.__index = self | |
setmetatable(newObj, self) | |
if type(value) == "string" then | |
newObj:from_string(value) | |
end | |
newObj:fix_compression() | |
return newObj | |
end | |
function HNum:from_string(value) | |
local precision = 2 | |
self.thousands = math.floor((string.len(value) - 1) / 3) | |
if self.thousands == 0 then | |
self.value = tonumber(value) | |
self.thousands = 0 | |
end | |
local baseValue = string.sub(value, 1, string.len(value) - (self.thousands * 3)) | |
self.value = tonumber(baseValue) | |
precision = math.min(self.thousands, precision) | |
for i = 0, precision - 1 do | |
local subString = string.sub(value, string.len(baseValue) + (i * 3) + 1, string.len(baseValue) + (i * 3) + 3) | |
self.value = self.value + tonumber(subString) / math.pow(1000.0, i + 1) | |
end | |
end | |
function HNum:fix_compression() | |
while math.abs(self.value) >= 1000.0 or self.thousands < 0 do | |
self.value = self.value / 1000.0 | |
self.thousands = self.thousands + 1 | |
end | |
while math.abs(self.value) < 1.0 and self.thousands > 0 do | |
self.value = self.value * 1000.0 | |
self.thousands = self.thousands - 1 | |
end | |
end | |
function HNum:to_string() | |
local outText = tostring(self.value) | |
local charsCount = string.len(tostring(math.floor(self.value))) + (self.thousands * 3) | |
local dotIdx = string.find(outText, "%.") | |
if dotIdx ~= nil then | |
outText = string.sub(outText, 1, dotIdx - 1) .. string.sub(outText, dotIdx + 1) | |
end | |
while string.len(outText) < charsCount do | |
outText = outText .. "0" | |
end | |
return outText | |
end | |
function HNum:get_formatted() | |
local outAbbrev = "" | |
local abbrevs = { "", "k", "m", "b", "t" } | |
if self.thousands == 0 then | |
return tostring(math.floor(self.value+0.5)) | |
elseif self.thousands < #abbrevs then | |
outAbbrev = abbrevs[self.thousands + 1] | |
else | |
local firstLetterCharCode = 97 | |
local lastLetterCharCode = 123 | |
local abbrevIdx = self.thousands - #abbrevs | |
outAbbrev = string.char(firstLetterCharCode + abbrevIdx % (lastLetterCharCode - firstLetterCharCode)) | |
abbrevIdx = math.floor(abbrevIdx / (lastLetterCharCode - firstLetterCharCode)) | |
outAbbrev = "" .. string.char(firstLetterCharCode + abbrevIdx % (lastLetterCharCode - firstLetterCharCode)) .. outAbbrev | |
end | |
local truncate_mult = (10 ^ 2) | |
local truncated_val = math.floor(self.value * truncate_mult) /truncate_mult | |
local out = string.format("%.2f", truncated_val) | |
if self.thousands > 0 then | |
out = out:gsub("%.?0*$", "") | |
end | |
return out .. outAbbrev | |
end | |
function HNum:to_float() | |
local outVal = self.value | |
for i = 1, self.thousands do | |
outVal = outVal * 1000 | |
end | |
return outVal | |
end | |
function HNum:is_less_than(other) | |
other = convert(other) | |
return other.thousands > self.thousands or (other.thousands == self.thousands and other.value > self.value) | |
end | |
function HNum:equals(other) | |
other = convert(other) | |
return self.thousands == other.thousands and math.abs(self.value - other.value) < 0.00001 | |
end | |
function HNum.__add(left_side, right_side) | |
left_side = convert(left_side) | |
right_side = convert(right_side) | |
HNum.align_values(left_side, right_side) | |
return HNum:new(left_side.value + right_side.value, left_side.thousands) | |
end | |
function HNum.__sub(left_side, right_side) | |
left_side = convert(left_side) | |
right_side = convert(right_side) | |
HNum.align_values(left_side, right_side) | |
return HNum:new(left_side.value - right_side.value, left_side.thousands) | |
end | |
function HNum.__mul(left_side, right_side) | |
left_side = convert(left_side) | |
right_side = convert(right_side) | |
return HNum:new(left_side.value * right_side.value, left_side.thousands + right_side.thousands) | |
end | |
function HNum.__div(left_side, right_side) | |
left_side = convert(left_side) | |
right_side = convert(right_side) | |
return HNum:new(left_side.value / right_side.value, left_side.thousands - right_side.thousands) | |
end | |
function HNum.__eq(left_side, right_side) | |
return left_side:queals(right_side) | |
end | |
function HNum.__lt(left_side, right_side) -- less than | |
return left_side.is_less_than(right_side) | |
end | |
function HNum.__le(left_side, right_side) -- less or equal | |
return left_side:is_less_than(right_side) or left_side:equals(right_side) | |
end | |
function HNum.align_values(first, second) | |
first = convert(first) | |
second = convert(second) | |
if first.thousands == second.thousands then | |
return | |
end | |
if first.thousands > second.thousands then | |
local diff = first.thousands - second.thousands | |
for i = 1, diff do | |
second.value = second.value / 1000.0 | |
end | |
second.thousands = first.thousands | |
else | |
local diff = second.thousands - first.thousands | |
for i = 1, diff do | |
first.value = first.value / 1000.0 | |
end | |
first.thousands = second.thousands | |
end | |
end | |
return HNum |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment