Mutant Basic

Programmer’s Reference Guide

 

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

Immediate Mode Statements. 2

QUIT. 2

SAVE. 2

LIST. 3

CLIST. 3

LABELS. 3

FIND term.. 3

RUN.. 3

VARS. 3

REM / UNREM... 3

RENUM... 3

NEW... 4

NEW range. 4

EDIT. 4

DEBUG.. 4

Program Statements. 4

About Basic Programs and Line Numbers. 4

Copying / Moving Program Lines. 4

CLS. 5

PRINT. 5

DEBUG.. 5

COLOR. 5

POSITION.. 6

UP, DOWN, LEFT, RIGHT, HOME. 6

FOR. 6

Edge-cases with FOR loops. 6

NEXT. 7

Scoped variables: 7

Scoped Variables in Nested Blocks. 7

INPUT. 8

GET. 8

DATA.. 8

READ.. 8

RESTORE. 8

RANDOMIZE. 9

REM... 9

UNREM... 9

GOTO.. 9

GOSUB. 10

RETURN.. 10

ON.. 10

Variables. 10

Strings vs Numbers. 10

Evaluation. 10

Evaluation Functions. 10

LET. 11

CLR. 12

CLR [variable list]. 12

Arrays. 12

Associative Arrays (Dictionaries). 12

Multi-dimensional Associative Arrays. 12

Array value types. 12

Environment Variables. 12

Undeclared / Undefined Variables. 13

Checking if a Variable is Defined. 13

ASCII Table. 13

Table of Characters. 13

Extended ASCII Table. 14

PETSCIIUPPER / PETSCIILOWER. 14

DEF FN – Function Definition. 14

Non-Blocking User Input. 14

What is Non-Blocking User Input?. 14

Mutant Basic’s variation on INKEY$. 14

Clearing INKEY$. 14

Database Operations. 15

Queries vs Commands. 15

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. 16

Changing existing data in a table. 16

Getting data from a table. 16

Checking query results. 16

Notes on SQL Statement Building. 16

Save States. 17

SaveState “key”. 17

LoadState “key” [varibale list]. 17

ClearState “key”. 17

States. 17

Save State Examples: 17

Extensions for Creating Word Games. 17

IsWord(“word”). 17

GetWord(). 17

GetWord(length). 18

GetWord(minLength, maxLength). 18

GetWord(“starts with”). 18

GetWordContains(“word”). 18

GetNextWord(). 18

ResetNextWord. 18

Other Functionality. 18

Key Polling. 18

Showfile "filename". 18

Lock. 18

Mutant Basic Script (Creating BOTs for Community). 18

Advice & Suggestions on Making Bots. 19

Debugging. 19

Usage Information. 19

UPRINT vs PRINT. 19

 

 

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 1970’s and 1980’s 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 it’s 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 Community.  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/community.html or you can telnet into the BBS at mutinybbs.com:23. 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 Mutant 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 they’re saying that is a bad thing.

However, even though Mutant Basic is also an interpreted language, it is not running on a 1970’s or 1980’s 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 can’t do things like high resolution graphics.  As with any BBS software you’re 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. 

 

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.

 

QUIT

Exits Mutant Basic

 

SAVE

Saves your program, giving you the option to add a description.  If there is no description then the program will be visible only to you otherwise it'll be in a "published" state and other users will be able to run it.  Outside of Mutant Basic, in the Files section of the BBS, you can use the commands "publish" and "unpublish" to control this visibility.

 

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.

 

CLIST

Continuous List.  If you want a continuous listing use "CLIST" instead of "LIST".  This can be handy for outputting your program to a text file or a printer.

LABELS

Lists all labels in your program.

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 doesn’t 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, …)

 

NEW

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

 

NEW range

Optionally you can supply a range of line numbers.  This will erase only those lines from your program rather than your entire program.

 

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 you’re 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 you’re 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.  

 

DEBUG

