Forum / Dev Talk / Snippets & Tutorials / Maths and lists

Maths and lists

#1 19 Aug 2026 06:25
New in the script engine: maths and lists


The last update gave the language a proper maths library and real list handling — building lists from scratch, searching them, and folding them down to a number. Everything is additive: your existing scripts are untouched, and nothing under the hood changed.

A few ground rules first, because they shape everything below:

  • Numbers are whole numbers. There are no decimals — one 64-bit integer type, on purpose. Where you'd reach for a fraction, work in percentages or scale explicitly (more on scale below).
  • Overflow stops the script rather than silently wrapping to a wrong number. A bot that quietly computed nonsense is worse than one that stopped and told you.
  • A list is a value, not a box. Nothing mutates it. append hands you back a new list, so the pattern is [icode]xs = append(xs, v)[/icode].


Maths


abs(n)                 min(a, b)          max(a, b)
clamp(value, low, high)   sign(n)         gcd(a, b)
floorDiv(a, b)         floorMod(a, b)     pow(base, exponent)     sqrt(n)
scale(value, num, den) percentOf(part, whole)
parseInt(text)         numberIn(text)     commas(n)   padded(n, width)


A percentage without a fraction in sight:

let used = percentOf(30 - invFree(), 30)
uiProgress("bag", clamp(used, 0, 100), 100)


floorMod, not % — this is the one you want to cycle an index. Plain [icode]%[/icode] takes the sign of the left side, so a negative counter would run backwards off the front of your rotation. [icode]floorMod(-1, 4)[/icode] is 3.

let corner = floorMod(trips, 4)


Need "half again as long"? scale is the fixed-point workhorse — it does [icode]value * num / den[/icode] with room to spare so the middle step can't overflow:

return scale(600, 3, 2)   # 900ms


And pulling a number out of a chat line (commas ignored):

on message(line, from) {
    let coins = numberIn(line)
    if coins == none { return }
    if coins > 1000 { print("nice:", commas(coins)) }
}


Note parseInt and numberIn answer none when there's no number, the same way nearestNpc does when it finds nothing — so the checker makes you handle the miss.

Lists


Build one — the answer to "there's no list literal":

for i in range(0, 5) {          # 0 1 2 3 4
    print("tick", i)
}
# also: rangeStep(0, 100, 10)   repeated(0, 5)   range(0, 0) is empty


Search (numbers and text — those compare by value):

let opts = menuOptions()
if contains(opts, "Attack") {
    print("attack sits at", indexOf(opts, "Attack"))   # -1 if absent
}


Reshapefirst, last, append, concat, reversed (each answers a new list; first/last answer none on an empty list).

Fold to a numbersum, minOf, maxOf, mean, sorted, join:

on message(line, from) {
    let nums = numbersIn(line)
    if isEmpty(nums) { return }
    let avg = mean(nums)
    if avg == none { return }
    print("biggest", maxOf(nums), "- average", avg)
}


What still works exactly as before


Indexing ([icode]xs[0][/icode], which answers none when out of range), [icode]for x in list[/icode], and [icode]list<T>[/icode] as a [icode]fun[/icode] parameter or return type were already in the language — the new commands just give you lists worth doing that to. Lists are capped at 4096 items so a runaway builder can't eat the client's memory.

Full command reference is in the docs. Post your favourite one-liners below.