Mutant Basic

Programmers Reference Guide

Alpha Version, subject to change

 

Table of Contents

Overview.. 2

What is Basic?. 2

What is Mutant Basic?. 2

What is different about Mutant Basic?. 2

What else does the door offer besides a Basic development environment?. 2

About this Guide. 2

DOS Commands. 2

DISKS. 2

NEWDISK name. 2

CLONEDISK name. 2

DISK. 2

DISK name. 2

DELDISK name. 2

DIR. 2

LOAD name. 2

SAVE name. 3

DEL name. 3

QUIT. 3

Immediate Mode Statements. 3

LIST. 3

FIND term. 3

RUN.. 3

VARS. 3

REM / UNREM... 3

RENUM... 3

PUBLISH.. 4

UNPUBLISH.. 4

NEW... 4

EDIT. 4

Program Statements. 4

About Basic Programs and Line Numbers. 4

Copying / Moving Program Lines. 4

CLS. 5

PRINT. 5

COLOR. 5

POSITION.. 5

UP, DOWN, LEFT, RIGHT. 6

FOR. 6

Edge-cases with FOR loops. 6

NEXT. 6

Scoped variables: 7

Scoped Variables in Nested Blocks. 7

INPUT. 7

GET. 8

DATA.. 8

READ.. 8

RESTORE. 8

RANDOMIZE. 9

REM... 9

UNREM... 9

GOTO.. 9

GOSUB. 9

RETURN.. 10

ON.. 10

Variables. 10

Strings vs Numbers. 10

Evaluation. 10

Evaluation Functions. 10

LET. 11

Arrays. 11

Associative Arrays (Dictionaries). 11

Multi-dimensional Associative Arrays. 12

Array value types. 12

Environment Variables. 12

Undeclared / Undefined Variables. 12

Checking if a Variable is Defined. 12

ASCII Table. 12

Table of Characters. 12

Extended ASCII Table. 14

DEF FN Function Definition. 14

Non-Blocking User Input. 14

What is Non-Blocking User Input?. 14

Mutant Basics variation on INKEY$. 14

Database Operations. 14

Queries vs Commands. 14

Creating a database. 15

How to issue SQL statements. 15

Creating a table. 15

Putting data into a table. 15

Deleting data in a table. 15

Changing existing data in a table. 15

Getting data from a table. 16

Checking query results. 16

Notes on SQL Statement Building. 16

Extensions for Creating Word Games. 16

IsWord(word). 16

GetWord(). 16

GetWord(length). 17

GetWord(minLength, maxLength). 17

GetWord(starts with). 17

GetWordContains(word). 17

GetNextWord(). 17

ResetNextWord. 17

 

 

Overview

What is Basic?

Basic is a programming language which has been around for many decades. The word BASIC is an acronym for Beginners All-Purpose Symbolic Instruction Code. It was made very popular in the 1970s and 1980s when home computers of the day almost all came with some kind of Basic Interpreter and the language is fairly approachable by the novice as there are only a handful of commands (statements) and its pretty simple to get a not-too complex program up and running.

 

What is Mutant Basic?

Mutant Basic is designed to run as a door (add-on software) for the online Bulletin Board System (BBS) called Mutiny. The purpose of the system is to allow users of the BBS to create their own games which then they and other users could play either alone or with (or against) other users.

Mutiny is freely accessible by anyone at the web site http://mutinybbs.com or you can telnet into the BBS at mutinybbs.com:2332. Alternatively you can use SSH to connect at mutinybbs.com:2232.

The name comes from trying to smash together the words Mutiny and Basic and it always coming out sounding just a little like Mutant Basic. However there are some mutations to the Basic language that make Mutiny Basic unique.

 

What is different about Mutant Basic?

Ask anyone who has written computer programs in Basic and they will tell you two things: It is an interpreted language and it is difficult to manage complexity as the program grows.

An interpreted language is one where the instructions are converted to machine language while the program is running. The alternative to this is a compiled language which means a separate program, called a compiler, converts your program code into machine language and then you run that program instead. When someone says that Basic is an interpreted language theyre saying that is a bad thing.

However, even though Mutant Basic is also an interpreted language, it is not running on a 1970s or 1980s computer with only a few kilobytes of memory and a 1 MHZ CPU. Basic programs running through Mutant Basic, on Mutiny BBS, are running with ample resources. Any slow-down due to the time it takes to interpret instructions are not noticeable, especially since the BBS can only communicate over a telnet connection so fast and since you cant do things like high resolution graphics. As with any BBS software youre doing simple things in text only.

The other issue, the one of managing complexity, is a problem with traditional Basic. Mutant Basic incorporates some new features to improve on traditional Basic and make your program more manageable as it gets bigger. These include scoped variables, labels, and associative arrays (aka dictionaries). These will be discussed in more detail throughout this guide.

 

What else does the door offer besides a Basic development environment?

In addition to being able to create and run programs in Basic there is a built-in disk operating system (DOS) which allows you to create, insert, and manage virtual floppy disks. Programs in Mutant Basic can be thought of as existing on these virtual disks so a DOS is needed to manage them.

In addition to virtual disks you also get a SQL (structured query language) database for each virtual floppy disk. This allows you to store and load game data. Having this database available allows you create multi-player games as you can use the database to save the state of the game between users play sessions.

 

About this Guide

Many Basic programming guides list all of the Basic commands in alphabetical order. This could be helpful if you are familiar with a command and what it does but just want a refresher on how it works.

This guide takes a different approach. The Basic statements are grouped by how they relate to other statements. For example REM and UNREM are near each other even though one starts with R and the other U.

 

DOS Commands

Programs written in Mutant Basic can be thought of as being stored on virtual floppy disks. Why floppy if its virtual? Because we need to keep in mind the concept of these disks being insert-able and removable unlike a hard drive where all programs from all users would live in the same space. In reality there are no disks these are just logical groupings which allow for access control.

 

Additionally, as will be discussed later, each disk includes one SQL database which can be used by the programs on that disk. You can put multiple basic programs on one disk but these would all share the same SQL database so you would have to be careful to name your tables in such a way that one basic program doesnt corrupt data used by another.

 

The following is a list of immediate mode commands which can be used to manage disks and files. Immediate mode is any command you enter at the ] prompt that doesnt begin with a line number.

 

DISKS

The DISKS command simply lists all of the virtual disks you have. If you have a disk selected (inserted into the virtual disk drive) then that disk name will show up on the list with (current) displayed.

 

NEWDISK name

This creates a new virtual disk with the given name. This does not insert the disk however.

 

CLONEDISK name

This is similar to the NEWDISK command as it creates a new disk with the given name. This command will only work if you have a disk inserted into the virtual disk drive already and if it is your disk. This command will create a new disk, copy any programs that are on the current disk to the new disk (removing published flag), and copy the database from the current disk (if any) to the new disk.

This is generally used as a way to make backups of a disk.

DISK

The command DISK, without any other text after DISK, simply tells you the name of the disk you currently have inserted into the virtual disk drive.

 

DISK name

This command will insert the disk with the given name into the virtual disk drive. This does not clear the current program however. It is possible to copy programs from one disk to another by LOADing the program from one disk, swap disks, and then SAVE the program to the new disk.

 

DELDISK name

This command deletes a virtual disk and any programs stored on it. There is no confirmation so be careful when using this command!

 

DIR

DIR lists the programs saved to the currently loaded disk. If the current program in memory is on the disk then it will be shown with (current). However this does not verify that the program in memory is identical to the one on the disk, you may have unsaved changes.

 

LOAD name

This command loads the basic program with the given name from the disk to memory so that you can view, edit, and run it.

 

SAVE name

This command will save (or re-save) the program in memory to the disk with the given name. You can copy a program by LOADing it by one name and SAVEing it by another.

 

DEL name

This command deletes the program with the given name from the disk. There is no confirmation so be careful when using this command!

 

QUIT

This statement quits out of Mutant Basic returning you to the BBS. No saves will be performed so be sure to save your program before quitting!

 

Immediate Mode Statements


The previous section covered using disks and files. These commands are all only usable in immediate mode. Immediate mode statements may be typed in and executed immediately but may not be used within the program. Therefore these statements may not have a line number preceding them.

 

