User Tools

Site Tools


elderscript_basic_values

Numeric Values

ELDARScript provides standard mathematical ways of working with numeric values. By default, numbers are integers (whole numbers), such as 5 and -23. Standard operations can be performed:

Expression Value Notes
8 + 3 11 Standard addition
8 - 3 5 Standard subtraction
8 × 3 24 Standard multiplication
8 / 3 2 Standard division, truncating the fractional result if any
-8 / 3 -2 While the result is actually -2 ⅔, we truncate the result which brings it towards zero (or in this case 2)
8 ÷ 3 3 Standard division, rounding
8 /+ 3 3 Standard division, rounding all fractions up (taking the “ceiling” of the result)
8 /- 3 2 Standard division, rounding all fractions down (taking the “floor” of the result)
8 /= 3 2 ⅔ Exact division, giving a rational value (see “Rational Values” for rational values)
8 ∨ 3 8 Higher value of the two
8 ∧ 3 3 Lower value of the two
8 ≫ 3 8 If the left value is any value other than zero (false), the right value is ignored, otherwise the left value is ignored (useful to provide a default value in case something isn’t initialized)

Since dividing 8 by 3 results in 2 ⅔ (not an integer), there are multiple different versions of division to produce an integer result by either truncating (rounding towards zero), rounding to the nearest value, rounding up, rounding down, or not rounding at all. Note that truncation may seem identical to rounding down, but it is not for negative values (truncation of negative values results in rounding up). If you want an exact fractional value, see “Rational Values” for more details about rational values.

Numbers can be compared, resulting in either a “1” (true) or “0” (false):

Expression Value Notes
8 = 3 0 Equal
8 ≠ 3 1 Not Equal
8 > 3 1 Greater Than
8 ≥ 3 1 Greater Than or Equal To
8 < 3 0 Less Than
8 ≤ 3 0 Less Than or Equal To

Also useful (with these comparison results) is the ability to see if both are true or one or the other is true (“and”, “or” respectively). We can actually use the “lower” and “higher” operators exactly like “and” and “or”. This works because the lower of two boolean values is going to only be true (1) if both are true (1). Similarly, with higher, the result will be true (1) if either are true (1)

Expression Value Notes
1 ∧ 1 1 True and True = True
1 ∧ 0 0 True and False = False
0 ∧ 1 0 False and True = False
0 ∧ 0 0 False and False = False
1 ∨ 1 1 True or True = True
1 ∨ 0 1 True or False = True
0 ∨ 1 1 False or True = True
0 ∨ 0 0 False or False = False

String Values

ELDARScript also includes the ability to have string (textual) values. These can be enclosed by either a single quote at the start and end, double quote at the start and end, or left and right “typographic” double quotes:

1. ’Single Quotes’

2. “Double Quotes”

3. “Typographic Quotes”

The ability to use different styles allows you to embed one inside another easier. Within a single our double quoted string, you can precede that quote with a backslash to make a literal quote (part of the string, not ending it) such as “Double Quotes with \”quotes\“ inside it”. Note that strings can also have formatting in them - which also use a back slash to indicate the start of formatting (escaping quotes is just a simple version of that). This formatting can be quite rich, including the ability to change color, font, size, and even embed images and icons in it. See “Advanced String Operations” for more details. Strings can also be operated on to combine them:

Expression Value Notes
“Hello” + “World” “Hello World” Concatenate the string with a space between them. A space is added automatically since this is normally what you want
“Hello” - “World” “HelloWorld” Concatenate the string without a space between them
“Hello” × 3 “HelloHelloHello” Repeat and concatenate
“a, b, c, d” / “, ” (“a”, “b”, “c”, “d”) Splits a string into a list (see “Lists” below).
(“a”, “b”, “c”, “d”) × “|” “a|b|c|d” Takes a list and joins it back together, separating each element by the string
“a=5, b=6” ÷ “a=%#, b=%#” (5, 6) Parse a string and extract parts of it from a format pattern (see “Advanced String Operations”)
“There is a % potion” % “red” “There is a red potion” Formatting values into a string (see “Advanced String Operations”). Note that the right hand side can be a single value or a list
“Hello” × “World” 0 Currently undefined. Any expression that uses operators that doesn’t have a well defined behavior for the types involved will result in attempting to use numeric operations

Strings can also be compared using the standard comparison operators, but this will result in comparing the strings in a case insensitive form (and ignoring diacriticals) and also treating numbers as numbers. For example:

Expression Value Notes
“Hello” = “hello” 1 Equal since case is ignored
“Aardvark” < “Zebra” 1 Compare alphabetically
“AC 5” < “AC 10” 1 The 5 and 10 are treated as numbers and compared, so this is true

Mixing Strings and Numbers

