Skip to content

Example Games

Andreas AFENTAKIS edited this page Nov 26, 2025 · 1 revision

Games

The concept of using games as test units for a programming language or its associated framework is a powerful methodology that goes beyond traditional unit testing. The development of a full game functions as an end-to-end litmus test, subjecting a programming language or engine to extreme computational and logical stress, thereby serving as a dynamic, holistic test unit that proves system robustness where static unit tests fall short.

Index of example Games

  1. The Hangman - Guess the word
  2. Dice - Roll the dice and play any game
  3. Biorhythm - Plan your day using Biorhythms
  4. Maze - Escape from the random maze
  5. Tic-Tac-Toe - Play against the computer
  6. Life - The game of the life

Hanoi Towers is implemented but not ready yet. Program file: hanoi.bas


The Hangman

Hangman is a classic guessing game where one player thinks of a word, and the other attempts to guess it letter by letter. The guessing player is shown a series of dashes representing the word's length and must name letters they believe are in the word. For every correct guess, the letter is revealed in its proper position; however, each incorrect guess contributes a part to a drawing of a hanged man. The game ends either when the guessing player correctly identifies the entire word, winning the round, or when the hangman drawing is completed due to too many incorrect guesses, resulting in a loss.

Source program: hangman.bas

rem Hangman (game)
rem
sdata words "apple", "banana", "computer", "python", "hangman", "programming" 
sdata words "science", "guitar", "electric", "holiday"
sdata words "keyboard", "library", "mountain", "notebook", "oxygen", "picture"
sdata words "quality", "rainbow", "sunshine", "travel"
fetch words

print "Welcome to Hangman!"

let randomIndex = int(rnd() * ubound("words"))

let wordToGuess = SCI("words",randomIndex)
let wordLength = len(wordToGuess)

let guessedSoFar = ""
For i = 1 To wordLength
    let guessedSoFar = guessedSoFar + "-"
Next i

let maxTries = 6
let tries = 0

startGame:
print "Word: " + guessedSoFar
print "Enter guess (single letter):";
input guess

let found = 0
For i = 1 To wordLength

If lcase(mid(wordToGuess, i, 1)) = lcase(guess) Then
    let guessedSoFar = left(guessedSoFar, i - 1) + guess + mid(guessedSoFar, i + 1, wordLength)
    let found = 1
EndIf
Next i

If found = 0 Then
    let tries = tries + 1
    print "Wrong guess! Tries left: " + str(maxTries - tries)
Else
print "Good guess!"
EndIf

If tries >= maxTries Then
print "You lost! The word was: " + wordToGuess
    GoTo endGame
EndIf

If guessedSoFar = wordToGuess Then
print "Congratulations! You guessed the word: " + wordToGuess
    GoTo endGame
EndIf

GoTo startGame

endGame:
print "Game over!"

Halt

Dice

Roll one dice per enter press, see the art desgin of the dice on screen and play any game need dices.

Source program: dice.bas

REM FBASIC Dice Roller with ASCII Art
REM --- Main Program ---

REM Leave 3 lines empty as requested
PRINT ""
PRINT ""
PRINT ""

PRINT "-----------------------------------"
PRINT "  Rolling a Six-Sided Die..."
PRINT "-----------------------------------"

REM Generate a random integer from 1 to 6
REM RND() returns a float from 0 to < 1.0
REM INT(RND() * 6) gives 0 to 5.
REM Adding 1 gives 1 to 6.
rollagain:
LET DieRoll = int(rnd() * 6) + 1

PRINT "The result is: " + str(DieRoll)
PRINT ""

REM Call the subroutine to draw the dice
GOSUB DrawDice

print "Enter to roll again or Q to quit: ";
input cmd
let cmd=left(ucase(cmd),1)

if cmd="Q" Then
   Halt
endif

goto rollagain



HALT

REM --- Subroutines ---

