Create: Computing AgesBrass Docs
Libraries

math

Numbers: rounding, roots, trigonometry, logarithms and chance.

All computers

The math library works on numbers. Every number in Brass is the same kind of number (a 64-bit floating point number, like 2, -0.5 or 1e6), so there is no separate integer type: math.floor(7 / 2) gives 3, and 3 prints without a decimal point.

You will use it to turn sensor readings into clean values (math.floor, math.round), to keep a value between two limits (math.min and math.max), to compute angles and distances for a vehicle or a drawing (math.sqrt, math.atan2, math.sin), and to roll dice (math.random).

Brass
local stock = 1234
local stacks = math.floor(stock / 64)
local rest = stock % 64
print(stacks .. " stacks and " .. rest .. " items")
Screen
19 stacks and 18 items
Tip

Two operators often replace a call: a // b is math.floor(a / b), and a % b is the remainder that is never negative when b is positive. Both are faster than a function call.

Functions
math.floor(x)Rounds down, towards minus infinity.
math.ceil(x)Rounds up, towards plus infinity.
math.round(x)Rounds to the nearest whole number. Halves go up: 2.5 gives 3 and -2.5 gives -2.
math.abs(x)The absolute value: the distance between x and zero.
math.min(...)The smallest of the numbers given. At least one number is required.
math.max(...)The largest of the numbers given. At least one number is required.
math.hugeInfinity: larger than every other number. -math.huge is smaller than every number.
math.sqrt(x)The square root. The square root of a negative number is not a number: it prints as nan.
math.exp(x)e to the power x. Useful for smooth curves, like a value that decays a little every tick.
math.log(x [, base])The logarithm of x: the power the base must be raised to in order to give x. Without a base, the natural logarithm.
math.piThe number π, half a turn in radians.
math.sin(x)The sine of an angle in radians.
math.cos(x)The cosine of an angle in radians.
math.tan(x)The tangent of an angle in radians: the slope of a line at that angle.
math.atan(y [, x])The arc tangent: the angle whose tangent is y / x. With two arguments it does exactly the same as math.atan2.
math.atan2(y, x)The angle of the direction from the origin to the point (x, y), in radians. Unlike math.atan(y / x), it knows which quarter the point is in, and does not fail when x is zero.
math.fmod(a, b)The remainder of the division of a by b, which keeps the sign of a. The % operator differs for negative numbers: its result has the sign of b.
math.random([m [, n]])A random number. Three forms:
math.randomseed(seed)Restarts the random sequence from seed: the same seed always gives the same numbers afterwards.

Rounding

#

math.floor(x)

→ number

Rounds down, towards minus infinity.