In some cases, there is an explicit operation performed when mixing strings and numbers in an expression (for example, the repeated concatenation operator when you multiply a string by a number). In other cases, it isn’t explicitly defined - this also applies to some cases when both operators are strings. In both of these cases, the string(s) attempts to be converted into a number, and then the numeric operation is performed (and if not, the number is converted to a string and string based operations are performed).

Expression Value Notes
“3” × 5 “33333” Normal string repeat and concat
3 × “5” 15 The string 5 is converted into a numeric 5 and normal multiplication happens
“3” × “5” 15 Both strings values are converted to numbers and normal multiplication happens
“3 score” × “5 years” 15 Both strings values are converted to numbers (which ignores the first non-numeric part) and normal multiplication happens
5 + “score” “5 score”

This can be useful to convert between strings and numbers. Note that the first example we get string repeating - we may want to make sure that we use a number - we do this by multiplying the number 1 by the string. Similarly, we can force something to a string by using the format operator.

Expression Value Notes
1 × “5” 5 Force the second value to become a number
“%” % 5 “5” Format the value as a string
string(5) “5“ The string function will also convert a simple value into a string (but also performs special formatting on frames - see below)

Obviously, this isn’t all that useful for constant values, but when we have a variable of unknown type, these two idioms do the trick.

List Values

Besides being able to operate on single values, ELDARScript expressions include native support for immutable lists of values. For example, you can roll six dice and get list of the each of the results (6d6@). You can also create a list by having two or more values separated by commas. Lists can contain other values such as numbers, strings, or fields, but can not contain other lists - doing so will just “flatten” the list by appending them all together. The magic of ELDARScript is that you can then operate on all of the values in the list at the same time (in positional pairwise form between values in each list). Furthermore, if you operate on a list and a single value or a single value and a list, it treats that single value as if it were a list with as many elements as the list (there are excepts such formatting via the string % operator or joining via the x operator).

Expression Value Notes
1, 2, 3 (1, 2, 3) Concatenate each value into a list
(1, 2, 3), 4 (1, 2, 3, 4) Concatenate the 4 to the list
(1, 2, 3), (4, 5, 6) (1, 2, 3, 4, 5, 6) Concatenate the two lists together. Note that this does not create a “list of lists” - concatenation via the comma operator always flattens the list
(1, 2, 3) x 5 (5, 10, 15) Each element of the list is multiplied by five and produces a new list
(1, 2, 3) x “/“ “1/2/3” One of the exceptions - a list multiplied by a string will join all elements of list separated by the string and produce a new string. This is other half of taking a string and splitting it (division) by another string which produces a list.
10 + (1, 2, 3) (11, 12, 13) Ten is added to each element
(1, 2, 3) + (4, 5, 6) (5, 7, 9) Each element of the first list is added to the corresponding element in the second
(1, 2, 3) + (4, 5) (5, 7, 3) Each element of the first list is added to the corresponding element in the second, with the second being padded out with zeros.
(1, 2, 3) ≥ 2 (0, 1, 1) Returns a new list (of zeros and ones for false and true) after applying the “≥” operator to each element to find which ones are greater than or equal to 2
1 … 10 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) The … operator creates a list starting at a given value and extending to include the next
(1, 3 … 7, 9) (1, 3, 4, 5, 6, 7, 9) The … operator works inside a list
(1, 4) … (7, 10) (1, 4, 5, 6, 7, 10) The last value of the first list and the first value of the second list are used to determine the range to fill in
5 in (1, 3, 5, 7) 1 The in operator allows you to determine if a value is contained in a given list
(1, 3, 5, 7) in 4 … 8 (0, 0, 1, 1) Each value of the list on the left is tested to see if it is contained in the list on the right (created via the … operator)
× 3 : “b” (“b”, “b”, “b”) The × (count) : (value) construct is an easy way to make a list of multiple items
(“a”, × 3 : “b”, × 5 : “c”, “d”) (“a”, “b”, “b”, “b”, “c”, “c”, “c”, “c”, “c”, “d”) And combining it with other list concatenation makes an easy way to make a “weighted” list (where if you pick an item at random, you are more likely to get a “c” or “b” than either “a” or “d” in this example)

There are a couple of important things to note about list operations. First is that lists are immutable values (just like numbers and strings are - you can make a new one, but you can’t change an existing one). These operations will create a new list. Second is that there is a special case difference a single value and a list with multiple values - the former will be padded out with the first value repeated, the second will add zeros to the list as needed to make it the same length as the other list. Furthermore, a list with one element is identical to the value of that single element (and a single element is identical to a list with one element). Lists support a number special functions. Note that passing a list value as a parameter is the same as passing each element of the list as a separate parameter (i.e., function calls actually take a single list value which is automatically constructed via the comma operator between the parameters). A number of these functions are considered to be “numerically parameterized” functions where there is a number in the name of the parameter. This will be documented as, for example, “high#” which means that any integer can be used there (e.g., “high3”, “high1”) or even a parameter (“high#1”):