REM DrawDice: Determines which pattern to call based on DieRoll.
DrawDice:

    REM Print the top border of the square matrix
    PRINT "+-------+"

    REM Use GOSUB to draw the correct inner pattern
    IF DieRoll = 1 THEN 
        GOSUB DrawPattern1
    endif
    IF DieRoll = 2 THEN 
        GOSUB DrawPattern2
    endif
    IF DieRoll = 3 THEN 
        GOSUB DrawPattern3
    endif
    IF DieRoll = 4 THEN 
        GOSUB DrawPattern4
    endif
    IF DieRoll = 5 THEN 
        GOSUB DrawPattern5
    endif
    IF DieRoll = 6 THEN 
        GOSUB DrawPattern6
    endif

    REM Print the bottom border of the square matrix
    PRINT "+-------+"
    
RETURN

REM --- Individual Die Patterns (3x3 Matrix) ---
REM A dot is 'O', empty space is ' '

REM Pattern for 1: Center dot only
DrawPattern1:
    PRINT "|       |"
    PRINT "|   O   |"
    PRINT "|       |"
RETURN

REM Pattern for 2: Top-left and bottom-right dots
DrawPattern2:
    PRINT "| O     |"
    PRINT "|       |"
    PRINT "|     O |"
RETURN

REM Pattern for 3: Top-left, center, and bottom-right dots
DrawPattern3:
    PRINT "| O     |"
    PRINT "|   O   |"
    PRINT "|     O |"
RETURN

REM Pattern for 4: Four corner dots
DrawPattern4:
    PRINT "| O   O |"
    PRINT "|       |"
    PRINT "| O   O |"
RETURN

REM Pattern for 5: Four corner dots and a center dot
DrawPattern5:
    PRINT "| O   O |"
    PRINT "|   O   |"
    PRINT "| O   O |"
RETURN

REM Pattern for 6: Six dots (no center dot)
DrawPattern6:
    PRINT "| O   O |"
    PRINT "| O   O |"
    PRINT "| O   O |"
RETURN


Biorhythm

Biorhythms are a pseudoscientific theory suggesting that a person's life is influenced by cyclical biological waves or energies, typically starting at birth. The three primary cycles are the Physical (23 days, affecting strength and coordination), the Emotional (28 days, influencing mood and creativity), and the Intellectual (33 days, relating to alertness and learning ability). The theory posits that days where a cycle is at its peak are "high" days, and days where it crosses the baseline (a "critical day") are prone to accidents or instability, though there is no scientific evidence to support these claims.

Source program: Biorhythm.bas

REM FBASIC Biorhythm Calculator 
REM Category : example
REM
print ""
print ""
print "Enter your birth date (year month day), press enter after each value:";
input BYR, BMO, BDA


let BDATE = right("0"+BDA,2)+"-"+right("0"+BMO,2)+"-"+BYR

'print "Enter target date (year month day):"
'input TYR, TMO, TDA
let TODAY=date()
let TYR=year(TODAY)
let TMO=month(TODAY)
let TDA=day(TODAY)

print "Calculating biorhythm for date: " + TODAY+ "  Birth: "+BDATE

let diffDays = datediff("day", BDATE, TODAY)
print "You are alive for "+diffDays+" days!"
print ""

REM Constants
let physCycle = 23
let emoCycle = 28
let intCycle = 33

REM Calculate sine value helper emulation using mathFunctions.bas
REM We'll inline math function calls and calculations

REM Start chart header
print "Day          Phys Emo  Int"
print "--------------------------"