This section covers more statements that are only available in immediate mode but are related to programming, not disk or file operations.

 

LIST

LIST lists your program. You can list the entire program, a single line, or a range of lines. You can also refer to lines by labels rather than numbers.

LIST Lists all program lines.
LIST 50 Lists only line 50 (if it exists).
LIST 50- Lists line 50 and all lines after 50.
LIST -50 Lists all lines up to 50 (including 50).
LIST DOSTUFF Lists the line that contains the DOSTUFF label.
LIST DOSTUFF- Lists the line that contains the DOSTUFF label and all lines after this line.

 

FIND term

FIND works similar to LIST and is unique to Mutant Basic. You can use this to list lines that contain the search term.

 

Example, Finding program statements:

] FIND gosub 1210

560 IF x>5 THEN GOSUB 1210

720 GOSUB 1210

985 ON a GOSUB 1210, 1220

 

Note that this is a literal keyword search, if the line 985 above instead read:

985 ON a GOSUB 1200, 1210, 1220

Then this would not show up as a search result as the text GOSUB 1210 does not exist on that line. In that case FIND 1210 would be a better search.

 

RUN

This command runs the program which is in memory.

Examples:

RUN

Runs the entire program from the beginning.

RUN 10

Runs only the statement on line 10.

RUN 10-

Runs the program beginning on line 10 and continuing from there.

RUN 10-50

Runs only the statements on lines between 10 and 50 (including 10 and 50).

RUN -50

Runs from the beginning of the program up to, and including, line 50.

RUN -49

Runs from the beginning of the program up to, but not including, line 50. Line 49 does not need to exist in the program.

 

The optional ranges shown in the previous examples work differently than other Basics you may be familiar with. In other Basics you cannot specify a range but you can start running from a particular line. The range options in Mutant Basic make debugging easier as you can test small units of code at a time.

 

Important: A statement like RUN 10 in other Basics would be equivalent to RUN 10- in Mutant Basic.


There may be situations where you will run into errors however, for example if you start running in the middle of a FOR loop, when the NEXT statement is executed you will encounter a ? NEXT WITHOUT FOR error since the FOR statement did not get executed.

 

Example, Testing Subroutine with RUN:

 

10 GOSUB 100

20 PRINT ALL DONE!

30 END

100 REM UPDATE STATUS SUBROUTINE

110 PRINT HIT POINTS: ;HP; MAGIC POINTS: ;MP; STAMINA: ;STAMINA

120 RETURN

] LET HP=50

] LET MP=25

] LET STAMINA=10

] RUN 100-119

HIT POINTS: 50 MAGIC POINTS: 25 STAMINA: 10

 

Note in this example the RUN statement specified a range of 100-119. The line 119 doesnt exist in the program but it can be used to mean run up to, but not including, line 120.

 

VARS

This command lists all variables and their values. This does not include special scoped variables which may optionally be used in FOR and GOSUB routines.

 

REM / UNREM

The REM and UNREM commands will be covered in the Program Statements section as REM is a statement that can be used in a program. However there are improvements to REM (and also the new UNREM command) which Mutant Basic introduces to allow you to easily comment out or uncomment out program lines. These are discussed in Program Statements in the REM and UNREM sections.

 

RENUM

RENUM can be used to re-number your program lines.

 

Example, RENUM:

] RENUM

Renumbers your program starting on line 10 and increasing by 10: (10, 20, 30, )

] RENUM 100
Renumbers your program starting on line 100 and increasing by 10: (100, 110, 120, )

] RENUM 100,50
Renumbers your program starting on line 100 and increasing by 50: (100, 150, 200, )

 

PUBLISH

PUBLISH saves your program and also flags it as visible to other BBS users. This allows users to see your game on the games list and to load/play your game.

 

When you use the PUBLISH command you are asked if you would also like to allow other users to see your source code. If you reply Yes then when the user selects your game they will have the option to either jump into the game right away (play it) or just load it and then sit in Basic. If they chose this option they can then LIST your program and even copy it to their own disks if they want. This is helpful for the other users as they can learn from your technique or even branch off of your code and maybe derive something new. However sometimes giving people access to the source code might give the users an unfair advantage in the game so you may wish to not allow this.

 

Even if you do allow users to view your source code they cannot change it. Although they can edit the program in their session and run the program with their changes, they cannot save it to your disk.

 

UNPUBLISH

UNPUBLISH is the opposite of PUBLISH. You may want to do this if you find a bug in your program to temporarily remove your game from the games list so that user users cant access it. You can then use the PUBLISH command again when youre ready to open it up to other users.

 

NEW

NEW erases the current program from memory. This does not affect the program saved to disk.

 

EDIT

EDIT can be used to perform search & replace type edits on program lines. There are two ways to use EDIT. If neither the part of the line youre replacing (search) and the replacement (replace) contain any spaces then you can do the edit in a single statement: EDIT n search replace. Where n is the line number to edit, search is the part being replace, and replace is what to replace search with.

 

The other way to use the EDIT command is to type EDIT n (where n is the line number you want to edit) and then press enter. You will be prompted for the part of the line youre replacing (search), enter it and press enter. You will then be prompted a second time for the replacement. To abort enter nothing for either search or replace.

 

This second method must be used if the search or the replace contain spaces.

 

Example, editing a program line:

] LIST
10 INPUT Hello, what is your name;name$

20 PRINT Saving new player as ;name

] EDIT 20 name name$

Line 20, Original:

PRINT Saving new player as ;name

Edited:

PRINT Saving new player as ;name$

]

 

Example, editing a program line using the second method:

] LIST

10 INPUT Hello, what is your name;name$

20 PRINT Saving new player as ;name

] EDIT 20

Part to replace: name

Replace with: name$

Line 20, Original:

PRINT Saving new player as ;name

Edited:

PRINT Saving new player as ;name$

]

 

Notice that after performing an EDIT the original and updated lines are displayed. The EDIT command may not accommodate all changes and you may need to re-type the line from scratch.

 

Program Statements

This section will cover statements that may be used in your Basic program. Most (but not all) of these may also be used in immediate mode (without line numbers).

 

About Basic Programs and Line Numbers

In order for a statement to be a part of the program it must begin with a line number. For example if you were to type:
PRINT Hello World!

Then the text Hello World! would simply print out. But if you type:
10 PRINT Hello World!

Then nothing seems to happen at first. But what did happen is that you entered the statement as part of the program. When you type RUN then the program will run and youll see the text Hello World!.

 

The order in which program statements are executed is based off of the line number. Line 10 runs before line 20 and line 20 runs before line 30, etc There are exceptions to this however which will be covered throughout this guide. The reason many Basic program examples use numbering systems like 10, 20, 30, or 100, 110, 120, is because you will, no doubt, find that you need to insert a line between existing lines. If you find that you need to add a line between 10 and 20 you can simply number it 15, or any number between 11 and 19.

 

If this happens a lot you may end up not able to insert a new line number. Since each line number must be an integer (whole number) you cant insert a line between 12 and 13 (you cant use the line number 12.5). When this happens you will need to use the RENUM command (described previously) to re-number your program statements. This command will renumber your whole program and take care of statements such as GOTO and GOSUB which refer to specific line numbers.

 

You may also enter more than one statement per line if you separate them with a colon (:). For example:

 

Example, multiple statements per line:

10 cls:down 5:color red:print Welcome to Game of Doom!

This is equivalent to:

10 cls

20 down 5

30 color red

40 print Welcome to Game of Doom!

 

Copying / Moving Program Lines

You can copy a program line by typing new=old where old is the line number to copy and new is the new line number to assign the copy.

Example, copying program lines:

10 PRINT Hello!

] 20 = 10

Copied line 10 to 20.

] LIST

10 PRINT Hello!

20 PRINT Hello!

 

If your intent was to move the line to the new line number you can then delete the original line by entering just 10.

 

This next section will go through each of the Basic statements and describe what they do and how to use them. Some of the examples may use statements that havent been discussed yet. Do not be concerned about those statements as they will be explained later in this guide.

 

CLS

CLS simply clears the screen. This requires that the user be connected to the BBS with ANSI support.


PRINT

