Chapter 11: More Than One of Something

Pop quiz! You're making a space shooter. You need to track 50 enemies. How do you store their X positions?

enemy1X = 100
enemy2X = 150
enemy3X = 200
enemy4X = 250
' ... 46 more variables ...
enemy50X = 900

Ugh. That's 50 separate variables! And what if you want to move them all? You'd need 50 separate lines of code. What if you want 100 enemies? 1000?

There HAS to be a better way. And there is: ARRAYS!


What's an Array?

An array is a single variable that holds MANY values. Instead of 50 separate variables, you have ONE variable with 50 "slots" (called elements).

DIM enemyX(50) AS INTEGER    ' 50 slots: indices 0 through 49

Now you can access any enemy's X position using a number (called an index):

enemyX(0) = 100     ' First enemy
enemyX(1) = 150     ' Second enemy
enemyX(49) = 900    ' 50th enemy

PRINT enemyX(0)     ' Prints: 100

The magic? You can use a VARIABLE as the index:

FOR i = 0 TO 49
    enemyX(i) = i * 20    ' Set all 50 positions at once!
NEXT i

ONE loop handles ALL 50 enemies. That's the power of arrays!


Creating Arrays: DIM

Use DIM to create an array:

DIM scores(10) AS INTEGER      ' 10 numbers (indices 0-9)
DIM names$(20) AS STRING       ' 20 strings (indices 0-19)
DIM values(100)                ' 100 numbers (type inferred)

The number in parentheses is the size—how many elements you want. Arrays start at index 0 by default, so DIM scores(10) creates 10 elements with indices: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.

Why Start at 0?

Most programming languages start arrays at 0. It's a computer science thing—memory addresses work better that way. You'll get used to it!

If it really bugs you, you can change it:

OPTION BASE 1
DIM scores(10) AS INTEGER    ' 10 elements with indices 1 through 10

But honestly? Just stick with 0. It's the standard, and most code examples you'll find use 0-based arrays.


Array Types

Arrays can hold any type of data:

DIM numbers(100) AS INTEGER    ' 100 whole numbers
DIM decimals(50) AS SINGLE     ' 50 decimal numbers
DIM words$(30) AS STRING       ' 30 text strings
DIM flags(10) AS BOOLEAN       ' 10 true/false values