let d = diffDays - 7
startLoop:

    if d > diffDays + 7 then
        goto endLoop
    EndIf

    REM Calculate angle for each cycle
    let angPhys = 2 * PI * (d / physCycle)
    let angEmo = 2 * PI * (d / emoCycle)
    let angInt = 2 * PI * (d / intCycle)

    REM Call sin function (assumed available from mathFunctions.bas)
    let valPhys = sin(angPhys)
    let valEmo = sin(angEmo)
    let valInt = sin(angInt)

    REM Convert val to symbol: +, -, 0
    if valPhys > 0.3 then
        let symbolPhys = "+"
    else
        if valPhys < -0.3 then
            let symbolPhys = "-"
        else
            let symbolPhys = "0"
        endif
    endif

    if valEmo > 0.3 then
        let symbolEmo = "+"
    else
        if valEmo < -0.3 then
            let symbolEmo = "-"
        else
            let symbolEmo = "0"
        endif
    endif

    if valInt > 0.3 then
        let symbolInt = "+"
    else
        if valInt < -0.3 then
            let symbolInt = "-"
        else
            let symbolInt = "0"
        endif
    endif

    print dateadd("day",d,BDATE) + "    " + symbolPhys + "    " + symbolEmo + "    " + symbolInt

    let d = d + 1
    goto startLoop

endLoop:

end

Maze

A maze is a digital labyrinth where the user actively participates in its creation by "drawing" the walls or paths. Instead of solving a pre-generated puzzle, players are given a canvas and a tool to lay down segments, constructing the intricate passages and dead ends that will eventually form the maze. This interaction shifts the focus from problem-solving to creative design, allowing for infinite variations and personal expression within the familiar structure of a maze. No all maze result have resolution

Source program: maze.bas

REM FBASIC 40x22 Random Maze Generator
REM
REM This program generates a 40x22 grid where each inner cell has a 60% 
REM chance of being an open path (' ') and a 40% chance of being a wall ('#').
REM It includes one Entrance (E) and one Exit (X).
REM
REM Wall character: #
REM Path character: (space)
REM
REM NOTE: This method generates a random pattern but does NOT guarantee a solvable path.

REM --- Configuration ---
let horizontal = 40 ' Horizontal size (Columns)
let vertical = 22 ' Vertical size (Rows)
let pathchance = 0.60 ' 60% probability that an inner cell is a path (' ')

REM --- Main Program ---

REM Leave 3 lines empty as requested
print ""
print ""
print ""

print "------------------------------------------------"
print "  40x22 RANDOM Maze Generator"
print "------------------------------------------------"
print "Entrance (E) at (Row 1, Col 2)"
print "Exit (X) at (Row 22, Col 39)"
print ""

gosub generateandprintmaze

halt

REM -------------------------------------
REM --- Drawing Subroutine ---
REM -------------------------------------

generateandprintmaze:
    for row = 1 to vertical
        let mazeline = ""
        
        for col = 1 to horizontal
            let char = ""

            REM --- 1. Handle Entrance and Exit ---
            
            REM Entrance Check: Row 1, Col 2
            if row = 1 and col = 2 then
                let char = "E" ' Entrance (Top Left area)
                goto nextcharcheck
            endif

            REM Exit Check: Row 22, Col 39
            if row = vertical and col = horizontal - 1 then
                let char = "X" ' Exit (Bottom Right area)
                goto nextcharcheck
            endif
            
            REM --- 2. Handle Outer Walls ---
            
            REM Check Top or Bottom Boundary
            if row = 1 or row = vertical then
                let char = "#" ' Fixed boundary walls (Top/Bottom)
                goto nextcharcheck
            endif

            REM Check Left or Right Boundary
            if col = 1 or col = horizontal then
                let char = "#" ' Fixed boundary walls (Left/Right)
                goto nextcharcheck
            endif
            
            REM --- 3. Handle Randomized Inner Content ---
            
            REM Generate a random number between 0 and 1
            let randval = rnd() 
            
            if randval < pathchance then
                let char = " " ' Path (60% chance)
            else
                let char = "#" ' Wall (40% chance)
            endif
            
            nextcharcheck:
            
            REM Concatenate the character to the current line string
            let mazeline = mazeline + char
        next col

        REM Print the completed line for the current row
        print mazeline
    next row
return


Tic-Tac-Toe