Print is one of those primary building blocks that you will be using a lot. This statement displays text, a number, the result of a mathematical calculation, variables, or any combination of these things.

 

Concatenation (joining together) of elements can be done in multiple ways. This short example program shows some ways to concatenate strings (text) with numbers or mathematical expressions:

 

Example, print with concatenation:

10 PRINT "THE SQUARE ROOT OF ";49;" IS ";SQR(49)

20 PRINT "THE SQUARE ROOT OF "49" IS "SQR(49)

30 PRINT "THE SQUARE ROOT OF " 49 " IS " SQR(49)

] RUN

THE SQUARE ROOT OF 49 IS 7

THE SQUARE ROOT OF 49 IS 7

THE SQUARE ROOT OF 49 IS 7

 

The semicolon (;) symbol can be used to join elements but as you can see in the above example this symbol isnt always necessary.

 

The plus symbol (+) may be used to concatenate together two strings but not a number and a string. The semicolon (;) may be used in either case so when working with strings it is generally best to concatenate with semicolons instead of plusses. Also when joining two numbers + will add them together whereas semicolon (;) will simply show both numbers.

 

By default the PRINT statement also prints a newline character at the end. It is possible to print something without causing a newline by adding a semicolon (;) character to the end of the expression.

 

Example, print without newline:

10 PRINT WHAT IS YOUR NAME;

20 INPUT NAME$

30 PRINT OH, HELLO THERE, ;NAME$;, PLEASED TO MEET YOU!

 

Note the ; at the end of line 10. This tells the system to keep the cursor at the end of the line. When the next line runs (INPUT) this causes a ? to appear.

 

The full list of things that can be PRINTed is not listed here as that is covered under a separate section, expression evaluation. This is because such expressions apply to more than just the PRINT statement.

 

COLOR

COLOR is used to set the foreground and background colors. This requires that the user be connected to the BBS with ANSI support. You can check the ANSI environment variable to confirm this, more on environment variables later.

 

Example, Changing Colors:

10 COLOR WHITE,BLACK

15 PRINT WHITE ON BLACK

20 COLOR GREEN

25 PRINT GREEN ON BLACK

30 COLOR ,CYAN

35 PRINT GREEN ON CYAN

40 COLOR 14,1

45 PRINT YELLOW ON BLUE

 

As you can see in the examples you can set foreground (the first value) and background (the second value) or just one of the two.

 

You can also specify the color either by name or by number. This table shows the valid names and numbers of the available colors.

 

Number

Color Name

Number

Color Name

0

Black

8

DarkGray

1

DarkBlue

9

Blue

2

DarkGreen

10

Green

3

DarkCyan

11

Cyan

4

DarkRed

12

Red

5

DarkMagenta

13

Magenta

6

DarkYellow

14

Yellow

7

Gray

15

White

 

Note: when setting the background color only the colors 0 through 7 are usable. If colors 8 through 15 are used as a background the color applied will be the same as 0 through 7 (8=0, 9=1, 10=2, etc)

 

POSITION

POSITION is used to set the location of the cursor. This is where the next PRINT statement will begin printing. This is helpful in games as it can be used to position game elements on the screen. This requires that the user be connected to the BBS with ANSI support.

 

Example, Position:

10 CLS

20 FOR X=1 TO 39 STEP 2

30 FOR Y=2 TO 20 STEP 2

40 POSITION X,Y

50 PRINT X

60 NEXT

70 NEXT

This example prints a grid of Xs on the screen each separated by a space both horizontally and vertically. The CLS on line 10 clears the screen.

 

The first value passed to POSITION, the x coordinate can range between 1 and 80. The second value, the y coordinate, can range between 1 and 25.

 

UP, DOWN, LEFT, RIGHT

You saw how you can position the cursor using the POSITION statement but that statement must be given exact X and Y values. If you want to move the cursor relative to where it is now you can do this using the UP, DOWN, LEFT, and RIGHT statements.

 

These statements can be used with a numeric value indicating how far in the given direction you want to movie. For example UP will move the cursor up one line but UP 5 will move the cursor up 5 lines.

 

FOR

FOR is a very powerful statement and serves as one of the main building blocks of any useful Basic program. FOR is used in conjunction with the NEXT statement to build loops. Loops allow you to execute the same set of statements multiple times.

 

For the most part FOR in Mutant Basic may be used exactly how it is used in other Basics but there are some optional special features unique to Mutant Basic.

 

Example, a simple FOR loop:

10 FOR I=1 TO 10

20 PRINT HELLO!

30 NEXT

This will print the word HELLO 10 times each on a new line.

 

Example, two nested FOR loops:

Within a loop you can have any statements including another loop:

10 FOR I=1 TO 10

20 FOR J=2 TO 12 STEP 2

30 PRINT IXJ=X*J

40 NEXT

50 NEXT
This will print 1X2=2, 1X4=4, 1X6=6 1X12=12, 2X2=4, 2X4=8, 2X6=12, all the way up to 10X12=120

 

NOTE: When nesting FOR loops within loops you must have a unique index variable name for each FOR. For example you cant re-use FOR I on line 20 above or you will encounter an error.

 

Example, early exit from FOR loop:

A FOR loop will continue as long as the index (I and J used in previous examples) is within the set range. If you want to exit the FOR loop early one way is to manually change the index variable to be outside of the range:

10 FOR I=1 TO 20

20 IF I>10 THEN LET I=21

30 PRINT I;, ;

40 NEXT

50 PRINT

This will print 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21,

 

The final 21 is printed because the print statement is after the line that set I to 21 but before the NEXT statement so the value of I isnt checked until after the PRINT.

 

Edge-cases with FOR loops

FOR loops are fairly straightforward but there are some edge cases where the behavior is not completely intuitive and in-fact in these cases the behavior will sometimes differ from one Basic interpreter to the next.

 

The Zero Step Case

Consider this loop:

10 FOR I=1 TO 1 STEP 0

20 PRINT HELLO WORLD!

30 NEXT I

 

How many times should the loop run? Will it run at all? The STEP controls how the index (I in this example) increases (or decreases) each loop. If the STEP is not specified then it is assumed to be +1 (add 1 to I each time through the loop). In Mutant Basic this loop will run forever (unless there is a statement inside the loop to set the value of I greater than 1.

 

The Zero End Case

Consider this loop:

10 FOR I=1 TO 0

20 PRINT Game #+STR$(I)+ : +game$(I-1,Name)

30 NEXT I

 

In this case the index (I) is already outside of the range (beyond the ending index of 0) before the loop even has a chance to run once. However the loop still runs once and only once. This is consistent with the behavior on the Apple 2 and the Commodore 64 as well as other popular Basic interpreters.

 

This condition can come up if processing rows from a SQL query (hence the example on line 20) and will cause an error if there are no results. Therefore it is recommended that you check for any results before looping over the results. As mentioned in the Environment Variables section you can use the QCOUNT variable to get the count of rows fetched from the last SQL select query.

 

The Zero Start & End Case

Consider this loop:

10 FOR I=0 TO 0

20 PRINT HELLO WORLD!

30 NEXT I

 

Although this loop may look confusing it is not actually an edge-case. The loop will run exactly once as it should under any Basic interpreter. Remember that the ending index is always included in the loop (for example in FOR I=1 to 10 the loop will have a run where there value of I is 10).

 

NEXT

The NEXT statement was covered in detail in the previous section (FOR) however there is a little more that can be discussed on the subject.

 

In the examples in the FOR section you will notice that all of the NEXT statements are used without any variable names. In Mutant Basic the variable name is optional with the NEXT statement.

 

There is one good reason to use the variable name, however: Code readability. For example you may set up a nested loop like this:

 

10 FOR I=1 TO 5

20 FOR J=6 TO 10

30 PRINT I*J

40 NEXT I

50 NEXT J

 

This is invalid logic and will cause an error because line 40 is trying to advance the I loop while in the J loop. If you omit the variable names then you wont get an error at runtime error but the result you get might not be what you were expecting.

Also if you have a large number of statements between the FOR and the NEXT it may make it easier to read your program if you include the variable name in the NEXT statement as your eye can more easily associate it with its FOR statement.

 

It is also possible to do simple loops without using FOR/NEXT at all:

 

Example, Simple loop without FOR/NEXT:

10 LET I=1

20 IF I>10 THEN GOTO 60

30 PRINT I

40 LET I=I+1

50 GOTO 20

60 PRINT DONE!

 

If your game needed an infinite loop (which most games do), such as keep asking the user for input until they say they want to quit then looping this way would be better than using a FOR loop.

 

Scoped variables:

One of the unique features to Mutant Basic is scoped variables. A scoped variable is a variable that only lives in a small area of code, specifically within FOR loops and GOSUB routines. Once program flow exits a FOR loop or returns from a GOSUB any scoped variable defined during those routines are forgotten.

 

This can be very helpful to prevent accidentally changing other program variables. For example if a GOSUB routine is used to calculate the distance between two game objects then some temporary variables may be needed to perform that calculation. You can use scoped variables in the GOSUB routine and be certain that a) youre not affecting any other program variables and b) these variables will not be used outside of the routine.

 