You can even have arrays of custom types (we'll cover those later)!


Reading and Writing Array Elements

Access elements using their index in parentheses:

DIM scores(5) AS INTEGER

' Writing to the array
scores(0) = 100
scores(1) = 250
scores(2) = 175
scores(3) = 300
scores(4) = 225

' Reading from the array
PRINT "First score: "; scores(0)
PRINT "Last score: "; scores(4)

' Using variables as indices
FOR i = 0 TO 4
    PRINT "Score "; i; ": "; scores(i)
NEXT i

Watch Out for Out-of-Bounds!

If you try to access an index that doesn't exist, bad things happen:

DIM numbers(5) AS INTEGER    ' 5 elements, valid indices: 0-4

numbers(5) = 100    ' ERROR! Index 5 doesn't exist!
numbers(-1) = 50    ' ERROR! Negative index!

Always make sure your index is within the array's bounds!


UBOUND and LBOUND: Knowing Your Limits

How do you know how big an array is? Use UBOUND (upper bound) and LBOUND (lower bound):

DIM scores(10) AS INTEGER

PRINT UBOUND(scores)    ' Prints: 9 (highest valid index)
PRINT LBOUND(scores)    ' Prints: 0 (lowest valid index)

' Loop through entire array safely
FOR i = LBOUND(scores) TO UBOUND(scores)
    scores(i) = i * 10
NEXT i

This is especially useful when you don't know the array size ahead of time (like when loading data from a file).


Multi-Dimensional Arrays: Grids and Beyond

What if you need a grid? Like a game board, a tilemap, or a spreadsheet? That's where multi-dimensional arrays come in!

2D Arrays (Rows and Columns)

DIM grid(10, 10) AS INTEGER    ' 10x10 grid (100 elements!)

Now you have TWO indices—think of it as (row, column) or (x, y):

grid(0, 0) = 1    ' Top-left corner
grid(9, 9) = 2    ' Bottom-right corner
grid(5, 3) = 3    ' Row 5, Column 3

Visualizing a 2D Array:

         Column: 0   1   2   3   4
                +---+---+---+---+---+
    Row 0       |   |   |   |   |   |
                +---+---+---+---+---+
    Row 1       |   |   |   |   |   |
                +---+---+---+---+---+
    Row 2       |   | X |   |   |   |  <- grid(2, 1) = X
                +---+---+---+---+---+
    Row 3       |   |   |   |   |   |
                +---+---+---+---+---+

Looping Through 2D Arrays

Use nested loops:

DIM board(8, 8) AS INTEGER    ' 8x8 chess board

' Fill the board
FOR row = 0 TO 7
    FOR col = 0 TO 7
        board(row, col) = (row + col) MOD 2    ' Checkerboard pattern
    NEXT col
NEXT row

' Display the board
FOR row = 0 TO 7
    FOR col = 0 TO 7
        IF board(row, col) = 0 THEN
            PRINT ".";
        ELSE
            PRINT "#";
        END IF
    NEXT col
    PRINT    ' New line after each row
NEXT row

3D Arrays (and Beyond!)

Need even more dimensions? Go for it:

DIM cube(10, 10, 10) AS INTEGER    ' 10x10x10 cube (1000 elements!)
cube(5, 3, 2) = 42                 ' Access element at x=5, y=3, z=2

You can have as many dimensions as you need, but honestly, 2D is the most common for games. 3D arrays are rare—usually for voxel games or complex simulations.


Real Example: A Tile Map

Here's how you'd use a 2D array for a game level:

SCREEN 13

' Define our tile map (0=empty, 1=wall, 2=player start, 3=goal)
' ASARRAY creates the array for us - no DIM needed!

DATA "level1"
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1
    1, 2, 0, 0, 0, 0, 0, 0, 0, 1
    1, 0, 1, 1, 1, 1, 1, 1, 0, 1
    1, 0, 1, 0, 0, 0, 0, 0, 0, 1
    1, 0, 1, 0, 1, 1, 1, 1, 0, 1
    1, 0, 0, 0, 1, 3, 0, 1, 0, 1
    1, 1, 1, 0, 1, 1, 0, 1, 0, 1
    1, 0, 0, 0, 0, 0, 0, 1, 0, 1
    1, 0, 1, 1, 1, 1, 1, 1, 0, 1
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1
END DATA

' Load the level into the array
level = ASARRAY("level1", 10, 10)

' Find the player start position
playerX = 0
playerY = 0
FOR row = 0 TO 9
    FOR col = 0 TO 9
        IF level(row, col) = 2 THEN
            playerY = row
            playerX = col
            level(row, col) = 0    ' Clear the start marker
        END IF
    NEXT col
NEXT row

' Game loop
DO
    ' Input
    oldX = playerX
    oldY = playerY

    IF KEYDOWN("UP") THEN playerY = playerY - 1
    IF KEYDOWN("DOWN") THEN playerY = playerY + 1
    IF KEYDOWN("LEFT") THEN playerX = playerX - 1
    IF KEYDOWN("RIGHT") THEN playerX = playerX + 1

    ' Collision with walls
    IF level(playerY, playerX) = 1 THEN
        playerX = oldX
        playerY = oldY
    END IF

    ' Check for goal
    IF level(playerY, playerX) = 3 THEN
        LOCATE 12, 12
        PRINT "YOU WIN!"
    END IF

    ' Draw
    BEGIN FRAME
    CLS

    FOR row = 0 TO 9
        FOR col = 0 TO 9
            x = col * 16
            y = row * 16

            IF level(row, col) = 1 THEN
                LINE (x, y)-(x + 15, y + 15), 8, BF    ' Wall (gray)
            ELSEIF level(row, col) = 3 THEN
                LINE (x, y)-(x + 15, y + 15), 10, BF   ' Goal (green)
            END IF
        NEXT col
    NEXT row

    ' Draw player
    CIRCLE (playerX * 16 + 8, playerY * 16 + 8), 6, 14, 14

    END FRAME
    DELAY 100
LOOP

Resizing Arrays: REDIM

Sometimes you don't know how big an array needs to be until your program is running. REDIM lets you resize:

DIM scores(10) AS INTEGER    ' Start with 10 slots

' Later, need more space!
REDIM scores(20)             ' Now 20 slots (but data is LOST!)

Warning: REDIM erases all the data in the array!

REDIM PRESERVE: Keep Your Data

If you want to resize AND keep the existing data:

DIM scores(10) AS INTEGER
scores(0) = 100
scores(1) = 200

REDIM PRESERVE scores(20)    ' Now 20 slots, but 100 and 200 are still there!

PRINT scores(0)    ' Still 100!
PRINT scores(1)    ' Still 200!

PRESERVE only works when making the array LARGER. If you shrink it, data at the end is lost.


ERASE: Clearing Arrays

When you're done with an array and want to free the memory:

DIM bigArray(10000) AS INTEGER

' ... use the array ...

ERASE bigArray    ' Array is gone, memory freed

You can erase multiple arrays at once:

ERASE enemies, bullets, particles

Stacks: Last In, First Out (LIFO)

A stack is like a stack of plates—you can only add or remove from the TOP.

  • PUSH adds an item to the top
  • POP removes and returns the item from the top
DIM history(100) AS STRING

' Add items to the stack
PUSH history, "Move left"
PUSH history, "Jump"
PUSH history, "Attack"

' Remove items (in REVERSE order!)
action$ = POP(history)    ' Returns "Attack" (last one added)
action$ = POP(history)    ' Returns "Jump"
action$ = POP(history)    ' Returns "Move left"

Use stacks for:

  • Undo systems (most recent action first)
  • Back buttons (return to previous screen)
  • Expression evaluation (calculators)
  • Depth-first search algorithms

Real Example: Undo System

DIM undoStack(50) AS STRING

SUB SaveState(description$)
    PUSH undoStack, description$
    PRINT "Saved: "; description$
END SUB

SUB Undo()
    IF UBOUND(undoStack) >= 0 THEN
        action$ = POP(undoStack)
        PRINT "Undoing: "; action$
        ' ... actually reverse the action ...
    ELSE
        PRINT "Nothing to undo!"
    END IF
END SUB

' Usage
CALL SaveState("Placed block at 5,3")
CALL SaveState("Deleted enemy")
CALL SaveState("Changed color to red")

CALL Undo()    ' "Undoing: Changed color to red"
CALL Undo()    ' "Undoing: Deleted enemy"

Queues: First In, First Out (FIFO)

A queue is like a line at a store—first person in line gets served first.

  • QUEUE adds an item to the END
  • DEQUEUE removes and returns the item from the FRONT
DIM tasks(100) AS STRING

' Add items to the queue
QUEUE tasks, "Load level"
QUEUE tasks, "Spawn enemies"
QUEUE tasks, "Start music"

' Process items (in ORDER they were added!)
task$ = DEQUEUE(tasks)    ' Returns "Load level" (first one added)
task$ = DEQUEUE(tasks)    ' Returns "Spawn enemies"
task$ = DEQUEUE(tasks)    ' Returns "Start music"

Use queues for:

  • Task processing (do things in order)
  • Message systems (process messages as they arrive)
  • Event handling (handle events in order)
  • Turn-based games (whose turn is it?)

Real Example: Turn System

DIM turnOrder(10) AS STRING

' Set up turn order
QUEUE turnOrder, "Player"
QUEUE turnOrder, "Goblin"
QUEUE turnOrder, "Wizard"
QUEUE turnOrder, "Dragon"

' Process turns
DO WHILE UBOUND(turnOrder) >= 0
    currentTurn$ = DEQUEUE(turnOrder)
    PRINT currentTurn$; "'s turn!"

    ' After their turn, they go back to the end of the line
    QUEUE turnOrder, currentTurn$

    PRINT "Press any key for next turn..."
    WHILE INKEY$ = "": WEND
LOOP

Stack vs Queue: Which to Use?

Situation Use This
Undo/Redo Stack (LIFO)
Back button navigation Stack
Processing tasks in order Queue (FIFO)
Turn-based systems Queue
Message/event handling Queue
Depth-first search Stack
Breadth-first search Queue

Think of it this way:

  • Stack: "I need to handle the MOST RECENT thing first"
  • Queue: "I need to handle things in the ORDER they arrived"

DATA Blocks and ASARRAY: Pre-Made Data

Sometimes you want to fill an array with pre-defined data. DATA blocks make this easy:

DATA "highScores"
    1000, 800, 600, 400, 200
END DATA

scores = ASARRAY("highScores", 5)

FOR i = 0 TO 4
    PRINT "Score "; i + 1; ": "; scores(i)
NEXT i

ASARRAY reads the DATA block and creates an array automatically!

2D Arrays from DATA

DATA "level"
    1, 1, 1, 1, 1
    1, 0, 0, 0, 1
    1, 0, 2, 0, 1
    1, 0, 0, 0, 1
    1, 1, 1, 1, 1
END DATA

map = ASARRAY("level", 5, 5)    ' 5x5 grid

' Now map(2, 2) = 2 (the center tile)

ASIMAGE: Hardcoded Images

Want to embed a small image directly in your code? ASIMAGE creates an image from a DATA block of color values:

SCREEN 13

' Define a tiny 8x8 smiley face
' 0 = transparent, 14 = yellow, 0 = black (for eyes/mouth)
DATA "smiley"
    0,  0, 14, 14, 14, 14,  0,  0
    0, 14, 14, 14, 14, 14, 14,  0
   14, 14,  0, 14, 14,  0, 14, 14
   14, 14, 14, 14, 14, 14, 14, 14
   14, 14,  0, 14, 14,  0, 14, 14
   14, 14, 14,  0,  0, 14, 14, 14
    0, 14, 14, 14, 14, 14, 14,  0
    0,  0, 14, 14, 14, 14,  0,  0
END DATA

' Create the image (8 wide, 8 tall)
ASIMAGE "smiley", "smile", 8, 8

' Now use it like any other image!
PUTIMAGE (100, 50), "smile"
PUTIMAGE (150, 50), "smile", SCALE(32, 32)    ' Scaled up 4x

PAUSE

This is great for:

  • Tiny icons or UI elements
  • Placeholder graphics during development
  • Games that need to work without external files
  • Retro-style games where everything is in the code

The numbers in the DATA block are palette color indices (0-255 in SCREEN 13). Use 0 for transparent pixels.


Quick Reference

Command What It Does
DIM arr(n) AS TYPE Create array with n elements (indices 0 to n-1)
DIM arr(r, c) Create 2D array (r rows, c columns)
arr(i) Access element at index i
arr(r, c) Access element at row r, column c
UBOUND(arr) Get highest valid index
LBOUND(arr) Get lowest valid index (usually 0)
REDIM arr(n) Resize array to n elements (loses data)
REDIM PRESERVE arr(n) Resize array to n elements (keeps data)
ERASE arr Delete array, free memory
PUSH arr, value Add to end (stack)
POP(arr) Remove and return from end (stack)
QUEUE arr, value Add to end (queue)
DEQUEUE(arr) Remove and return from front (queue)
ASARRAY("data", n) Create array of n elements from DATA block
ASIMAGE "data", "name", w, h Create image from DATA block
OPTION BASE 1 Make arrays start at 1 instead of 0

Common Array Mistakes

1. Off-by-One Errors

DIM scores(10) AS INTEGER   ' 10 elements, indices 0-9

FOR i = 0 TO 10             ' ERROR! Index 10 is out of bounds!
    scores(i) = i * 100
NEXT i

' FIX: Use UBOUND
FOR i = 0 TO UBOUND(scores)
    scores(i) = i * 100
NEXT i

2. Forgetting Arrays Start at 0

DIM names$(5) AS STRING    ' 5 elements: indices 0, 1, 2, 3, 4

names$(1) = "Alice"        ' This is the SECOND element!
names$(0) = "Zero"         ' THIS is the first element

3. Using the Wrong Dimension Order

DIM grid(rows, cols)       ' (row, col) or (y, x)

' Be consistent! Pick one and stick with it:
grid(y, x) = value         ' Some people prefer (y, x)
grid(row, col) = value     ' Same thing, different names

Try It Yourself!

  1. Create an array of 10 random numbers, then find the largest one

  2. Make a simple to-do list using QUEUE and DEQUEUE

  3. Build a "back button" system using PUSH and POP that tracks the last 5 screens visited

  4. Create a 2D array representing a tic-tac-toe board, and write code to check for a winner

  5. Use ASARRAY to load a level map and display it with different colors for different tile types

Arrays are EVERYWHERE in game development. Master them, and you'll be able to handle any amount of data!

See you in Chapter 12!


Next Chapter: We'll create our own custom types—blueprints for organizing complex game objects like players and enemies!