A game of Tic-Tac-Toe against the computer is a classic adversarial experience played on a $3 \times 3$ grid, where you (usually 'X') and the computer (usually 'O') take turns marking empty cells. The primary goal for both players is to strategically place three of their marks in a row, either horizontally, vertically, or diagonally, before the opponent can achieve the same. The computer player typically uses an algorithm, ranging from simple random moves to sophisticated minimax strategies, to try and block your winning attempts while simultaneously setting up its own threats, culminating in either a win for you, a win for the machine, or a draw if all nine cells are filled without a victor.

Source program: ttt1.bas

REM TicTacToe FBASIC (No Arrays)
REM Category: Games
REM FBASIC Tic-Tac-Toe Game (Player 'X' vs. Computer 'O') ---
REM Follows strict constraints: No arrays, No CALL.
REM Uses individual variables (C1-C9) and GOSUB/RETURN/GOTO only.
REM Variables: 
rem PlayerTurn  1 for Player (X), 2 for Computer (O)
rem GlobalWinStatus  Holds the result of CheckWinLabel (0, 1, 2, or 3)
rem CellNumber Cell number input by user
rem MoveInput Raw string input
rem DrawCount Counter for draw logic
rem MarkIndex Loop counter for win check
rem I stands for Loop counter for win check
rem Mark is the Current mark being checked (X or O)
rem FoundMove as Counter for computer move loop

' --- Constants ---
LET PlayerMark = "X"
LET ComputerMark = "O"

' ----------------------------------------------------------------------
' --- MAIN GAME LOOP ---
' ----------------------------------------------------------------------

GOSUB InitBoardLabel

LET PlayerTurn = 1 ' Player X goes first

MainGameLoop:
rem clear screen
for tms=1 To 10
    print ""
next tms
GOSUB DrawBoardLabel

' Check for game end
GOSUB CheckWinLabel

' Win/Draw check logic using sequential IF blocks
IF GlobalWinStatus = 1 THEN
    PRINT "Player X wins! Congratulations!"
    HALT
ENDIF

IF GlobalWinStatus = 2 THEN
    PRINT "Computer O wins! Better luck next time."
    HALT
ENDIF

IF GlobalWinStatus = 3 THEN
    PRINT "It's a draw!"
    HALT
ENDIF

' Execute turn based on PlayerTurn flag
IF PlayerTurn = 1 THEN
    PRINT "Your turn (X)."
    GOSUB PlayerMoveLabel
    LET PlayerTurn = 2 ' Switch to Computer's turn

ENDIF

IF PlayerTurn = 2 THEN
    PRINT "Computer's turn (O)..."
    GOSUB ComputerMoveLabel
    LET PlayerTurn = 1 ' Switch back to Player's turn

ENDIF

GOTO MainGameLoop

PRINT ""
PRINT "Game over. Press ENTER to end."
INPUT dummy
HALT

' ----------------------------------------------------------------------
' --- BOARD & DISPLAY ROUTINES ---
' ----------------------------------------------------------------------

InitBoardLabel:
REM Initialize the 9 board cells with their corresponding number as a string.
LET C1 = "1" : LET C2 = "2" : LET C3 = "3"
LET C4 = "4" : LET C5 = "5" : LET C6 = "6"
LET C7 = "7" : LET C8 = "8" : LET C9 = "9"
RETURN

DrawBoardLabel:
PRINT "--- Tic-Tac-Toe ---"
PRINT "-------------------"

' Print Row 1 (Cells 1, 2, 3)
PRINT "   | "; 
print C1; 
print " | "; 
print C2; 
print " | "; 
print C3; 
print " |"
PRINT "-------------------"

' Print Row 2 (Cells 4, 5, 6)
PRINT "   | "; 
print C4; 
print " | "; 
print C5; 
print " | "; 
print C6; 
print " |"
PRINT "-------------------"

' Print Row 3 (Cells 7, 8, 9)
PRINT "   | "; 
print C7; 
print " | "; 
print C8; 
print " | "; 
print C9; 
print " |"
PRINT "-------------------"
PRINT ""
RETURN