You can also use scoped variables within FOR loops. Additionally you can use a scoped variable as the index of the FOR loop itself. The previous examples used I and J as the indices of the loops. These were not scoped variables. This means the value of I and J could be read and used even after the loop is completed.

 

Also if I or J was being used before the loop then the loop would have changed the value of the variables possibly creating a bug or at least confusion.

 

To use a scoped variable in a FOR loop or a GOSUB subroutine is very simple, just start the variable with an underscore character (_). Normal (global) variables are not permitted to start with an underscore so you can be sure that you are not overwriting any current global variables.

 

Example, FOR with a Scoped Variable:

10 LET I=33

20 FOR _I=1 TO 10

30 PRINT I; ;_I

40 NEXT

50 PRINT I

60 PRINT _I

This will print:

33 1

33 2

33 3

33 10

33

(error)

 

The error message will indicate that the program was trying to access an undefined variable. This happens because the _I variable doesnt exist when the program reaches line 60, it only exists between lines 20 and 40 as it is scoped to the FOR loop.

 

Scoped Variables in Nested Blocks

Scoped variables are available anywhere in that scope even if its in a nested scoped block.

Example, Nested Scoped Variables:

10 GOSUB FOO

20 END

100 !FOO

110 _X$=FOO

120 GOSUB BAR

130 REM THE NEXT LINE WILL CAUSE AN ERROR!

140 PRINT _Y$

150 RETURN

160 !BAR

170 REM THE NEXT LINE WILL WORK FINE!

180 PRINT _X$

190 _Y$=BAR

200 RETURN

 

In this example the scoped variable _X$ is declared in, and lives in, the FOO subroutine. The BAR subroutine is called while still in the FOO subroutine. As a result the BAR subroutine has access to the _X$ variable.

 

The BAR subroutine also declares a scoped variable, _Y$. This variable, however, only lives in the BAR subroutine so when that subroutine ends (on line 200) _Y$ ceases to exist.

This causes an error on line 140 where, while back in the FOO subroutine, we try to print the variable _Y$.

 

This example used two GOSUBs but the same would hold true for nested FORs, if a FOR calls a GOSUB, or if a GOSUB executes a FOR.

 

INPUT

INPUT prompts the user to enter a line of text and stores the result in the variable defined after the word INPUT.

 

Example, INPUT:

10 PRINT WHAT IS YOUR NAME;

20 INPUT NAME$

30 PRINT OH, HELLO THERE, ;NAME$;!

 

The PRINT and INPUT statements can be combined:

10 INPUT WHAT IS YOUR NAME;NAME$

20 PRINT OH, HELLO THERE, ;NAME$;!

 

INPUT can also take in more than one value at a time if the variable names are separated by commas:

10 INPUT WHAT IS YOUR NAME,AGE;NAME$,AGE

20 PRINT OH, HELLO THERE, ;NAME$;! YOURE ;AGE; YEARS OLD, EH?

 

GET

GET works almost the same as INPUT except instead of accepting an entire line of text from the user it only gets a single character. As soon as the user presses almost* any key then the value of that key is stored in the variable defined after the word GET.

 

*almost: Keys such as CTRL, ALT, NUM-LOCK, etc do not register using the GET statement. Also note that unlike INKEY$ this does prevent any further statements from running until the user has pressed a key.

 

GET also differs from INPUT in that you cant combine a PRINT statement with it or GET multiple variables using a single GET statement.

 

Example, GET:

10 PRINT GUESS A NUMBER BETWEEN 1 AND 9

20 GET G$

30 PRINT YOU GUESSED ;G$

 

In this example as soon as the user presses a key then the program will continue with line 30. If this program used INPUT instead of GET then the user would also have to press enter and, depending on what they typed before pressing enter, the value of G$ could be 0, or more than 1 characters.

 

DATA

Another way to set variable values is through the use of the statements DATA, READ, and RESTORE. DATA simply defines variable values without actually assigning them. These can be anywhere in your program regardless of the other programming logic your program. For this reason it usually makes sense to put all of your DATA statements at the end of your program.

Example, DATA:

1000 DATA 32,16,88,22/7,are you sure,game over,USERID

 

Each value is separated by a comma. Notice that there is no sense of data type, that is the data can be numbers, strings (text), arithmetic expressions (like 22/7), and even variables (USERID). The data type will be important when it comes to actually using this data which is done with the READ statement.

 

READ

READ loads one or more variable value (defined in DATA statements anywhere in your program) into one or more variable.

Example, READ:

10 READ a,b,c,d,q$,go$,uid

20 PRINT a,b,c,d

30 PRINT q$;?

40 PRINT Its ;go$; for you, user #;uid

50 END

1000 DATA 32,16,88,22/7,are you sure,game over,USERID

] RUN

32 16 88 3.14285714285714

are you sure?

Its game over for you, user #7

 

When using a variable in a DATA statement the value is only evaluated when you use the READ statement.

READ can also be used in a loop:

Example, READ in a loop:

10 FOR i=1 TO 10

20 READ a$:print a$;

30 NEXT

40 PRINT:END

100 DATA H, E, L, LO, , W, O, R, L, D!

] RUN

HELLO WORLD!

 

In this example the DATA contains strings most of which contain only one character but two contain two characters (LO and D!).

 

RESTORE

Resets the DATA-READ pointer to the beginning so that data (defined by DATA statements) can be re-read (using the READ statement) from the beginning.

Example, RESTORE:

10 FOR j=1 to 2

20 FOR i=1 TO 10

30 READ a$:print a$;

40 NEXT i

50 NEXT j

60 PRINT:END

100 DATA H, E, L, LO, , W, O, R, L, D!

] RUN

HELLO WORLD!

? out of data

 

In this example the lines 20 through 40 are repeated twice (because they are in the FOR j loop). The second time through, when the READ statement on line 30 is executed there it tries to get the value after D!, however there is no more data.

 

To fix this we can add RESTORE prior to re-running the FOR I loop:

 

Example, RESTORE:

10 FOR j=1 to 2

20 FOR i=1 TO 10

30 READ a$:print a$;

40 NEXT i

45 PRINT:RESTORE

50 NEXT j

60 PRINT:END

100 DATA H, E, L, LO, , W, O, R, L, D!

] RUN

HELLO WORLD!
HELLO WORLD!

 

RANDOMIZE

Randomize sets the random number generators seed. If RANDOMIZE is not called at all or if RANDOMIZE is called without including a number, then the seed will be randomly chosen by Mutant Basic, thus creating a fairly decent random number generator.

 

If you want a repeatable sequence of numbers you can chose any seed number you want, for example RANDOMIZE 2 will set 2 as the random seed. RANDOMIZE itself, without a number, will set a random seed. This is the default so if you want a random seed you dont need to use the RANDOMIZE statement at all unless a previous seed was set.

 

Any numbers generated using the RND() function will be based on the seed.

 

REM

REM is short for remark. This statement simply tells the Basic interpreter to ignore this line. This has two purposes.


First, it can be used to add a remark in your program to help you read it more easily.

Example, commenting (remarking) your program:

10 FOR I=1 TO 10

20 GOSUB 100

30 PRINT I

40 NEXT

50 END

100 REM MY SUBROUTINE, DOES SOMETHING AWESOME!

110 PRINT AWESOME! ;

120 RETURN

 