The "debug" immediate mode command toggles whether debugging is enabled.  Debugging is disabled by default and always disabled if the program is running from outside of the Mutant Basic development environment such as when a user runs it as a BBS door.  When debug mode is on then debug print statements are shown.  See the debug programming statement for more information.

 

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 you’ll 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 can’t insert a line between 12 and 13 (you can’t 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 haven’t 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 (or CBM) 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)

40 ? "test"

] RUN

THE SQUARE ROOT OF 49 IS 7

THE SQUARE ROOT OF 49 IS 7

THE SQUARE ROOT OF 49 IS 7

test

 

The semicolon (;) symbol can be used to join elements but as you can see in the above example this symbol isn’t 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. 

 

The shortcut "?" can be used in place of the keyword "print" (as seen in line 40 above).

 

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.

 

DEBUG

A variation of "PRINT" which only happens if debug mode is on.  Debug mode is off by default and can be toggled with the "debug" immediate mode command.   Debug mode is always off if the program is running as a BBS door (outside of the Mutant Basic development environment).

This allows you to put debugging print statements in your program without having to go and remove them later. 

You can use the shortcut "??" in place of the keyword "debug".

Example, debug prints:

                10 ? "This will show up always"
                20 ?? "This will only show up if debugging mode is on"
                ] RUN
                This will show up always
                ] DEBUG
                Debug=True
                ] RUN
                This will show up always
                This will only show up if debugging mode is on

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 X’s 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, HOME

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.

 

The "Home" statement can be used to position the cursor in the top/left corner.

 

CBM Mode users: in CBM mode you can use up, down, left, right and home (as well as CLS to clear the screen) but absolute positioning using POSITION will not work.  If you want your game to work in both ANSI and CBM mode (or only in CBM mode) then you will need to use relative positioning (up, down, left right) and occasionally send the cursor to "home" when the position of the cursor cannot be determined.

 

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 I”X”J”=”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 can’t 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 isn’t 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 won’t 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) you’re 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.

 

* CBM mode users can use the left arrow character () (in the top/left of the C64/128 keyboard) in place of underscore.

 

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 doesn’t 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 it’s 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$;”!  YOU’RE ”;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 can’t 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 “It’s “;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?

                It’s 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 generator’s 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 don’t 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 won’t 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 doesn’t 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 you’ve 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 that’s 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 won’t 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”.

LTRIM$(n$)
Removes whitespace characters from the left (start) of the string n$.
RTRIM(n$)
Removes whitespace characters from the right (end) of the string n$.
TRIM(n$)
Removes whitespace characters from both the left and right (start and end) of the string n$).
ROUND(n, d)
Rounds the number N to D decimal digits.  Can use 0 for D if you want a whole number.
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.

NL$([n])

Returns one or more newline characters.  The number n may be omitted if only one newline is desired.  The value of n must be between 1 and 100. 

REPLACE$(n$, a$, b$)

Returns a copy of n$ where occurrences of a$ within n$ are replaced by b$.  For example REPLACE$(“Hello (username)!”, “(username)”, “Bob”) returns “Hello Bob!”

COUNT(a$, b$)

Returns the number of times the string b$ occurs within the string a$.  For example COUNT(“The Cat in the Hat”, “at”) returns the number 2.  This is not case sensitive.

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).

MOD(n, p)

Returns the remainder after the division n/p.  Can also be expressed using the % symbol: r=22%7

GUID$([n])

Generates a new Globally Unique Identifier (GUID).  The optional n can limit the value to the first n characters (to up 32 which is the full length of the GUID.

SECONDS(dt1$, dt2$)

Returns the total number of seconds between date dt1$ and dt2$ (where dt1$ is the older of the two).  Will only work if the values can be parsed as dates, times, or date-times.

 

See also Extensions for creating word-based games for special word-dictionary functions.

 

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) there’s 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 (=).

 

CLR

Clears all variables from memory, except environment variables.


CLR [variable list]