' ----------------------------------------------------------------------
' --- PLAYER MOVE LOGIC ROUTINE ---
' ----------------------------------------------------------------------

PlayerMoveLabel:

PlayerMoveLoop:
PRINT "Enter cell number (1-9): ";
INPUT MoveInput

LET CellNumber = int(num(MoveInput))

' 1. Check if input is in range (1 to 9)
IF CellNumber < 1 OR CellNumber > 9 THEN
    PRINT "Invalid number. Must be between 1 and 9."
    GOTO TryAgainPlayer
ENDIF

' Use GOTO logic to jump to the correct cell check/assignment
IF CellNumber = 1 THEN 
    GOTO CheckC1
ENDIF
IF CellNumber = 2 THEN 
    GOTO CheckC2
ENDIF
IF CellNumber = 3 THEN 
    GOTO CheckC3
ENDIF
IF CellNumber = 4 THEN 
    GOTO CheckC4
ENDIF
IF CellNumber = 5 THEN 
    GOTO CheckC5
ENDIF
IF CellNumber = 6 THEN 
    GOTO CheckC6
ENDIF
IF CellNumber = 7 THEN 
    GOTO CheckC7
ENDIF
IF CellNumber = 8 THEN 
    GOTO CheckC8
ENDIF
IF CellNumber = 9 THEN 
    GOTO CheckC9
ENDIF
' Safety net in case of unexpected input
GOTO TryAgainPlayer 

CheckC1:
    IF C1 <> "1" THEN 
        GOSUB CellTaken
        GOTO TryAgainPlayer 
    ENDIF
    LET C1 = PlayerMark
    GOTO PlayerMoveExit

CheckC2:
    IF C2 <> "2" THEN 
        GOSUB CellTaken
        GOTO TryAgainPlayer 
    ENDIF
    LET C2 = PlayerMark
    GOTO PlayerMoveExit

CheckC3:
    IF C3 <> "3" THEN 
        GOSUB CellTaken
        GOTO TryAgainPlayer 
    ENDIF
    LET C3 = PlayerMark
    GOTO PlayerMoveExit

CheckC4:
    IF C4 <> "4" THEN 
        GOSUB CellTaken
        GOTO TryAgainPlayer 
    ENDIF
    LET C4 = PlayerMark
    GOTO PlayerMoveExit

CheckC5:
    IF C5 <> "5" THEN 
        GOSUB CellTaken
        GOTO TryAgainPlayer 
    ENDIF
    LET C5 = PlayerMark
    GOTO PlayerMoveExit

CheckC6:
    IF C6 <> "6" THEN 
        GOSUB CellTaken
        GOTO TryAgainPlayer 
    ENDIF
    LET C6 = PlayerMark
    GOTO PlayerMoveExit

CheckC7:
    IF C7 <> "7" THEN 
        GOSUB CellTaken
        GOTO TryAgainPlayer 
    ENDIF
    LET C7 = PlayerMark
    GOTO PlayerMoveExit

CheckC8:
    IF C8 <> "8" THEN 
        GOSUB CellTaken
        GOTO TryAgainPlayer 
    ENDIF
    LET C8 = PlayerMark
    GOTO PlayerMoveExit

CheckC9:
    IF C9 <> "9" THEN 
        GOSUB CellTaken
        GOTO TryAgainPlayer 
    ENDIF
    LET C9 = PlayerMark
    GOTO PlayerMoveExit

TryAgainPlayer:

GOTO PlayerMoveLoop

CellTaken:
PRINT "Cell "; 
PRINT CellNumber; 
PRINT " is already taken. Try again."
RETURN

PlayerMoveExit:
RETURN

' ----------------------------------------------------------------------
' --- COMPUTER MOVE LOGIC ROUTINE ---
' ----------------------------------------------------------------------

ComputerMoveLabel:
LET FoundMove = 0

ComputerMoveLoop:
LET CellNumber = int(rnd() * 9) + 1