In this example the remark on line 100 serves to inform you what the subroutine is supposed to do.

 

The second use for REM is to temporarily remove a statement from your program without actually deleting it.

 

Example, using REM to temporarily disable program statements:

10 FOR I=1 TO 10

20 REM PRINT AWESOME!

30 PRINT FOO

40 NEXT

 

In this example the program will print 10 FOO lines but wont print AWESOME! because that line is commented out by the REM statement.

 

One special feature of Mutant Basic is that you can comment out a line by using the immediate mode command REM line-number.

Example, commenting out a program statement:

10 PRINT HELLO

] REM 10

] LIST

10 REM PRINT HELLO

 

You can also comment out blocks of lines by using a range in the same way ranges can be used on the LIST and RUN commands.

 

UNREM

You can use the immediate mode command UNREM to uncomment out a line number. Be careful that this is being done on a program statement and not on an actual remark (comment).

Example, uncommenting out a program statement:

10 REM PRINT HELLO

] UNREM 10

] LIST

10 PRINT HELLO

 

You can also uncomment out blocks of lines by using a range in the same way ranges can be used on the LIST and RUN commands.

 

GOTO

GOTO causes program flow to jump to a particular line number or label.


Example, GOTO with a line number:

10 PRINT LINE TEN!

20 GOTO 50

30 PRINT LINE THIRTY!

40 END

50 PRINT LINE FIFTY!

 

This will print:

LINE TEN!

LINE FIFTY!

 

Lines 30 and 40 are skipped.

 

GOTO and GOSUB can both be used with labels instead of line numbers. This makes your program read a lot easier as the label usually indicates what the code does that is at the GOTO or GOSUB.

 

Example, GOTO with a label:

10 PRINT LINE 10!

20 GOTO PRINT50

30 PRINT LINE THIRTY!

40 END

50 !PRINT50

60 PRINT LINE FIFTY!

 

This will have the same output as the previous example.

 

GOSUB

GOSUB temporarily redirects program flow to a subroutine.

 

At first glance GOSUB seems a lot like GOTO but there is a key difference. GOSUBs expect that at some point the program will come back to the next statement after the GOSUB. Although it is possible to do that with GOTO as well but as you will see GOSUB is much better.

 

GOSUB, like GOTO, redirects program flow to the indicated line number or label but when a RETURN statement is encountered then program flow immediately returns to the next statement after the GOSUB.

 

This allows you to call the same line number or label over and over from multiple locations in your program. If you tried to do this with just GOTOs how would you know what line number to GOTO at the end of the subroutine?

 

There is another feature of GOSUBs which is unique to Mutant Basic. As mentioned in the FOR loop section scoped variables may be declared and used in subroutines. Please refer to the scoped variables section for more details on that subject.

Example, subroutines:

10 SCORE=0

20 REM DO GAME STUFF

30

100 GOSUB UPDATESTATUS

110 IF LIVES>0 THEN GOTO 20

115 END

120 !UPDATESTATUS

130 POSITION 0,20

140 COLOR YELLOW,DARKBLUE

150 PRINT SCORE: ;SCORE

160 COLOR WHITE,BACK

170 RETURN

 

In this example the GOSUB on line 100 redirects program flow to line 120 (where the UPDATESTATUS label is). You could also use GOSUB 100. When the RETURN statement is reached on line 170 then program flow is redirected again to the next statement after the GOSUB, line 110.

 

If you wanted to call the UPDATESTATUS subroutine again from another place in your program you can because the RETURN will always return to the next statement after the GOSUB that called it, this is why the RETURN statement doesnt specify a line number like a GOTO does.

 

RETURN

The RETURN statement is used in conjunction with GOSUB. This marks the end of a subroutine. When the RETURN statement is executed the program flow will return to the next statement after the GOSUB that called the subroutine.

 

If a RETURN statement is encountered without a GOSUB being called first then an error will occur.

 

ON

ON is similar to GOTO or GOSUB. The format of this statement is: ON (expression) GOTO/GOSUB (line number list). The expression must resolve to a number and in order for program flow to be redirected to one of the line numbers in the line number list the value must be between 1 and the number of line numbers in the list. You can also use labels in place of line numbers.

 

If the expression does not resolve to a number between 1 and n (where n is the number of line numbers in the line number list) then the statement is skipped.

Example, using the ON statement:

Consider this series of IF statements:

10 IF X=1 THEN GOTO 100

20 IF X=2 THEN GOTO 110

30 IF X=3 THEN GOTO 120

40 IF X=4 THEN GOTO UPDATE : REM using a label instead of a line number

 

These four lines can be re-written using a single ON statement:

10 ON X GOTO 100,110,120,UPDATE

 

Variables

Variables are the lifeblood of any Basic program. Variables are locations in memory where data is stored and accessed while your program is running.

 

Variables have already been covered some in the previous sections. This section will go into more detail and describe how variables work in Mutant Basic and how they differ from variables in other Basics.

 

In previous examples youve seen the LET statement which assigns a value to a variable and the VARS immediate mode statement which lists all of the variables and their values. You have also seen how to PRINT variable values and later you will see how to use them in mathematical calculations.

 

Strings vs Numbers

As with any Basic, there are essentially two types of variables: Text, so-called strings as they are a string of characters, and numbers. The primary difference between the two is that number variables are evaluated mathematically and string variables are taken literally without evaluation.

 

To signify that a variable is a string the variable name should end with a dollar sign ($).

 

Evaluation

Evaluation of an expression happens when assigning a value to a numeric variable, when printing something thats not enclosed in quotation marks, when printing the value of a numeric variable, as part of the range of a FOR loop, or when referencing an index on an array (arrays will be covered later).

 

Evaluation involves:

         Substituting any referenced variables for the values of those variables

         Performing mathematical functions such as square root (SQR), random (RND), etc and substituting the result of those functions with the function call in the expression.

         Combining text and numerical expressions together through concatenation.

Evaluation Functions

There quite a few special functions which may be used in evaluated statements such as PRINT, LET, and IF. These are listed below, where n is used the function takes a number (or variable containing a numeric value). Where n$ is used the function takes a string (or a variable containing a string value).

SQR(n)

Returns the square root of n. Although this only works with square roots other roots can be calculated using POW. For example POW(27,1/3) returns the cube root of 27 (3).

RND()

Returns a random number. See RANDOMIZE (earlier in this guide) for information about setting the random seed.
NOTE: Unlike other Basic interpreters the RND() function in Mutant Basic does not expect (or pay attention to) any numbers passed in the parenthesis. The purpose of such a number in other Basics depends on the system (for example the Commodore 64 and the Apple ][ computers differ in how they interpret this number). You can pass a number in the parenthesis but it wont have any effect on the resulting randomly generated number. Instead see the RANDOMIZE statement for configuring the random number generator.

SIN(n)

Returns the sine of n.

COS(n)

Returns the cosine of n.

TAN(n)

Returns the tangent of n.

ATAN(n)

Returns the arc-tangent of n.

ASIN(n)

Returns the arc-sine of n.

ACOS(n)

Returns the arc-cosine of n.

POW(n,p)

Returns n raised to the p power. As discussed in the SQR() function above this can also be used to get roots other than square roots.

INT(n)

Returns the integer portion of n. For example INT(3.14) returns 3. This is not a rounding; it simply takes the whole number portion. For example INT(3.98) also returns 3, not 4.

CHR$(n)

Returns the ASCII character with the value n. See the ASCII section for a table of values.

STR$(n)

Converts the number n to a string representation of it. This is helpful when concatenating strings and numbers. For example: PRINT YOUR SCORE IS: + STR$(SCORE)

ASC(n$)

Returns the ASCII value of the character in the string n$. For example ASC(*) gives you 42 because the ASCII code for the asterisk is 42.

RIGHT$(n$, n)

Returns the right n characters from the string n$. For example RIGHT$(Hello, 2) returns lo.
MID$(n$, p, n)

Returns a substring of n$ starting at position p and going up to n characters. For example MID$(abcdefg, 2, 3) returns bcd.

LEFT$(n$, n)

Returns the left n characters from string n$. For example LEFT$(Hello, 2) return He.

UC$(n$)