Clears only the specified variables from memory, this can be one or more variable names separated by spaces

 

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 DIM’d (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. 

EMULATION$

Either “Ascii”, “Ansi”, “Cbm”, or "Atascii" depending on the user’s emulation type.

TERMROWS

The user’s terminal rows (usually 24).

TERMCOLS

The user’s terminal columns (usually 40 or 80).

DATE$

The current date.

TIME$

The current time (UTC).

TICKS

The number of ticks on the BBS’s 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 don’t have values set.  For example you can run the command “PRINT A” even if A doesn’t 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

1

23

45

-

67

C

89

Y

111

o

2

24

46

.

68

D

90

Z

112

p

3

25

47

/

69

E

91

[

113

q

4

26

48

0

70

F

92

\

114

r

5

27

49

1

71

G

93

]

115

s

6

28

50

2

72

H

94

^

116

t

7

N/A

29

51

3

73

I

95

_

117

u

8

N/A

30

52

4

74

J

96

`

118

v

9

N/A

31

53

5

75

K

97

a

119

w

10

N/A

32

(space)

54

6

76

L

98

b

120

x

11

N/A

33

!

55

7

77

M

99

c

121

y

12

N/A

34

"

56

8

78

N

100

d

122

z

13

N/A

35

#

57

9

79

O

101

e

123

{

14

36

$

58

:

80

P

102

f

124

|

15

37

%

59

;

81

Q

103

g

125

}

16

38

&

60

< 

82

R

104

h

126

~

17

39

'

61

=

83

S

105

i

127

18

40

(

62

> 

84

T

106

j

 

 

19

41

)

63

?

85

U

107

k

 

 

20

42

*

64

@

86

V

108

l

 

 

21

§

43

+

65

A

87

W

109

m

 

 

22

44

,

66

B

88

X

110

n

 

 


 

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 isn’t a space.  Characters shown as N/A will print a “?” as that character is not available.

 

Note 1: Characters highlighted in yellow have been mapped to equivalent characters for Atascii and Petscii (CBM) mode.  Use the extended character numbers above and the characters should show up in all emulation modes.

Note 2: Characters highlighted in blue have been mapped to equivalent characters for Atascii and a close approximation for Petscii (CBM) mode.

Note 3: For CBM mode only the horizontal line, vertical line, and playing card characters (heart, diamond, spade, club) requires the user be in uppercase mode.  Use the statement "petsciiupper" to do this and "petsciilower" to revert to lowercase.

 

PETSCIIUPPER / PETSCIILOWER

This statement has no effect for users who are not using CBM emulation mode.  For CBM users this puts the output into uppercase-petscii mode.  The user is returned to lowercase mode as soon as the program completes or when the PETSCIILOWER statement is executed.

 

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 couldn’t 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 isn’t pressing a key then you need some kind of non-blocking input.

 

Mutant Basic’s 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 user’s 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 it’s 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).

 

Clearing INKEY$

It may be necessary to clear the value of INKEY$ after using it if you are going back and forth between using INKEY$ and INPUT, otherwise INPUT will automatically prepend the value of INKEY$.  To do this just use this empty assignment:

                ] INKEY$=

This will erase the value of INKEY$ until a new value is added automatically when the user presses a key.

 

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 we’ll go over the Commands as you’ll need to run some commands to set up your database before you can start getting (“Selecting”) data using Queries.

 

Creating a database

You can use any number of database files provided they are in the same directory as your Basic program.  These database files do not need to exist prior to using them; if they don’t exist they will be created as soon as you try to create the first table.  The way you specify which database file to use is by setting the variable DBFILE$.  A database file should have a .db extension.  If you don’t supply an extension, or if the extension is anything other than .db then “.db” will be appended to the filename automatically.

 

Example: Using a database file in Mutant Basic:

] DBFILE$=”gamedata”

                ] @ “create table games (Id integer, Name text)”

 

Switching to different database files is as easy as changing the value of DBFILE$.  The filename must contain only letters, numbers, or dots, therefore it is not possible to access database files outside of the directory that your Basic program is in.

 

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.

 

CBM Mode users: some SQL queries may require using the pipe (|) character, such as concatenating strings together:
select FirstName || ' ' || LastName from Users

Since the C64/128 do not have pipe characters you can use shift+minus instead.  Also the left-arrow key (top/left of keyboard) can be used in place of underscore.

 

Creating a table

Once you have a disk “inserted” then you can start creating tables to store data.  Let’s 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 player’s tank

Name

Text

What is the name of this player’s 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 it’s 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 didn’t 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.  We’ll 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 we’re 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 you’ll 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 player’s 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 won’t 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.

 

Save States

The purpose of integrating SQL into Mutant Basic is to provide a way to create turn-based, multiplayer, asynchronous games.  In order to make games like this you will need to be able to save data about the game and reload it later.  But after reading the previous section about using SQL you might feel a little overwhelmed.  However there is a much simpler way to save and load game data without knowing SQL at all, using Save States.

 

Save States also use the SQL engine but you don’t need to know any SQL to use it.  This feature boils down to just 4 commands: “savestate”, “loadstate”, “clearstate”, and “states”.  

 

Before you can use any of these commands you have to specify the name of the database file that will be used to store states, see “Creating a Database”.  Once you’ve done that you can issue any of these commands:

 

SaveState “key”

The key can be any string or a variable containing a string value.  The purpose of the key is to allow you to save and load multiple states.  For example you might use the USERNAME$ environment variable, this would let you save and loads states specific to one user (for single-player games). 

 

This command will store all of the global variables currently in memory into the SQL database with the given key.  This does not include environment variables or scoped variables.

 

LoadState “key” [varibale list]

This command will load all of the global variables into memory from SQL with the given key.  This will overwrite any current values in memory but will not clear out variables that are currently in memory but not in the state data.  If you want to clear all current variables first then use the CLR command first.

 

The optional variable list is a space-delimited list of variables names you want to load from the state.  If you do not provide a variable list then all global variables will be loaded from the state.  Specifying which variables to load from the state can be helpful to load some variables from a state without affecting others.

 

When loading arrays you need only specify the name of the array not the individual indices, see examples below.

 

ClearState “key”

This command removes the state data with the given key from the SQL database (if it exists).  This does not have any effect to the variables in memory.

 

States

This command can be used in immediate mode to list all of the saved states which are in the currently selected SQL database file.

 

Save State Examples:

Example 1:

] 10 dbfile$=”mygame.db”

] 20 score=100

] 30 loadstate USERNAME$ score

] 40 print “Welcome back, “;USERNAME$;”, your score is: “;str$(score)

… game code …

] 100 !Save

] 110 savestate USERNAME$

] 120 print “Your game is saved, thanks for playing!”

] 130 return

 

In this example the game state only consists of the variable “score” but as you can imagine many more variables may also make up a part of the game state.  On line 20 a default value is initialized to 100.  On line 30 we attempt to load variables from a state with the same name as the user.  If that state existed (and the database existed) then the score (and other global variables) would be updated as they are loaded from the saved state into memory.  If the state didn’t exist (such as this is the first time this user has played) then the score variable (and all other global variables) would stay as they are.  Line 110 shows how to save the state to the database.

 

Example 2:

                ] 10 dbfile$="mygame.db"
                ] 20 dim grid(10,10):playerx=0:playery=0:score=100:turnsremaining=5
                ] 30 loadstate USERNAME$ grid score playerx playery
                ] 40 print "Welcome back, ";USERNAME$;", you have ";turnsremaining;" turns remaining today and are currently at position (";playerx;",";playery;")"
                ... game code ...
                ] 100 !Save
                ] 110 savestate USERNAME$
                ] 120 print “Your game is saved, thanks for playing!”
                ] 130 return

In this example we have a game state made up of a 10x10 grid (the 2D array 'grid'), a position for the player on that grid (playerx,playery) which is initialized to (0,0), a score, and the number of turns remanining.  The values of the grid array, the player position (playerx and playery) and the score are updated from the state (if the state exists) while the turnsremaining value is left alone (even if it exists in the state data) so that it's always 5 when the program is ran.

 

Notice that the list of variables is only specified on the LOADSTATE statement and not SAVESTATE.  SAVESTATE always saves all global variables in memory even if you won't be loading them later.

 

 

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 doesn’t 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 hasn’t 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 you’re starting with a clean slate each time.

 

Other Functionality

Key Polling

Key Polling refers to a method Mutant Basic uses behind the scenes to try to detect if the user is trying to break out of the program using either Escape or CTRL+C.  This is not an easy thing to do over a terminal connection as we don't have direct access to query the state of the keyboard.  Without getting into too much detail here are the things you would need to keep in mind about this mechanism:

·         Key Polling is (normally) turned on automatically when the program starts and is turned off when it ends

·         If Key Polling is turned on (default) then you can break out of the program using ESC or CTRL+C.

·         It is not unusual that you may accidently get the program into an infinite loop during development and you need to be able to break out of the program, Key Polling makes this possible.

·         Any Mutant Basic program will auto-end itself after a total execution time of one hour (as an infinite loop protection)

·         With key polling on there are some side effects:

·         If you use the "showfile" statement (explained below), and if that file is long enough to warrant a page-pause (MORE? prompt) then the key polling will interfere with that pause and the text will scroll on by without waiting for the user to press a key.

·         When key polling is turned off (either at the end of the program or when the "polloff" statement is ran) then the user will need to press a key to continue.

If you know for sure that your program won't be entering an infinite loop and you are okay with disabling the ability to break out of the program with ESC or CTRL+C then you can disable polling altogether in your program.  The way this is accomplished is to have "polloff" as the first statement in your program.  It is highly recommended that you don't do this until you are done with development of  your program as accidently writing an infinite loop is a common occurance.

POLLON - Turns on key polling.  This is done automatically at the start of the program but using "POLLON" can be used to re-activate key polling after using the "POLLOFF" statement.

POLLOFF - Turns off key polling.  If this is the first statement in the program then key polling will not be turned on automatically but it can still be turned on manually using the "POLLON" statement.

Showfile "filename"

The "showfile" statement can be used to display the contents of a file you have in the same directory as the basic program.  This can be helpful to draw a static game board prior to placing game pieces or to show the game's documentation without having to code a large set of print statements.

Note: If your text file might be more than one screen full you may need to use POLLOFF prior to showing the file and POLLON after.  Remember key-polling is turned on at the start of a program and is necessary for the user to be able to break out of infinite loops with either ESC or CTRL+C.

Lock

The "lock" statement can be used to prevent multiple users from running your Basic program at the same time.  Some programs may end up with corrupted game state if there are more than one user playing at once.  If your game requires a lock such as this it is generally best to have the "lock" statement at, or near, the start of your program.  The lock is created when the program is run and only removed once the program has completed.  There is no "unlock" statement.

 

Mutant Basic Script (Creating BOTs for Community)

 

As of Community version 4.4 it is now possible for users to create bots (robots) which will take user input from the chat room, process it (using Mutant Basic) and spit out some result which is shown to all users currently online in that chat room (except users in Do Not Disturb mode).

 

As described earlier in this document the way you create a new Mutant Basic program on Community is to go to the files browser (/files), go to your home directory (create it if you haven’t done so yet), and then edit a file with a .BAS extension. (edit mygame.bas)  Mutant Basic Scripts work in a similar way but instead of using the .BAS extension you use the .BOT extension.

 

What is the difference between a normal Mutant Basic program and Mutant Basic Script bot?  Basically it boils down to the following limitations:

·         A bot script can’t use inkey$, input, or get to get user input.

·         A bot script will automatically end as soon as it does a PRINT or UPRINT.  The difference between PRINT and UPRINT are explained below.

·         A bot script must complete execution in 5 seconds otherwise it will be canceled.

 

Here is the justification for having these limitations:  The script is supposed to take one string as input, do some processing through whatever Basic algorithm you come up with, and result in one string as a response.  It should not lock up the system, start spamming the channel, or create resource leaks.  Therefore the only input it can use and work with is the text that came from the command the user used to launch the bot (which is CHAT$ and explained below).  No further inputting may be done and only one print so you can’t have a FOR loop that prints “you suck Jim” 1000 times.  You can print “you suck Jim” if you want but only once.  Also your script can’t eat up excessive processing time with an infinite (or nearly infinite) loop, this is why the script must complete in 5 seconds.

 

In addition to these limitations there also a few extra things available to you which are not available while running a normal Mutant Basic program:

·         The Environment Variable SCRIPTNAME$ contains the name of the bot being ran.

·         The Environment Variable CHANNEL$ contains the name of the channel the bot is being run from.

·         The Global Variable CHAT$ is automatically populated.  This is the text passed into the bot by the user running the bot.  This is the text after the bot command.  For example if the user launch the bot using “/foobot move a3 to b7” then the environment variable CHAT$ would be “move a3 to b7”

·         The Environment Variable DEBUGGING contains a “1” if you are running the bot from within Mutant Basic or from the files command line.  If the bot is running as a result of a bot-launch command from a chat room then the value will be “0”.  This environment variable is used internally to determine if the PRINT statement will be shown to all users in the channel or only to you.

·         The command UPRINT (for user-print) can be used to send the response ONLY to the user that called the bot and not to the entire channel.  PRINT will send the response to the entire channel.

 

Example, creating a simple bot:

edit foo.bot

] 10 if CHAT$=”” then uprint “Hello,”;USERNAME$;”, please type something after ‘/foobot’”

] 20 print “Hello, “;USERNAME$;”, you said ‘”;CHAT$;”’”

] save

 (add description to make it published)

 

Example, running the bot:

(from chat room)

/foobot

<FOOBOT> Hello, Jimbob, please type something after ‘/foobot’

   This is shown only to the user that called the bot (Jimbob)

 

/foobot Why Hello There!

<FOOBOT> Hello, Jimbob, you said ‘Why Hello There!’

  Everyone in the channel will see this response

 

NOTE: Since a scripted bot exits as soon as it does a print you may have to build up a string to print rather than having multiple print statements.  Also if you need a newline, whereas normally you would do that with just a simple “print” command, you can use the nl$() expression such as this:

100 print “Game Over, “;USERNAME$;nl$();”You scored:”;str$(score);nl$(2);”Goodbye!”

This will print:

Game Over, Jimbob

You scored: 323

 

Goodbye!

 

Advice & Suggestions on Making Bots

Debugging

When debugging you can’t change the input string (CHAT$) because that’s an environment variable which is set when a user calls the bot.  Therefore a good approach is, early on in your program probably after loading state (if you use state loading), is to copy CHAT$ to a standard, global, variable and reference that from that point on.  For example:

 

] 10 loadstate(“mystate”)
] 20 inp$=CHAT$

] 30 print “You said, “;inp$

 

Then when you want to debug you can do this:

 

] rem 20

] 25 inp$=”foobar”

] list

 

10 loadstate(“mystate”)

20 rem inp$=CHAT$

25 inp$=”foobar”

30 print “You said, “;inp$

 

 

Usage Information

Although not required there is a convention with BOTS that if CHAT$ is empty (if CHAT$=””) then the response is a) only shown to the user calling the bot, using UPRINT and b) shows how to use the bot.

] 10 if CHAT$=”” then UPRINT “Usage: ‘/quotebot quote’, where ‘quote’ is some quote you want me to record.”

] 20 quote$=CHAT$

] 30 dbfile$=”quotes.db”

] 40 savestate (USERNAME$)

] 50 print USERNAME$;” added the quote: ‘”;quote$;”’”

 

 

UPRINT vs PRINT

As explained earlier PRINT will show the output to everyone in the channel and UPRINT will only show the message to the user who launched the bot.  If your bot is performing some kind of game you may need to use UPRINT to show information to one player while not exposing it to the others.  For example if you were making a POKERBOT you would only want to show the player’s hand to the player that called the bot, not to everyone:

] 100 if CHAT$=”hand” then UPRINT “You are holding: “;cards$

] 110 if LEFT$(CHAT$,3) = “bet” then PRINT USERNAME$;” bets “;MID$(CHAT$,4,LEN(CHAT$)-3);” dollars!”