Crash BASIC Variables and Types

Crash BASIC Data Types

This guide covers all data types in Crash BASIC, their operations, and how they convert between each other.

Type Overview

Type Declaration Description Example
INTEGER DIM x AS INTEGER 64-bit signed whole number 42, -100, 0
FLOAT DIM x AS FLOAT 64-bit floating point 3.14, -0.5, 1.0
STRING DIM x AS STRING Text (UTF-8) "hello", ""
COORDINATE DIM p AS COORDINATE N-dimensional point (10, 20), (1, 2, 3)
RANGE DIM r AS RANGE Rectangle from two coordinates (0,0)-(100,100)
ARRAY DIM arr(10) AS INTEGER Indexed collection arr(0), arr(5)
OBJECT DIM obj AS OBJECT Key-value dictionary obj("key")
MUTATION DIM m AS MUTATION List of 2D points MUTATION((0,0), (100,100))

INTEGER

What it is: A 64-bit signed whole number. Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.

What it's used for: Counters, indices, sprite IDs, screen coordinates, game scores, health points, or any value that doesn't need decimal precision.

Operations

DIM a AS INTEGER
DIM b AS INTEGER

a = 10
b = 3

' Arithmetic
PRINT a + b    ' Addition: 13
PRINT a - b    ' Subtraction: 7
PRINT a * b    ' Multiplication: 30
PRINT a \ b    ' Integer division: 3 (truncates toward zero)
PRINT a MOD b  ' Modulo (remainder): 1
PRINT -a       ' Negation: -10

Integer Division vs Float Division

PRINT 7 \ 2    ' Integer division: 3
PRINT 7 / 2    ' Float division: 3.5

Use \ when you need a whole number result. Use / when you need decimal precision.

Number Literal Formats

Whole numbers can be written in decimal, hexadecimal, or octal. Hex and
octal use the classic BASIC &H / &O prefixes (case-insensitive):

x = 255        ' decimal
x = &HFF       ' hexadecimal (also &hff) -> 255
x = &O377      ' octal (also &o377)       -> 255

These are most useful for packed color values in direct-color mode
(SET PALETTE OFF), where a color is a literal 0xAARRGGBB:

SET PALETTE OFF
COLOR &HFFFF8800   ' opaque orange: A=FF, R=FF, G=88, B=00