Returns the value of n$ in all upper-case characters. This is helpful when checking user input against an expected value since the user may or may not have entered the correct case.

LC$(n$)

Returns the value of n$ in all lower-case characters. This is helpful when checking user input against an expected value since the user may or may not have entered the correct case.

LEN(n$)

Returns the length (number of characters) in string n$.

INSTR(h$, n$)

Returns the position of the first occurrence of the needle n$ within the haystack h$. This will be 0 if no match was found. The search is not case sensitive. For example INSTR(abcdefg, cd) returns 3.

VAL(n$)

Converts the string n$ into a number if possible. This is helpful when taking input from the user and converting it into a number so that it can be used as an array index or in a calculation.

TAB(n)

Prints out n space characters. The number n may be omitted if you just want 1 space character.

ABS(n)

Returns the absolute value of the number n. ABS(5) = 5, ABS(-5) = 5. This gives you the magnitude of the number without regard for the sign (positive/negative).

 

LET

LET simply assigns a value to a variable. If the variable exists then the value is updated, otherwise the variable is created with the new value.

 

Example, assigning values with LET:

] LET PI=3.14

] LET X = 22/7

] LET Y = 5+X*13

] LET X$ = STR$(X)

] Y$ = THE VALUE IS ;X$

] VARS

PI = 3.14

X = 3.14285714285714

Y = 45.85714285714282

X$ = 3.14285714285714

Y$ = THE VALUE IS 3.14285714285714

 

Notice that the last assignment (Y$) does not use the LET keyword. Variable assignment can be implied due to the fact that a) theres no known statement prior to the variable name and b) there is an equals (=) symbol in the statement.

 

Unique to Mutant Basic you can also remove a variable by not including a value. If you want to delete all variables use the CLR command instead.

Example, deleting a variable:

] X$="HELLO"

] Y$="WORLD"

] VARS

X$ = HELLO

Y$ = WORLD

] X$=

] VARS

Y$ = WORLD

Notice that the line X$= removed the X$ variable because no value was given after the equals (=).

 

Arrays

Arrays are extremely powerful types of variables as they let you reference multiple variables in loops or by a number. An array variable is one which contains one or more index enclosed in parenthesis.

 

Example, a simple array:

10 FOR I=1 TO 10

20 LET PLAYERSCORE(I)=3+I*2

30 NEXT

 

This example creates an array called PLAYERSCORE. This array is called one dimensional because it has one index (I in this example). You can then reference a particular element of the array by number:

] PRINT PLAYERSCORE(3)

9

 

You can have more than one index, doing this makes the array multi-dimensional.

 

Example, a two-dimensional array:

10 FOR X=1 TO 10

20 FOR Y=2 TO 12 STEP 2

30 LET RESULT(X,Y)=X*Y

40 NEXT

50 NEXT

 

This creates an array called RESULT which has two indices, X and Y.

 

Associative Arrays (Dictionaries)

An associative array, also known as a dictionary, is an array where the index is a string (text) rather than a number. Although this has the disadvantage of not being able to reference elements of the array using loop counters it is often helpful for storing attributes of something.

 

NOTE: Unlike with LET and PRINT, you must enclose the string in single quotes when using it as an index for an array.

 

Example, a simple dictionary:

10 PRINT WHAT NAME SHALL YOU BE KNOWN AS;

20 INPUT PLAYER$(NAME)

30 PRINT YOU SHALL BE KNOWN, THEN, AS ;PLAYER$(NAME);!

 

Multi-dimensional Associative Arrays

The real power of associative arrays is apparent when you are dealing with two-dimensional arrays. You can have the first index be a number and the second a string. This allows you to store and reference lists of objects and their properties.

 

Example, declaring a two-dimensional dictionary:

10 FOR I=1 TO 10

20 LET TANK(I,HEALTH)=100

30 LET TANK(I,AMMO)=15

40 LET TANK(I,COLOR)=I+1

50 NEXT

 

In this example the TANK array contains 10 tanks, and each tank has a HEALTH, AMMO, and COLOR attribute. Later in your program you can do something like this:

 

Example, using a two-dimensional dictionary:

100 REM FIRE TANK SUB, VARIABLE T IS THE TANK NUMBER CURRENTLY IN PLAY

110 IF TANK(T,AMMO) <= 0 THEN RETURN

120 LET TANK(T,AMMO) = TANK(T,AMMO)-1

130 RETURN

 

Array value types

Earlier it was mentioned that variables can be either strings (text) or numeric. When assigning a string value to a variable the variable name should end with a dollar sign ($). This is true with arrays as well.

 

Example, mixed value types in arrays:

10 LET PLAYER$(1,NAME)=JIMBOB

20 LET PLAYER(1,SCORE)=50+BONUS

30 LET PLAYER(1,2)=22/7

 

In this example PLAYER$ and PLAYER are technically two separate arrays but you can still be used together as if they were one. PLAYER$ holds string values and PLAYER holds numeric values.

 

Unlike other Basic languages, arrays in Mutant Basic do not need to be DIMd (declared) and elements can be stored randomly. For example you can set PLAYER$(5,NAME) without having a player #4, 3, 2, 1 or 0. Essentially you just created one variable called PLAYER$(5,NAME).

 

Environment Variables

There are a handful of reserved variables which are present by the system. These special variable names cannot be deleted or changed but they can be used in expressions in statements such as LET, PRINT, and IF.

Here is a table of the environment variables:

Variable Name

Description

USERNAME$

The name of the current user. This will be your username as you are designing your game but when another user plays your game this will be their username.

USERID

The ID of the user.

ANSI

1 if the user supports ANSI, 0 otherwise. This can be helpful to determine if you should use ANSI specific statements such as CLS and COLOR.

DATE$

The current date.

TIME$

The current time.

TICKS

The number of ticks on the BBSs internal clock. This can be used to pace your game by waiting for a set number of ticks to pass.

INKEY$

The last key the user pressed. See more about non-blocking user input later.

INKEY

The value TICKS was at the last time INKEY$ was updated.

QCOUNT

The count of rows returned from the last SQL select query executed.

 

Undeclared / Undefined Variables

Most Basics let you use variables that dont have values set. For example you can run the command PRINT A even if A doesnt have a value. In Mutant Basic this is not the case and will cause an error.

 

Checking if a Variable is Defined

There are four ways to check if a variable is defined and has a non-empty value:

Method 1: IF DEFINED a$ THEN The statement after THEN is executed only if a$ is defined, even if the value of a$ is empty.
Method 2: IF NOT DEFINED a$ THEN The statement after THEN is executed only if a$ is not defined. If a$ is defined as empty then the statement is NOT executed.

Method 2: IF a$<> THEN The statement after THEN is executed if a$ is defined AND the value of a$ is not empty.

Method 3: IF a$= THEN The statement after THEN is executed either if a$ is not defined or if a$ is defined but has an empty value.

 

ASCII Table

ASCII stands for American Standard Code for Information Interchange. The ASCII table is a table of each character that can be displayed through the terminal. In addition to the standard letters, numbers, and punctuation marks there are also special characters, known as extended ASCII characters, which may also be displayed.

To display an extended ASCII character you will have to use the CHR$() function.

Mutant Basic uses the Windows-1252 character encoding which means most of the useful extended ASCII characters will be available for you but there are some which are not available.

You can create a simple program to display all of the characters:

Example, printing all extended ASCII characters:

10 CLS

20 FOR I=128 TO 255

30 PRINT I,CHR$(I)

40 NEXT

 

Table of Characters

#

CHR

#

CHR

#

CHR

#

CHR

#

CHR

#

CHR

32

(space)

48

0

64

@

80

P

96