' Check if the cell is empty (i.e., still contains a number)
IF CellNumber = 1 And C1 = "1" THEN 
    LET C1 = ComputerMark
    GOTO FoundMoveExit 
ENDIF
IF CellNumber = 2 And C2 = "2" THEN 
    LET C2 = ComputerMark
    GOTO FoundMoveExit 
ENDIF
IF CellNumber = 3 And C3 = "3" THEN 
    LET C3 = ComputerMark 
    GOTO FoundMoveExit 
ENDIF
IF CellNumber = 4 And C4 = "4" THEN 
    LET C4 = ComputerMark 
    GOTO FoundMoveExit 
ENDIF
IF CellNumber = 5 And C5 = "5" THEN 
    LET C5 = ComputerMark 
    GOTO FoundMoveExit 
ENDIF
IF CellNumber = 6 And C6 = "6" THEN 
    LET C6 = ComputerMark 
GOTO FoundMoveExit 
ENDIF
IF CellNumber = 7 And C7 = "7" THEN 
    LET C7 = ComputerMark 
    GOTO FoundMoveExit 
ENDIF
IF CellNumber = 8 And C8 = "8" THEN 
    LET C8 = ComputerMark 
    GOTO FoundMoveExit 
ENDIF
IF CellNumber = 9 And C9 = "9" THEN 
    LET C9 = ComputerMark 
    GOTO FoundMoveExit 
ENDIF

' Check for a draw condition to prevent infinite loops 
LET FoundMove = FoundMove + 1
IF FoundMove < 9 THEN 
    GOTO ComputerMoveLoop
endif

' Fallthrough if no move found (Draw is caught by CheckWinLabel)
GOTO ComputerMoveExit

FoundMoveExit:
PRINT "Computer has moved."

ComputerMoveExit:
RETURN

' ----------------------------------------------------------------------
' --- WIN/DRAW CHECK ROUTINE ---
' ----------------------------------------------------------------------

CheckWinLabel:
REM Sets the result in GlobalWinStatus: 0 (No win/No draw), 1 (Player X win), 2 (Computer O win), 3 (Draw)

LET GlobalWinStatus = 0 ' Default: Game is ongoing

' Loop through both marks (X then O)
FOR MarkIndex = 1 TO 2
IF MarkIndex = 1 THEN 
    LET Mark = PlayerMark 
ENDIF
IF MarkIndex = 2 THEN 
    LET Mark = ComputerMark 
ENDIF

' --- Check Winning Combinations ---

' Rows (1-2-3, 4-5-6, 7-8-9)
IF C1 = Mark And C2 = Mark And C3 = Mark THEN 
    GOTO WinnerFound 
ENDIF
IF C4 = Mark And C5 = Mark And C6 = Mark THEN 
    GOTO WinnerFound 
ENDIF
IF C7 = Mark And C8 = Mark And C9 = Mark THEN 
    GOTO WinnerFound 
ENDIF

' Columns (1-4-7, 2-5-8, 3-6-9)
IF C1 = Mark And C4 = Mark And C7 = Mark THEN 
    GOTO WinnerFound 
ENDIF
IF C2 = Mark And C5 = Mark And C8 = Mark THEN 
    GOTO WinnerFound 
ENDIF
IF C3 = Mark And C6 = Mark And C9 = Mark THEN 
    GOTO WinnerFound 
ENDIF

' Diagonals (1-5-9, 3-5-7)
IF C1 = Mark And C5 = Mark And C9 = Mark THEN 
    GOTO WinnerFound 
ENDIF
IF C3 = Mark And C5 = Mark And C7 = Mark THEN 
    GOTO WinnerFound 
ENDIF

GOTO NextMarkCheck ' Skip to the next mark check

WinnerFound:
LET GlobalWinStatus = MarkIndex ' Set 1 for X win, 2 for O win

GOTO CheckWinExit ' Exit the routine immediately

NextMarkCheck:

NEXT MarkIndex

' --- Check for Draw (Board Full) ---
LET DrawCount = 0

