Skip to content

Commit

Permalink
Proper rounding implementation (#23)
Browse files Browse the repository at this point in the history
* Implement math.round(x): nearest integer, halfway away from zero

   ...in accordance with Basisregelwerk page 13 ("Rundungen").

   The previous implementation relies on `string.format("%.0f", x)` whose
behaviour is inconsistent accross Lua versions.

* Use math.round() over common.round() in trivial cases
* common.render_delta(): use math.round()
* common.render_delta() used to generate the wrong sign for non-zero
  values in [-0.5, 0.5].
- Emit "&" and the integral value with a single tex.sprint() since the latter
  can't result in special characters.
* Remove common.round()
  • Loading branch information
davidfoerster authored Mar 23, 2024
1 parent 7755e64 commit 7809670
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 49 deletions.
24 changes: 11 additions & 13 deletions src/common.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require("stdext")
local data = require("data")
local schema = require("schema")

Expand Down Expand Up @@ -264,11 +265,6 @@ function common.checkboxlist(items)
end
end

function common.round(v, down)
local delta = down and -0.0001 or 0.0001 -- round up at 0.5 unless down given
return tonumber(string.format("%.0f", v + delta))
end

function common.ritualkenntnis(items, count)
tex.sprint([[{\normalfont\normalsize\setlength{\arrayrulewidth}{1pt}
\begin{NiceTabular}{p{3.7cm}@{(}x{3.2cm}@{):\hspace{2pt}}x{0.7cm}x{0.5cm}}]])
Expand Down Expand Up @@ -355,15 +351,17 @@ end

function common.render_delta(input)
if type(input) == "number" then
if input < 0 then
tex.sprint(-2, "")
elseif input > 0 then
tex.sprint(-2, "+")
local sign
if input >= 0.5 then
sign = "+"
elseif input <= -0.5 then
sign = ""
else
sign = ""
end
tex.sprint(common.round(math.abs(input)))
else
tex.sprint(-2, input)
input = string.format("%s%d", sign, math.round(math.abs(input)))
end
tex.sprint(-2, input)
end

function common.merkmalliste(input, zauber)
Expand Down Expand Up @@ -501,7 +499,7 @@ function common.schaden.render(tp)
elseif tp.die ~= nil then
tex.sprint(-2, "+")
end
tex.sprint(-2, common.round(math.abs(tp.num)))
tex.sprint(-2, math.round(math.abs(tp.num)))
end
end

Expand Down
5 changes: 3 additions & 2 deletions src/frontseite.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local data = require("data")
local common = require("common")
local schema = require("schema")
require("stdext")

local frontseite = {}

Expand Down Expand Up @@ -213,7 +214,7 @@ function eigenschaften.rechts(self)
if data.Nachteile.Glasknochen then
base = base - 2
end
tex.sprint(-2, common.round(base))
tex.sprint(-2, math.round(base))
end
end
tex.sprint([[\\\hline\end{tabular}}\egroup\par]])
Expand Down Expand Up @@ -261,4 +262,4 @@ end

frontseite.eigenschaften = eigenschaften

return frontseite
return frontseite
18 changes: 9 additions & 9 deletions src/kampfbogen.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require("stdext")
local data = require("data")
local common = require("common")
local schemadef = require("schemadef")
Expand All @@ -14,7 +15,7 @@ local function calc_be(input)
if string.find(input, "^BEx") then
return be * input:sub(4)
elseif string.find(input, "^BE%-") then
return common.round(math.max(0, be - tonumber(input:sub(4))))
return math.round(math.max(0, be - tonumber(input:sub(4))))
elseif string.find(input, "^BE") then
return be
else
Expand Down Expand Up @@ -158,14 +159,14 @@ nahkampf_render[11]= {false, function(v, talent, ebe)
if talent == nil or #talent < 4 or atb == "" or #v < 8 then
return
end
tex.sprint(-2, atpa_mod(atb - common.round(ebe/2, true), talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM AT"], art(v), talent.Spezialisierungen))
tex.sprint(-2, atpa_mod(atb - ebe // 2, talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM AT"], art(v), talent.Spezialisierungen))
end}
nahkampf_render[12]= {false, function(v, talent, ebe)
local pab = data:cur("PA")
if talent == nil or #talent < 5 or pab == "" or #v < 9 then
return
end
tex.sprint(-2, atpa_mod(pab - common.round(ebe/2), data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM PA"], art(v), talent.Spezialisierungen))
tex.sprint(-2, atpa_mod(pab - (ebe + 1) // 2, data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM PA"], art(v), talent.Spezialisierungen))
end}
nahkampf_render[13]= {false, function(v, talent, ebe)
if #v < 6 then
Expand Down Expand Up @@ -252,7 +253,7 @@ local waffenlos_render = {
added = added + 1
end
end
tex.sprint(-2, atpa_mod(atb - common.round(ebe/2, true), talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], 0))
tex.sprint(-2, atpa_mod(atb - ebe // 2, talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], 0))
end},
[6]= {false, function(v, talent, ebe)
local pab = data:cur("PA")
Expand All @@ -266,7 +267,7 @@ local waffenlos_render = {
added = added + 1
end
end
tex.sprint(-2, atpa_mod(pab - common.round(ebe/2), data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], 0))
tex.sprint(-2, atpa_mod(pab - (ebe + 1) // 2, data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], 0))
end},
[7]= {false, function(v, talent, ebe)
tp = common.schaden.mod({dice=1, die=6, num=0}, v["TP/KK Schwelle"], v["TP/KK Schritt"])
Expand Down Expand Up @@ -404,13 +405,12 @@ end

function kampfbogen.energieleiste(label, val)
tex.sprint(-2, label)
num = tonumber(val)
if num == nil then
val = tonumber(val)
if val == nil then
tex.sprint("&&&&")
else
for i=1,4 do
tex.sprint("&")
tex.sprint(-2, common.round(num/i))
tex.sprint("&", math.round(val / i))
end
end
tex.sprint("&")
Expand Down
18 changes: 9 additions & 9 deletions src/skt.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require("stdext")

local skt = {
spalte = {
{n="A*", f=1}, {n="A", f=1, [31] = 50}, {n="B", f=2}, {n="C", f=3}, {n="D", f=4, [28] = 170, [31] = 200}, {n="E", f=5, [8] = 48}, {n="F", f=7.5, [30] = 350, [31] = 375}, {n="G", f=10, [30] = 480, [31] = 500}, {n="H", f=20, [2] = 35, [6] = 140, [24] = 720, [27] = 830, [31] = 1000}
Expand Down Expand Up @@ -52,7 +54,7 @@ function skt.spalte:effektiv(basis, zielwert, methode)
end

function skt.faktor:apply(value)
return math.floor(value * self[1] + 0.5)
return math.round(value * self[1])
end

for _, f in ipairs(skt.faktor) do
Expand All @@ -61,11 +63,9 @@ end

function skt:kosten(spalte, zielwert)
local index = self.spalte:num(spalte)
if zielwert > 31 then
zielwert = 31
end
zielwert = math.min(zielwert, 31)
if zielwert <= 0 then
return 5 * math.floor(self.spalte[index].f + 0.5)
return 5 * math.round(self.spalte[index].f)
end
if index == 1 then
return math.max(self:kosten("A", zielwert) - 2, 1)
Expand All @@ -74,14 +74,14 @@ function skt:kosten(spalte, zielwert)
if explicit ~= nil then
return explicit
end
local val = tonumber(string.format("%.0f", 0.8 * self.spalte[index].f * zielwert^1.2))
local val = math.round(0.8 * self.spalte[index].f * zielwert^1.2)

if val > 200 then
val = math.floor((val + 5) / 10) * 10
val = math.round(val / 10) * 10
elseif val > 50 then
val = math.floor((val + 2) / 5) * 5
val = math.round(val / 5) * 5
end
return val
end

return skt
return skt
10 changes: 10 additions & 0 deletions src/stdext.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Rounds to the nearest integer. Halfway cases are arounded _away_ from zero.
function math.round(x)
local integral, fractional = math.modf(x)
if fractional >= 0.5 then
return integral + 1
elseif fractional <= -0.5 then
return integral - 1
end
return integral
end
5 changes: 3 additions & 2 deletions src/talentbogen.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local schema = require("schema")
local data = require("data")
local common = require("common")
require("stdext")

local talent = {}

Expand Down Expand Up @@ -187,7 +188,7 @@ function talent.meta(v)
::found::
end
if sum ~= nil and #v[5] > 0 then
tex.sprint(-2, common.round(sum / #v[5]))
tex.sprint(-2, math.round(sum / #v[5]))
end
end

Expand Down Expand Up @@ -379,4 +380,4 @@ function talentbogen.gruppen()
end


return talentbogen
return talentbogen
26 changes: 14 additions & 12 deletions src/values.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require("stdext")
local d = require("schemadef")
local schema = assert(loadfile("schema.lua", "t"))(false)
local skt = require("skt")
Expand Down Expand Up @@ -134,7 +135,7 @@ function getter_map.sparse(val, div)
if val == 0 then
return ""
end
return tonumber(string.format("%.0f", val/div + 0.0001)) -- round up at 0.5
return math.round(val / div)
end

values.sparse = getter_map.sparse
Expand Down Expand Up @@ -515,13 +516,12 @@ function values:gesamtRuestung(teile, decimals)
end
if gRS == nil then return nil, nil end
gRS = gRS/20
gBE = gBE/20 - sterne
if gBE < 0 then gBE = 0 end
if decimals then
return gRS, gBE
else
return tonumber(string.format("%.0f", gRS + 0.0001)), tonumber(string.format("%.0f", gBE + 0.0001))
gBE = math.max(gBE/20 - sterne, 0)
if not decimals then
gRS = math.round(gRS)
gBE = math.round(gBE)
end
return gRS, gBE
end

function values:ap_mod(kosten)
Expand Down Expand Up @@ -756,9 +756,11 @@ function values:spezialisierung(e)
end
event[1] = event[1] .. e.Fertigkeit .. ", " .. e.Methode .. "): " .. e.Name
ziel.Spezialisierungen:append(e.Name)
local ap = #ziel.Spezialisierungen * 20 * math.floor(skt.spalte[skt.spalte:num(spalte)].f + 0.5)
local ap = (
#ziel.Spezialisierungen * 20
* math.round(skt.spalte[skt.spalte:num(spalte)].f))
if e.Methode == "SE" then
ap = math.floor(ap / 2 + 0.5)
ap = math.round(ap / 2)
end
event[2] = -1 * ap
event[3] = faktor
Expand Down Expand Up @@ -924,7 +926,7 @@ function values:aktiviere(e)
elseif smt.name == "Ritual" then
ap = e.Subjekt.Lernkosten
if e.Methode == "SE" then
ap = math.floor((ap / 2) + 0.51)
ap = math.round(ap / 2)
end
faktor = self:tgruppe_faktor("Zauber")
self.Magie.Rituale:append(e.Subjekt, e.Sortierung)
Expand Down Expand Up @@ -1062,7 +1064,7 @@ function values:senkung(e)
if found == nil then
tex.error("\n[Senkung] unbekannte Schlechte Eigenschaft: '" .. e.Name .. "'")
end
local ap = math.floor((found.Wert - e.Zielwert) * 50 * found.GP + 0.5)
local ap = math.round((found.Wert - e.Zielwert) * 50 * found.GP)
if e.Zielwert == 0 then
event[1] = event[1] .. ": Schlechte Eigenschaft mit Wert " .. found.Wert .. " entfernt"
table.remove(self.Nachteile.Eigenschaften.value, found_index)
Expand Down Expand Up @@ -1213,4 +1215,4 @@ for _, e in ipairs(schema.Ereignisse:instance()) do
table.insert(values.Ereignisse, event)
end

return values
return values
5 changes: 3 additions & 2 deletions src/zauberdokument.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require("stdext")
local data = require("data")
local common = require("common")

Expand All @@ -16,7 +17,7 @@ function zauberdokument.asp_regeneration()
end
local mr = data.SF.Magisch.MeisterlicheRegeneration
if mr ~= nil then
val.num = val.num + 3 + math.floor((data:cur(mr) + 1)/3)
val.num = val.num + 3 + math.round(data:cur(mr) / 3)
tex.sprint(-2, val.num)
else
local reg = data.SF.Magisch:getlist("Regeneration")
Expand All @@ -30,4 +31,4 @@ function zauberdokument.asp_regeneration()
end
end

return zauberdokument
return zauberdokument

0 comments on commit 7809670

Please sign in to comment.