`

112

p

33

!

49

1

65

A

81

Q

97

a

113

q

34

"

50

2

66

B

82

R

98

b

114

r

35

#

51

3

67

C

83

S

99

c

115

s

36

$

52

4

68

D

84

T

100

d

116

t

37

%

53

5

69

E

85

U

101

e

117

u

38

&

54

6

70

F

86

V

102

f

118

v

39

'

55

7

71

G

87

W

103

g

119

w

40

(

56

8

72

H

88

X

104

h

120

x

41

)

57

9

73

I

89

Y

105

i

121

y

42

*

58

:

74

J

90

Z

106

j

122

z

43

+

59

;

75

K

91

[

107

k

123

{

44

,

60

< 

76

L

92

\

108

l

124

|

45

-

61

=

77

M

93

]

109

m

125

}

46

.

62

> 

78

N

94

^

110

n

126

~

47

/

63

?

79

O

95

_

111

o

127


 

Extended ASCII Table

#

CHR

#

CHR

#

CHR

#

CHR

#

CHR

#

CHR

#

CHR

#

CHR

128

N/A

144

160

176

192

208

224

α

240

129

145

N/A

161

177

193

209

225

241

130

N/A

146

N/A

162

178

194

210

226

Γ

242

131

N/A

147

N/A

163

179

195

211

227

π

243

132

N/A

148

N/A

164

180

196

212

228

Σ

244

133

N/A

149

N/A

165

181

197

213

229

σ

245

134

N/A

150

N/A

166

182

198

214

230

246

135

N/A

151

N/A

167

183

199

215

231

τ

247

136

N/A

152

N/A

168

184

200

216

232

248

137

N/A

153

N/A

169

185

201

217

233

Θ

249

138

N/A

154

N/A

170

186

202

218

234

Ω

250

139

N/A

155

N/A

171

187

203

219

235

δ

251

140

N/A

156

N/A

172

188

204

220

236

252

141

157

173

189

205

221

237

253

142

N/A

158

N/A

174

190

206

222

238

ε

254

143

159

N/A

175

191

207

223

239

255

 

 

Notice that character 255 looks empty like a space but technically isnt a space. Characters shown as N/A will print a ? as that character is not available.

 

DEF FN Function Definition

A less common but still useful statement (DEF) can be used to define a mathematical function. This function can take zero or more variables, perform some mathematical calculation, and return a result. This should not be confused with the concept of Functions in other programming languages such as C which can execute whole blocks of code and even call other functions.

 

Example, function definition and usage:

10 def fndist(a,b,c,d) = SQR(POW(a-c,2) + POW(b-d,2))

20 print The distance between points (5,9) and (13,3) is ;fndist(5,9,13,3)

 

There are a few limitations to DEF: The function name must start with fn, the variable names must be exactly one letter, the values passed to the function must be numeric (or variables containing numeric values), and the function must return a number. The function may not run any basic code other than mathematical expressions.

 

Non-Blocking User Input

I decided to have a whole section for this topic in the guide because there is quite a lot to say on the subject and because in Mutant Basic it works quite differently than you may be used to in other Basics.

 

What is Non-Blocking User Input?

In the previous section user input was shown using INPUT and GET. With INPUT you can read a line of text from the keyboard and with GET you can read one keystroke. Both of these have one issue that may make it difficult to develop certain types of games: No program statements after the INPUT or GET statement will be executed until the user has pressed a key (with GET) or pressed enter (with INPUT).

If you wanted to build, for example, space invaders you couldnt move the invaders or have them fire until the user pressed left, right, or fire. On a BBS, given that the user may be connected at a low baud rate, it might not be a bad idea to have this kind of turn-based implementation of space invaders.

However if you want the action to continue even if the user isnt pressing a key then you need some kind of non-blocking input.

 

Mutant Basics variation on INKEY$

Unfortunately because of the way BBS doors are executed the input & output of Mutant Basic is intercepted by the BBS software and therefore Mutant Basic is not able to implement this feature in the same way other Basics do.

The way this is done in Basic normally is by referencing the INKEY$ variable. This is a reserved keyword in the Basic language (also known as an Environment Variable in Mutant Basic).

In other Basics the value of this variable is the key that the user is pressing at the time. When the user is not pressing a key the value will be empty. With Mutant Basic however the users keyboard is not directly connected to your running program rather their key strokes are being sent over the internet via some sort of Telnet or SSH connection so things must work a little differently.

Unlike other Basics, the value of INKEY$ will stay unchanged even after the user has let go of the key.

To get around this issue there is another environment variable called INKEY (without the $). This variable holds that value of the TICKS environment variable when the INKEY$ variable was last set. In other words its a timestamp of when the key was pressed.

You can use these two variables together to a) determine that a new key hit occurred and b) what it was.

 

Example, getting input without blocking:

20 T=TICKS

30 K$=INKEY$

40 IF K$=A AND INKEY>T THEN GOSUB MOVELEFT

50 IF K$=D AND INKEY>T THEN GOSUB MOVERIGHT

60 IF K$=Q AND INKEY>T THEN END

70 GOTO 30

100 !MOVELEFT

110 REM DO STUFF

120 T=INKEY

130 RETURN

140 !MOVERIGHT

150 REM DO STUFF

160 T=INKEY

170 RETURN

 

In this example the variable T is used to hold the time (TICKS) when the game last responded to a keypress. The actions on lines 40, 50, and 60 are only ran if the value of INKEY (the time when the INKEY$ value was updated) is later than T (the time when we last responded to a keypress).

 

Database Operations

Each virtual floppy disk includes one database which you can use in the programs you write and save on that disk. This is a SQL database using the SqLite database engine.

 

The ins and outs of Structured Query Language (SQL) will not be covered in this guide as the topic is vast and there are plenty of other resources available on the subject. This section will cover how to use the database through Mutant Basic.

 

Queries vs Commands

There are three terms that can often be used interchangeably when talking about SQL: Query, Statement, and Command. For the purposes of this guide a Command is a SQL statement that does not return any data (such as create a table, or put data into a table). A Query is a SQL statement that does return data.

First well go over the Commands as youll need to run some commands to set up your database before you can start getting (Selecting) data using Queries.

 

Creating a database

Each virtual floppy disk includes one database. So it isnt necessary to create a database file. What is necessary is that you are working with a floppy disk. If you just start Mutant Basic and didnt use the DISK diskname or NEWDISK diskname commands then no disk will be in the virtual floppy disk drive and therefore no database will be available. Please refer to the DOS section at the start of this guide for information on working with disks.

 

How to issue SQL statements

The keyword SQL, or the shorthand @ character, may be used to issue SQL commands. In queries (select statements) the @ symbol must be used and the value must be assigned to a variable. This will be covered in more detail later.

 

There are also special commands which start with a dot (.) or a question mark (?) which will be discussed in more detail in the following examples.

Example, a simple SQL command:

] SQL DELETE FROM MYTABLE WHERE ID=7

] @ DELETE FROM MYTABLE WHERE ID=7

Both of these commands do the same thing, the second line just uses the shorthand @ in place of the SQL keyword.

 

Example, listing existing tables:

] @ .TABLES

This will simply list all of the names of the tables in the database on the current disk.

 

Example, listing columns on a table:

] @ .COLUMNS MYTABLE

This lists the columns which are on the table named MYTABLE.

 

Example, selecting data without variable assignment:

] @ .SELECT * FROM MYTABLE WHERE UserId = 32

] @ ?SELECT * FROM MYTABLE WHERE UserId = 32

These two commands do the same thing, they run the select statement and output the result to the screen without assigning the data to a variable.

 

The first one will include column headers and also a newline after the results. The second one will not include a column header and will not include a newline after the results.

 

Creating a table

Once you have a disk inserted then you can start creating tables to store data. Lets say you want to write a game where each player controls a tank, a sort of turn-based version of the Atari 2600 game Combat. We can refer to each player by either of these two environment variables: USERNAME$ or USERID.

 

So here is our table layout for storing tanks:

Column Name

Data Type

Notes

Id

Integer

Database Generated

UserId

Integer

ID of the player

Color

Integer

What color is this players tank

Name

Text

What is the name of this players tank

PositionX

Integer

What is the X coordinate of the tank?

PositionY

Integer

What is the Y coordinate of the tank?

Ammo

Integer

How many shots does the tank have remaining?

Condition

Integer

How damaged is the tank?

LastPlayed

Text

Date when the player last moved this tank (stored as text)

 

To create a table in our database to store this data we can use this command in immediate mode:


Example, creating a table:

] @ CREATE TABLE Tanks (Id INTEGER PRIMARY KEY AUTOINCREMENT, UserId INTEGER NOT NULL, Color INTEGER NOT NULL, Name TEXT NOT NULL, PositionX INTEGER NOT NULL, PositionY INTEGER NOT NULL, Ammo INTEGER NOT NULL, Condition INTEGER NOT NULL, LastPlayed TEXT NULL)

 

We can then use @ .TABLES to see the table on the tables list and @ .COLUMNS Tanks to see the columns of that table.

 

If you make a mistake and want to delete the table you can use the command below.

Example, deleting a table:

] @ DROP TABLE Tanks

 

Be careful with this as this will delete the table even if there is data in it and its not reversible.

 

Putting data into a table

The SQL command insert puts data into a table, here is an example of creating a new record for a new player:

Example, inserting data:

] 100 REM CREATE NEW PLAYER RECORD

] 110 @ INSERT INTO Tanks (UserId, Color, Name, PositionX, PositionY, Ammo, Condition) VALUES (+STR$(USERID)+,+STR$(tankColor)+,+tankName$+,+STR$(tankPosition(0))+,+STR$(tankPosition(1))+,100,100)

] 120 RETURN:REM RETURN FROM GOSUB

 

Note on line 110 the integer values must be converted to strings using STR$(). This example also assumes the variables tankColor, tankName$, and the array tankPosition already exist. The Ammo and Condition are given a default, hard-coded, value of 100.


Deleting data in a table

The SQL command delete deletes one or more row from a table if it exists. Great care should be taken when using this command as you could delete data you didnt intend, including all data.


Example, deleting data:

] @ DELETE FROM Tanks

] @ DELETE FROM Tanks WHERE Id=7

] @ DELETE FROM Tanks WHERE UserId=32

] @ DELETE FROM Tanks WHERE UserId=32 and Color=3

 

The first statement will delete all data from the Tanks table so be careful not to do this unless you really do want to clear out the table.

 

The other three statements only delete rows that match the WHERE clause, if any exist.

 

Changing existing data in a table

The update command can be used to change existing data. The main reason we included an Id column in the previous examples is to allow for easily deleting or updating a specific record. Well use that in this example:


Example, updating data:

100 @ UPDATE Tanks SET Ammo=Ammo-1 WHERE Id=16

 

As you can see in this example the value for Ammo for the tank with ID # 16 will be decreased by one. In this case were letting the database engine do the math. If we wanted to do it in Basic we could rewrite it like this:

100 if ammo < 1 then goto 120

110 ammo=ammo-1:@ UPDATE Tanks Set Ammo=+str$(ammo)+ WHERE Id=16

120 rem more program statements

 

There are some advantages to this second approach. First, if the ammo is already 0 and we used the first example then the value in the database would become -1. Secondly the Basic variable (ammo) is also updated. In the first example we updated the value in the database but not the Basic variable.

 

Getting data from a table

The SQL command select can be used to get data from a table. Earlier examples showed how to do this in immediate mode to simple print it out but if you want to work with the data in your game youll want to assign data to one or more variables.

 

Mutant Basic uses the Array system to handle SQL queries because a single SELECT statement could fetch multiple rows of data and multiple columns within a row.

 

The following example shows a simple select and assign routine.

 

Example, selecting and using data in your game:

100 !LoadUserData

110 tank$=@ select * from Tanks where UserId=+STR$(USERID)

120 RETURN

 

After running this query there will be a two-dimensional array called tank$, the first index is a number (starting at 0) which corresponds to the row number (minus 1) and the second index is a word which corresponds to the column name.

 

Here is an example of the results:

tank$(0, Id)=1

tank$(0,UserId)=32

tank$(0,Color)=7

tank$(1,Id)=2

tank$(1,UserId)=77

 

Note that each value is a string even if the data is stored in the database as a number. You may need to use the VAL() function to convert strings into numbers. For example when drawing the players tank the command COLOR(VAL(tank$(t,Color))) can be used to convert the variable to a number and then pass it to the COLOR function to set the foreground color.

 

Checking query results

In the above example it is assumed that there is any data in the Tanks table. If not the tank$ array will not be populated. Also it may be important to know how many rows were fetched from the query so that if you are going to loop through them in a FOR loop you know where to stop.

You can use the environment variable QCOUNT to determine a) if any data was returned and b) how much. This environment variable holds the count of rows returned from the last SQL select statement.

 

Example, using QCOUNT to check for data from query:

100 !GetTanksByColor

105 rem c holds color number

110 tank$=@ select * from Tanks where Color=+STR$(c)

120 IF QCOUNT < 1 THEN RETURN

130 FOR _i=1 to QCOUNT

140 PRINT tank$(_i,Name)

150 NEXT

160 RETURN

 

Notes on SQL Statement Building

As you can see in the previous examples the SQL statements can get quite long and there is a lot of room for error. It is a good idea to build up the statement as a variable using multiple lines which will make the code more readable and you can also print out the statement prior to (or instead of) running while debugging.

Example, statement building:

100 !createPlayerRecord : rem subroutine to create a new player record

110 _sql$=insert into Tanks : rem scoped variable _sql, exists only in this subroutine note trailing space within string

120 _sql$ = _sql$ + (UserId, Color, Name, PositionX, PositionY, Ammo, Condition) values (

130 _sql$ = _sql$ + str$(USERID) + ,

140 _sql$ = _sql$ + str$(tankColor) + ,

150 _sql$ = _sql$ + + tankName$ + , : rem note single quote around TEXT field value

160 _sql$ = _sql$ + str$(tankPosition(0)) + ,

170 _sql$ = _sql$ + str$(tankPosition(1)) + ,

180 _sql$ = _sql$ + 100, 100) : rem 100 for both ammo and condition, and closing parenthesis

190 rem @ _sql$

200 print _sql$

210 return

 

In this example the scoped variable _sql$ is used to build up the SQL statement one part at a time. The statement on line 190 (which would submit the command to insert the record) has been commented out so it wont do anything. Instead, the value of _sql$ is printed out on line 200. If the statement appears to be formatted correctly you could then uncomment out line 190 and either comment out line 200 or remove it.

 

Extensions for Creating Word Games

There are special custom language commands built into Mutant Basic specifically for creating word-based games such as crosswords, scrabble, boggle, or whatever you can imagine. Built into the language is a large dictionary of words and a small collection of programming statements to access and use that collection. All of these statements are case-insensitive, for example IsWord(HARD) will return true (1) as well as IsWord(Hard), IsWord(hArD), etc

 

IsWord(word)

IsWord returns true (1) or false (0) if the word is in the dictionary. This can be used in an IF statement and therefore is useful to check if user input is a word.

Example, IsWord:

100 input enter word for 4 down: ;word$

110 if not IsWord(word$) then print sorry, that is not a word.:goto 100

 

GetWord()

GetWord, without any parameters passed in the parenthesis, simply returns a random word from the dictionary. This uses the random number generator so if you have used the RANDOMIZE statement to configure the random seed this will affect the random word selection.

Example, GetWord:

100 for i=1 to 10

110 print GetWord()

120 next i

] run

 

bonnie

implies

marbles

kingstown

balkan

belittles

seth

spate

pompeii

reintroduces

 

GetWord(length)

GetWord, when given a single number, returns a random word of exactly that length. If no word is returned then no words of that length exist in the word database.

 

GetWord(minLength, maxLength)

GetWord, when given two numbers, returns a random word whose length is at least minLength and not greater than maxLength. As above, if no word is returned then no words of the specified lengths exist in the word database.

 

GetWord(starts with)

GetWord, when given a string, returns a random word that starts with the given string.

Example, GetWord(starts with):

print GetWord(ess)

esteem

 

GetWordContains(word)

GetWordContains works almost exactly like GetWord(starts with) except that it returns a word that contains the string rather than starts with. Note that it might also return a word that starts with the string as it doesnt consider where in the word the substring begins.

 

GetNextWord()

GetNextWord works exactly like GetWord() except that when called multiple times it tries to find a word that hasnt been retrieved before. It does this by keeping an internal list of words returned from any GetNextWord statement.

 

Also available:

GetNextWord(length), GetNextWord(minLength, maxLength), GetNextWord(starts with), and GetNextWordContains(contains).

 

ResetNextWord

This is a statement that will clear the internal list of previously returned words used by the GetNextWord statements. This can be helpful at the start of the program so that if the program is run multiple times youre starting with a clean slate each time.