' Check if each cell is marked (i.e., not a number 1-9)
IF C1 <> "1" THEN 
    LET DrawCount = DrawCount + 1 
ENDIF
IF C2 <> "2" THEN 
    LET DrawCount = DrawCount + 1 
ENDIF
IF C3 <> "3" THEN 
    LET DrawCount = DrawCount + 1 
ENDIF
IF C4 <> "4" THEN 
    LET DrawCount = DrawCount + 1 
ENDIF
IF C5 <> "5" THEN 
    LET DrawCount = DrawCount + 1 
ENDIF
IF C6 <> "6" THEN 
    LET DrawCount = DrawCount + 1 
ENDIF
IF C7 <> "7" THEN 
    LET DrawCount = DrawCount + 1 
ENDIF
IF C8 <> "8" THEN 
    LET DrawCount = DrawCount + 1 
ENDIF
IF C9 <> "9" THEN 
    LET DrawCount = DrawCount + 1 
ENDIF

IF DrawCount = 9 THEN
    LET GlobalWinStatus = 3 ' Set 3 for a draw

ENDIF

CheckWinExit:
RETURN


End

Life

Conway's Game of Life is a zero-player cellular automaton that, despite its misleading name, is not a game in the conventional sense but a simulation of mathematical rules governing an evolving universe on an infinite two-dimensional grid. Every cell on this grid is either alive or dead, and its state in the next generation is determined solely by the state of its eight immediate neighbors according to four simple rules: (1) Any live cell with fewer than two live neighbors dies (underpopulation). (2) Any live cell with two or three live neighbors lives on to the next generation (survival). (3) Any live cell with more than three live neighbors dies (overpopulation). (4) Any dead cell with exactly three live neighbors becomes a live cell (reproduction). These elemental rules give rise to an astonishing complexity of patterns, including static "still lifes," repeating "oscillators," and traveling "spaceships," demonstrating how simple local interactions can generate emergent global order and behavior, making it Turing complete and a profound subject in computer science and theoretical biology.

Source program: gameoflife.bas authoring credits to: Georgios P. Afentakis

REM Conway's Game of Life using 1-D SDATA "array"
REM Grid: N x N, stored row-major in GameScreen

print "Simulating Conway's game of life using standard rules"
REM The line below sets the grid size. One can also use INPUT to set it.
let N = 16

print "How many generations of Game of life should I run? ";
Input M

'SEED is a random 5 digit number or more
let SEED=rnd()*34214*M


REM =========================
REM 1. INITIAL RANDOM GRID
REM =========================

let i = 0

For i = 1 To N * N
    let gameChar = "."
    SEED = mod(SEED * 16807, 2147483647)
    let rng = mod(SEED * 11, 100)
    if rng < 27 Then
        gameChar = "@"
    EndIf
    sdata GameScreen gameChar
Next i

REM Create NextScreen with same size (so SSET works on it)
reset GameScreen
foreach GameScreen
    sdata NextScreen [GameScreen.Item]
endforeach GameScreen

REM =========================
REM 2. LOOP OVER GENERATIONS
REM =========================
let gen = 0