Expression Value Notes
high2(1,2,3) (2,3) high# - returns a list of the N largest elements in a list. Also, low#
nth2(5,1,4,7) 4 nth# - Find the Nth number when sorted from low to high
last3(1,3,4,6,7) (4,6,7) last# - Returns the last N items. If N is more than the number of items in the list, it just returns the entire list. Also, first#
gt3(5,1,4,7) (5, 4, 7) gt# - Returns the values in the list greater than the value N. Also, eq#, ne#, lt#, le#, ge#
sum(5,1,4,7) 17 Sum up all the values in the list
sort(5,1,4,7) (1, 4, 5, 7) Sort the values into a new list. Also rsort (for reversed sorted)
count5(5,1,5,7) 5 count# - Returns the number of elements equal to the value N. This is equivalent to sum(eq#(…))
reverse(5,1,4,7) (7,4,1,5) Reverse the list
allof(1,1,1) 1 Returns 1 if all of the values are “true” (non-zero); Also anyof which returns 1 if any of them are.

Let’s suppose you have a roll where you need to roll three six sided dice (in three colors) and only pass if the first one is 4 or more, the second is 5 or more, and the last is 6. This could be done like this:

1. @A Rd6.

2. @B Gd6.

3. @C Bd6.

4. if @A ≥ 4 @B ≥ 5 @C ≥ 6 then

5. “good”

6. else

7. “fail”

8. end

(The @A is a “local variable” - a place to save a result, covered in more detail in the Variables section. The ← is an assignment operator used to save a value there. if then else end are used to conditionally alter things, discussed in the Flow Control section below) We maintain each roll, and explicitly test each value vs a different threshold and if they are all true, we are good, otherwise we fail. If we wanted this with more than three dice tests, it would start to get verbose. We can instead do this:

1. @A (Rd6,Gd6,Bd6).

2. if allof(@A ≥ (4, 5, 6)) then

3. “good”

4. else

5. “fail”

6. end

In this case we keep the values as a list, and compare them vs their respective thresholds and we are good if all of those comparisons are true. Lists are especially useful when you don’t know how many dice will be rolled (where it is a parameter). For example, we roll a bunch of six sided dice and we are only good if the highest die is 4 or more than the lowest:

1. @A #1d6@.

2. if high1(@A) ≥ low1(@A) + 4 then

3. “good”

4. else

5. “fail”

6. end

Subscription

If you have a list of values, you can access individual values via subscripting

Expression Value Notes
(“a”,”b”,”c”)[1] “a” First value
(“a”,”b”,”c”)[2] “b“ Second value
(“a”,”b”,”c”)[3] “c” Third value
(“a”,”b”,”c”)[4] 0 Values beyond the end are zero
(“a”,”b”,”c”)[0] 0 As are values before the start
(“a”,”b”,”c”)[“hello”] 0 As are values that aren’t numeric subscripts
(“a”,”b”,”c”)[“2”] “b” But like other operators, if the string can be converted into a number, it will be

For convenience, strings can also be treated as if they were “records” and be subscripted. A record string is of the form “field1:value1|field2:value2|…” and provides a simple and easy way to keep track of multiple values (NB: Earlier versions of ELDARScript used an equal sign instead of a colon between field names and values - that syntax is still supported but not recommended):

Expression Value Notes
“a:Alpha|b:Bravo|c:Charlie|1:one”[“a”] “Alpha“ The value of the field named “a”
“a:Alpha|b:Bravo|c:Charlie|1:one”[1] “one” Subscripts can also be numeric, and match the corresponding field with that value for a name
“a:Alpha|b:Bravo|c:Charlie|1:one”[“d”] 0 Missing values are zero
“a:Alpha|b:Bravo|c:Charlie|1:one|:other”[“d”] “other” If no field name is specified, this entry is treated as the default value for all other entries

Since a list is immutable (can’t be changed, just like numbers and strings), ELDARScript has a way to manipulate individual elements which returns a new list with changes. It does not alter the original list:

Expression Value Notes
(“a”,”b”,”c”)[2] “beta” (“a”,”beta”,”c”) A new list with the second element set to the string “beta”
(“a”, “b”, “c”)[4] “d” (“a”, “b”, “c”, “d”) Setting the fourth element adds to the end
(“a”, “b”, “c”)[5] “d” (“a”, “b”, “c”, “d”) Since setting the fifth element is beyond the end of the list, the new value is just added to the list
(“a”, “b”, “c”)[0] “d” (“d”, “a”, “b”, “c”) Setting a value before the start of the list (zeroth element) will prepend it to the start of the list