Parameters
x number
any number
Returns
number
the largest whole number that is not above x
Brass
print(math.floor(2.9))
print(math.floor(-2.1))
print(7 // 2)
Screen
2
-3
3

Use it to cut a value into whole parts, for example a count of items into stacks, or a pixel coordinate into a character cell:

Brass
local px = 137
local column = math.floor((px - 1) / 6) + 1  -- characters are 6 pixels wide
print("pixel " .. px .. " is in column " .. column)
Screen
pixel 137 is in column 23

See also math.ceil() math.round()

#

math.ceil(x)

→ number

Rounds up, towards plus infinity.

Parameters
x number
any number
Returns
number
the smallest whole number that is not below x

math.ceil is the right tool when you need "enough" of something: crates to hold items, trips of a train, lines to show a list.

Brass
local items = 200
print(math.ceil(items / 64) .. " chest slots needed")
print(math.ceil(-2.9))
Screen
4 chest slots needed
-2

See also math.floor() math.round()

#

math.round(x)

→ number

Rounds to the nearest whole number. Halves go up: 2.5 gives 3 and -2.5 gives -2.

Parameters
x number
any number
Returns
number
the nearest whole number

math.round is a Brass extension (Lua has none). It is the same as math.floor(x + 0.5). To keep one decimal, scale the number first:

Brass
local speed = 12.3456
print(math.round(speed))
print(math.round(speed * 10) / 10)
print(math.round(-2.5))
Screen
12
12.3
-2
Note

To show a number with a fixed count of decimals (3.10 rather than 3.1), use string.format("%.2f", x): it returns text, ready to print.

See also math.floor() math.ceil()

#

math.abs(x)

→ number

The absolute value: the distance between x and zero.

Parameters
x number
any number
Returns
number
x without its sign

Handy to compare two values without caring which one is larger, for example to tell whether a contraption is close enough to its target:

Brass
local target, position = 64, 61.5
if math.abs(target - position) < 3 then
  print("close enough")
end
print(math.abs(-7))
Screen
close enough
7

Limits

#

math.min(...)

→ number

The smallest of the numbers given. At least one number is required.

Parameters
x number
one or more numbers
Returns
number
the smallest of them
Brass
print(math.min(4, 2, 8))
local fuel = 30
print("burn " .. math.min(fuel, 16) .. " this round")
Screen
2
burn 16 this round

Together with math.max, it clamps a value between two limits, which you will need all the time with redstone (0 to 15) and with Create speeds (-256 to 256 RPM):

Brass
local function clamp(x, low, high)
  return math.max(low, math.min(high, x))
end
print(clamp(22, 0, 15))
print(clamp(-4, 0, 15))
print(clamp(9, 0, 15))
Screen
15
0
9

See also math.max()

#

math.max(...)

→ number

The largest of the numbers given. At least one number is required.

Parameters
x number
one or more numbers
Returns
number
the largest of them
Brass
local readings = {12, 40, 7, 33}
local best = readings[1]
for i = 2, #readings do
  best = math.max(best, readings[i])
end
print("peak: " .. best)
Screen
peak: 40

See also math.min()

#

math.huge

→ numbervalue

Infinity: larger than every other number. -math.huge is smaller than every number.

Returns
number
infinity

It is the usual starting value when you look for the smallest of a list: the first value you compare is always smaller.

Brass
local distances = {48.2, 12.9, 30}
local nearest = math.huge
for _, d in ipairs(distances) do
  if d < nearest then nearest = d end
end
print(nearest)
print(math.huge, -math.huge)
Screen
12.9
inf -inf

Powers and roots

#

math.sqrt(x)

→ number

The square root. The square root of a negative number is not a number: it prints as nan.

Parameters
x number
a number, zero or more
Returns
number
the square root of x

Its everyday use is the distance between two points, with Pythagoras:

Brass
local function distance(x1, z1, x2, z2)
  local dx, dz = x2 - x1, z2 - z1
  return math.sqrt(dx * dx + dz * dz)
end
print(math.sqrt(16))
print(distance(0, 0, 30, 40))
Screen
4
50

Raising to a power needs no function: 2 ^ 10 is 1024, and x ^ 0.5 is another way to write the square root.

#

math.exp(x)

→ number

e to the power x. Useful for smooth curves, like a value that decays a little every tick.

Parameters
x number
the exponent
Returns
number
e (2.718...) to the power x
Brass
print(math.exp(0))
print(math.exp(1))
Screen
1
2.718281828459

See also math.log()

#

math.log(x [, base])

→ number

The logarithm of x: the power the base must be raised to in order to give x. Without a base, the natural logarithm.

Parameters
x number
a number above zero
base number optional
the base of the logarithm, e (2.718...) when left out
Returns
number
the logarithm of x
Brass
print(math.log(100, 10))
print(math.log(1024, 2))
print(math.log(math.exp(3)))
Screen
2
10
3

A logarithm counts digits or doublings: math.floor(math.log(n, 10)) + 1 is the number of digits of a whole number n above zero, handy to align columns of numbers on a screen.

See also math.exp()

Trigonometry

Angles are in radians: a full turn is 2 * math.pi. Create and the sensors of Create Aeronautics talk in degrees, so convert with degrees * math.pi / 180 and radians * 180 / math.pi.

#

math.pi

→ numbervalue

The number π, half a turn in radians.

Returns
number
3.14159...
Brass
local r = 5
print(math.pi)
print("area: " .. math.pi * r * r)
Screen
3.1415926535898
area: 78.539816339745
#

math.sin(x)

→ number

The sine of an angle in radians.

Parameters
x number
an angle in radians
Returns
number
its sine, between -1 and 1

With math.cos, it places a point on a circle: the base of every round gauge, radar sweep or orbiting sprite drawn with gfx.

Brass
local cx, cy, radius = 100, 60, 40
for step = 0, 3 do
  local angle = step * math.pi / 2
  local x = cx + math.round(radius * math.cos(angle))
  local y = cy + math.round(radius * math.sin(angle))
  print(step, x, y)
end
Screen
0   140 60
1   100 100
2   60  60
3   100 20

On a screen, the same two lines draw a ring of dots. A screen picture like this one is made by running the code:

Brass
gfx.clear("black")
local cx, cy = 153, 90
for degrees = 0, 359, 10 do
  local a = degrees * math.pi / 180
  gfx.pixel(cx + math.round(70 * math.cos(a)), cy + math.round(70 * math.sin(a)), "lime", 3)
end
gfx.line(cx, cy, cx + 70, cy, "yellow", 2)
gfx.text(4, 4, "36 POINTS ON A CIRCLE", "white")
Screen
Screen

See also math.cos() math.pi

#

math.cos(x)

→ number

The cosine of an angle in radians.

Parameters
x number
an angle in radians
Returns
number
its cosine, between -1 and 1
Brass
print(math.cos(0))
print(math.cos(math.pi))
Screen
1
-1

A needle gauge: the needle of a speedometer sweeps half a circle, from the left (0 RPM) to the right (256 RPM). Since a function returns a single value in Brass, the tip of the needle comes back as a table:

Brass
local function needle(rpm)
  local angle = math.pi - (rpm / 256) * math.pi
  return {x = math.round(math.cos(angle) * 20), y = math.round(-math.sin(angle) * 20)}
end
local tip = needle(128)
print(tip.x, tip.y)
Screen
0   -20

See also math.sin() math.pi

#

math.tan(x)

→ number

The tangent of an angle in radians: the slope of a line at that angle.

Parameters
x number
an angle in radians
Returns
number
its tangent
Brass
print(math.round(math.tan(math.pi / 4)))
Screen
1
#

math.atan(y [, x])

→ number

The arc tangent: the angle whose tangent is y / x. With two arguments it does exactly the same as math.atan2.

Parameters
y number
the vertical part, or the slope when x is left out
x number optional
the horizontal part, 1 when left out
Returns
number
an angle in radians
Brass
print(math.atan(1) * 180 / math.pi)
Screen
45

See also math.atan2()

#

math.atan2(y, x)

→ number

The angle of the direction from the origin to the point (x, y), in radians. Unlike math.atan(y / x), it knows which quarter the point is in, and does not fail when x is zero.

Parameters
y number
the vertical part (or the difference of Z, on a map)
x number
the horizontal part (or the difference of X)
Returns
number
the angle of the point (x, y), between -π and π

This is the function for "which way do I turn?": the direction from a vehicle to its target.

Brass
local function heading(fromX, fromZ, toX, toZ)
  local radians = math.atan2(toZ - fromZ, toX - fromX)
  return math.round(radians * 180 / math.pi)
end
print(heading(0, 0, 10, 0))
print(heading(0, 0, 0, 10))
print(heading(0, 0, -10, 0))
Screen
0
90
180

See also math.atan()

Remainders

#

math.fmod(a, b)

→ number

The remainder of the division of a by b, which keeps the sign of a. The % operator differs for negative numbers: its result has the sign of b.

Parameters
a number
the number to divide
b number
the divisor
Returns
number
the remainder of a / b, with the sign of a
Brass
print(math.fmod(7, 3), 7 % 3)
print(math.fmod(-7, 3), -7 % 3)
Screen
1   1
-1  2

For positions that wrap around (a pattern that repeats, a ring of 16 lamps), % is usually what you want, because it never goes below zero.

Chance

Each computer has its own random number generator. It starts from a different point every time the world loads, so two runs of a program do not roll the same numbers, unless you call math.randomseed.

#

math.random([m [, n]])

→ number

A random number. Three forms:

Parameters
m number optional
the upper bound (with one argument) or the lower bound (with two)
n number optional
the upper bound
Returns
number
a random number
callresult
math.random()a number with decimals, from 0 included to 1 excluded
math.random(m)a whole number from 1 to m, both included
math.random(m, n)a whole number from m to n, both included

Decimal bounds are rounded down. When the lower bound is above the upper one, the program stops with the error bad argument to 'random' (interval is empty).

Brass
print(math.random(1, 6))   -- a die: 1, 2, 3, 4, 5 or 6
print(math.random(10))     -- 1 to 10
print(math.random())       -- for example 0.71828...

Pick a random element of a list:

Brass
local songs = {"cat", "blocks", "chirp", "far", "mall"}
local pick = songs[math.random(#songs)]
print("now playing: " .. pick)

A chance in percent, for an event that should happen one time out of five:

Brass
if math.random() < 0.2 then
  print("a creeper appears!")
end

See also math.randomseed()

#

math.randomseed(seed)

Restarts the random sequence from seed: the same seed always gives the same numbers afterwards.

Parameters
seed number
any number

Use a fixed seed to make a "random" world or puzzle that is the same every time, or the time to get a new sequence on each run:

Brass
math.randomseed(42)
local a = math.random(1000)
math.randomseed(42)
local b = math.random(1000)
print(a == b)
Screen
true
Brass
math.randomseed(os.time() + os.id() * 1000)  -- different on each computer and each run

Common patterns

Map a reading to another scale. A redstone signal (0 to 15) to a speed (0 to 256 RPM), or a stock (0 to the capacity) to the width of a bar on the screen:

Brass
local function map(value, inLow, inHigh, outLow, outHigh)
  local t = (value - inLow) / (inHigh - inLow)
  return outLow + t * (outHigh - outLow)
end
print(map(15, 0, 15, 0, 256))
print(math.floor(map(750, 0, 2000, 0, 40)))
Screen
256
15

Smooth a jumpy reading. Sensors move every tick; a running average hides the noise. Each new reading counts for 20 % of the result:

Brass
local smooth = 0
for _, reading in ipairs({10, 12, 30, 11, 10}) do
  smooth = smooth + (reading - smooth) * 0.2
end
print(math.round(smooth * 10) / 10)
Screen
9.6

See also math.random()