For gen = 0 To M - 1

    REM ---- Print current generation from GameScreen ----
    print "Generation " + str(gen)

    reset GameScreen
    let GameOutput = ""
    let cnt = 0

    foreach GameScreen
        GameOutput = GameOutput + [GameScreen.Item] + " "
        cnt = cnt + 1
        if mod(cnt, N) = 0 Then
            GameOutput = GameOutput + "\n"
        EndIf
    endforeach GameScreen

    print GameOutput
    print ""          ' blank line between generations

    REM If this was the last generation, do not compute a next one
    if gen = M - 1 Then
        goto DoneAll
    EndIf

    REM =========================
    REM 3. COMPUTE NEXT GENERATION (into NextScreen)
    REM =========================

    REM Variables used by neighbor lookups
    let row = 0
    let col = 0
    let qRow = 0
    let qCol = 0
    let targetIndex = 0
    let idx = 0
    let cellChar = "."
    let liveNeighbors = 0
    let newChar = "."
    let curCell = "."
    let cellIndex = 0

    for row = 1 to N
        for col = 1 to N

            REM Get current cell state at (row, col)
            qRow = row
            qCol = col
            gosub GetCell
            curCell = cellChar

            REM Count live neighbors
            liveNeighbors = 0

            REM (row-1, col-1)
            qRow = row - 1
            qCol = col - 1
            gosub GetCell
            if cellChar = "@" Then
                liveNeighbors = liveNeighbors + 1
            EndIf

            REM (row-1, col)
            qRow = row - 1
            qCol = col
            gosub GetCell
            if cellChar = "@" Then
                liveNeighbors = liveNeighbors + 1
            EndIf

            REM (row-1, col+1)
            qRow = row - 1
            qCol = col + 1
            gosub GetCell
            if cellChar = "@" Then
                liveNeighbors = liveNeighbors + 1
            EndIf

            REM (row, col-1)
            qRow = row
            qCol = col - 1
            gosub GetCell
            if cellChar = "@" Then
                liveNeighbors = liveNeighbors + 1
            EndIf

            REM (row, col+1)
            qRow = row
            qCol = col + 1
            gosub GetCell
            if cellChar = "@" Then
                liveNeighbors = liveNeighbors + 1
            EndIf

            REM (row+1, col-1)
            qRow = row + 1
            qCol = col - 1
            gosub GetCell
            if cellChar = "@" Then
                liveNeighbors = liveNeighbors + 1
            EndIf

            REM (row+1, col)
            qRow = row + 1
            qCol = col
            gosub GetCell
            if cellChar = "@" Then
                liveNeighbors = liveNeighbors + 1
            EndIf

            REM (row+1, col+1)
            qRow = row + 1
            qCol = col + 1
            gosub GetCell
            if cellChar = "@" Then
                liveNeighbors = liveNeighbors + 1
            EndIf

            REM Apply Game of Life rules WITHOUT ELSE

            REM default: cell will be dead
            newChar = "."

            REM live cell survives only with 2 or 3 neighbors
            if curCell = "@" Then
                if liveNeighbors = 2 Then
                    newChar = "@"
                EndIf
                if liveNeighbors = 3 Then
                    newChar = "@"
                EndIf
            EndIf

            REM dead cell becomes live only with exactly 3 neighbors
            if curCell <> "@" Then
                if liveNeighbors = 3 Then
                    newChar = "@"
                EndIf
            EndIf

            cellIndex = (row - 1) * N + col
            SSET NextScreen cellIndex newChar

        next col
    next row

    REM =========================
    REM 4. COPY NextScreen BACK INTO GameScreen
    REM =========================
    idx = 0
    reset NextScreen
    foreach NextScreen
        idx = idx + 1
        if idx <= N * N Then
            SSET GameScreen idx [NextScreen.Item]
        EndIf
    endforeach NextScreen

Next gen

DoneAll:
HALT   REM Program ends, subroutine is below


REM ======================================================
REM SUBROUTINE: GetCell(qRow, qCol) -> cellChar ("@" or ".")
REM Uses GameScreen as the current generation.
REM ======================================================
GetCell:

    REM Out-of-bounds = dead cell
    if qRow < 1 Then
        cellChar = "."
        return
    EndIf
    if qRow > N Then
        cellChar = "."
        return
    EndIf
    if qCol < 1 Then
        cellChar = "."
        return
    EndIf
    if qCol > N Then
        cellChar = "."
        return
    EndIf

    REM Convert 2D → 1D index
    targetIndex = (qRow - 1) * N + qCol

    idx = 0
    reset GameScreen
    foreach GameScreen
        idx = idx + 1
        if idx = targetIndex Then
            cellChar = [GameScreen.Item]
            return
        EndIf
    endforeach GameScreen

    REM fallback (should never hit)
    cellChar = "."
    return


Clone this wiki locally