Note: The &H / &O prefixes take precedence over the & name
dereference operator (see Strings as Handles below). &HFF is always
the hex value 255 — to dereference a variable whose name starts with
H/O, the rest must contain a non-digit (e.g. &Handler is still a
deref because andler isn't all hex digits).

Truthiness

  • 0 is falsy
  • Any non-zero value (1, -1, 42, etc.) is truthy
DIM health AS INTEGER
health = 0
IF health THEN
    PRINT "Alive"
ELSE
    PRINT "Dead"  ' This prints
END IF

FLOAT

What it is: A 64-bit double-precision floating point number. Provides approximately 15-17 significant decimal digits.

What it's used for: Physics calculations, smooth movement, rotation angles, scaling factors, or any value requiring decimal precision.

Operations

DIM x AS FLOAT
DIM y AS FLOAT

x = 10.5
y = 3.0

PRINT x + y    ' 13.5
PRINT x - y    ' 7.5
PRINT x * y    ' 31.5
PRINT x / y    ' 3.5
PRINT -x       ' -10.5

Truthiness

  • 0.0 is falsy
  • Any non-zero value (0.001, -3.14, etc.) is truthy

Precision Note

Floating point math can have small rounding errors:

' Don't compare floats with exact equality
IF x > 3.13 AND x < 3.15 THEN  ' Good
IF x = 3.14 THEN               ' Risky

STRING

What it is: A sequence of UTF-8 characters. Can be empty ("") or contain any text.

What it's used for: Player names, messages, file paths, dialog text, state names, tags.

Operations

DIM s1 AS STRING
DIM s2 AS STRING

s1 = "Hello"
s2 = "World"

' Concatenation
PRINT s1 + " " + s2    ' "Hello World"

' Repetition (works in either order, like Python)
PRINT "ab" * 3         ' "ababab"
PRINT 3 * "xy"         ' "xyxyxy"
PRINT "=" * 20         ' a 20-character divider rule
PRINT "x" * 0          ' ""  (zero or negative count yields an empty string)
PRINT "x" * 2.6        ' "xx" (a float count is truncated toward zero)

' Length
PRINT LEN(s1)          ' 5
PRINT LEN("")          ' 0

Common String Functions

' Substrings
PRINT LEFT$("Hello", 2)      ' "He"
PRINT RIGHT$("Hello", 2)     ' "lo"
PRINT MID$("Hello", 2, 3)    ' "ell" (start at 2, take 3)

' Case conversion
PRINT UCASE$("hello")        ' "HELLO"
PRINT LCASE$("HELLO")        ' "hello"

' Trimming
PRINT TRIM$("  hi  ")        ' "hi"

' Search
PRINT INSTR("Hello", "ll")   ' 3 (1-based position)

' Replace
PRINT REPLACE$("hello", "l", "L")  ' "heLLo"

Escape Sequences

Use "" to include a quote inside a string:

PRINT "He said ""Hello"""    ' He said "Hello"

Truthiness

Strings don't have direct truthiness. Use LEN() to check if empty:

DIM name AS STRING
name = ""
IF LEN(name) > 0 THEN
    PRINT "Has name"
ELSE
    PRINT "No name"  ' This prints
END IF

Strings as Handles (Dereferencing with `&`)

A string can hold the name of a variable, function, or subroutine, and the & prefix operator looks up the thing with that name and operates on it. The same prefix works in reads, writes, and calls — think of it as "follow the pointer stored in this string."

Calling functions and subs by name

FUNCTION Add(a, b)
    Add = a + b
END FUNCTION

FUNCTION Multiply(a, b)
    Multiply = a * b
END FUNCTION

op$ = "ADD"
PRINT &op$(3, 4)         ' 7   — calls Add

op$ = "MULTIPLY"
PRINT &op$(3, 4)         ' 12  — now calls Multiply

Subs work the same way — the & prefix is valid anywhere a call target appears:

SUB OnIdle()
    PRINT "idle"
END SUB

SUB OnActive()
    PRINT "active"
END SUB

handler$ = "ONIDLE"
CALL &handler$()
handler$ = "ONACTIVE"
CALL &handler$()

Names are case-insensitive (internally uppercased), and namespaced paths are allowed:

handler$ = "Player.Attack"
CALL &handler$(25)       ' calls Player.Attack(25)

Reading and writing variables by name

score = 42
ref$ = "SCORE"
PRINT &ref$              ' 42   — read
&ref$ = 100              ' write
PRINT score              ' 100

You can build variable names at runtime — useful for indexed-by-name patterns:

FOR i = 1 TO 3
    ref$ = "VAL" + STR$(i)
    &ref$ = i * 10
NEXT i
' Now VAL1=10, VAL2=20, VAL3=30

Naming a field or an array element

A handle isn't limited to a plain variable. It can name a field down a chain of
objects, an element of an array, or both at once — and it reads and writes the
same place either way:

TYPE Unit
    hp AS INTEGER
END TYPE
TYPE Squad
    members(0 TO 2) AS Unit
    leader AS Unit
END TYPE

DIM squad AS Squad
DIM grid(4, 4) AS INTEGER

ref$ = "squad.leader.hp"       ' a field down a path
&ref$ = 100

ref$ = "squad.members(1).hp"   ' an element partway down a path
&ref$ = 50

ref$ = "grid(2, 3)"            ' any number of dimensions
&ref$ = 7

The subscript may be a literal or the name of a variable holding one, so a
handle can be assembled while the program runs:

DIM enemies(8) AS Unit
DIM i AS INTEGER

FOR i = 0 TO 7
    ref$ = "enemies(" + STR$(i) + ").hp"
    &ref$ = 100
NEXT i

' Or let the path name the variable and resolve it when used:
i = 3
ref$ = "enemies(i).hp"
PRINT &ref$                    ' 100

A subscript is resolved when the deref happens, not when the string is
built, so "enemies(i).hp" follows i — read it while i is 3 and you get
element 3.

Common use cases

State dispatcher — swap the active handler by name:

state$ = "IDLE"

SUB OnIdle()    ... END SUB
SUB OnWalking() ... END SUB
SUB OnAttack()  ... END SUB

' In the game loop:
CALL &("ON" + state$)()

Event handlers keyed by string — route by message name:

SUB HandleJump()    ... END SUB
SUB HandleFire()    ... END SUB

input$ = GetInput()
CALL &("HANDLE" + input$)()

Runtime-swappable behavior — no IF ladder needed:

currentAttack$ = "SLASH"
SUB Slash()    ... END SUB
SUB Stab()     ... END SUB
SUB Bash()     ... END SUB

' Later:
currentAttack$ = "BASH"
CALL &currentAttack$()

Where string expressions work without `&`

Several statements accept a string expression as a name by design — you don't need & for these because the statement itself already knows to look the name up:

Statement String expression What it looks up
SET STATE expr state name SUB of that name
EVENT expr event name fires event listeners by name
DELAY UNTIL expr event name waits for that event
STOP TIMER expr / STOP THREAD expr / STOP RENDER expr callback id the running callback with that id
CALL ... AS expr id names the registered callback

Use & when you're invoking a function or sub through its name, or reading/writing a variable by name. Use the bare string expression where a statement documents it accepting one.


COORDINATE

What it is: An N-dimensional point with components stored as floats. Supports 2D, 3D, and 4D coordinates.

What it's used for: Positions, velocities, directions, offsets. Essential for sprite movement and physics.

Operations

DIM pos AS COORDINATE
DIM vel AS COORDINATE

pos = (100, 200)
vel = (5, -3)

' Addition (move by velocity)
pos = pos + vel            ' (105, 197)

' Subtraction (get difference)
DIM diff AS COORDINATE
diff = (10, 20) - (3, 8)   ' (7, 12)

' Scaling (multiply by number)
vel = vel * 2              ' (10, -6)
pos = 0.5 * pos            ' (52.5, 98.5)

' Scaling down (divide by number) — the mirror of multiplying
vel = vel / 2              ' (5, -3)
DIM mid AS COORDINATE
mid = ((0, 0) + (4, 6)) / 2   ' (2, 3) — midpoint

' Negation
vel = -vel                 ' (-10, 6)

Multiplying and dividing by a number both work component by component and give
you back a COORDINATE, so v * 2 / 2 is v again. Division divides each
component rather than scaling by the reciprocal, so exact cases stay exact:
(3, 6, 6) / 3 is (1, 2, 2), not (0.999…, 2, 2).

Dividing by zero is a runtime error, the same as it is for numbers.

Dividing by a coordinate is a different question, and gives a number. Where
a coordinate meets arithmetic that has no component-wise reading, it stands for
its length (MAG). So dividing one coordinate by another compares their
lengths, and dividing a number by a coordinate divides by one:

p = (3, 4)                 ' length 5
q = (6, 8)                 ' length 10

PRINT q / p                ' 2    — q is twice as long as p
PRINT 2 / p                ' 0.4  — 2 / 5
PRINT q / 2                ' (3, 4) — a COORDINATE, per above

This is the same rule that lets IF MAG(vel) > 10 be written as a plain
comparison. If you want lengths explicitly, say so with MAG().

Component Access

Access individual components with .X, .Y, .Z, .W:

pos = (100, 200)
PRINT pos.X                ' 100
PRINT pos.Y                ' 200

' Works with 3D and 4D coordinates
pos3d = (10, 20, 30)
PRINT pos3d.Z              ' 30

pos4d = (1, 2, 3, 4)
PRINT pos4d.W              ' 4

' Missing dimensions return 0
pos2d = (10, 20)
PRINT pos2d.Z              ' 0 (not defined, defaults to 0)

Distance Between Points

Use MAG() with coordinate subtraction to compute distance:

a = (10, 20)
b = (40, 60)

' MAG(a - b) = Euclidean distance
dist = MAG(a - b)

' Works with any dimensionality
p1 = (1, 2, 3)
p2 = (4, 6, 3)
dist3d = MAG(p1 - p2)     ' sqrt(9 + 16 + 0) = 5

Interpolation

Use LERP() for smooth coordinate interpolation:

start = (0, 0)
dest = (100, 200)
current = LERP(start, dest, 0.5)  ' (50, 100)

Comparison

Coordinates compare component-wise:

IF pos = (100, 200) THEN
    PRINT "At target"
END IF

Use Cases

' Sprite movement
DIM playerPos AS COORDINATE
DIM playerVel AS COORDINATE
playerPos = (160, 120)
playerVel = (0, 0)

' In game loop:
playerPos = playerPos + playerVel

' Direction vectors
DIM direction AS COORDINATE
direction = (1, 0)    ' Right
direction = (0, -1)   ' Up
direction = (-1, 1)   ' Down-left diagonal

' Distance check
IF MAG(playerPos - enemyPos) < 32 THEN
    PRINT "Too close!"
END IF

' Smooth camera follow
camPos = LERP(camPos, playerPos, 0.1)

RANGE

What it is: A rectangle defined by two corner coordinates.

What it's used for: Defining screen regions, clipping areas, texture source rectangles.

Declaration

DIM area AS RANGE

' Two coordinates separated by dash
area = (0, 0)-(100, 100)

' Top-left to bottom-right
area = (10, 20)-(50, 80)

Use Cases

Ranges are primarily used in graphics operations that require rectangular regions:

' Line drawing uses range syntax
LINE (0, 0)-(100, 100), 15

' Box drawing
LINE (10, 10)-(50, 50), 12, BF

ARRAY

What it is: An indexed collection of values of the same type. Always zero-indexed. A reference type (see below).

What it's used for: Lists of enemies, inventory items, high scores, tile maps, any collection of similar data.

Declaration

' DIM arr(n) creates n elements with indices 0 through n-1
DIM scores(5) AS INTEGER    ' 5 elements: indices 0, 1, 2, 3, 4

' Empty array (useful with PUSH/QUEUE)
DIM stack(0) AS INTEGER     ' 0 elements (empty array)

' 2D array
DIM grid(5, 3) AS INTEGER   ' 5x3 = 15 elements

' String array
DIM names(10) AS STRING     ' 10 elements: indices 0 through 9

' Range syntax with a ZERO lower bound: DIM arr(0 TO end) creates (end + 1) elements
DIM items(0 TO 4) AS INTEGER    ' 5 elements: indices 0, 1, 2, 3, 4

Arrays are zero-based — the lower bound must be 0. A non-zero lower
bound such as DIM items(1 TO 5) is a loud runtime error, not a
silent re-base. (Indexing never applied a lower-bound offset, so a
non-zero base would have left the top index out of bounds and index 0
a phantom — so it's rejected outright.) Use DIM arr(N) for N elements
indexed 0..N-1, or DIM arr(0 TO N) if you want to spell out the
upper bound.

Access

DIM arr(5) AS INTEGER

' Set values
arr(0) = 100
arr(1) = 200
arr(4) = 400    ' Last valid index

' Read values
PRINT arr(0)    ' 100

' In loops
FOR i = 0 TO 4    ' 0 to n-1
    arr(i) = i * 10
NEXT i

Bounds

DIM arr(10) AS INTEGER

PRINT LBOUND(arr)   ' 0 (always — arrays are zero-based)
PRINT UBOUND(arr)   ' 9 (highest valid index = n-1)

' With an explicit zero-lower-bound range
DIM items(0 TO 4) AS INTEGER
PRINT LBOUND(items) ' 0
PRINT UBOUND(items) ' 4

LBOUND is always 0; only UBOUND carries information.

Dynamic Operations

DIM items(3) AS STRING
items(0) = "sword"
items(1) = "shield"
items(2) = "potion"

' Remove and return last element
lastItem = POP(items)        ' Returns "potion", array now has 2 elements

' Remove and return first element
firstItem = DEQUEUE(items)   ' Returns "sword", array now has 1 element

Resizing

DIM data(10) AS INTEGER
' ... use it ...

' Resize (preserves existing data)
REDIM data(20)

Arrays Are Reference Types

An array is a reference, not a value. Assigning one array to another with
= makes a second name for the same array — they share storage. Change one,
you change both:

DIM a(3) AS INTEGER
a(0) = 10
b = a              ' b is an ALIAS of a, NOT a copy
b(0) = 99
PRINT a(0)         ' 99 — same underlying array

To get an independent copy, use CLONE. It's a deep copy — nested arrays
and objects are duplicated all the way down:

b = CLONE(a)       ' a real, separate array
b(0) = 99
PRINT a(0)         ' 10 — untouched

This reference behavior also governs how arrays move through parameters:

Passing Element writes through the parameter Reassigning the parameter
BYREF (default) Reach the caller's array (shared) Reaches the caller (BYREF)
BYVAL Do not reach the caller — the callee got a deep copy Local only

So SUB Fill(arr) (BYREF) mutates the caller's array, while
SUB Fill(BYVAL arr) works on its own private copy.

Note the SUB: this is a subroutine's story. A FUNCTION parameter is always
BYVAL, so an array handed to one is always a private copy — BYREF is not
allowed there at all. (See the variable-scoping guide for the full
parameter-passing story.)

Sorting an Array

BEGIN SORT sorts a one-dimensional array in place with a comparator block:

BEGIN SORT scores AS a, b
  RETURN a - b          ' ascending; RETURN b - a for descending
END SORT

You name the two elements being compared and RETURN a three-way sign:
negative if a sorts before b, 0 if equal, positive if after. It's stable,
and it sorts the array itself (no copy returned). Because a 1-D slice is a
view that shares the parent's storage (see below), you can sort one row of a
grid in place: BEGIN SORT grid(0) AS a, b ... END SORT.

Partial Indexing — Slicing Higher-D Arrays

Want a 2D plane out of a 3D array? Pin the outer dimension and use it like a normal 2D array — no copy, no reshape.

When you index a higher-dimensional array with fewer indices than its rank, you get back a shared-storage view of the remaining dimensions:

DIM Rooms(2, 9, 9) AS INTEGER    ' shape [3, 10, 10] — 3 rooms, each 10x10

' Pin the outer dim: get back a 2D view that aliases Rooms[1, :, :]
room1 = Rooms(1)
PRINT room1(4, 3)                ' reads Rooms(1, 4, 3)

' Writes flow through to the parent — these two are equivalent:
Rooms(0)(3, 2) = 4               ' partial-index write
Rooms(0, 3, 2) = 4               ' full-index write (same cell, same storage)

Key rules:

Indices given Indices on array Result
Fewer than rank 3D array, 1 index 2D view (rows × cols)
Fewer than rank 3D array, 2 indices 1D view (cols)
Equal to rank Any array The element value
Out of bounds Any array Halts loudly with the shape

Views share storage. A view is a window onto the parent. Mutating through the view mutates the parent. Mutating the parent shows through the view. Pointers, not copies.

Views can be assigned to variables. level = Rooms(currentLevel) snapshots the view at one outer index. Later writes to level(...) still land on the parent — the variable holds the view, not a fresh array.

Views can't be resized. PUSH/POP/DEQUEUE on a view is a no-op — the parent's shape is fixed in the outer dimensions you didn't pin. Want a slice you can grow or mutate without touching the parent? CLONE it first (row = CLONE(Rooms(1))) to get an independent array, or operate on the parent directly.

A 1-D slice can be sorted in place. BEGIN SORT Rooms(1) AS a, b ... END SORT sorts that row inside the parent, since the slice shares storage. (The slice must be 1-D — slicing a 3-D array down to a 2-D view and handing that to BEGIN SORT is an error.)

Where this shines:

  • TILEMAP "lvl" TILES 0 FROM Rooms(level) — feed one 2D slice of a 3D room atlas to the renderer
  • Iterating outer dim: FOR i = 0 TO 2 ... process(Rooms(i)) ... NEXT i
  • Passing one slice of a multi-player state grid to a per-player handler

OBJECT

What it is: A key-value dictionary where keys are strings and values can be any type.

What it's used for: Structured data, configuration, JSON-like data, polygon vertices.

Declaration and Use

Objects support two access syntaxes:

DIM player AS OBJECT

' Dot notation (keys become uppercase internally)
player.name = "Hero"
player.health = 100

PRINT player.NAME      ' "Hero"
PRINT player.HEALTH    ' 100

' Parentheses notation (keys are uppercase)
PRINT player("NAME")   ' "Hero"

JSON Serialization

' Convert to JSON string
DIM jsonStr AS STRING
jsonStr = SERIALIZE$(42)           ' "42"
jsonStr = SERIALIZE$("hello")      ' "\"hello\""

' Parse JSON back to value
DIM data AS OBJECT
data = DESERIALIZE$("{""name"":""test""}")
PRINT data.NAME   ' "test"

' Parse JSON array
jsonArr = DESERIALIZE$("[1,2,3]")
PRINT jsonArr(0)  ' 1

MUTATION

What it is: A list of 2D coordinate points that can be animated with behaviors.

What it's used for: Defining paths for sprite behaviors, animation waypoints, multi-point effects.

Declaration and Use

DIM m AS MUTATION

' Create with literal syntax - list of coordinate pairs
m = MUTATION((100, 200), (300, 400), (500, 600))

' Query number of points
PRINT MUTATIONCOUNT(m)        ' 3

' Query point coordinates (0-indexed)
PRINT MUTATIONPOINTX(m, 0)    ' 100
PRINT MUTATIONPOINTY(m, 0)    ' 200
PRINT MUTATIONPOINTX(m, 1)    ' 300

' Modify a point
SET MUTATIONPOINT m, 1, 999, 888
PRINT MUTATIONPOINTX(m, 1)    ' 999

With Expressions

Points can use expressions:

DIM x AS INTEGER, y AS INTEGER
x = 50
y = 75

DIM path AS MUTATION
path = MUTATION((x, y), (x * 2, y * 2), (x + 100, y + 100))

Use with Behaviors

Mutations can be registered with the behavior engine to receive coordinate updates during animations. See the behavior system documentation for details on how mutations integrate with sprite animations.


Type Coercion

Crash BASIC automatically converts types in many situations.

Addition (+)

Left Right Result Meaning
Integer Integer Integer Numeric addition
Integer Float Float Numeric addition (promotes to float)
Float Float Float Numeric addition
String Any String Concatenation (right converted to string)
Any String String Concatenation (left converted to string)
Coordinate Coordinate Coordinate Component-wise addition
Mutation Mutation Mutation Combines point lists

Subtraction (-)

Left Right Result Meaning
Integer Integer Integer Numeric subtraction
Integer Float Float Numeric subtraction (promotes to float)
Float Float Float Numeric subtraction
Coordinate Coordinate Coordinate Component-wise subtraction

Multiplication (*)

Left Right Result Meaning
Integer Integer Integer Numeric multiplication
Integer Float Float Numeric multiplication (promotes to float)
Float Float Float Numeric multiplication
String Integer String Repeat string N times
Integer String String Repeat string N times
String Float String Repeat string N times (truncates float)
Coordinate Number Coordinate Scale all components
Number Coordinate Coordinate Scale all components

String repetition clamps the count at zero, so a zero or negative count yields an empty string, and a float count is truncated toward zero ("x" * 2.6 is "xx").

Division (/)

Left Right Result Meaning
Any Any Float Always returns float (converts both to float)

Division by zero returns NaN.

Integer Division (\\)

Left Right Result Meaning
Any Any Integer Both converted to integer, then divided

Integer division by zero returns 0.

Modulo (MOD)

Left Right Result Meaning
Integer Integer Integer Integer remainder
Any Any Float Float remainder (if either is non-integer)

Negation (-)

Operand Result Meaning
Integer Integer Negate value
Float Float Negate value
Coordinate Coordinate Negate all components

Comparison (=, <>, <, >, <=, >=)

Left Right Behavior
Integer Integer Direct comparison
Float Float Direct comparison
Integer Float Integer promoted to float
String String Lexicographic (case-sensitive)
String Number String parsed as number, then compared
Coordinate Coordinate Component-wise equality only

All comparisons return INTEGER (1 for true, 0 for false).

Logical (AND, OR, NOT)

Operation Behavior
A AND B Bitwise AND of integer values
A OR B Bitwise OR of integer values
NOT A Bitwise NOT of integer value

Non-integers are converted to integers first.

Quick Reference

Operation Result Type Example
Integer + Integer Integer 5 + 3 → 8
Integer + Float Float 5 + 2.5 → 7.5
Float + Float Float 1.5 + 2.5 → 4.0
Integer / Integer Float 7 / 2 → 3.5
Integer \ Integer Integer 7 \ 2 → 3
String * Integer String "ab" * 3 → "ababab"
String + Integer String "x" + 5 → "x5"
Coordinate + Coordinate Coordinate (1,2) + (3,4) → (4,6)
Coordinate * Number Coordinate (10,20) * 2 → (20,40)

Comparison Coercion

Integers and floats compare naturally:

IF 5 = 5.0 THEN PRINT "Equal"     ' True
IF 5 < 5.5 THEN PRINT "Less"      ' True
IF 6 > 5.5 THEN PRINT "Greater"   ' True

Strings compare lexicographically (case-sensitive):

IF "abc" < "abd" THEN PRINT "Yes"   ' True
IF "abc" = "ABC" THEN PRINT "Yes"   ' False (case matters)

Explicit Conversion

Function From To Example
VAL() String Number VAL("42") → 42
STR$() Number String STR$(42) → "42"
INT() Float Integer INT(3.7) → 3 (floor)
FIX() Float Integer FIX(-3.7) → -3 (toward zero)
CHR$() Integer String CHR$(65) → "A"
ASC() String Integer ASC("A") → 65

INT vs FIX

Both convert floats to integers, but differ for negative numbers:

PRINT INT(3.7)     ' 3  (floor)
PRINT INT(-3.7)    ' -4 (floor - toward negative infinity)

PRINT FIX(3.7)     ' 3  (truncate)
PRINT FIX(-3.7)    ' -3 (truncate - toward zero)

Boolean Logic

Crash BASIC uses integers for boolean values:

  • TRUE = 1
  • FALSE = 0

Logical Operators

DIM a AS INTEGER
DIM b AS INTEGER

a = 1
b = 0

PRINT a AND b    ' 0 (false)
PRINT a OR b     ' 1 (true)
PRINT NOT a      ' 0 (false)
PRINT NOT b      ' -1 (true, bitwise NOT)

Comparison Results

Comparisons return 0 (false) or 1 (true):

DIM result AS INTEGER
result = (5 > 3)    ' 1
result = (5 < 3)    ' 0

' Can use directly in expressions
health = health - (10 * (isHit = TRUE))

Summary Table

Type Default Value Truthy When Converts To
INTEGER 0 Non-zero Float (auto), String (STR$)
FLOAT 0.0 Non-zero Integer (INT/FIX), String (STR$)
STRING "" n/a (use LEN) Number (VAL)
COORDINATE (0, 0) n/a Components via .X, .Y, .Z, .W
RANGE n/a n/a n/a
ARRAY Empty n/a JSON (SERIALIZE$)
OBJECT Empty n/a JSON (SERIALIZE$)
MUTATION Empty n/a n/a