Note that since all simple values are also list values with a single element, attempting to set a numeric subscript to a value will create a new list with the original value and the new value.

Expression Value Notes
“alpha”[2] “beta” (“alpha”,”beta”) Treats “alpha” as a list with one element, so this appends “beta” to the end of it
“alpha”[0] “beta” (“beta”,”alpha”) Treats “alpha” as a list with one element, so this prepends to the start of it
“alpha”[1] “beta” (“beta”) Treats “alpha” as a list with one element, which is replaced by “beta”, resulting in a list with a single element

This can also be applied to string based records:

Expression Value Notes
“a:Alpha|b:Bravo|c:Charlie|1:one”[“b”] “Beta” “a:Alpha|b:Beta|c:Charlie|1:one” Replace one of the field values
“a:Alpha|b:Bravo|c:Charlie|1:one”[“d”] “Delta” “a:Alpha|b:Bravo|c:Charlie|1:one|d:Delta” Adds a new field value
“a:Alpha|b:Bravo|c:Charlie|1:one”[“2”] “two” “a:Alpha|b:Bravo|c:Charlie|1:one|2:two“ Note the use of a string as a subscript here - this will add to the record…
“a:Alpha|b:Bravo|c:Charlie|1:one”[2] “two” (“a:Alpha|b:Bravo|c:Charlie|1:one“,”two”) …but in this case, we get a list with two elements (since this corresponds to the case above where we use list subscripting with numeric subscripts). Record manipulation only works with string subscripts

Frame Objects

While string based records provide an easy way to manage multiple associated values in a single entity, it is limited to simple string based keys (field names) and values. Frames are created by using an open curly brace (to indicate the start of the frame) followed by a field name (which is either a string or a numeric value), a colon, and then some sort of value (which can be any sort of expression, including another frame). Each field/value pair is separated by the vertical bar character, and a closing curly brace is used at the end:

1. {

2. “a”:“Alpha”

3. | “b”:“Beta”

4. | 1:“One”

5. | 2:“T” + “w” + “o”

6. | “Meaning of Life”:6 × 7

7. | “:default:” : “???”

8. | “:before:” : “Zero or negative”

9. | “:after:” : “Lots”

10.| “:length:” : 2

11.}

This example is similar to the record “a:Alpha|b:Beta|1:One” but with several difference. First, the curly braces on lines 1 and 10 are used to show it is a frame - though the same colon is used between keys and values and vertical bar is used between pairs of keys and values. Also note that the keys are either strings or numbers - strings need to be quoted (a string record treats all keys as strings). More importantly, each value is actually an expression - in line 5 we are concatenating strings together to form the string “Two” while in line 6 we value 6 x 7 to get 42. Next, we have a “special” string “:default:” that indicates what the default value is (for use when the subscript doesn’t correspond to one of the field name values). Finally, we've got three other special strings. “:before:” is used if the index is less than one, “:length:” is used to say how many integer subscripts we have on this frame (so we can pretend that this is a list). If the index is greater than “:length:” we return the value “:after:” for anything greater than that value. So, assuming we stored the above frame into a local variable @X:

Expression Value Notes
@X[1] “One” The string “One” (line 4)
@X[2] “Two“ The expression “T” + “w” + “o” (line 5)
@X[3] “Lots” Since 3 is more than the “:length:” of the frame (line 10), we return “:after:” (line 9). Note that if “:length:” were defined to be 4, this would instead use the “:default:” value and result in “???”
@X[0] “Zero or negative“ Since 0 is less one, we return “:before:” (line 8)
@X[“a”] “Alpha” Line 2
@X(“d”] “???” The “:default:” value (line 7)

You can also use the “:first:” key to define where the array starts (instead of 1 by default) so all values less than that are the “:before:” or “:default:” value. If no “:default:” value is defined, like other subscripting, the result will be zero. One very important difference between string based records and frames is that frames are mutable - as a result, the subscript setting actually does alter the frame (the now modified frame is also returned).

Frames as Lists

As documented above, frames can act like lists, or, more accurately, lists that can have other associated properties besides integer indexed values. This only happens if the frame includes the “:length:” key - without it, you’d still be able to access and change the data, but the frame wouldn’t look fully like a list.

Rational Values

Rational values are special forms of numbers that represent exact values when dividing an integer (or other rational values) by another integer (or rational value). For example, dividing one by two results in ½, a rational value (i.e., being expressed as a ratio of two whole numbers). While not commonly used in game, there are a number of cases where they are. The simplest example is when dividing up treasure (and then making change as needed). 59 gold divided by 4 people leaves a bit left over, which we can then, by keeping track of the result more accurately with rational number, come up with a more even distribution (by making change in silver and copper if needed).

elderscript_basic_values.txt · Last modified: 2019/07/12 11:14 by dicenomiwiki