From 57f48d3e72da9cc7961ed52eeb7da49c84ca3768 Mon Sep 17 00:00:00 2001 From: JiFish Date: Sun, 29 Nov 2020 02:19:16 +0000 Subject: [PATCH 001/183] Add new functions: CHR$, ASC, STR$, MID$, IFF, IF$. Also add modulo operator. --- README.md | 82 ++++++++++++++++++++++++++++++++++++++++-------- basicparser.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++++-- basictoken.py | 19 +++++++++--- 3 files changed, 166 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 570ab6e..6994fa5 100644 --- a/README.md +++ b/README.md @@ -12,35 +12,47 @@ The interpreter is a homage to the home computers of the early 1980s, and when e typical of such a home computer. Commands to run, list, save and load BASIC programs can be entered at the prompt as well as program statements themselves. -The BASIC dialect that has been implemented is slightly simplified, and naturally avoids machine specific instructions, such as those -concerned with sound and graphics for example. It allows a limited range of arithmetic expressions composed of multiplication, division, -addition and subtraction (including the use of parentheses to change precedence), thus: +The BASIC dialect that has been implemented is slightly simplified, and naturally avoids machine specific instructions, +such as those concerned with sound and graphics for example. + +There is reasonably comprehensive error checking. Syntax errors will be picked up and reported on by the +lexical analyser as statements are entered. Runtime errors will highlight the cause and the line number of +the offending statement. + +The interpreter can be invoked as follows: + +``` +$ python interpreter.py +``` + +## Operators + +A limited range of arithmetic expressions are provided. Addition and subtraction have the lowest precedence, +but this can be changed with parentheses. + +* **+** - Addition +* **-** - Subtraction +* **\*** - Multiplication +* **/** - Division +* **MOD** (or **%**) - Modulo ``` > 10 PRINT 2 * 3 > 20 PRINT 20 / 10 > 30 PRINT 10 + 10 > 40 PRINT 10 - 10 +> 50 PRINT 15 MOD 10 > RUN 6 2 20 0 +5 > ``` Additional numerical operations may be performed using numeric functions (see below). -There is reasonably comprehensive error checking. Syntax errors will be picked up and reported on by the -lexical analyser as statements are entered. Runtime errors will highlight the cause and the line number of -the offending statement. - -The interpreter can be invoked as follows: - -``` -$ python interpreter.py -``` - ## Commands Programs may be listed using the **LIST** command: @@ -494,6 +506,24 @@ Allowable relational operators are: * '>=' (greater than or equal) * '<>' (not equal) +### Ternary Functions + +As an alternative to branching, Ternary functions are provided. The same relational operators are used. + +* **IFF**(x, y, z) - Evaluates *x* and returns *y* if true, otherwise returns *z*. *y* and *z* are expected to be numeric. +* **IF$**(x, y, z) - As above, but *y* and *z* are expected to be strings. + +``` +> 10 LET I = 10 +> 20 LET J = 5 +> 30 PRINT IF$(I > J, "I is greater than J", "I is not greater than J") +> 40 K = IFF(I > J, 20, 30) +> 50 PRINT K +> RUN +I is greater than J +20 +``` + ### User input The **INPUT** statement is used to solicit input from the user: @@ -599,6 +629,32 @@ Random integers can be generated by combining **RND** and **INT**: e.g. * **TAN**(x) - Calculates the tangent of *x*, where *x* is an angle in radians +### String Manipulation + +Some functions are provided to help you manipulate strings. Functions that return a string +have a '$' suffix like string variables. + +The functions are: + +* **ASC**(x$) - Returns the character code for *x$*. *x$* is expected to be a single character. +Note that despite the name, this function can return codes outside the ASCII range. +* **CHR$**(x) - Returns the character specified by character code *x*. +* **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y* and ending at *z*. *z* can +be omitted to get the rest of the string. If *y* or *z* is negative, the position will instead be +counted from the end of the string +* **STR$**(x) - Returns a string representation of numeric value *x*. + +Examples for **ASC**, **CHR$** and **STR$** +``` +> 10 I = 65 +> 20 J$ = CHR$(I) + " - " + STR$(I) +> 30 PRINT J$ +> 40 PRINT ASC("Z") +RUN +A - 65 +90 +``` + ## Example programs A number of example BASIC programs have been supplied in the repository: diff --git a/basicparser.py b/basicparser.py index 2524614..991f462 100644 --- a/basicparser.py +++ b/basicparser.py @@ -560,7 +560,7 @@ def __term(self): # minuses self.__factor() # Leaves value of term on top of stack - while self.__token.category in [Token.TIMES, Token.DIVIDE]: + while self.__token.category in [Token.TIMES, Token.DIVIDE, Token.MODULO]: savedcategory = self.__token.category self.__advance() self.__sign = 1 # Initialise sign @@ -571,9 +571,12 @@ def __term(self): if savedcategory == Token.TIMES: self.__operand_stack.append(leftoperand * rightoperand) - else: + elif savedcategory == Token.DIVIDE: self.__operand_stack.append(leftoperand / rightoperand) + else: + self.__operand_stack.append(leftoperand % rightoperand) + def __factor(self): """Evaluates a numerical expression and leaves its value on top of the @@ -954,6 +957,53 @@ def __evaluate_function(self, category): raise ValueError("Invalid value supplied to POW in line " + str(self.__line_number)) + if category == Token.TERNARY: + self.__consume(Token.LEFTPAREN) + + self.__relexpr() + condition = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + whentrue = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + whenfalse = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + return whentrue if condition else whenfalse + + if category == Token.MID: + self.__consume(Token.LEFTPAREN) + + self.__expr() + instring = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + start = self.__operand_stack.pop() + + if self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + end = self.__operand_stack.pop() + else: + end = None + + self.__consume(Token.RIGHTPAREN) + + try: + return instring[start:end] + + except TypeError: + raise TypeError("Invalid type supplied to MID$ in line " + + str(self.__line_number)) + self.__consume(Token.LEFTPAREN) self.__expr() @@ -1033,6 +1083,36 @@ def __evaluate_function(self, category): raise ValueError("Invalid value supplied to TAN in line " + str(self.__line_number)) + elif category == Token.CHR: + try: + if value < 0 or value > 255: + raise ValueError("Value supplied to CHR$ out of range in line " + + str(self.__line_number)) + return chr(value) + + except TypeError: + raise TypeError("Invalid type supplied to CHR$ in line " + + str(self.__line_number)) + + except ValueError: + raise ValueError("Invalid value supplied to CHR$ in line " + + str(self.__line_number)) + + elif category == Token.ASC: + try: + return ord(value) + + except TypeError: + raise TypeError("Invalid type supplied to ASC in line " + + str(self.__line_number)) + + except ValueError: + raise ValueError("Invalid value supplied to ASC in line " + + str(self.__line_number)) + + elif category == Token.STR: + return str(value) + else: raise SyntaxError("Unrecognised function in line " + str(self.__line_number)) diff --git a/basictoken.py b/basictoken.py index 4ece6d0..1a550aa 100644 --- a/basictoken.py +++ b/basictoken.py @@ -89,6 +89,12 @@ class BASICToken: DATA = 56 # DATA keyword READ = 57 # READ keyword INT = 58 # INT function + CHR = 59 # CHR$ function + ASC = 60 # ASC function + STR = 61 # STR$ function + MID = 62 # MID$ function + MODULO = 63 # MODULO operator + TERNARY = 64 # TERNARY functions # Displayable names for each token category catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN', @@ -101,14 +107,15 @@ class BASICToken: 'UNSIGNEDFLOAT', 'STRING', 'TO', 'NEW', 'EQUAL', 'COMMA', 'STOP', 'COLON', 'ON', 'POW', 'SQR', 'ABS', 'DIM', 'RANDOMIZE', 'RND', 'ATN', 'COS', 'EXP', - 'LOG', 'SIN', 'TAN', 'DATA', 'READ', 'INT'] + 'LOG', 'SIN', 'TAN', 'DATA', 'READ', 'INT', + 'CHR', 'ASC', 'STR', 'MID', 'MODULO', 'TERNARY'] smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN, '+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE, '\n': NEWLINE, '<': LESSER, '>': GREATER, '<>': NOTEQUAL, '<=': LESSEQUAL, '>=': GREATEQUAL, ',': COMMA, - ':': COLON} + ':': COLON, '%': MODULO} # Dictionary of BASIC reserved words keywords = {'LET': LET, 'LIST': LIST, 'PRINT': PRINT, @@ -123,10 +130,14 @@ class BASICToken: 'RANDOMIZE': RANDOMIZE, 'RND': RND, 'ATN': ATN, 'COS': COS, 'EXP': EXP, 'LOG': LOG, 'SIN': SIN, 'TAN': TAN, - 'DATA': DATA, 'READ': READ, 'INT': INT} + 'DATA': DATA, 'READ': READ, 'INT': INT, + 'CHR$': CHR, 'ASC': ASC, 'STR$': STR, + 'MID$':MID, 'MOD': MODULO, + 'IF$':TERNARY, 'IFF': TERNARY} # Functions - functions = {ABS, ATN, COS, EXP, INT, LOG, POW, RND, SIN, SQR, TAN} + functions = {ABS, ATN, COS, EXP, INT, LOG, POW, RND, SIN, SQR, TAN, + CHR, ASC, MID, TERNARY, STR} def __init__(self, column, category, lexeme): From 88db8440a5f494b7542355b929d332c124bbd9a2 Mon Sep 17 00:00:00 2001 From: JiFish Date: Sun, 29 Nov 2020 02:58:51 +0000 Subject: [PATCH 002/183] README.md fixes and reword --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6994fa5..aaab1ee 100644 --- a/README.md +++ b/README.md @@ -490,7 +490,7 @@ called, otherwise execution continues to the next statement without making the c > 30 ON I > J GOSUB 100 > 40 STOP > 100 REM THE SUBROUTINE -> 110 PRINT "I is greater thn J" +> 110 PRINT "I is greater than J" > 120 RETURN > RUN I is greater than J @@ -629,7 +629,7 @@ Random integers can be generated by combining **RND** and **INT**: e.g. * **TAN**(x) - Calculates the tangent of *x*, where *x* is an angle in radians -### String Manipulation +### String functions Some functions are provided to help you manipulate strings. Functions that return a string have a '$' suffix like string variables. @@ -640,8 +640,8 @@ The functions are: Note that despite the name, this function can return codes outside the ASCII range. * **CHR$**(x) - Returns the character specified by character code *x*. * **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y* and ending at *z*. *z* can -be omitted to get the rest of the string. If *y* or *z* is negative, the position will instead be -counted from the end of the string +be omitted to get the rest of the string. If *y* or *z* are negative, the position is counted +backwards from the end of the string. * **STR$**(x) - Returns a string representation of numeric value *x*. Examples for **ASC**, **CHR$** and **STR$** From 82587c0ba49fef262d6f1ed344b48f7adde378e7 Mon Sep 17 00:00:00 2001 From: JiFish Date: Sun, 29 Nov 2020 03:12:53 +0000 Subject: [PATCH 003/183] Remove accidentally committed fork code from chr function --- basicparser.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/basicparser.py b/basicparser.py index 991f462..5f99a10 100644 --- a/basicparser.py +++ b/basicparser.py @@ -1085,9 +1085,6 @@ def __evaluate_function(self, category): elif category == Token.CHR: try: - if value < 0 or value > 255: - raise ValueError("Value supplied to CHR$ out of range in line " + - str(self.__line_number)) return chr(value) except TypeError: From 0505d505d2e0da57ba0c0cf50888897394684941 Mon Sep 17 00:00:00 2001 From: JiFish Date: Sun, 29 Nov 2020 14:13:35 +0000 Subject: [PATCH 004/183] Add VAL, LEN, UPPER$ and LOWER$ functions. Update Informal grammar definition. --- README.md | 27 ++++++++++++++++++++++++++- basicparser.py | 33 +++++++++++++++++++++++++++++++++ basictoken.py | 15 +++++++++++---- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index aaab1ee..227e809 100644 --- a/README.md +++ b/README.md @@ -639,10 +639,14 @@ The functions are: * **ASC**(x$) - Returns the character code for *x$*. *x$* is expected to be a single character. Note that despite the name, this function can return codes outside the ASCII range. * **CHR$**(x) - Returns the character specified by character code *x*. +* **LEN**(x$) - Returns the length of *x$*. +* **LOWER$**(x$) - Returns a lower-case version of *x$*. * **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y* and ending at *z*. *z* can be omitted to get the rest of the string. If *y* or *z* are negative, the position is counted backwards from the end of the string. * **STR$**(x) - Returns a string representation of numeric value *x*. +* **UPPER$**(x$) - Returns an upper-case version of *x$* +* **VAL**(x$) - Attempts to convert *x$* to a numeric value. If *x$* is not numeric, returns 0. Examples for **ASC**, **CHR$** and **STR$** ``` @@ -677,8 +681,12 @@ as we all would have done in the 1980s!* **ABS**(*numerical-expression*) - Calculates the absolute value of the result of *numerical-expression* +**ASC**(*string-expression*) - Returns the character code of the result of *string-expression*. + **ATN**(*numerical-expression*) - Calculates the arctangent value of the result of *numerical-expression* +**CHR$**(*numerical-expression*) - Returns the character specified by character code of the result of *numerical-expression*. + **COS**(*numerical-expression*) - Calculates the cosine value of the result of *numerical-expression* **DATA**(*expression-list*) - Defines a list of string or numerical values @@ -697,20 +705,31 @@ as we all would have done in the 1980s!* **IF** *relational-expression* **THEN** *line-number* [**ELSE** *line-number*] - Conditional branch +**IFF**(*relational-expression*, *numeric-expression*, *numeric-expression*) - Evaluates *relational-expression* and returns the value of the result of the first *numeric-expression* if true, or the second if false. + +**IF$**(*relational-expression*, *string-expression*, *string-expression*) - Evaluates *relational-expression* and returns the value of the result of the first *string-expression* if true, or the second if false. + **INPUT** [*input-prompt*:] *simple-variable-list* - Processes user input presented as a comma separated list +**LEN**(*string-expression*) - Returns the length of the result of *string-expression* + [**LET**] *variable* = *numeric-expression* | *string-expression* - Assigns a value to a simple variable or array variable **LIST** - Lists the program **LOAD** *filename* - Loads a program from disk +**LOWER$**(*string-expression*) - Returns a lower-case version of the result of *string-expression*. + **LOG**(*numerical-expression*) - Calculates the natural logarithm value of the result of *numerical-expression* **NEW** - Clears the program from memory **NEXT** *loop-variable* - See **FOR** statement +**MID$**(*string-expression*, *start-position*[, *end-position*]) - Takes the result of *string-expression* and returns part of it, starting at position *start-position*, and ending at *end-position*. *end-position* can +be omitted to get the rest of the string. If *start-position* or *end-position* are negative, the position is counted backwards from the end of the string. + **ON** *relational-expression* **GOSUB** *line-number* - Conditional subroutine call **POW**(*base*, *exponent*) - Calculates the result of raising the base to the power of the exponent @@ -718,7 +737,7 @@ as we all would have done in the 1980s!* **PRINT** *print-list* - Prints a comma separated list of literals or variables **RANDOMIZE** [*numeric-expression*] - Resets random number generator to an unpredictable sequence. With -optional seed (*numeric expression*), the sequence is predictable. +optional seed (*numeric expression*), the sequence is predictable. **READ** *simple-variable-list* - Reads a set of constants into the list of variables. @@ -738,8 +757,14 @@ optional seed (*numeric expression*), the sequence is predictable. **STOP** - Terminates a program +**STR$**(*numerical-expression*) - Returns a string representation of the result of *numerical-expression* + **TAN**(*numerical-expression*) - Calculates the tangent value of the result of *numerical-expression* +**UPPER$**(*string-expression*) - Returns an upper-case version of the result of *string-expression* + +**VAL**(*string-expression*) - Attempts to convert the result of *string-expression* to a numeric value. If it is not numeric, returns 0. + ## Architecture The interpreter is implemented using the following Python classes: diff --git a/basicparser.py b/basicparser.py index 5f99a10..a544dea 100644 --- a/basicparser.py +++ b/basicparser.py @@ -1110,6 +1110,39 @@ def __evaluate_function(self, category): elif category == Token.STR: return str(value) + elif category == Token.VAL: + try: + numeric = float(value) + if numeric.is_integer(): + return int(numeric) + return numeric + + # Like other BASIC variants, non-numeric strings return 0 + except ValueError: + return 0 + + elif category == Token.LEN: + try: + return len(value) + + except TypeError: + raise TypeError("Invalid type supplied to LEN in line " + + str(self.__line_number)) + + elif category == Token.UPPER: + if not isinstance(value, str): + raise TypeError("Invalid type supplied to UPPER$ in line " + + str(self.__line_number)) + + return value.upper() + + elif category == Token.LOWER: + if not isinstance(value, str): + raise TypeError("Invalid type supplied to LOWER$ in line " + + str(self.__line_number)) + + return value.lower() + else: raise SyntaxError("Unrecognised function in line " + str(self.__line_number)) diff --git a/basictoken.py b/basictoken.py index 1a550aa..8d90e3c 100644 --- a/basictoken.py +++ b/basictoken.py @@ -95,6 +95,10 @@ class BASICToken: MID = 62 # MID$ function MODULO = 63 # MODULO operator TERNARY = 64 # TERNARY functions + VAL = 65 # VAL function + LEN = 66 # LEN function + UPPER = 67 # UPPER function + LOWER = 68 # LOWER function # Displayable names for each token category catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN', @@ -108,7 +112,8 @@ class BASICToken: 'COMMA', 'STOP', 'COLON', 'ON', 'POW', 'SQR', 'ABS', 'DIM', 'RANDOMIZE', 'RND', 'ATN', 'COS', 'EXP', 'LOG', 'SIN', 'TAN', 'DATA', 'READ', 'INT', - 'CHR', 'ASC', 'STR', 'MID', 'MODULO', 'TERNARY'] + 'CHR', 'ASC', 'STR', 'MID', 'MODULO', 'TERNARY', + 'VAL', 'LEN', 'UPPER', 'LOWER'] smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN, '+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE, @@ -132,12 +137,14 @@ class BASICToken: 'LOG': LOG, 'SIN': SIN, 'TAN': TAN, 'DATA': DATA, 'READ': READ, 'INT': INT, 'CHR$': CHR, 'ASC': ASC, 'STR$': STR, - 'MID$':MID, 'MOD': MODULO, - 'IF$':TERNARY, 'IFF': TERNARY} + 'MID$': MID, 'MOD': MODULO, + 'IF$': TERNARY, 'IFF': TERNARY, + 'VAL': VAL, 'LEN': LEN, + 'UPPER$': UPPER, 'LOWER$': LOWER} # Functions functions = {ABS, ATN, COS, EXP, INT, LOG, POW, RND, SIN, SQR, TAN, - CHR, ASC, MID, TERNARY, STR} + CHR, ASC, MID, TERNARY, STR, VAL, LEN, UPPER, LOWER} def __init__(self, column, category, lexeme): From 2555798f58485317470e2773f925eb96562cb149 Mon Sep 17 00:00:00 2001 From: JiFish Date: Sat, 5 Dec 2020 23:28:34 +0000 Subject: [PATCH 005/183] Adds ROUND, MIN and MAX functions --- README.md | 19 +++++++++++++++++++ basicparser.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ basictoken.py | 12 +++++++++--- 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 227e809..889160e 100644 --- a/README.md +++ b/README.md @@ -599,6 +599,18 @@ Allowable numeric functions are: * **LOG**(x) - Calculates the natural logarithm of *x* +* **MAX**(x, y[, z]...) - Returns the highest value from a list of expressions + +* **MIN**(x, y[, z]...) - Returns the lowest value from a list of expressions + +``` +> 10 PRINT MAX(-2, 0, 1.5, 4) +> 20 PRINT MIN(-2, 0, 1.5, 4) +> RUN +> 4 +> -2 +``` + * **POW**(x, y) - Calculates *x* to the power *y* * **RND** - Generates a pseudo random number N, where *0 <= N < 1*. Can be @@ -622,6 +634,7 @@ Random integers can be generated by combining **RND** and **INT**: e.g. 6 > ``` +* **ROUND**(x) - Rounds number to the nearest integer. * **SIN**(x) - Calculates the sine of *x*, where *x* is an angle in radians @@ -727,9 +740,13 @@ as we all would have done in the 1980s!* **NEXT** *loop-variable* - See **FOR** statement +**MAX**(*expression-list*) - Returns the highest value in *expression-list* + **MID$**(*string-expression*, *start-position*[, *end-position*]) - Takes the result of *string-expression* and returns part of it, starting at position *start-position*, and ending at *end-position*. *end-position* can be omitted to get the rest of the string. If *start-position* or *end-position* are negative, the position is counted backwards from the end of the string. +**MIN**(*expression-list*) - Returns the lowest value in *expression-list* + **ON** *relational-expression* **GOSUB** *line-number* - Conditional subroutine call **POW**(*base*, *exponent*) - Calculates the result of raising the base to the power of the exponent @@ -747,6 +764,8 @@ optional seed (*numeric expression*), the sequence is predictable. **RND** - Generates a pseudo random number N, where 0 <= N < 1 +**ROUND**(*numerical-expression*) - Rounds *numerical-expression* to the nearest integer + **RUN** - Runs the program **SAVE** *filename* - Saves a program to disk diff --git a/basicparser.py b/basicparser.py index a544dea..bc94088 100644 --- a/basicparser.py +++ b/basicparser.py @@ -937,6 +937,46 @@ def __evaluate_function(self, category): if category == Token.RND: return random.random() + if category == Token.MAX: + self.__consume(Token.LEFTPAREN) + + self.__expr() + value_list = [self.__operand_stack.pop()] + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + value_list.append(self.__operand_stack.pop()) + + self.__consume(Token.RIGHTPAREN) + + try: + return max(*value_list) + + except TypeError: + raise TypeError("Invalid type supplied to MAX in line " + + str(self.__line_number)) + + if category == Token.MIN: + self.__consume(Token.LEFTPAREN) + + self.__expr() + value_list = [self.__operand_stack.pop()] + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + value_list.append(self.__operand_stack.pop()) + + self.__consume(Token.RIGHTPAREN) + + try: + return min(*value_list) + + except TypeError: + raise TypeError("Invalid type supplied to MIN in line " + + str(self.__line_number)) + if category == Token.POW: self.__consume(Token.LEFTPAREN) @@ -1059,6 +1099,14 @@ def __evaluate_function(self, category): raise ValueError("Invalid value supplied to INT in line " + str(self.__line_number)) + elif category == Token.ROUND: + try: + return round(value) + + except TypeError: + raise TypeError("Invalid type supplied to LEN in line " + + str(self.__line_number)) + elif category == Token.LOG: try: return math.log(value) diff --git a/basictoken.py b/basictoken.py index 8d90e3c..05a0340 100644 --- a/basictoken.py +++ b/basictoken.py @@ -99,6 +99,9 @@ class BASICToken: LEN = 66 # LEN function UPPER = 67 # UPPER function LOWER = 68 # LOWER function + ROUND = 69 # ROUND function + MAX = 70 # MAX function + MIN = 71 # MIN function # Displayable names for each token category catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN', @@ -113,7 +116,8 @@ class BASICToken: 'DIM', 'RANDOMIZE', 'RND', 'ATN', 'COS', 'EXP', 'LOG', 'SIN', 'TAN', 'DATA', 'READ', 'INT', 'CHR', 'ASC', 'STR', 'MID', 'MODULO', 'TERNARY', - 'VAL', 'LEN', 'UPPER', 'LOWER'] + 'VAL', 'LEN', 'UPPER', 'LOWER', 'ROUND', + 'MAX', 'MIN'] smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN, '+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE, @@ -140,11 +144,13 @@ class BASICToken: 'MID$': MID, 'MOD': MODULO, 'IF$': TERNARY, 'IFF': TERNARY, 'VAL': VAL, 'LEN': LEN, - 'UPPER$': UPPER, 'LOWER$': LOWER} + 'UPPER$': UPPER, 'LOWER$': LOWER, + 'ROUND': ROUND, 'MAX': MAX, 'MIN': MIN} # Functions functions = {ABS, ATN, COS, EXP, INT, LOG, POW, RND, SIN, SQR, TAN, - CHR, ASC, MID, TERNARY, STR, VAL, LEN, UPPER, LOWER} + CHR, ASC, MID, TERNARY, STR, VAL, LEN, UPPER, LOWER, + ROUND, MAX, MIN} def __init__(self, column, category, lexeme): From 40c9b82bc76c2c76ca45225d34e5bb0192f4a712 Mon Sep 17 00:00:00 2001 From: JiFish Date: Sun, 6 Dec 2020 02:31:58 +0000 Subject: [PATCH 006/183] Adds INSTR function --- README.md | 6 ++++++ basicparser.py | 34 ++++++++++++++++++++++++++++++++++ basictoken.py | 8 +++++--- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 889160e..ba0e0c5 100644 --- a/README.md +++ b/README.md @@ -647,11 +647,15 @@ Random integers can be generated by combining **RND** and **INT**: e.g. Some functions are provided to help you manipulate strings. Functions that return a string have a '$' suffix like string variables. +Note that unlike some other BASIC variants, string positions start at *0*. + The functions are: * **ASC**(x$) - Returns the character code for *x$*. *x$* is expected to be a single character. Note that despite the name, this function can return codes outside the ASCII range. * **CHR$**(x) - Returns the character specified by character code *x*. +* **INSTR**(x$, y$[, start[, end]]) - Returns position of *y$* inside *x$*, optionally start searching +at position *start* and end at *end*. Returns -1 if no match found. * **LEN**(x$) - Returns the length of *x$*. * **LOWER$**(x$) - Returns a lower-case version of *x$*. * **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y* and ending at *z*. *z* can @@ -724,6 +728,8 @@ as we all would have done in the 1980s!* **INPUT** [*input-prompt*:] *simple-variable-list* - Processes user input presented as a comma separated list +**INSTR**(*hackstack-string-expression*, *needle-string-expression*[, *start-numeric-expression*[, *end-numeric-expression*]]) - Returns position of first *needle-string-expression* inside first *hackstack-string-expression*, optionally start searching at position given by *start-numeric-expression* and optionally ending at position given by *end-numeric-expression*. Returns -1 if no match found. + **LEN**(*string-expression*) - Returns the length of the result of *string-expression* [**LET**] *variable* = *numeric-expression* | *string-expression* - Assigns a value to a simple variable or array variable diff --git a/basicparser.py b/basicparser.py index bc94088..df55b3b 100644 --- a/basicparser.py +++ b/basicparser.py @@ -1044,6 +1044,40 @@ def __evaluate_function(self, category): raise TypeError("Invalid type supplied to MID$ in line " + str(self.__line_number)) + if category == Token.INSTR: + self.__consume(Token.LEFTPAREN) + + self.__expr() + hackstackstring = self.__operand_stack.pop() + if not isinstance(hackstackstring, str): + raise TypeError("Invalid type supplied to INSTR in line " + + str(self.__line_number)) + + self.__consume(Token.COMMA) + + self.__expr() + needlestring = self.__operand_stack.pop() + + start = end = None + if self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + start = self.__operand_stack.pop() + + if self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + end = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + try: + return hackstackstring.find(needlestring, start, end) + + except TypeError: + raise TypeError("Invalid type supplied to INSTR in line " + + str(self.__line_number)) + self.__consume(Token.LEFTPAREN) self.__expr() diff --git a/basictoken.py b/basictoken.py index 05a0340..f3c0b24 100644 --- a/basictoken.py +++ b/basictoken.py @@ -102,6 +102,7 @@ class BASICToken: ROUND = 69 # ROUND function MAX = 70 # MAX function MIN = 71 # MIN function + INSTR = 72 # INSTR function # Displayable names for each token category catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN', @@ -117,7 +118,7 @@ class BASICToken: 'LOG', 'SIN', 'TAN', 'DATA', 'READ', 'INT', 'CHR', 'ASC', 'STR', 'MID', 'MODULO', 'TERNARY', 'VAL', 'LEN', 'UPPER', 'LOWER', 'ROUND', - 'MAX', 'MIN'] + 'MAX', 'MIN', 'INSTR'] smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN, '+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE, @@ -145,12 +146,13 @@ class BASICToken: 'IF$': TERNARY, 'IFF': TERNARY, 'VAL': VAL, 'LEN': LEN, 'UPPER$': UPPER, 'LOWER$': LOWER, - 'ROUND': ROUND, 'MAX': MAX, 'MIN': MIN} + 'ROUND': ROUND, 'MAX': MAX, 'MIN': MIN, + 'INSTR': INSTR} # Functions functions = {ABS, ATN, COS, EXP, INT, LOG, POW, RND, SIN, SQR, TAN, CHR, ASC, MID, TERNARY, STR, VAL, LEN, UPPER, LOWER, - ROUND, MAX, MIN} + ROUND, MAX, MIN, INSTR} def __init__(self, column, category, lexeme): From b8ae8c3b169851327cce5fb2d95e8e2727b8051b Mon Sep 17 00:00:00 2001 From: JiFish Date: Sun, 6 Dec 2020 02:34:54 +0000 Subject: [PATCH 007/183] Whitespace cleanup --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ba0e0c5..7efc2aa 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ are all valid variable names, whereas: * *5_VAR* * *_VAR$* -* *66$* +* *66$* are all invalid. @@ -209,7 +209,7 @@ for each dimension. Arrays may have a maximum of three dimensions. As for simple variables, a string array has its name suffixed by a '$' character, while a numeric array does not carry a suffix. An attempt to assign a string value to a numeric array or vice versa will generate an error. -Note that the same variable name cannot be used for both an array and a simple variable. For example, the variables +Note that the same variable name cannot be used for both an array and a simple variable. For example, the variables *I$* and *I$(10)* should not be used within the same program, the results may be unpredictable. Also, array variables with the same name but different *dimensionality* are treated as the same. For example, using a **DIM** statement to define *I(5)* and then a second **DIM** statement to define *I(5, 5)* will @@ -217,7 +217,7 @@ result in the second definition (the two dimensional array) overwriting the firs Array values may be used within any expression, such as in a **PRINT** statement for string values, or in any numerical expression for number values. However, you must be specific about which array element you are referencing, using the -correct number of in-range indexes. If that particular array value has not yet been assigned, then an error +correct number of in-range indexes. If that particular array value has not yet been assigned, then an error message will be printed. ``` @@ -246,9 +246,9 @@ and are declared in a comma separated list: These values can then later be assigned to variables using the **READ** statement. Note that the type of the value (string or numeric) must match the type of the variable, otherwise an error message will be triggered. Therefore, -attention should be paid to the relative ordering of constants and variables. Further, +attention should be paid to the relative ordering of constants and variables. Further, there must be enough constants to fill all of the variables defined in the **READ** statement, or else an -error will be given. This is to ensure that the program is not left in a state where a variable has not been +error will be given. This is to ensure that the program is not left in a state where a variable has not been assigned a value, but nevertheless an attempt to use that variable is made later on in the program. The constants defined in the **DATA** statement may be consumed using several **READ** statements: @@ -303,7 +303,7 @@ The **STOP** statement may be used to cease program execution: > 30 PRINT "two" > RUN one -> +> ``` A program will automatically cease execution when it reaches the final statement, so a **STOP** may not be necessary. However @@ -327,7 +327,7 @@ The **LET** keyword is also optional: > 10 I = 10 ``` -Array variables may also have values assigned to them. The indexes can be derived from numeric +Array variables may also have values assigned to them. The indexes can be derived from numeric expressions: ``` @@ -617,7 +617,7 @@ Allowable numeric functions are: reset using the **RANDOMIZE** instruction with an optional seed value: e.g. ``` -> 10 RANDOMIZE 100 +> 10 RANDOMIZE 100 > 20 PRINT RND > RUN 0.1456692551041303 @@ -689,7 +689,7 @@ calculate the corresponding factorial *N!*. * *rock_scissors_paper.bas* - A BASIC implementation of the rock-paper-scissors game. *Note that you cannot simply load these programs from the text files. They must -be entered line by line into the interpreter. The program can then be saved and +be entered line by line into the interpreter. The program can then be saved and reloaded using the* **SAVE** and **LOAD** *commands as described above. Of course, this is no more inconvenient than saving a program to cassette tape and reloading it, as we all would have done in the 1980s!* From dfc7382c106f166d8c0e051a89a5f2f1be2aa0e0 Mon Sep 17 00:00:00 2001 From: JiFish Date: Sun, 6 Dec 2020 02:49:00 +0000 Subject: [PATCH 008/183] Correction to IF$ documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7efc2aa..e693c87 100644 --- a/README.md +++ b/README.md @@ -511,7 +511,7 @@ Allowable relational operators are: As an alternative to branching, Ternary functions are provided. The same relational operators are used. * **IFF**(x, y, z) - Evaluates *x* and returns *y* if true, otherwise returns *z*. *y* and *z* are expected to be numeric. -* **IF$**(x, y, z) - As above, but *y* and *z* are expected to be strings. +* **IF$**(x, y$, z$) - As above, but *y$* and *z$* are expected to be strings. ``` > 10 LET I = 10 From ae595dacf668e2b03f223404dd0bb585a0564556 Mon Sep 17 00:00:00 2001 From: JiFish Date: Wed, 16 Dec 2020 17:09:20 +0000 Subject: [PATCH 009/183] Changes to input command --- README.md | 9 ++++----- basicparser.py | 16 +++++----------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index e693c87..c94341e 100644 --- a/README.md +++ b/README.md @@ -551,19 +551,18 @@ Input a number - 22 Multiple items may be input by supplying a comma separated list. Input variables will be assigned to as many input values as supplied at run time. If there are more input values supplied than input -variables, the excess input values will be ignored. Conversely, if not enough input values are +variables, excess commas will be left in place. Conversely, if not enough input values are supplied, then the excess input variables will not be initialised (and will trigger an error if an attempt is made to evaluate those variables later in the program). -Further, numeric input values must be valid numbers (integers or floating point), and must -be unquoted. String input values must be surrounded by double quotes: +Further, numeric input values must be valid numbers (integers or floating point). ``` > 10 INPUT "Num, Str, Num: ": A, B$, C > 20 PRINT A, B$, C > RUN -Num, Str, Num: 22, " hello ", 33 -22 hello 33 +Num, Str, Num: 22, hello!, 33 +22 hello!33 > ``` diff --git a/basicparser.py b/basicparser.py index df55b3b..a0f5c30 100644 --- a/basicparser.py +++ b/basicparser.py @@ -419,6 +419,9 @@ def __inputstmt(self): # Acquire the comma separated input variables variables = [] if not self.__tokenindex >= len(self.__tokenlist): + if self.__token.category != Token.NAME: + raise ValueError('Expecting NAME in INPUT statement ' + + 'in line ' + str(self.__line_number)) variables.append(self.__token.lexeme) self.__advance() # Advance past variable @@ -428,7 +431,7 @@ def __inputstmt(self): self.__advance() # Advance past variable # Gather input from the user into the variables - inputvals = input(prompt).split(',') + inputvals = input(prompt).split(',', (len(variables)-1)) for variable in variables: left = variable @@ -437,16 +440,7 @@ def __inputstmt(self): right = inputvals.pop(0) if left.endswith('$'): - # Python inserts quotes around input data - if not right.find('"') == 1 and \ - not right.find('"', 2): - raise ValueError('Non-string input provided to a string variable ' + - 'in line ' + str(self.__line_number)) - - else: - # Strip the quotes from the stored string - stripped = right.strip() # May be space before or after quotes - self.__symbol_table[left] = stripped.replace('"', '') + self.__symbol_table[left] = str(right) elif not left.endswith('$'): try: From 3c8078c90a4c49d3856ae084e1c91d6432b59e43 Mon Sep 17 00:00:00 2001 From: JiFish Date: Sat, 26 Dec 2020 22:57:04 +0000 Subject: [PATCH 010/183] Adds AND, OR and NOT operators. Adds optional keywords for comptability with other BASIC dialects --- README.md | 57 +++++++++++++++++++++++++++++++++++++++++++------- basicparser.py | 56 +++++++++++++++++++++++++++++++++++++++++-------- basictoken.py | 10 ++++++--- 3 files changed, 103 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index c94341e..182a3e7 100644 --- a/README.md +++ b/README.md @@ -295,7 +295,7 @@ Note that comments will be automatically normalised to upper case. ### Stopping a program -The **STOP** statement may be used to cease program execution: +The **STOP** statement may be used to cease program execution. The command **END** has the same effect. ``` > 10 PRINT "one" @@ -480,7 +480,9 @@ made depending upon the result of the evaluation. Note that the **ELSE** clause is optional and may be omitted. In this case, the **THEN** branch is taken if the expression evaluates to true, otherwise the following statement is executed. -It is also possible to call a subroutine depending upon the result of a relational expression +You can optionally give the **GOTO** keyword before your line numbers. This is for compatibility with other BASIC dialects. e.g. `40 IF I > J THEN GOTO 50 ELSE GOTO 70` + +It is also possible to call a subroutine depending upon the result of a conditional expression using the **ON-GOSUB** statement. If the expression evaluates to true, then the subroutine is called, otherwise execution continues to the next statement without making the call: @@ -504,11 +506,47 @@ Allowable relational operators are: * '>' (greater than) * '<=' (less than or equal) * '>=' (greater than or equal) -* '<>' (not equal) +* '<>' / '!=' (not equal) + +The logical operators **AND** and **OR** are also provided to allow you to join two or more expressions. The **NOT** operator can also be given before an expression. + +*=* and *<>* can also be considered logical operators. However, unlike **AND** or **OR** they can't be used to join more than two expressions. + +| Inputs | | *AND* | *OR* | *=* | *<>* / *!=* | +|--------|-------|-------|-------|-------|-------------| +| FALSE | FALSE | FALSE | FALSE | TRUE | FALSE | +| TRUE | FALSE | FALSE | TRUE | FALSE | TRUE | +| TRUE | TRUE | TRUE | TRUE | TRUE | FALSE | + +| Input | NOT | +|-------|-------| +| TRUE | FALSE | +| FALSE | TRUE | + +Example: + +``` +> 10 a = 10 +> 20 b = 20 +> 30 IF NOT a > b AND b = 20 OR a >= 5 THEN 60 +> 40 PRINT "Test failed!" +> 50 STOP +> 60 PRINT "Test passed!" +> RUN +Test passed! +``` + +Expressions can be inside brackets to change the order of evaluation. Compare the output when line 30 is changed: + +``` +> 30 IF NOT a > b AND (b = 20 OR a >= 5) THEN 60 +> RUN +Test failed! +``` ### Ternary Functions -As an alternative to branching, Ternary functions are provided. The same relational operators are used. +As an alternative to branching, Ternary functions are provided. * **IFF**(x, y, z) - Evaluates *x* and returns *y* if true, otherwise returns *z*. *y* and *z* are expected to be numeric. * **IF$**(x, y$, z$) - As above, but *y$* and *z$* are expected to be strings. @@ -633,6 +671,9 @@ Random integers can be generated by combining **RND** and **INT**: e.g. 6 > ``` + +Seeds may not produce the same result on another platform. + * **ROUND**(x) - Rounds number to the nearest integer. * **SIN**(x) - Calculates the sine of *x*, where *x* is an angle in radians @@ -719,11 +760,11 @@ as we all would have done in the 1980s!* **GOTO** *line-number* - Unconditional branch -**IF** *relational-expression* **THEN** *line-number* [**ELSE** *line-number*] - Conditional branch +**IF** *expression* **THEN** *line-number* [**ELSE** *line-number*] - Conditional branch -**IFF**(*relational-expression*, *numeric-expression*, *numeric-expression*) - Evaluates *relational-expression* and returns the value of the result of the first *numeric-expression* if true, or the second if false. +**IFF**(*expression*, *numeric-expression*, *numeric-expression*) - Evaluates *expression* and returns the value of the result of the first *numeric-expression* if true, or the second if false. -**IF$**(*relational-expression*, *string-expression*, *string-expression*) - Evaluates *relational-expression* and returns the value of the result of the first *string-expression* if true, or the second if false. +**IF$**(*expression*, *string-expression*, *string-expression*) - Evaluates *expression* and returns the value of the result of the first *string-expression* if true, or the second if false. **INPUT** [*input-prompt*:] *simple-variable-list* - Processes user input presented as a comma separated list @@ -752,7 +793,7 @@ be omitted to get the rest of the string. If *start-position* or *end-position* **MIN**(*expression-list*) - Returns the lowest value in *expression-list* -**ON** *relational-expression* **GOSUB** *line-number* - Conditional subroutine call +**ON** *expression* **GOSUB** *line-number* - Conditional subroutine call **POW**(*base*, *exponent*) - Calculates the result of raising the base to the power of the exponent diff --git a/basicparser.py b/basicparser.py index a0f5c30..1933dd7 100644 --- a/basicparser.py +++ b/basicparser.py @@ -210,12 +210,12 @@ def __printstmt(self): # Check there are items to print if not self.__tokenindex >= len(self.__tokenlist): - self.__relexpr() + self.__logexpr() print(self.__operand_stack.pop(), end='') while self.__token.category == Token.COMMA: self.__advance() - self.__relexpr() + self.__logexpr() print(self.__operand_stack.pop(), end='') # Final newline @@ -289,7 +289,7 @@ def __assignmentstmt(self): else: # We are assigning to a simple variable self.__consume(Token.ASSIGNOP) - self.__relexpr() + self.__logexpr() # Check that we are using the right variable name format right = self.__operand_stack.pop() @@ -373,7 +373,7 @@ def __arrayassignmentstmt(self, name): self.__consume(Token.RIGHTPAREN) self.__consume(Token.ASSIGNOP) - self.__relexpr() + self.__logexpr() # Check that we are using the right variable name format right = self.__operand_stack.pop() @@ -412,7 +412,7 @@ def __inputstmt(self): prompt = '? ' if self.__token.category == Token.STRING: # Acquire the input prompt - self.__relexpr() + self.__logexpr() prompt = self.__operand_stack.pop() self.__consume(Token.COLON) @@ -647,7 +647,7 @@ def __factor(self): # Save sign because expr() calls term() which resets # sign to 1 savesign = self.__sign - self.__expr() # Value of expr is pushed onto stack + self.__logexpr() # Value of expr is pushed onto stack if savesign == -1: # Change sign of expression @@ -723,13 +723,17 @@ def __ifstmt(self): """ self.__advance() # Advance past IF token - self.__relexpr() + self.__logexpr() # Save result of expression saveval = self.__operand_stack.pop() # Process the THEN part and save the jump value self.__consume(Token.THEN) + + if self.__token.category == Token.GOTO: + self.__advance() # Advance past optional GOTO + self.__expr() then_jump = self.__operand_stack.pop() @@ -741,6 +745,10 @@ def __ifstmt(self): # See if there is an ELSE part if self.__token.category == Token.ELSE: self.__advance() + + if self.__token.category == Token.GOTO: + self.__advance() # Advance past optional GOTO + self.__expr() # Set up and return the flow signal @@ -867,7 +875,7 @@ def __ongosubstmt(self): """ self.__advance() # Advance past ON token - self.__relexpr() + self.__logexpr() # Save result of expression saveval = self.__operand_stack.pop() @@ -917,6 +925,36 @@ def __relexpr(self): elif savecat == Token.GREATEQUAL: self.__operand_stack.append(left >= right) # Push True or False + def __logexpr(self): + """Parses a logical expression + """ + self.__notexpr() + + while self.__token.category in [Token.OR, Token.AND]: + savecat = self.__token.category + self.__advance() + self.__notexpr() + + right = self.__operand_stack.pop() + left = self.__operand_stack.pop() + + if savecat == Token.OR: + self.__operand_stack.append(left or right) # Push True or False + + elif savecat == Token.AND: + self.__operand_stack.append(left and right) # Push True or False + + def __notexpr(self): + """Parses a logical not expression + """ + if self.__token.category == Token.NOT: + self.__advance() + self.__relexpr() + right = self.__operand_stack.pop() + self.__operand_stack.append(not right) + else: + self.__relexpr() + def __evaluate_function(self, category): """Evaluate the function in the statement and return the result. @@ -994,7 +1032,7 @@ def __evaluate_function(self, category): if category == Token.TERNARY: self.__consume(Token.LEFTPAREN) - self.__relexpr() + self.__logexpr() condition = self.__operand_stack.pop() self.__consume(Token.COMMA) diff --git a/basictoken.py b/basictoken.py index f3c0b24..6432573 100644 --- a/basictoken.py +++ b/basictoken.py @@ -103,6 +103,9 @@ class BASICToken: MAX = 70 # MAX function MIN = 71 # MIN function INSTR = 72 # INSTR function + AND = 73 # AND operator + OR = 74 # OR operator + NOT = 75 # NOT operator # Displayable names for each token category catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN', @@ -118,14 +121,14 @@ class BASICToken: 'LOG', 'SIN', 'TAN', 'DATA', 'READ', 'INT', 'CHR', 'ASC', 'STR', 'MID', 'MODULO', 'TERNARY', 'VAL', 'LEN', 'UPPER', 'LOWER', 'ROUND', - 'MAX', 'MIN', 'INSTR'] + 'MAX', 'MIN', 'INSTR', 'AND', 'OR', 'NOT'] smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN, '+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE, '\n': NEWLINE, '<': LESSER, '>': GREATER, '<>': NOTEQUAL, '<=': LESSEQUAL, '>=': GREATEQUAL, ',': COMMA, - ':': COLON, '%': MODULO} + ':': COLON, '%': MODULO, '!=': NOTEQUAL} # Dictionary of BASIC reserved words keywords = {'LET': LET, 'LIST': LIST, 'PRINT': PRINT, @@ -147,7 +150,8 @@ class BASICToken: 'VAL': VAL, 'LEN': LEN, 'UPPER$': UPPER, 'LOWER$': LOWER, 'ROUND': ROUND, 'MAX': MAX, 'MIN': MIN, - 'INSTR': INSTR} + 'INSTR': INSTR, 'END': STOP, + 'AND': AND, 'OR': OR, 'NOT': NOT} # Functions functions = {ABS, ATN, COS, EXP, INT, LOG, POW, RND, SIN, SQR, TAN, From 92c25f0544fc0837c1f8889226e7d54401e1ba7b Mon Sep 17 00:00:00 2001 From: JiFish Date: Tue, 29 Dec 2020 00:47:29 +0000 Subject: [PATCH 011/183] Fix issue that prevented the value '0' from being fetched from an array --- basicparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/basicparser.py b/basicparser.py index 1933dd7..b6b7c77 100644 --- a/basicparser.py +++ b/basicparser.py @@ -624,7 +624,7 @@ def __factor(self): BASICarray = self.__symbol_table[arrayname] arrayval = self.__get_array_val(BASICarray, indexvars) - if arrayval: + if arrayval != None: self.__operand_stack.append(self.__sign*arrayval) else: From dfa10d26df2eabf7b20b5ee96fafdc2211ffb919 Mon Sep 17 00:00:00 2001 From: JiFish Date: Tue, 29 Dec 2020 07:54:30 +0000 Subject: [PATCH 012/183] Fix unnessary stripping of string inputs in READ (Can cause undesired results, as the earlier INPUT fix) Also remove unnecessary try/catch block. (Situation is already caught above.) --- basicparser.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/basicparser.py b/basicparser.py index b6b7c77..a689e2e 100644 --- a/basicparser.py +++ b/basicparser.py @@ -494,28 +494,25 @@ def __readstmt(self): # Gather input from the DATA statement into the variables for variable in variables: left = variable + right = readlist.pop(0) - try: - right = self.__data_values.pop(0) + if left.endswith('$'): + # Python inserts quotes around input data + if isinstance(right, int): + raise ValueError('Non-string input provided to a string variable ' + + 'in line ' + str(self.__line_number)) - if left.endswith('$'): - # Python inserts quotes around input data - if isinstance(right, int): - raise ValueError('Non-string input provided to a string variable ' + - 'in line ' + str(self.__line_number)) + else: + self.__symbol_table[left] = right - else: - # Strip the quotes from the stored string - stripped = right.strip() # May be space before or after quotes - self.__symbol_table[left] = stripped.replace('"', '') + elif not left.endswith('$'): + try: + self.__symbol_table[left] = int(right) - elif not left.endswith('$'): - try: - self.__symbol_table[left] = int(right) + except ValueError: + raise ValueError('String input provided to a numeric variable ' + + 'in line ' + str(self.__line_number)) - except ValueError: - raise ValueError('String input provided to a numeric variable ' + - 'in line ' + str(self.__line_number)) except IndexError: # No more input to process From 692378972d48816f27675ba93e789d0c795dd067 Mon Sep 17 00:00:00 2001 From: JiFish Date: Sun, 24 Jan 2021 23:57:21 +0000 Subject: [PATCH 013/183] Improvements to BASICArray error handling: - Catches negative array sizes - Catches fractional array sizes, while allowing e.g. 1.0f - Specific error message when attempting to use an array like a normal var in assignment --- basicparser.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/basicparser.py b/basicparser.py index a689e2e..ca44f24 100644 --- a/basicparser.py +++ b/basicparser.py @@ -36,20 +36,26 @@ def __init__(self, dimensions): corresponding sizes """ - if len(dimensions) == 0: + self.dims = min(3,len(dimensions)) + + if self.dims == 0: raise SyntaxError("Zero dimensional array specified") - if len(dimensions) == 1: - self.data = [None for x in range(dimensions[0])] - self.dims = 1 + # Check for invalid sizes and ensure int + for i in range(self.dims): + if dimensions[i] < 0: + raise SyntaxError("Negative array size specified") + # Allow sizes like 1.0f, but not 1.1f + if int(dimensions[i]) != dimensions[i]: + raise SyntaxError("Fractional array size specified") + dimensions[i] = int(dimensions[i]) - if len(dimensions) == 2: + if self.dims == 1: + self.data = [None for x in range(dimensions[0])] + elif self.dims == 2: self.data = [[None for x in range(dimensions[1])] for x in range(dimensions[0])] - self.dims = 2 - - if len(dimensions) > 2: + else: self.data = [[[None for x in range(dimensions[2])] for x in range(dimensions[1])] for x in range(dimensions[0])] - self.dims = 3 def pretty_print(self): print(str(self.data)) @@ -606,7 +612,11 @@ def __factor(self): # Capture the index variables self.__advance() # Advance past the array name - self.__consume(Token.LEFTPAREN) + try: + self.__consume(Token.LEFTPAREN) + except RuntimeError: + raise RuntimeError('Array used without index in line ' + + str(self.__line_number)) indexvars = [] if not self.__tokenindex >= len(self.__tokenlist): From 45207e8b5b5ed689b2f487794cbb5ff1ab12320e Mon Sep 17 00:00:00 2001 From: JiFish Date: Mon, 25 Jan 2021 00:31:33 +0000 Subject: [PATCH 014/183] Adds PI constant and RNDINT functions. - PI simply returns the value of pi, as per many BASIC variants - RNDINT(lo, hi) --- README.md | 8 ++++++++ basicparser.py | 23 +++++++++++++++++++++++ basictoken.py | 10 +++++++--- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 182a3e7..b0ced53 100644 --- a/README.md +++ b/README.md @@ -648,6 +648,8 @@ Allowable numeric functions are: > -2 ``` +* **PI** - Returns the value of pi. + * **POW**(x, y) - Calculates *x* to the power *y* * **RND** - Generates a pseudo random number N, where *0 <= N < 1*. Can be @@ -674,6 +676,8 @@ Random integers can be generated by combining **RND** and **INT**: e.g. Seeds may not produce the same result on another platform. +* **RNDINT**(*lo*, *hi*) - Generates a pseudo random integer N, where *lo* <= N <= *hi*. Uses the same seed as above. + * **ROUND**(x) - Rounds number to the nearest integer. * **SIN**(x) - Calculates the sine of *x*, where *x* is an angle in radians @@ -795,6 +799,8 @@ be omitted to get the rest of the string. If *start-position* or *end-position* **ON** *expression* **GOSUB** *line-number* - Conditional subroutine call +**PI** - Returns the value of pi + **POW**(*base*, *exponent*) - Calculates the result of raising the base to the power of the exponent **PRINT** *print-list* - Prints a comma separated list of literals or variables @@ -810,6 +816,8 @@ optional seed (*numeric expression*), the sequence is predictable. **RND** - Generates a pseudo random number N, where 0 <= N < 1 +**RNDINT**(*lo-numerical-expression*, *hi-numerical-expression*) - Generates a pseudo random integer N, where *lo-numerical-expression* <= N <= *hi-numerical-expression* + **ROUND**(*numerical-expression*) - Rounds *numerical-expression* to the nearest integer **RUN** - Runs the program diff --git a/basicparser.py b/basicparser.py index ca44f24..7357606 100644 --- a/basicparser.py +++ b/basicparser.py @@ -976,6 +976,29 @@ def __evaluate_function(self, category): if category == Token.RND: return random.random() + if category == Token.PI: + return math.pi + + if category == Token.RNDINT: + self.__consume(Token.LEFTPAREN) + + self.__expr() + lo = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + hi = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + try: + return random.randint(lo, hi) + + except ValueError: + raise ValueError("Invalid value supplied to RNDINT in line " + + str(self.__line_number)) + if category == Token.MAX: self.__consume(Token.LEFTPAREN) diff --git a/basictoken.py b/basictoken.py index 6432573..873946c 100644 --- a/basictoken.py +++ b/basictoken.py @@ -106,6 +106,8 @@ class BASICToken: AND = 73 # AND operator OR = 74 # OR operator NOT = 75 # NOT operator + PI = 76 # PI constant + RNDINT = 77 # RNDINT function # Displayable names for each token category catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN', @@ -121,7 +123,8 @@ class BASICToken: 'LOG', 'SIN', 'TAN', 'DATA', 'READ', 'INT', 'CHR', 'ASC', 'STR', 'MID', 'MODULO', 'TERNARY', 'VAL', 'LEN', 'UPPER', 'LOWER', 'ROUND', - 'MAX', 'MIN', 'INSTR', 'AND', 'OR', 'NOT'] + 'MAX', 'MIN', 'INSTR', 'AND', 'OR', 'NOT', 'PI', + 'RNDINT'] smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN, '+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE, @@ -151,12 +154,13 @@ class BASICToken: 'UPPER$': UPPER, 'LOWER$': LOWER, 'ROUND': ROUND, 'MAX': MAX, 'MIN': MIN, 'INSTR': INSTR, 'END': STOP, - 'AND': AND, 'OR': OR, 'NOT': NOT} + 'AND': AND, 'OR': OR, 'NOT': NOT, + 'PI': PI, 'RNDINT': RNDINT} # Functions functions = {ABS, ATN, COS, EXP, INT, LOG, POW, RND, SIN, SQR, TAN, CHR, ASC, MID, TERNARY, STR, VAL, LEN, UPPER, LOWER, - ROUND, MAX, MIN, INSTR} + ROUND, MAX, MIN, INSTR, PI, RNDINT} def __init__(self, column, category, lexeme): From 3188e7925aabf072d8eae84d3881616ac02e44a8 Mon Sep 17 00:00:00 2001 From: JiFish Date: Mon, 25 Jan 2021 00:35:44 +0000 Subject: [PATCH 015/183] Fix danging except statement from previous removed try block. --- basicparser.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/basicparser.py b/basicparser.py index 7357606..2a7486a 100644 --- a/basicparser.py +++ b/basicparser.py @@ -519,11 +519,6 @@ def __readstmt(self): raise ValueError('String input provided to a numeric variable ' + 'in line ' + str(self.__line_number)) - - except IndexError: - # No more input to process - pass - def __expr(self): """Parses a numerical expression consisting of two terms being added or subtracted, From 0bffdc0e1e0b1e0d7ee5dbc9ef002f3ae7cb7de3 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 7 Mar 2021 18:54:37 +0000 Subject: [PATCH 016/183] Fixed numeric input bug --- basicparser.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/basicparser.py b/basicparser.py index 2a7486a..5213514 100644 --- a/basicparser.py +++ b/basicparser.py @@ -450,10 +450,13 @@ def __inputstmt(self): elif not left.endswith('$'): try: - self.__symbol_table[left] = int(right) + if '.' in right: + self.__symbol_table[left] = float(right) + else: + self.__symbol_table[left] = int(right) except ValueError: - raise ValueError('String input provided to a numeric variable ' + + raise ValueError('Non-numeric input provided to a numeric variable ' + 'in line ' + str(self.__line_number)) except IndexError: From 4ae5cefd9bccceb1616b34573d2730cf3e5b06a6 Mon Sep 17 00:00:00 2001 From: JiFish Date: Tue, 9 Mar 2021 00:32:29 +0000 Subject: [PATCH 017/183] Fix same numeric bug as #18, but for READ command --- basicparser.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/basicparser.py b/basicparser.py index 5213514..cabaeb3 100644 --- a/basicparser.py +++ b/basicparser.py @@ -516,10 +516,13 @@ def __readstmt(self): elif not left.endswith('$'): try: - self.__symbol_table[left] = int(right) + if '.' in right: + self.__symbol_table[left] = float(right) + else: + self.__symbol_table[left] = int(right) except ValueError: - raise ValueError('String input provided to a numeric variable ' + + raise ValueError('Non-numeric input provided to a numeric variable ' + 'in line ' + str(self.__line_number)) def __expr(self): From 91a83978a4020c2e9445b6c08577d1abf7192044 Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 27 Mar 2021 15:22:08 +0000 Subject: [PATCH 018/183] Fixed typo in Open Issues section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b0ced53..14fb73b 100644 --- a/README.md +++ b/README.md @@ -876,7 +876,7 @@ control flow changes to the Program object, is used consistently throughout the * It is not possible to renumber a program. This would require considerable extra functionality. * Negative values are printed with a space (e.g. '- 5') in program listings because of tokenization. This does not affect functionality. -* Decimal values less than zero must be expressed with a leading zero (i.e. 0.34 rather than .34) +* Decimal values less than one must be expressed with a leading zero (i.e. 0.34 rather than .34) * User input values cannot be directly assigned to array variables in an **INPUT** or **READ** statement * Strings representing numbers (e.g. "10") can actually be assigned to numeric variables in **INPUT** and **READ** statements without an error, Python will silently convert them to integers. From 1c078e2428f74c1df87496adc996d009d4462697 Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 3 Apr 2021 19:49:13 +0100 Subject: [PATCH 019/183] Made input more robust, fixed bug in readstmt --- basicparser.py | 62 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/basicparser.py b/basicparser.py index cabaeb3..fafb431 100644 --- a/basicparser.py +++ b/basicparser.py @@ -436,32 +436,39 @@ def __inputstmt(self): variables.append(self.__token.lexeme) self.__advance() # Advance past variable - # Gather input from the user into the variables - inputvals = input(prompt).split(',', (len(variables)-1)) + valid_input = False + while not valid_input: + # Gather input from the user into the variables + inputvals = input(prompt).split(',', (len(variables)-1)) - for variable in variables: - left = variable + for variable in variables: + left = variable - try: - right = inputvals.pop(0) + try: + right = inputvals.pop(0) + + if left.endswith('$'): + self.__symbol_table[left] = str(right) + valid_input = True + + elif not left.endswith('$'): + try: + if '.' in right: + self.__symbol_table[left] = float(right) - if left.endswith('$'): - self.__symbol_table[left] = str(right) + else: + self.__symbol_table[left] = int(right) - elif not left.endswith('$'): - try: - if '.' in right: - self.__symbol_table[left] = float(right) - else: - self.__symbol_table[left] = int(right) + valid_input = True - except ValueError: - raise ValueError('Non-numeric input provided to a numeric variable ' + - 'in line ' + str(self.__line_number)) + except ValueError: + valid_input = False + print('Non-numeric input provided to a numeric variable - redo from start') - except IndexError: - # No more input to process - pass + except IndexError: + # No more input to process + valid_input = False + print('Not enough values input - redo from start') def __datastmt(self): """Parses a DATA statement""" @@ -503,7 +510,7 @@ def __readstmt(self): # Gather input from the DATA statement into the variables for variable in variables: left = variable - right = readlist.pop(0) + right = self.__data_values.pop(0) if left.endswith('$'): # Python inserts quotes around input data @@ -516,10 +523,15 @@ def __readstmt(self): elif not left.endswith('$'): try: - if '.' in right: - self.__symbol_table[left] = float(right) - else: - self.__symbol_table[left] = int(right) + #if '.' in right: + # self.__symbol_table[left] = float(right) + #else: + # self.__symbol_table[left] = int(right) + + numeric = float(right) + if numeric.is_integer(): + numeric = int(numeric) + self.__symbol_table[left] = numeric except ValueError: raise ValueError('Non-numeric input provided to a numeric variable ' + From 93fa02d1f7fc43f78ed0f3ab6d24d46460d3446e Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 3 Apr 2021 19:53:15 +0100 Subject: [PATCH 020/183] Describe new input behaviour --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 14fb73b..0acbb9d 100644 --- a/README.md +++ b/README.md @@ -590,8 +590,7 @@ Input a number - 22 Multiple items may be input by supplying a comma separated list. Input variables will be assigned to as many input values as supplied at run time. If there are more input values supplied than input variables, excess commas will be left in place. Conversely, if not enough input values are -supplied, then the excess input variables will not be initialised (and will trigger an error if -an attempt is made to evaluate those variables later in the program). +supplied, an error message will be printed and the user will be asked to re-input the values again. Further, numeric input values must be valid numbers (integers or floating point). @@ -604,7 +603,8 @@ Num, Str, Num: 22, hello!, 33 > ``` -A mismatch between the input value and input variable type will trigger an error. +A mismatch between the input value and input variable type will trigger an error, and the user will be asked +to re-input the values again. It is a limitation of this BASIC dialect that it is not possible to assign constants directly to array variables within an **INPUT** statement, only simple variables. From bdf710d7e898cbd6729cf4a1436671e46f94655c Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 3 Apr 2021 20:15:22 +0100 Subject: [PATCH 021/183] Really fixed input this time --- basicparser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/basicparser.py b/basicparser.py index fafb431..c8fd45c 100644 --- a/basicparser.py +++ b/basicparser.py @@ -464,11 +464,13 @@ def __inputstmt(self): except ValueError: valid_input = False print('Non-numeric input provided to a numeric variable - redo from start') + break except IndexError: # No more input to process valid_input = False print('Not enough values input - redo from start') + break def __datastmt(self): """Parses a DATA statement""" From dfd5c0b87ce4bc37bec18deb5410235380fb8537 Mon Sep 17 00:00:00 2001 From: brickbots Date: Thu, 2 Sep 2021 18:09:30 -0700 Subject: [PATCH 022/183] Switch to ascii load/save for programs --- program.py | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/program.py b/program.py index 8a25353..165e6e1 100644 --- a/program.py +++ b/program.py @@ -24,7 +24,7 @@ from basictoken import BASICToken as Token from basicparser import BASICParser from flowsignal import FlowSignal -import pickle +from lexer import Lexer class Program: @@ -62,24 +62,52 @@ def list(self): def save(self, file): """Save the program - :param file: The name and path of the save file + :param file: The name and path of the save file, .bas is + appended """ try: - with open(file, 'wb') as outfile: - pickle.dump(self.__program, outfile) - outfile.close() + with open(file + ".bas", "w") as outfile: + line_numbers = self.line_numbers() + + for line_number in line_numbers: + outfile.write(str(line_number) + " ") + + statement = self.__program[line_number] + for i in range(len(statement)): + token = statement[i] + # Add in quotes for strings + if token.category == Token.STRING: + outfile.write('"' + token.lexeme + '"') + + else: + outfile.write(token.lexeme) + if i + 1 < len(statement): + outfile.write(" ") + + outfile.write("\n") + outfile.close() except OSError: raise OSError("Could not save to file") def load(self, file): """Load the program - :param file: The name and path of the file to be loaded""" + :param file: The name and path of the file to be loaded, .bas is + appended + + """ + + # New out the program + self.delete() try: - with open(file, 'rb') as infile: - self.__program = pickle.load(infile) + lexer = Lexer() + with open(file + ".bas", "r") as infile: + for line in infile: + line = line.replace("\r", "").replace("\n", "").strip() + tokenlist = lexer.tokenize(line) + self.add_stmt(tokenlist) infile.close() except OSError: From 054ea35b039d1291cc48acc8f7093c7cff3f713d Mon Sep 17 00:00:00 2001 From: brickbots Date: Thu, 2 Sep 2021 19:12:34 -0700 Subject: [PATCH 023/183] Clean up dup code in list/save --- program.py | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/program.py b/program.py index 165e6e1..a1c5464 100644 --- a/program.py +++ b/program.py @@ -41,23 +41,28 @@ def __init__(self): # and loop returns self.__return_stack = [] - def list(self): - """Lists the program""" + def __str__(self): + + program_text = "" line_numbers = self.line_numbers() for line_number in line_numbers: - print(line_number, end=' ') + program_text += str(line_number) + " " statement = self.__program[line_number] for token in statement: # Add in quotes for strings if token.category == Token.STRING: - print('"' + token.lexeme + '"', end=' ') + program_text += '"' + token.lexeme + '" ' else: - print(token.lexeme, end=' ') + program_text += token.lexeme + " " + program_text += "\n" + return program_text - print(flush=True) + def list(self): + """Lists the program""" + print(str(self), end="") def save(self, file): """Save the program @@ -68,26 +73,7 @@ def save(self, file): """ try: with open(file + ".bas", "w") as outfile: - line_numbers = self.line_numbers() - - for line_number in line_numbers: - outfile.write(str(line_number) + " ") - - statement = self.__program[line_number] - for i in range(len(statement)): - token = statement[i] - # Add in quotes for strings - if token.category == Token.STRING: - outfile.write('"' + token.lexeme + '"') - - else: - outfile.write(token.lexeme) - - if i + 1 < len(statement): - outfile.write(" ") - - outfile.write("\n") - outfile.close() + outfile.write(str(self)) except OSError: raise OSError("Could not save to file") @@ -108,7 +94,6 @@ def load(self, file): line = line.replace("\r", "").replace("\n", "").strip() tokenlist = lexer.tokenize(line) self.add_stmt(tokenlist) - infile.close() except OSError: raise OSError("Could not read file") From a9c54126cf6dcad98bbda51eacffaedbf47ad979 Mon Sep 17 00:00:00 2001 From: brickbots Date: Thu, 2 Sep 2021 19:44:54 -0700 Subject: [PATCH 024/183] Fixes to allow execution in micropython / circuitpython --- basicparser.py | 3 ++- lexer.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/basicparser.py b/basicparser.py index c8fd45c..065ff49 100644 --- a/basicparser.py +++ b/basicparser.py @@ -19,6 +19,7 @@ from flowsignal import FlowSignal import math import random +from time import monotonic """Implements a BASIC array, which may have up @@ -1320,5 +1321,5 @@ def __randomizestmt(self): random.seed(seed) else: - random.seed() + random.seed(int(monotonic())) diff --git a/lexer.py b/lexer.py index 3a8edbe..8385c10 100644 --- a/lexer.py +++ b/lexer.py @@ -132,7 +132,7 @@ def tokenize(self, stmt): # Break if not a letter or a dollar symbol # (the latter is used for string variable names) - if not (c.isalnum() or c == '_' or c == '$'): + if not ((c.isalpha() or c.isdigit()) or c == '_' or c == '$'): break # Normalise keywords and names to upper case From e07ee36ea58bfcae79bc9c8a3fde230c3eaac4be Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 4 Sep 2021 01:22:36 -0400 Subject: [PATCH 025/183] Add logic to ignore everything on REM statement --- lexer.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/lexer.py b/lexer.py index 3a8edbe..02f8385 100644 --- a/lexer.py +++ b/lexer.py @@ -56,6 +56,9 @@ def tokenize(self, stmt): # Establish a list of tokens to be # derived from the statement tokenlist = [] + firstToken = False + firstNumber = True + commentStmt = False # Process every character until we # reach the end of the statement string @@ -70,9 +73,22 @@ def tokenize(self, stmt): # incremented token = Token(self.__column - 1, None, '') + # Remark Statments - process rest of statement without checks + if commentStmt: + token.category = Token.REM + firstToken = False + + while True: + token.lexeme += c # Append the current char to the lexeme + c = self.__get_next_char() + + if c == '': + break + # Process strings - if c == '"': + elif c == '"': token.category = Token.STRING + firstToken = False # Consume all of the characters # until we reach the terminating @@ -102,6 +118,9 @@ def tokenize(self, stmt): elif c.isdigit(): token.category = Token.UNSIGNEDINT found_point = False + if firstNumber: + firstToken = True + firstNumber = False # Consume all of the digits, including any decimal point while True: @@ -143,11 +162,17 @@ def tokenize(self, stmt): if token.lexeme in Token.keywords: token.category = Token.keywords[token.lexeme] + if firstToken and token.lexeme == "REM": + commentStmt = True + firstToken = False + else: token.category = Token.NAME + firstToken = False # Process operator symbols elif c in Token.smalltokens: + firstToken = False save = c c = self.__get_next_char() # c might be '' (end of stmt) twochar = save + c @@ -163,7 +188,7 @@ def tokenize(self, stmt): token.lexeme = save # We do not recognise this token - else: + elif c != '': raise SyntaxError('Syntax error') # Append the new token to the list @@ -190,4 +215,4 @@ def __get_next_char(self): if __name__ == "__main__": import doctest - doctest.testmod() + doctest.testmod() \ No newline at end of file From ac0a5246975217b12e715b6a2d04b25f1affc8fd Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 4 Sep 2021 02:00:25 -0400 Subject: [PATCH 026/183] ongoto/gosub update The on gosub statement behaved in non standard way, this update adds the goto option as well as making the statement operate in a more typical fashion --- basicparser.py | 30 ++++++++++++++++++++++++------ interpreter.py | 4 ++-- ontest.bas | Bin 0 -> 1053 bytes 3 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 ontest.bas diff --git a/basicparser.py b/basicparser.py index c8fd45c..9b138ed 100644 --- a/basicparser.py +++ b/basicparser.py @@ -897,17 +897,36 @@ def __ongosubstmt(self): """ self.__advance() # Advance past ON token - self.__logexpr() + self.__expr() # Save result of expression saveval = self.__operand_stack.pop() - # Process the GOSUB part and save the jump value - # if the condition is met - if saveval: - return self.__gosubstmt() + if self.__token.category == Token.GOTO: + self.__consume(Token.GOTO) + branchtype = 1 else: + self.__consume(Token.GOSUB) + branchtype = 2 + + branch_values = [] + # Acquire the comma separated values + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() + branch_values.append(self.__operand_stack.pop()) + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + branch_values.append(self.__operand_stack.pop()) + + if saveval < 1 or saveval > len(branch_values) or len(branch_values) == 0: return None + elif branchtype == 1: + return FlowSignal(ftarget=branch_values[saveval-1]) + else: + return FlowSignal(ftarget=branch_values[saveval-1], + ftype=FlowSignal.GOSUB) def __relexpr(self): """Parses a relational expression @@ -1321,4 +1340,3 @@ def __randomizestmt(self): else: random.seed() - diff --git a/interpreter.py b/interpreter.py index dc14a64..6507d1a 100644 --- a/interpreter.py +++ b/interpreter.py @@ -34,7 +34,7 @@ def main(): banner = ( """ PPPP Y Y BBBB AAA SSSS I CCC - P P Y Y B B A A S I C + P P Y Y B B A A S I C P P Y Y B B A A S I C PPPP Y BBBB AAAAA SSSS I C P Y B B A A S I C @@ -117,4 +117,4 @@ def main(): if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/ontest.bas b/ontest.bas new file mode 100644 index 0000000000000000000000000000000000000000..e97617a688e65e577a8f90aab71360345af154dc GIT binary patch literal 1053 zcmZvbTXWK25Qd=vO3`X*sai{Gs#Vh#D+iDDP0eIPcRlS z8nI9))b1V*j$A~`S|cux&A#)Y+xw1Kd48B5I^Ucl2jh(>r=@D`z(Rb`>WE~5kT5#Y z@RW_p$BFw+}2q0*CtDM=SNol$8C z$wA90r)*3c!vkjsDS0Wnx|d zX~Kd4lMGBVpfQlkX0fR9a|B)TDb5YmVg^epJx{nGX>z1G&(+H+zeuVS2^RU!Os2;p`=J4?ul%CM31 zRCz+dR|N)fO_{C}ZU`Wgl0nF%co13K6w!Mdw>;5p!iI2>e|oy3Om_+Qgh((UZ-|W4 zbRFCmk!xeq6Fneo`JzBa+sd;;*cCwT7U-xbqF**1dZInTBVQDpQc0N}6L@*Sj;ya; zJQ2}v8x>DfC78Y_&{0i!o)YT+?P#Axl}y-%<(Uo$&wNv`rRPf3AiNlDNfu{ydX9sa s;`*c6c;&fX6Ko-)JchIyXT4a#8)bS+c;`1IACW9>*Q^%NWYJ*#FDo1fmjD0& literal 0 HcmV?d00001 From 34404c710aee3a78083111489376374fd88aa52c Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 4 Sep 2021 02:08:06 -0400 Subject: [PATCH 027/183] try to undo spacing change on PyBasic banner --- interpreter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interpreter.py b/interpreter.py index 6507d1a..dc14a64 100644 --- a/interpreter.py +++ b/interpreter.py @@ -34,7 +34,7 @@ def main(): banner = ( """ PPPP Y Y BBBB AAA SSSS I CCC - P P Y Y B B A A S I C + P P Y Y B B A A S I C P P Y Y B B A A S I C PPPP Y BBBB AAAAA SSSS I C P Y B B A A S I C @@ -117,4 +117,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() From 6386acf7e22a251428579f5aa5ab26fc8ad2ab9b Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 4 Sep 2021 11:23:22 -0400 Subject: [PATCH 028/183] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 0acbb9d..4b50fa1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +## This repository is being used for pull requests back to https://github.com/richpl/PyBasic +For the Microcontroller version of PyBasic see: https://github.com/RetiredWizard/PyDOS/tree/main/PyBasic + # A BASIC Interpreter - Program like it's 1979! ## Introduction From 62bac52d67b4f1f86573f9d3bc8e1a6fa3aadf32 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 4 Sep 2021 11:24:26 -0400 Subject: [PATCH 029/183] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b50fa1..47d5428 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ## This repository is being used for pull requests back to https://github.com/richpl/PyBasic -For the Microcontroller version of PyBasic see: https://github.com/RetiredWizard/PyDOS/tree/main/PyBasic +For the Microcontroller version of PyBasic see: https://github.com/RetiredWizard/PyDOS/tree/main/PyBasic or https://github.com/brickbots/PicoBasic # A BASIC Interpreter - Program like it's 1979! From dcf9cc0157260ad738624c6320ebf6a27f556929 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 4 Sep 2021 12:32:23 -0400 Subject: [PATCH 030/183] changed behavior of loop variable when loop ends As was mentioned in the original comments, it's possible the loop variable may have been used before the loop begins so it didn't seem like a good idea to remove the loop variable from the symbol table when the loop ends. --- basicparser.py | 23 ++++++++++++++--------- testloop.bas | Bin 0 -> 1604 bytes 2 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 testloop.bas diff --git a/basicparser.py b/basicparser.py index c8fd45c..32026b5 100644 --- a/basicparser.py +++ b/basicparser.py @@ -85,7 +85,8 @@ def __init__(self): self.__tokenindex = None # Set to keep track of extant loop variables - self. __loop_vars = set() + self.__loop_vars = set() + self.last_flowsignal = None def parse(self, tokenlist, line_number): """Must be initialised with the list of @@ -836,12 +837,19 @@ def __forstmt(self): # Now determine the status of the loop - # If the loop variable is not in the set of extant - # variables, this is the first time we have entered the loop # Note that we cannot use the presence of the loop variable in # the symbol table for this test, as the same variable may already # have been instantiated elsewhere in the program - if loop_variable not in self.__loop_vars: + # + # Need to initialize the loop variable anytime the for + # statement is reached from a statement other than an active NEXT. + + from_next = False + if self.last_flowsignal: + if self.last_flowsignal.ftype == FlowSignal.LOOP_REPEAT: + from_next = True + + if not from_next: self.__symbol_table[loop_variable] = start_val # Also add loop variable to set of extant loop @@ -865,10 +873,8 @@ def __forstmt(self): if stop: # Loop must terminate, so remove loop vriable from set of - # extant loop variables and remove loop variable from - # symbol table + # extant loop variables self.__loop_vars.remove(loop_variable) - del self.__symbol_table[loop_variable] return FlowSignal(ftype=FlowSignal.LOOP_SKIP, ftarget=loop_variable) else: @@ -1320,5 +1326,4 @@ def __randomizestmt(self): random.seed(seed) else: - random.seed() - + random.seed() \ No newline at end of file diff --git a/testloop.bas b/testloop.bas new file mode 100644 index 0000000000000000000000000000000000000000..1d6bc3d2ded3cc3ba35856784fcc3f352c58703e GIT binary patch literal 1604 zcmZvc=R*@w5X6y?fB{6YAlPD$og((GpfMU0qsDS7fx|;Wjsc=r*4}&nd)>+HX;8oA z_BT7bZ{OxUjtHuf>9~Vg$-(@<-C=*iO%!~8 zJTbB-fo9Xr7c8K*P2G~}HcPdevC!xuK?~^;rSpxZu$T=EIcNs76Im6-Csd zx33>d&19KiIeFa-lNCl2f|aCkrEx2U$eZ!KI*;%YrM!Y^7qrS1sESTqCvtm%TH%ZuEv= zfRvZlVwLwHZ*)*FL|QrQPPj&g1vg3m8MbGF5y34A{u%bD2?~NS3c_LA3Kx06r~^MZ z;J5%(Rcnu2uyAS}j zYY{yB+j=J+(fX%@$AR?|!P78#=3nla**_P&pn~_!H<3%mXX~Z$SAy5%P0IDz^1=t} zjo>Zgb2T1rT?+3k>%HIuDIbK&tdD|EjPn{;jsM_8QOkX{#4m!c6fFKL@tfc~ Date: Sat, 4 Sep 2021 12:43:19 -0400 Subject: [PATCH 031/183] loop variable behavior after ending As was mentioned in the original comments, it's possible the loop variable may have been used before the loop begins so it didn't seem like a good idea to remove the loop variable from the symbol table when the loop ends. --- README.md | 3 --- basictoken.py | 1 - program.py | 3 ++- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 47d5428..0acbb9d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -## This repository is being used for pull requests back to https://github.com/richpl/PyBasic -For the Microcontroller version of PyBasic see: https://github.com/RetiredWizard/PyDOS/tree/main/PyBasic or https://github.com/brickbots/PicoBasic - # A BASIC Interpreter - Program like it's 1979! ## Introduction diff --git a/basictoken.py b/basictoken.py index 873946c..48d17a2 100644 --- a/basictoken.py +++ b/basictoken.py @@ -178,4 +178,3 @@ def pretty_print(self): def print_lexeme(self): print(self.lexeme, end=' ') - diff --git a/program.py b/program.py index 8a25353..9829337 100644 --- a/program.py +++ b/program.py @@ -160,6 +160,7 @@ def execute(self): # has line number has been reached while True: flowsignal = self.__execute(self.get_next_line_number()) + self.__parser.last_flowsignal = flowsignal if flowsignal: if flowsignal.ftype == FlowSignal.SIMPLE_JUMP: @@ -322,4 +323,4 @@ def set_next_line_number(self, line_number): :param line_number: The new line number """ - self.__next_stmt = line_number + self.__next_stmt = line_number \ No newline at end of file From aa2584627d4c0ad0c05041ef6392c6df7d8e4ea6 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 4 Sep 2021 12:45:56 -0400 Subject: [PATCH 032/183] Add files via upload --- basictoken.py | 1 + 1 file changed, 1 insertion(+) diff --git a/basictoken.py b/basictoken.py index 48d17a2..873946c 100644 --- a/basictoken.py +++ b/basictoken.py @@ -178,3 +178,4 @@ def pretty_print(self): def print_lexeme(self): print(self.lexeme, end=' ') + From c0afb760de370967efa8f5cb8d559dbf2cc3ecdd Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 4 Sep 2021 13:12:29 -0400 Subject: [PATCH 033/183] cleanup unused variables I don't think extant variable list isn't needed anymore --- basicparser.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/basicparser.py b/basicparser.py index 32026b5..701204a 100644 --- a/basicparser.py +++ b/basicparser.py @@ -85,7 +85,6 @@ def __init__(self): self.__tokenindex = None # Set to keep track of extant loop variables - self.__loop_vars = set() self.last_flowsignal = None def parse(self, tokenlist, line_number): @@ -852,10 +851,6 @@ def __forstmt(self): if not from_next: self.__symbol_table[loop_variable] = start_val - # Also add loop variable to set of extant loop - # variables - self.__loop_vars.add(loop_variable) - else: # We need to modify the loop variable # according to the STEP value @@ -872,9 +867,7 @@ def __forstmt(self): stop = True if stop: - # Loop must terminate, so remove loop vriable from set of - # extant loop variables - self.__loop_vars.remove(loop_variable) + # Loop must terminate return FlowSignal(ftype=FlowSignal.LOOP_SKIP, ftarget=loop_variable) else: From 1d810c60072350e1892695d3c4c69ec77ac1bd65 Mon Sep 17 00:00:00 2001 From: brickbots Date: Sat, 4 Sep 2021 14:25:29 -0700 Subject: [PATCH 034/183] Mostly done, no compound loops --- README.md | 44 +++++++--- basicparser.py | 231 +++++++++++++++++++++++++++++++++++++------------ basictoken.py | 14 ++- program.py | 3 +- regression.bas | 13 ++- 5 files changed, 223 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 0acbb9d..3d0a5bd 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,17 @@ replaced by re-entering a statement with the same line number: > ``` +Multiple statements may appear on one line separated by a colon: +``` +> 10 LET X = 10: PRINT X +``` + +*NOTE*: Currently inline loops are NOT supported +``` +10 FOR I = 1 to 10: PRINT I: NEXT +``` +Will need to be decomposed to individual lines + ### Variables Variable types follow the typical BASIC convention. *Simple* variables may contain either strings @@ -203,14 +214,16 @@ many dimensions the array has, and the sizes of those dimensions: > 10 DIM A(3, 3, 3) ``` -Note that the index of each dimension always starts at *zero*. So in the above example, valid index values for array *A* will be *0, 1* or *2* +Note that the index of each dimension always starts at *zero*, but for +compatibility with some basic dialects the bounds of each dimension will be +expanded by one to enable element access inlcuding the len. So in the above example, +valid index values for array *A* will be *0, 1*, *2* or *3* for each dimension. Arrays may have a maximum of three dimensions. As for simple variables, a string array has its name suffixed by a '$' character, while a numeric array does not carry a suffix. An attempt to assign a string value to a numeric array or vice versa will generate an error. -Note that the same variable name cannot be used for both an array and a simple variable. For example, the variables -*I$* and *I$(10)* should not be used within the same program, the results may be unpredictable. Also, array variables +Array variables with the same name but different *dimensionality* are treated as the same. For example, using a **DIM** statement to define *I(5)* and then a second **DIM** statement to define *I(5, 5)* will result in the second definition (the two dimensional array) overwriting the first definition (the one dimensional array). @@ -355,11 +368,11 @@ Hello > ``` -Multiple items may be printed by providing a comma separated list. The items in the list will be printed immediately after one +Multiple items may be printed by providing a semicolon separated list. The items in the list will be printed immediately after one another, so spaces must be inserted if these are required: ``` -> 10 PRINT 345, " hello ", 678 +> 10 PRINT 345; " hello "; 678 > RUN 345 hello 678 > @@ -378,6 +391,8 @@ There it was > ``` +A print statement terminated by a semicolon will not include a CR/LF. + ### Unconditional branching Like it or loath it, the **GOTO** statement is an integral part of BASIC, and is used to transfer control to the statement with the specified line number: @@ -576,10 +591,10 @@ The **INPUT** statement is used to solicit input from the user: ``` The default input prompt of '? ' may be changed by inserting a prompt string, which must be terminated -by a colon, thus: +by a semicolon, thus: ``` -> 10 INPUT "Input a number - ": A +> 10 INPUT "Input a number - "; A > 20 PRINT A > RUN Input a number - 22 @@ -652,12 +667,12 @@ Allowable numeric functions are: * **POW**(x, y) - Calculates *x* to the power *y* -* **RND** - Generates a pseudo random number N, where *0 <= N < 1*. Can be +* **RND**(mode) - Psuedorandom number generator. The behavior is different depending on the value passed. If the value is positive, the result will be a new random value between 0 and 1 (including 0 but not 1). If the value is negative, it will be rounded down to the nearest integer and used to reseed the random number generator. Pseudorandom sequences can be repeated by reseeding with the same number.Generates a pseudo random number N, where *0 <= N < 1*. Can be reset using the **RANDOMIZE** instruction with an optional seed value: e.g. ``` > 10 RANDOMIZE 100 -> 20 PRINT RND +> 20 PRINT RND(1) > RUN 0.1456692551041303 > @@ -666,7 +681,7 @@ reset using the **RANDOMIZE** instruction with an optional seed value: e.g. Random integers can be generated by combining **RND** and **INT**: e.g. ``` -> 10 PRINT INT(RND * 6) + 1 +> 10 PRINT INT(RND(1) * 6) + 1 > RUN 3 > RUN @@ -702,12 +717,15 @@ Note that despite the name, this function can return codes outside the ASCII ran at position *start* and end at *end*. Returns -1 if no match found. * **LEN**(x$) - Returns the length of *x$*. * **LOWER$**(x$) - Returns a lower-case version of *x$*. -* **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y* and ending at *z*. *z* can -be omitted to get the rest of the string. If *y* or *z* are negative, the position is counted -backwards from the end of the string. +* **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y*. If z is provided, that number of characters is returned, if omitted the entire rest of the string is returned +* **LEFT$**(x$, y) - Returns the left most r characters from string x$. If y * exceeds the length of x$, the entire string will be returned. +* **RIGHT$**(x$, y) - Returns the right most r characters from string x$. If y * exceeds the length of x$, the entire string will be returned. * **STR$**(x) - Returns a string representation of numeric value *x*. * **UPPER$**(x$) - Returns an upper-case version of *x$* * **VAL**(x$) - Attempts to convert *x$* to a numeric value. If *x$* is not numeric, returns 0. +* **TAB**(x) - Generates a string containing x spaces with no CR/LF + +**NOTE** For compatibility with older basic dialetcs, all string indexes are 1 based. Examples for **ASC**, **CHR$** and **STR$** ``` diff --git a/basicparser.py b/basicparser.py index c8fd45c..aa4fc97 100644 --- a/basicparser.py +++ b/basicparser.py @@ -50,12 +50,23 @@ def __init__(self, dimensions): raise SyntaxError("Fractional array size specified") dimensions[i] = int(dimensions[i]) + # MSBASIC: Initialize to Zero + # MSBASIC: Overdim by one, as some dialects are 1 based and expect + # to use the last item at index = size if self.dims == 1: - self.data = [None for x in range(dimensions[0])] + self.data = [0 for x in range(dimensions[0] + 1)] elif self.dims == 2: - self.data = [[None for x in range(dimensions[1])] for x in range(dimensions[0])] + self.data = [ + [0 for x in range(dimensions[1] + 1)] for x in range(dimensions[0] + 1) + ] else: - self.data = [[[None for x in range(dimensions[2])] for x in range(dimensions[1])] for x in range(dimensions[0])] + self.data = [ + [ + [0 for x in range(dimensions[2] + 1)] + for x in range(dimensions[1] + 1) + ] + for x in range(dimensions[0] + 1) + ] def pretty_print(self): print(str(self.data)) @@ -100,15 +111,27 @@ def parse(self, tokenlist, line_number): how to branch if necessary, None otherwise """ - self.__tokenlist = tokenlist - self.__tokenindex = 0 - # Remember the line number to aid error reporting self.__line_number = line_number + self.__tokenlist = [] + for token in tokenlist: + if token.category == token.COLON: + self.__tokenindex = 0 + + # Assign the first token + self.__token = self.__tokenlist[self.__tokenindex] + flow = self.__stmt() + if flow: + return flow + + self.__tokenlist = [] + else: + self.__tokenlist.append(token) + + self.__tokenindex = 0 # Assign the first token self.__token = self.__tokenlist[self.__tokenindex] - return self.__stmt() def __advance(self): @@ -219,7 +242,11 @@ def __printstmt(self): self.__logexpr() print(self.__operand_stack.pop(), end='') - while self.__token.category == Token.COMMA: + while self.__token.category == Token.SEMICOLON: + if self.__tokenindex == len(self.__tokenlist) - 1: + # If a semicolon ends this line, don't print + # a newline.. a-la ms-basic + return self.__advance() self.__logexpr() print(self.__operand_stack.pop(), end='') @@ -318,32 +345,44 @@ def __dimstmt(self): """ self.__advance() # Advance past DIM keyword - # Extract the array name, append a suffix so - # that we can distinguish from simple variables - # in the symbol table - name = self.__token.lexeme + '_array' - self.__advance() # Advance past array name + # MSBASIC: allow dims of multiple arrays delimited by commas + while True: + # Extract the array name, append a suffix so + # that we can distinguish from simple variables + # in the symbol table + name = self.__token.lexeme + "_array" + self.__advance() # Advance past array name - self.__consume(Token.LEFTPAREN) - - # Extract the dimensions - dimensions = [] - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - dimensions.append(self.__operand_stack.pop()) + self.__consume(Token.LEFTPAREN) - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma + # Extract the dimensions + dimensions = [] + if not self.__tokenindex >= len(self.__tokenlist): self.__expr() dimensions.append(self.__operand_stack.pop()) - self.__consume(Token.RIGHTPAREN) + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + dimensions.append(self.__operand_stack.pop()) + + self.__consume(Token.RIGHTPAREN) + + if len(dimensions) > 3: + raise SyntaxError( + "Maximum number of array dimensions is three " + + "in line " + + str(self.__line_number) + ) + + self.__symbol_table[name] = BASICArray(dimensions) - if len(dimensions) > 3: - raise SyntaxError("Maximum number of array dimensions is three " + - "in line " + str(self.__line_number)) + if self.__tokenindex == len(self.__tokenlist): + # We have parsed the last token here... + return + else: + self.__consume(Token.COMMA) - self.__symbol_table[name] = BASICArray(dimensions) def __arrayassignmentstmt(self, name): """Parses an assignment to an array variable @@ -420,7 +459,7 @@ def __inputstmt(self): # Acquire the input prompt self.__logexpr() prompt = self.__operand_stack.pop() - self.__consume(Token.COLON) + self.__consume(Token.SEMICOLON) # Acquire the comma separated input variables variables = [] @@ -616,12 +655,21 @@ def __factor(self): self.__operand_stack.append(self.__token.lexeme) self.__advance() - elif self.__token.category == Token.NAME and \ - self.__token.category not in Token.functions: + elif ( + self.__token.category == Token.NAME + and self.__token.category not in Token.functions + ): # Check if this is a simple or array variable - if (self.__token.lexeme + '_array') in self.__symbol_table: + # MSBASIC Allows simple and complex variables to have the + # same id. This is probably a bad idea, but it's used in + # some old example programs. So check if next token is parens + if ( + (self.__token.lexeme + "_array") in self.__symbol_table + and self.__tokenindex < len(self.__tokenlist) - 1 + and self.__tokenlist[self.__tokenindex + 1].category == Token.LEFTPAREN + ): # Capture the current lexeme - arrayname = self.__token.lexeme + '_array' + arrayname = self.__token.lexeme + "_array" # Array must be processed # Capture the index variables @@ -629,29 +677,31 @@ def __factor(self): try: self.__consume(Token.LEFTPAREN) - except RuntimeError: - raise RuntimeError('Array used without index in line ' + - str(self.__line_number)) - - indexvars = [] - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - indexvars.append(self.__operand_stack.pop()) - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma + indexvars = [] + if not self.__tokenindex >= len(self.__tokenlist): self.__expr() indexvars.append(self.__operand_stack.pop()) - BASICarray = self.__symbol_table[arrayname] - arrayval = self.__get_array_val(BASICarray, indexvars) + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + indexvars.append(self.__operand_stack.pop()) - if arrayval != None: - self.__operand_stack.append(self.__sign*arrayval) + BASICarray = self.__symbol_table[arrayname] + arrayval = self.__get_array_val(BASICarray, indexvars) - else: - raise IndexError('Empty array value returned in line ' + - str(self.__line_number)) + if arrayval != None: + self.__operand_stack.append(self.__sign * arrayval) + + else: + raise IndexError( + "Empty array value returned in line " + + str(self.__line_number) + ) + except RuntimeError: + raise RuntimeError( + "Array used without index in line " + str(self.__line_number) + ) elif self.__token.lexeme in self.__symbol_table: # Simple variable must be processed @@ -682,7 +732,8 @@ def __factor(self): else: raise RuntimeError('Expecting factor in numeric expression' + - ' in line ' + str(self.__line_number)) + ' in line ' + str(self.__line_number) + + self.__token.lexeme) def __get_array_val(self, BASICarray, indexvars): """Extracts the value from the given BASICArray at the specified indexes @@ -989,6 +1040,19 @@ def __evaluate_function(self, category): # Process arguments according to function if category == Token.RND: + self.__consume(Token.LEFTPAREN) + + self.__expr() + arg = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + # MSBASIC basic reseeds with negative values + # as arg to RND... not sure if it returned anything + # Zero returns the last value again (not implemented) + # Any positive value returns random fload btw 0 and 1 + if arg < 0: + random.seed(arg) + return random.random() if category == Token.PI: @@ -1094,6 +1158,46 @@ def __evaluate_function(self, category): return whentrue if condition else whenfalse + if category == Token.LEFT: + self.__consume(Token.LEFTPAREN) + + self.__expr() + instring = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + chars = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + try: + return instring[:chars] + + except TypeError: + raise TypeError("Invalid type supplied to LEFT$ in line " + + str(self.__line_number)) + + if category == Token.RIGHT: + self.__consume(Token.LEFTPAREN) + + self.__expr() + instring = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + chars = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + try: + return instring[-chars:] + + except TypeError: + raise TypeError("Invalid type supplied to RIGHT$ in line " + + str(self.__line_number)) + if category == Token.MID: self.__consume(Token.LEFTPAREN) @@ -1103,19 +1207,23 @@ def __evaluate_function(self, category): self.__consume(Token.COMMA) self.__expr() - start = self.__operand_stack.pop() + # Older basic dialets were always 1 based + start = self.__operand_stack.pop() - 1 if self.__token.category == Token.COMMA: self.__advance() # Advance past comma self.__expr() - end = self.__operand_stack.pop() + chars = self.__operand_stack.pop() else: - end = None + chars = None self.__consume(Token.RIGHTPAREN) try: - return instring[start:end] + if chars: + return instring[start:start+chars] + else: + return instring[start:] except TypeError: raise TypeError("Invalid type supplied to MID$ in line " + @@ -1139,12 +1247,13 @@ def __evaluate_function(self, category): if self.__token.category == Token.COMMA: self.__advance() # Advance past comma self.__expr() - start = self.__operand_stack.pop() + # Older basic dialets were always 1 based + start = self.__operand_stack.pop() -1 if self.__token.category == Token.COMMA: self.__advance() # Advance past comma self.__expr() - end = self.__operand_stack.pop() + end = self.__operand_stack.pop() -1 self.__consume(Token.RIGHTPAREN) @@ -1302,6 +1411,14 @@ def __evaluate_function(self, category): return value.lower() + elif category == Token.TAB: + # Return a string of value spaces + if not isinstance(value, int): + raise TypeError("Invalid type supplied to TAB in line " + + str(self.__line_number)) + + return " " * value + else: raise SyntaxError("Unrecognised function in line " + str(self.__line_number)) diff --git a/basictoken.py b/basictoken.py index 873946c..e795d58 100644 --- a/basictoken.py +++ b/basictoken.py @@ -108,6 +108,10 @@ class BASICToken: NOT = 75 # NOT operator PI = 76 # PI constant RNDINT = 77 # RNDINT function + TAB = 80 # TAB function + SEMICOLON = 81 # SEMICOLON + LEFT = 82 # LEFT$ function + RIGHT = 83 # RIGHT$ function # Displayable names for each token category catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN', @@ -124,14 +128,15 @@ class BASICToken: 'CHR', 'ASC', 'STR', 'MID', 'MODULO', 'TERNARY', 'VAL', 'LEN', 'UPPER', 'LOWER', 'ROUND', 'MAX', 'MIN', 'INSTR', 'AND', 'OR', 'NOT', 'PI', - 'RNDINT'] + 'RNDINT', 'TAB', 'SEMICOLON', 'LEFT', 'RIGHT'] smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN, '+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE, '\n': NEWLINE, '<': LESSER, '>': GREATER, '<>': NOTEQUAL, '<=': LESSEQUAL, '>=': GREATEQUAL, ',': COMMA, - ':': COLON, '%': MODULO, '!=': NOTEQUAL} + ':': COLON, '%': MODULO, '!=': NOTEQUAL, + ';': SEMICOLON} # Dictionary of BASIC reserved words keywords = {'LET': LET, 'LIST': LIST, 'PRINT': PRINT, @@ -155,12 +160,13 @@ class BASICToken: 'ROUND': ROUND, 'MAX': MAX, 'MIN': MIN, 'INSTR': INSTR, 'END': STOP, 'AND': AND, 'OR': OR, 'NOT': NOT, - 'PI': PI, 'RNDINT': RNDINT} + 'PI': PI, 'RNDINT': RNDINT, 'TAB': TAB, + 'LEFT$': LEFT, 'RIGHT$': RIGHT} # Functions functions = {ABS, ATN, COS, EXP, INT, LOG, POW, RND, SIN, SQR, TAN, CHR, ASC, MID, TERNARY, STR, VAL, LEN, UPPER, LOWER, - ROUND, MAX, MIN, INSTR, PI, RNDINT} + ROUND, MAX, MIN, INSTR, PI, RNDINT, TAB, LEFT, RIGHT} def __init__(self, column, category, lexeme): diff --git a/program.py b/program.py index a1c5464..68c8a66 100644 --- a/program.py +++ b/program.py @@ -231,7 +231,8 @@ def execute(self): # Put loop line number on the stack so # that it can be returned to when the loop # repeats - self.__return_stack.append(self.get_next_line_number()) + #self.__return_stack.append(self.get_next_line_number()) + self.__return_stack.append(line_numbers[index]) # Continue to the next statement in the loop index = index + 1 diff --git a/regression.bas b/regression.bas index 1bf56d7..6ad2dc2 100644 --- a/regression.bas +++ b/regression.bas @@ -1,8 +1,7 @@ 10 REM A BASIC PROGRAM THAT CAN BE USED FOR REGRESSION TESTING 20 REM OF ALL INTERPRETER FUNCTIONALITY -30 PRINT "*** Testing basic arithmetic functions ***" -40 LET I = 100 -50 LET J = 200 +30 PRINT "*** Testing basic arithmetic functions and multiple statements ***" +40 LET I = 100: LET J = 200 60 PRINT "Expecting the sum to be 300:" 70 PRINT I + J 80 PRINT "Expecting the product to be 20000:" @@ -12,10 +11,10 @@ 120 PRINT "Expecting sum to be 40000" 130 PRINT (100 + I) * J 140 IF I > J THEN 150 ELSE 180 -150 PRINT "Should not print the smaller value of I which is ", I +150 PRINT "Should not print the smaller value of I which is "; I 160 PRINT I 170 GOTO 180 -180 PRINT "Should print the larger value of J which is ", J +180 PRINT "Should print the larger value of J which is "; J 190 PRINT J 200 GOTO 220 210 PRINT "Should not print this line" @@ -37,7 +36,7 @@ 370 PRINT "These nested loops should print 11, 12, 13, 21, 22, 23:" 380 FOR I = 1 TO 2 390 FOR J = 1 TO 3 -400 PRINT I, J +400 PRINT I; J 410 NEXT J 420 NEXT I 430 PRINT "*** Testing arrays ***" @@ -48,7 +47,7 @@ 480 NEXT J 490 NEXT I 500 PRINT "This should print 555" -510 PRINT A(0, 0), A(1, 1), A(2, 2) +510 PRINT A(0, 0); A(1, 1); A(2, 2) 520 PRINT "*** Finished ***" 530 STOP 540 REM A SUBROUTINE TEST From e4c941f0c3af64d7ebf3416ea29933eedff61664 Mon Sep 17 00:00:00 2001 From: brickbots Date: Sat, 4 Sep 2021 14:55:52 -0700 Subject: [PATCH 035/183] Update README / check for .bas on save and load --- README.md | 14 +++++++------- program.py | 8 ++++++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0acbb9d..542fbc8 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ A program is executed using the **RUN** command: > ``` -A program may be saved to disk using the **SAVE** command. Not that the full path must be specified within double quotes: +A program may be saved to disk using the **SAVE** command. Note that the full path must be specified within double quotes: ``` > SAVE "C:\path\to\my\file" @@ -80,10 +80,7 @@ Program written to file > ``` -Saving is achieved by pickling the Python object that represents the BASIC program, i.e. the saved file is *not* a textual copy of -the program statements. - -The program may be re-loaded (i.e. unpickled) from disk using the **LOAD** command, again specifying the full path using double quotes: +The program may be re-loaded from disk using the **LOAD** command, again specifying the full path using double quotes: ``` > LOAD "C:\path\to\my\file" @@ -91,8 +88,11 @@ Program read from file > ``` -Since loading is performed by unpickling the program object from a file, only BASIC programs *previously saved -by the interpreter* may be loaded. +When loading or saving, the .bas extension is assumed if not provided. If you are loading a simple name (alpha/numbers only) and in the working dir, quotes can be omitted: +``` +> LOAD regression +``` +Will load REGRESSION.bas from the current working directory. Individual program statements may be deleted by entering their line number only: diff --git a/program.py b/program.py index a1c5464..0d95bd3 100644 --- a/program.py +++ b/program.py @@ -71,8 +71,10 @@ def save(self, file): appended """ + if not file.lower().endswith(".bas"): + file += ".bas" try: - with open(file + ".bas", "w") as outfile: + with open(file, "w") as outfile: outfile.write(str(self)) except OSError: raise OSError("Could not save to file") @@ -87,9 +89,11 @@ def load(self, file): # New out the program self.delete() + if not file.lower().endswith(".bas"): + file += ".bas" try: lexer = Lexer() - with open(file + ".bas", "r") as infile: + with open(file, "r") as infile: for line in infile: line = line.replace("\r", "").replace("\n", "").strip() tokenlist = lexer.tokenize(line) From 971806351fa0f55bbe601b161e9bbb3b18ede9c8 Mon Sep 17 00:00:00 2001 From: brickbots Date: Sat, 4 Sep 2021 14:57:08 -0700 Subject: [PATCH 036/183] Fix case in load example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 542fbc8..4606d88 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ When loading or saving, the .bas extension is assumed if not provided. If you a ``` > LOAD regression ``` -Will load REGRESSION.bas from the current working directory. +Will load regression.bas from the current working directory. Individual program statements may be deleted by entering their line number only: From b3001c429490b3e44f2ea23392a05e389deb874e Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 5 Sep 2021 21:36:14 +0100 Subject: [PATCH 037/183] Removed paragraph stating that ASCII files cannot be loaded --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 4606d88..0ff238d 100644 --- a/README.md +++ b/README.md @@ -732,12 +732,6 @@ calculate the corresponding factorial *N!*. * *rock_scissors_paper.bas* - A BASIC implementation of the rock-paper-scissors game. -*Note that you cannot simply load these programs from the text files. They must -be entered line by line into the interpreter. The program can then be saved and -reloaded using the* **SAVE** and **LOAD** *commands as described above. Of course, -this is no more inconvenient than saving a program to cassette tape and reloading it, -as we all would have done in the 1980s!* - ## Informal grammar definition **ABS**(*numerical-expression*) - Calculates the absolute value of the result of *numerical-expression* From ec2ab44576bdfb8818c547bcad092379a9bf9755 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 5 Sep 2021 18:31:33 -0400 Subject: [PATCH 038/183] Add files via upload --- testloop.bas | Bin 1604 -> 233 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/testloop.bas b/testloop.bas index 1d6bc3d2ded3cc3ba35856784fcc3f352c58703e..b9c7a1ed8019cc0dec0b9aa12a5e878c8e452f39 100644 GIT binary patch literal 233 zcmYk0F$=;l5QTTa|M19ahol+>(XrJm7bzK>#fU;MrAz<5cPRw7){b!d5TCmkN6y+&~RIt8oeJ1$sl+zinkS5GRDI7 vC1Me-9pU07xh+wCLESwpG+v1vVFM$aot7ryg!aE#inh8{pN74@iyYwxUw|+HX;8oA z_BT7bZ{OxUjtHuf>9~Vg$-(@<-C=*iO%!~8 zJTbB-fo9Xr7c8K*P2G~}HcPdevC!xuK?~^;rSpxZu$T=EIcNs76Im6-Csd zx33>d&19KiIeFa-lNCl2f|aCkrEx2U$eZ!KI*;%YrM!Y^7qrS1sESTqCvtm%TH%ZuEv= zfRvZlVwLwHZ*)*FL|QrQPPj&g1vg3m8MbGF5y34A{u%bD2?~NS3c_LA3Kx06r~^MZ z;J5%(Rcnu2uyAS}j zYY{yB+j=J+(fX%@$AR?|!P78#=3nla**_P&pn~_!H<3%mXX~Z$SAy5%P0IDz^1=t} zjo>Zgb2T1rT?+3k>%HIuDIbK&tdD|EjPn{;jsM_8QOkX{#4m!c6fFKL@tfc~ Date: Sun, 5 Sep 2021 18:44:34 -0400 Subject: [PATCH 039/183] attempt to reset github diff issue --- basicparser.py | 2646 ++++++++++++++++++++++++------------------------ 1 file changed, 1323 insertions(+), 1323 deletions(-) diff --git a/basicparser.py b/basicparser.py index 5f061ef..1e37dad 100644 --- a/basicparser.py +++ b/basicparser.py @@ -1,1323 +1,1323 @@ -#! /usr/bin/python - -# SPDX-License-Identifier: GPL-3.0-or-later -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from basictoken import BASICToken as Token -from flowsignal import FlowSignal -import math -import random -from time import monotonic - - -"""Implements a BASIC array, which may have up -to three dimensions of fixed size. - -""" -class BASICArray: - - def __init__(self, dimensions): - """Initialises the object with the specified - number of dimensions. Maximum number of - dimensions is three - - :param dimensions: List of array dimensions and their - corresponding sizes - - """ - self.dims = min(3,len(dimensions)) - - if self.dims == 0: - raise SyntaxError("Zero dimensional array specified") - - # Check for invalid sizes and ensure int - for i in range(self.dims): - if dimensions[i] < 0: - raise SyntaxError("Negative array size specified") - # Allow sizes like 1.0f, but not 1.1f - if int(dimensions[i]) != dimensions[i]: - raise SyntaxError("Fractional array size specified") - dimensions[i] = int(dimensions[i]) - - if self.dims == 1: - self.data = [None for x in range(dimensions[0])] - elif self.dims == 2: - self.data = [[None for x in range(dimensions[1])] for x in range(dimensions[0])] - else: - self.data = [[[None for x in range(dimensions[2])] for x in range(dimensions[1])] for x in range(dimensions[0])] - - def pretty_print(self): - print(str(self.data)) - -"""Implements a BASIC parser that parses a single -statement when supplied. - -""" -class BASICParser: - - def __init__(self): - # Symbol table to hold variable names mapped - # to values - self.__symbol_table = {} - - # Stack on which to store operands - # when evaluating expressions - self.__operand_stack = [] - - # List to hold contents of DATA statement - self.__data_values = [] - - # These values will be - # initialised on a per - # statement basis - self.__tokenlist = [] - self.__tokenindex = None - - # Set to keep track of extant loop variables - self.last_flowsignal = None - - def parse(self, tokenlist, line_number): - """Must be initialised with the list of - BTokens to be processed. These tokens - represent a BASIC statement without - its corresponding line number. - - :param tokenlist: The tokenized program statement - :param line_number: The line number of the statement - - :return: The FlowSignal to indicate to the program - how to branch if necessary, None otherwise - - """ - self.__tokenlist = tokenlist - self.__tokenindex = 0 - - # Remember the line number to aid error reporting - self.__line_number = line_number - - # Assign the first token - self.__token = self.__tokenlist[self.__tokenindex] - - return self.__stmt() - - def __advance(self): - """Advances to the next token - - """ - # Move to the next token - self.__tokenindex += 1 - - # Acquire the next token if there any left - if not self.__tokenindex >= len(self.__tokenlist): - self.__token = self.__tokenlist[self.__tokenindex] - - def __consume(self, expected_category): - """Consumes a token from the list - - """ - if self.__token.category == expected_category: - self.__advance() - - else: - raise RuntimeError('Expecting ' + Token.catnames[expected_category] + - ' in line ' + str(self.__line_number)) - - def __stmt(self): - """Parses a program statement - - :return: The FlowSignal to indicate to the program - how to branch if necessary, None otherwise - - """ - if self.__token.category in [Token.FOR, Token.IF, Token.NEXT, - Token.ON]: - return self.__compoundstmt() - - else: - return self.__simplestmt() - - def __simplestmt(self): - """Parses a non-compound program statement - - :return: The FlowSignal to indicate to the program - how to branch if necessary, None otherwise - - """ - if self.__token.category == Token.NAME: - self.__assignmentstmt() - return None - - elif self.__token.category == Token.PRINT: - self.__printstmt() - return None - - elif self.__token.category == Token.LET: - self.__letstmt() - return None - - elif self.__token.category == Token.GOTO: - return self.__gotostmt() - - elif self.__token.category == Token.GOSUB: - return self.__gosubstmt() - - elif self.__token.category == Token.RETURN: - return self.__returnstmt() - - elif self.__token.category == Token.STOP: - return self.__stopstmt() - - elif self.__token.category == Token.INPUT: - self.__inputstmt() - return None - - elif self.__token.category == Token.DIM: - self.__dimstmt() - return None - - elif self.__token.category == Token.RANDOMIZE: - self.__randomizestmt() - return None - - elif self.__token.category == Token.DATA: - self.__datastmt() - return None - - elif self.__token.category == Token.READ: - self.__readstmt() - return None - - else: - # Ignore comments, but raise an error - # for anything else - if self.__token.category != Token.REM: - raise RuntimeError('Expecting program statement in line ' - + str(self.__line_number)) - - def __printstmt(self): - """Parses a PRINT statement, causing - the value that is on top of the - operand stack to be printed on - the screen. - - """ - self.__advance() # Advance past PRINT token - - # Check there are items to print - if not self.__tokenindex >= len(self.__tokenlist): - self.__logexpr() - print(self.__operand_stack.pop(), end='') - - while self.__token.category == Token.COMMA: - self.__advance() - self.__logexpr() - print(self.__operand_stack.pop(), end='') - - # Final newline - print() - - def __letstmt(self): - """Parses a LET statement, - consuming the LET keyword. - """ - self.__advance() # Advance past the LET token - self.__assignmentstmt() - - def __gotostmt(self): - """Parses a GOTO statement - - :return: A FlowSignal containing the target line number - of the GOTO - - """ - self.__advance() # Advance past GOTO token - self.__expr() - - # Set up and return the flow signal - return FlowSignal(ftarget=self.__operand_stack.pop()) - - def __gosubstmt(self): - """Parses a GOSUB statement - - :return: A FlowSignal containing the first line number - of the subroutine - - """ - - self.__advance() # Advance past GOSUB token - self.__expr() - - # Set up and return the flow signal - return FlowSignal(ftarget=self.__operand_stack.pop(), - ftype=FlowSignal.GOSUB) - - def __returnstmt(self): - """Parses a RETURN statement""" - - self.__advance() # Advance past RETURN token - - # Set up and return the flow signal - return FlowSignal(ftype=FlowSignal.RETURN) - - def __stopstmt(self): - """Parses a STOP statement""" - - self.__advance() # Advance past STOP token - - return FlowSignal(ftype=FlowSignal.STOP) - - def __assignmentstmt(self): - """Parses an assignment statement, - placing the corresponding - variable and its value in the symbol - table. - - """ - left = self.__token.lexeme # Save lexeme of - # the current token - self.__advance() - - if self.__token.category == Token.LEFTPAREN: - # We are assiging to an array - self.__arrayassignmentstmt(left) - - else: - # We are assigning to a simple variable - self.__consume(Token.ASSIGNOP) - self.__logexpr() - - # Check that we are using the right variable name format - right = self.__operand_stack.pop() - - if left.endswith('$') and not isinstance(right, str): - raise SyntaxError('Syntax error: Attempt to assign non string to string variable' + - ' in line ' + str(self.__line_number)) - - elif not left.endswith('$') and isinstance(right, str): - raise SyntaxError('Syntax error: Attempt to assign string to numeric variable' + - ' in line ' + str(self.__line_number)) - - self.__symbol_table[left] = right - - def __dimstmt(self): - """Parses DIM statement and creates a symbol - table entry for an array of the specified - dimensions. - - """ - self.__advance() # Advance past DIM keyword - - # Extract the array name, append a suffix so - # that we can distinguish from simple variables - # in the symbol table - name = self.__token.lexeme + '_array' - self.__advance() # Advance past array name - - self.__consume(Token.LEFTPAREN) - - # Extract the dimensions - dimensions = [] - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - dimensions.append(self.__operand_stack.pop()) - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - dimensions.append(self.__operand_stack.pop()) - - self.__consume(Token.RIGHTPAREN) - - if len(dimensions) > 3: - raise SyntaxError("Maximum number of array dimensions is three " + - "in line " + str(self.__line_number)) - - self.__symbol_table[name] = BASICArray(dimensions) - - def __arrayassignmentstmt(self, name): - """Parses an assignment to an array variable - - :param name: Array name - - """ - self.__consume(Token.LEFTPAREN) - - # Capture the index variables - # Extract the dimensions - indexvars = [] - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - indexvars.append(self.__operand_stack.pop()) - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - indexvars.append(self.__operand_stack.pop()) - - try: - BASICarray = self.__symbol_table[name + '_array'] - - except KeyError: - raise KeyError('Array could not be found in line ' + - str(self.__line_number)) - - if BASICarray.dims != len(indexvars): - raise IndexError('Incorrect number of indices applied to array ' + - 'in line ' + str(self.__line_number)) - - self.__consume(Token.RIGHTPAREN) - self.__consume(Token.ASSIGNOP) - - self.__logexpr() - - # Check that we are using the right variable name format - right = self.__operand_stack.pop() - - if name.endswith('$') and not isinstance(right, str): - raise SyntaxError('Attempt to assign non string to string array' + - ' in line ' + str(self.__line_number)) - - elif not name.endswith('$') and isinstance(right, str): - raise SyntaxError('Attempt to assign string to numeric array' + - ' in line ' + str(self.__line_number)) - - # Assign to the specified array index - try: - if len(indexvars) == 1: - BASICarray.data[indexvars[0]] = right - - elif len(indexvars) == 2: - BASICarray.data[indexvars[0]][indexvars[1]] = right - - elif len(indexvars) == 3: - BASICarray.data[indexvars[0]][indexvars[1]][indexvars[2]] = right - - except IndexError: - raise IndexError('Array index out of range in line ' + - str(self.__line_number)) - - def __inputstmt(self): - """Parses an input statement, extracts the input - from the user and places the values into the - symbol table - - """ - self.__advance() # Advance past INPUT token - - prompt = '? ' - if self.__token.category == Token.STRING: - # Acquire the input prompt - self.__logexpr() - prompt = self.__operand_stack.pop() - self.__consume(Token.COLON) - - # Acquire the comma separated input variables - variables = [] - if not self.__tokenindex >= len(self.__tokenlist): - if self.__token.category != Token.NAME: - raise ValueError('Expecting NAME in INPUT statement ' + - 'in line ' + str(self.__line_number)) - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable - - valid_input = False - while not valid_input: - # Gather input from the user into the variables - inputvals = input(prompt).split(',', (len(variables)-1)) - - for variable in variables: - left = variable - - try: - right = inputvals.pop(0) - - if left.endswith('$'): - self.__symbol_table[left] = str(right) - valid_input = True - - elif not left.endswith('$'): - try: - if '.' in right: - self.__symbol_table[left] = float(right) - - else: - self.__symbol_table[left] = int(right) - - valid_input = True - - except ValueError: - valid_input = False - print('Non-numeric input provided to a numeric variable - redo from start') - break - - except IndexError: - # No more input to process - valid_input = False - print('Not enough values input - redo from start') - break - - def __datastmt(self): - """Parses a DATA statement""" - - self.__advance() # Advance past DATA token - - # Acquire the comma separated values - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - self.__data_values.append(self.__operand_stack.pop()) - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - self.__data_values.append(self.__operand_stack.pop()) - - def __readstmt(self): - """Parses a READ statement.""" - - self.__advance() # Advance past READ token - - # Acquire the comma separated input variables - variables = [] - if not self.__tokenindex >= len(self.__tokenlist): - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable - - # Check that we have enough data values to fill the - # variables - if len(variables) > len(self.__data_values): - raise RuntimeError('Insufficient constants supplied to READ ' + - 'in line ' + str(self.__line_number)) - - # Gather input from the DATA statement into the variables - for variable in variables: - left = variable - right = self.__data_values.pop(0) - - if left.endswith('$'): - # Python inserts quotes around input data - if isinstance(right, int): - raise ValueError('Non-string input provided to a string variable ' + - 'in line ' + str(self.__line_number)) - - else: - self.__symbol_table[left] = right - - elif not left.endswith('$'): - try: - #if '.' in right: - # self.__symbol_table[left] = float(right) - #else: - # self.__symbol_table[left] = int(right) - - numeric = float(right) - if numeric.is_integer(): - numeric = int(numeric) - self.__symbol_table[left] = numeric - - except ValueError: - raise ValueError('Non-numeric input provided to a numeric variable ' + - 'in line ' + str(self.__line_number)) - - def __expr(self): - """Parses a numerical expression consisting - of two terms being added or subtracted, - leaving the result on the operand stack. - - """ - self.__term() # Pushes value of left term - # onto top of stack - - while self.__token.category in [Token.PLUS, Token.MINUS]: - savedcategory = self.__token.category - self.__advance() - self.__term() # Pushes value of right term - # onto top of stack - rightoperand = self.__operand_stack.pop() - leftoperand = self.__operand_stack.pop() - - if savedcategory == Token.PLUS: - self.__operand_stack.append(leftoperand + rightoperand) - - else: - self.__operand_stack.append(leftoperand - rightoperand) - - def __term(self): - """Parses a numerical expression consisting - of two factors being multiplied together, - leaving the result on the operand stack. - - """ - self.__sign = 1 # Initialise sign to keep track of unary - # minuses - self.__factor() # Leaves value of term on top of stack - - while self.__token.category in [Token.TIMES, Token.DIVIDE, Token.MODULO]: - savedcategory = self.__token.category - self.__advance() - self.__sign = 1 # Initialise sign - self.__factor() # Leaves value of term on top of stack - rightoperand = self.__operand_stack.pop() - leftoperand = self.__operand_stack.pop() - - if savedcategory == Token.TIMES: - self.__operand_stack.append(leftoperand * rightoperand) - - elif savedcategory == Token.DIVIDE: - self.__operand_stack.append(leftoperand / rightoperand) - - else: - self.__operand_stack.append(leftoperand % rightoperand) - - def __factor(self): - """Evaluates a numerical expression - and leaves its value on top of the - operand stack. - - """ - if self.__token.category == Token.PLUS: - self.__advance() - self.__factor() - - elif self.__token.category == Token.MINUS: - self.__sign = -self.__sign - self.__advance() - self.__factor() - - elif self.__token.category == Token.UNSIGNEDINT: - self.__operand_stack.append(self.__sign*int(self.__token.lexeme)) - self.__advance() - - elif self.__token.category == Token.UNSIGNEDFLOAT: - self.__operand_stack.append(self.__sign*float(self.__token.lexeme)) - self.__advance() - - elif self.__token.category == Token.STRING: - self.__operand_stack.append(self.__token.lexeme) - self.__advance() - - elif self.__token.category == Token.NAME and \ - self.__token.category not in Token.functions: - # Check if this is a simple or array variable - if (self.__token.lexeme + '_array') in self.__symbol_table: - # Capture the current lexeme - arrayname = self.__token.lexeme + '_array' - - # Array must be processed - # Capture the index variables - self.__advance() # Advance past the array name - - try: - self.__consume(Token.LEFTPAREN) - except RuntimeError: - raise RuntimeError('Array used without index in line ' + - str(self.__line_number)) - - indexvars = [] - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - indexvars.append(self.__operand_stack.pop()) - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - indexvars.append(self.__operand_stack.pop()) - - BASICarray = self.__symbol_table[arrayname] - arrayval = self.__get_array_val(BASICarray, indexvars) - - if arrayval != None: - self.__operand_stack.append(self.__sign*arrayval) - - else: - raise IndexError('Empty array value returned in line ' + - str(self.__line_number)) - - elif self.__token.lexeme in self.__symbol_table: - # Simple variable must be processed - self.__operand_stack.append(self.__sign*self.__symbol_table[self.__token.lexeme]) - - else: - raise RuntimeError('Name ' + self.__token.lexeme + ' is not defined' + - ' in line ' + str(self.__line_number)) - - self.__advance() - - elif self.__token.category == Token.LEFTPAREN: - self.__advance() - - # Save sign because expr() calls term() which resets - # sign to 1 - savesign = self.__sign - self.__logexpr() # Value of expr is pushed onto stack - - if savesign == -1: - # Change sign of expression - self.__operand_stack[-1] = -self.__operand_stack[-1] - - self.__consume(Token.RIGHTPAREN) - - elif self.__token.category in Token.functions: - self.__operand_stack.append(self.__evaluate_function(self.__token.category)) - - else: - raise RuntimeError('Expecting factor in numeric expression' + - ' in line ' + str(self.__line_number)) - - def __get_array_val(self, BASICarray, indexvars): - """Extracts the value from the given BASICArray at the specified indexes - - :param BASICarray: The BASICArray - :param indexvars: The list of indexes, one for each dimension - - :return: The value at the indexed position in the array - - """ - if BASICarray.dims != len(indexvars): - raise IndexError('Incorrect number of indices applied to array ' + - 'in line ' + str(self.__line_number)) - - # Fetch the value from the array - try: - if len(indexvars) == 1: - arrayval = BASICarray.data[indexvars[0]] - - elif len(indexvars) == 2: - arrayval = BASICarray.data[indexvars[0]][indexvars[1]] - - elif len(indexvars) == 3: - arrayval = BASICarray.data[indexvars[0]][indexvars[1]][indexvars[2]] - - except IndexError: - raise IndexError('Array index out of range in line ' + - str(self.__line_number)) - - return arrayval - - def __compoundstmt(self): - """Parses compound statements, - specifically if-then-else and - loops - - :return: The FlowSignal to indicate to the program - how to branch if necessary, None otherwise - - """ - if self.__token.category == Token.FOR: - return self.__forstmt() - - elif self.__token.category == Token.NEXT: - return self.__nextstmt() - - elif self.__token.category == Token.IF: - return self.__ifstmt() - - elif self.__token.category == Token.ON: - return self.__ongosubstmt() - - def __ifstmt(self): - """Parses if-then-else - statements - - :return: The FlowSignal to indicate to the program - how to branch if necessary, None otherwise - - """ - - self.__advance() # Advance past IF token - self.__logexpr() - - # Save result of expression - saveval = self.__operand_stack.pop() - - # Process the THEN part and save the jump value - self.__consume(Token.THEN) - - if self.__token.category == Token.GOTO: - self.__advance() # Advance past optional GOTO - - self.__expr() - then_jump = self.__operand_stack.pop() - - # Jump if the expression evaluated to True - if saveval: - # Set up and return the flow signal - return FlowSignal(ftarget=then_jump) - - # See if there is an ELSE part - if self.__token.category == Token.ELSE: - self.__advance() - - if self.__token.category == Token.GOTO: - self.__advance() # Advance past optional GOTO - - self.__expr() - - # Set up and return the flow signal - return FlowSignal(ftarget=self.__operand_stack.pop()) - - else: - # No ELSE action - return None - - def __forstmt(self): - """Parses for loops - - :return: The FlowSignal to indicate that - a loop start has been processed - - """ - - # Set up default loop increment value - step = 1 - - self.__advance() # Advance past FOR token - - # Process the loop variable initialisation - loop_variable = self.__token.lexeme # Save lexeme of - # the current token - - if loop_variable.endswith('$'): - raise SyntaxError('Syntax error: Loop variable is not numeric' + - ' in line ' + str(self.__line_number)) - - self.__advance() # Advance past loop variable - self.__consume(Token.ASSIGNOP) - self.__expr() - - # Check that we are using the right variable name format - # for numeric variables - start_val = self.__operand_stack.pop() - - # Advance past the 'TO' keyword - self.__consume(Token.TO) - - # Process the terminating value - self.__expr() - end_val = self.__operand_stack.pop() - - # Check if there is a STEP value - increment = True - if not self.__tokenindex >= len(self.__tokenlist): - self.__consume(Token.STEP) - - # Acquire the step value - self.__expr() - step = self.__operand_stack.pop() - - # Check whether we are decrementing or - # incrementing - if step == 0: - raise IndexError('Zero step value supplied for loop' + - ' in line ' + str(self.__line_number)) - - elif step < 0: - increment = False - - # Now determine the status of the loop - - # Note that we cannot use the presence of the loop variable in - # the symbol table for this test, as the same variable may already - # have been instantiated elsewhere in the program - # - # Need to initialize the loop variable anytime the for - # statement is reached from a statement other than an active NEXT. - - from_next = False - if self.last_flowsignal: - if self.last_flowsignal.ftype == FlowSignal.LOOP_REPEAT: - from_next = True - - if not from_next: - self.__symbol_table[loop_variable] = start_val - - else: - # We need to modify the loop variable - # according to the STEP value - self.__symbol_table[loop_variable] += step - - # If the loop variable has reached the end value, - # remove it from the set of extant loop variables to signal that - # this is the last loop iteration - stop = False - if increment and self.__symbol_table[loop_variable] > end_val: - stop = True - - elif not increment and self.__symbol_table[loop_variable] < end_val: - stop = True - - if stop: - # Loop must terminate - return FlowSignal(ftype=FlowSignal.LOOP_SKIP, - ftarget=loop_variable) - else: - # Set up and return the flow signal - return FlowSignal(ftype=FlowSignal.LOOP_BEGIN) - - def __nextstmt(self): - """Processes a NEXT statement that terminates - a loop - - :return: A FlowSignal indicating that a loop - has been processed - - """ - - self.__advance() # Advance past NEXT token - - return FlowSignal(ftype=FlowSignal.LOOP_REPEAT) - - def __ongosubstmt(self): - """Process the ON-GOSUB statement - - :return: A FlowSignal indicating the subroutine line number - if the condition is true, None otherwise - - """ - - self.__advance() # Advance past ON token - self.__logexpr() - - # Save result of expression - saveval = self.__operand_stack.pop() - - # Process the GOSUB part and save the jump value - # if the condition is met - if saveval: - return self.__gosubstmt() - else: - return None - - def __relexpr(self): - """Parses a relational expression - """ - self.__expr() - - # Since BASIC uses same operator for both - # assignment and equality, we need to check for this - if self.__token.category == Token.ASSIGNOP: - self.__token.category = Token.EQUAL - - if self.__token.category in [Token.LESSER, Token.LESSEQUAL, - Token.GREATER, Token.GREATEQUAL, - Token.EQUAL, Token.NOTEQUAL]: - savecat = self.__token.category - self.__advance() - self.__expr() - - right = self.__operand_stack.pop() - left = self.__operand_stack.pop() - - if savecat == Token.EQUAL: - self.__operand_stack.append(left == right) # Push True or False - - elif savecat == Token.NOTEQUAL: - self.__operand_stack.append(left != right) # Push True or False - - elif savecat == Token.LESSER: - self.__operand_stack.append(left < right) # Push True or False - - elif savecat == Token.GREATER: - self.__operand_stack.append(left > right) # Push True or False - - elif savecat == Token.LESSEQUAL: - self.__operand_stack.append(left <= right) # Push True or False - - elif savecat == Token.GREATEQUAL: - self.__operand_stack.append(left >= right) # Push True or False - - def __logexpr(self): - """Parses a logical expression - """ - self.__notexpr() - - while self.__token.category in [Token.OR, Token.AND]: - savecat = self.__token.category - self.__advance() - self.__notexpr() - - right = self.__operand_stack.pop() - left = self.__operand_stack.pop() - - if savecat == Token.OR: - self.__operand_stack.append(left or right) # Push True or False - - elif savecat == Token.AND: - self.__operand_stack.append(left and right) # Push True or False - - def __notexpr(self): - """Parses a logical not expression - """ - if self.__token.category == Token.NOT: - self.__advance() - self.__relexpr() - right = self.__operand_stack.pop() - self.__operand_stack.append(not right) - else: - self.__relexpr() - - def __evaluate_function(self, category): - """Evaluate the function in the statement - and return the result. - - :return: The result of the function - - """ - - self.__advance() # Advance past function name - - # Process arguments according to function - if category == Token.RND: - return random.random() - - if category == Token.PI: - return math.pi - - if category == Token.RNDINT: - self.__consume(Token.LEFTPAREN) - - self.__expr() - lo = self.__operand_stack.pop() - - self.__consume(Token.COMMA) - - self.__expr() - hi = self.__operand_stack.pop() - - self.__consume(Token.RIGHTPAREN) - - try: - return random.randint(lo, hi) - - except ValueError: - raise ValueError("Invalid value supplied to RNDINT in line " + - str(self.__line_number)) - - if category == Token.MAX: - self.__consume(Token.LEFTPAREN) - - self.__expr() - value_list = [self.__operand_stack.pop()] - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - value_list.append(self.__operand_stack.pop()) - - self.__consume(Token.RIGHTPAREN) - - try: - return max(*value_list) - - except TypeError: - raise TypeError("Invalid type supplied to MAX in line " + - str(self.__line_number)) - - if category == Token.MIN: - self.__consume(Token.LEFTPAREN) - - self.__expr() - value_list = [self.__operand_stack.pop()] - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - value_list.append(self.__operand_stack.pop()) - - self.__consume(Token.RIGHTPAREN) - - try: - return min(*value_list) - - except TypeError: - raise TypeError("Invalid type supplied to MIN in line " + - str(self.__line_number)) - - if category == Token.POW: - self.__consume(Token.LEFTPAREN) - - self.__expr() - base = self.__operand_stack.pop() - - self.__consume(Token.COMMA) - - self.__expr() - exponent = self.__operand_stack.pop() - - self.__consume(Token.RIGHTPAREN) - - try: - return math.pow(base, exponent) - - except ValueError: - raise ValueError("Invalid value supplied to POW in line " + - str(self.__line_number)) - - if category == Token.TERNARY: - self.__consume(Token.LEFTPAREN) - - self.__logexpr() - condition = self.__operand_stack.pop() - - self.__consume(Token.COMMA) - - self.__expr() - whentrue = self.__operand_stack.pop() - - self.__consume(Token.COMMA) - - self.__expr() - whenfalse = self.__operand_stack.pop() - - self.__consume(Token.RIGHTPAREN) - - return whentrue if condition else whenfalse - - if category == Token.MID: - self.__consume(Token.LEFTPAREN) - - self.__expr() - instring = self.__operand_stack.pop() - - self.__consume(Token.COMMA) - - self.__expr() - start = self.__operand_stack.pop() - - if self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - end = self.__operand_stack.pop() - else: - end = None - - self.__consume(Token.RIGHTPAREN) - - try: - return instring[start:end] - - except TypeError: - raise TypeError("Invalid type supplied to MID$ in line " + - str(self.__line_number)) - - if category == Token.INSTR: - self.__consume(Token.LEFTPAREN) - - self.__expr() - hackstackstring = self.__operand_stack.pop() - if not isinstance(hackstackstring, str): - raise TypeError("Invalid type supplied to INSTR in line " + - str(self.__line_number)) - - self.__consume(Token.COMMA) - - self.__expr() - needlestring = self.__operand_stack.pop() - - start = end = None - if self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - start = self.__operand_stack.pop() - - if self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - end = self.__operand_stack.pop() - - self.__consume(Token.RIGHTPAREN) - - try: - return hackstackstring.find(needlestring, start, end) - - except TypeError: - raise TypeError("Invalid type supplied to INSTR in line " + - str(self.__line_number)) - - self.__consume(Token.LEFTPAREN) - - self.__expr() - value = self.__operand_stack.pop() - - self.__consume(Token.RIGHTPAREN) - - if category == Token.SQR: - try: - return math.sqrt(value) - - except ValueError: - raise ValueError("Invalid value supplied to SQR in line " + - str(self.__line_number)) - - elif category == Token.ABS: - try: - return abs(value) - - except ValueError: - raise ValueError("Invalid value supplied to ABS in line " + - str(self.__line_number)) - - elif category == Token.ATN: - try: - return math.atan(value) - - except ValueError: - raise ValueError("Invalid value supplied to ATN in line " + - str(self.__line_number)) - - elif category == Token.COS: - try: - return math.cos(value) - - except ValueError: - raise ValueError("Invalid value supplied to COS in line " + - str(self.__line_number)) - - elif category == Token.EXP: - try: - return math.exp(value) - - except ValueError: - raise ValueError("Invalid value supplied to EXP in line " + - str(self.__line_number)) - - elif category == Token.INT: - try: - return math.floor(value) - - except ValueError: - raise ValueError("Invalid value supplied to INT in line " + - str(self.__line_number)) - - elif category == Token.ROUND: - try: - return round(value) - - except TypeError: - raise TypeError("Invalid type supplied to LEN in line " + - str(self.__line_number)) - - elif category == Token.LOG: - try: - return math.log(value) - - except ValueError: - raise ValueError("Invalid value supplied to LOG in line " + - str(self.__line_number)) - - elif category == Token.SIN: - try: - return math.sin(value) - - except ValueError: - raise ValueError("Invalid value supplied to SIN in line " + - str(self.__line_number)) - - elif category == Token.TAN: - try: - return math.tan(value) - - except ValueError: - raise ValueError("Invalid value supplied to TAN in line " + - str(self.__line_number)) - - elif category == Token.CHR: - try: - return chr(value) - - except TypeError: - raise TypeError("Invalid type supplied to CHR$ in line " + - str(self.__line_number)) - - except ValueError: - raise ValueError("Invalid value supplied to CHR$ in line " + - str(self.__line_number)) - - elif category == Token.ASC: - try: - return ord(value) - - except TypeError: - raise TypeError("Invalid type supplied to ASC in line " + - str(self.__line_number)) - - except ValueError: - raise ValueError("Invalid value supplied to ASC in line " + - str(self.__line_number)) - - elif category == Token.STR: - return str(value) - - elif category == Token.VAL: - try: - numeric = float(value) - if numeric.is_integer(): - return int(numeric) - return numeric - - # Like other BASIC variants, non-numeric strings return 0 - except ValueError: - return 0 - - elif category == Token.LEN: - try: - return len(value) - - except TypeError: - raise TypeError("Invalid type supplied to LEN in line " + - str(self.__line_number)) - - elif category == Token.UPPER: - if not isinstance(value, str): - raise TypeError("Invalid type supplied to UPPER$ in line " + - str(self.__line_number)) - - return value.upper() - - elif category == Token.LOWER: - if not isinstance(value, str): - raise TypeError("Invalid type supplied to LOWER$ in line " + - str(self.__line_number)) - - return value.lower() - - else: - raise SyntaxError("Unrecognised function in line " + - str(self.__line_number)) - - def __randomizestmt(self): - """Implements a function to seed the random - number generator - - """ - self.__advance() # Advance past RANDOMIZE token - - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() # Process the seed - seed = self.__operand_stack.pop() - - random.seed(seed) - - else: - random.seed(int(monotonic())) +#! /usr/bin/python + +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from basictoken import BASICToken as Token +from flowsignal import FlowSignal +import math +import random +from time import monotonic + + +"""Implements a BASIC array, which may have up +to three dimensions of fixed size. + +""" +class BASICArray: + + def __init__(self, dimensions): + """Initialises the object with the specified + number of dimensions. Maximum number of + dimensions is three + + :param dimensions: List of array dimensions and their + corresponding sizes + + """ + self.dims = min(3,len(dimensions)) + + if self.dims == 0: + raise SyntaxError("Zero dimensional array specified") + + # Check for invalid sizes and ensure int + for i in range(self.dims): + if dimensions[i] < 0: + raise SyntaxError("Negative array size specified") + # Allow sizes like 1.0f, but not 1.1f + if int(dimensions[i]) != dimensions[i]: + raise SyntaxError("Fractional array size specified") + dimensions[i] = int(dimensions[i]) + + if self.dims == 1: + self.data = [None for x in range(dimensions[0])] + elif self.dims == 2: + self.data = [[None for x in range(dimensions[1])] for x in range(dimensions[0])] + else: + self.data = [[[None for x in range(dimensions[2])] for x in range(dimensions[1])] for x in range(dimensions[0])] + + def pretty_print(self): + print(str(self.data)) + +"""Implements a BASIC parser that parses a single +statement when supplied. + +""" +class BASICParser: + + def __init__(self): + # Symbol table to hold variable names mapped + # to values + self.__symbol_table = {} + + # Stack on which to store operands + # when evaluating expressions + self.__operand_stack = [] + + # List to hold contents of DATA statement + self.__data_values = [] + + # These values will be + # initialised on a per + # statement basis + self.__tokenlist = [] + self.__tokenindex = None + + # Set to keep track of extant loop variables + self.last_flowsignal = None + + def parse(self, tokenlist, line_number): + """Must be initialised with the list of + BTokens to be processed. These tokens + represent a BASIC statement without + its corresponding line number. + + :param tokenlist: The tokenized program statement + :param line_number: The line number of the statement + + :return: The FlowSignal to indicate to the program + how to branch if necessary, None otherwise + + """ + self.__tokenlist = tokenlist + self.__tokenindex = 0 + + # Remember the line number to aid error reporting + self.__line_number = line_number + + # Assign the first token + self.__token = self.__tokenlist[self.__tokenindex] + + return self.__stmt() + + def __advance(self): + """Advances to the next token + + """ + # Move to the next token + self.__tokenindex += 1 + + # Acquire the next token if there any left + if not self.__tokenindex >= len(self.__tokenlist): + self.__token = self.__tokenlist[self.__tokenindex] + + def __consume(self, expected_category): + """Consumes a token from the list + + """ + if self.__token.category == expected_category: + self.__advance() + + else: + raise RuntimeError('Expecting ' + Token.catnames[expected_category] + + ' in line ' + str(self.__line_number)) + + def __stmt(self): + """Parses a program statement + + :return: The FlowSignal to indicate to the program + how to branch if necessary, None otherwise + + """ + if self.__token.category in [Token.FOR, Token.IF, Token.NEXT, + Token.ON]: + return self.__compoundstmt() + + else: + return self.__simplestmt() + + def __simplestmt(self): + """Parses a non-compound program statement + + :return: The FlowSignal to indicate to the program + how to branch if necessary, None otherwise + + """ + if self.__token.category == Token.NAME: + self.__assignmentstmt() + return None + + elif self.__token.category == Token.PRINT: + self.__printstmt() + return None + + elif self.__token.category == Token.LET: + self.__letstmt() + return None + + elif self.__token.category == Token.GOTO: + return self.__gotostmt() + + elif self.__token.category == Token.GOSUB: + return self.__gosubstmt() + + elif self.__token.category == Token.RETURN: + return self.__returnstmt() + + elif self.__token.category == Token.STOP: + return self.__stopstmt() + + elif self.__token.category == Token.INPUT: + self.__inputstmt() + return None + + elif self.__token.category == Token.DIM: + self.__dimstmt() + return None + + elif self.__token.category == Token.RANDOMIZE: + self.__randomizestmt() + return None + + elif self.__token.category == Token.DATA: + self.__datastmt() + return None + + elif self.__token.category == Token.READ: + self.__readstmt() + return None + + else: + # Ignore comments, but raise an error + # for anything else + if self.__token.category != Token.REM: + raise RuntimeError('Expecting program statement in line ' + + str(self.__line_number)) + + def __printstmt(self): + """Parses a PRINT statement, causing + the value that is on top of the + operand stack to be printed on + the screen. + + """ + self.__advance() # Advance past PRINT token + + # Check there are items to print + if not self.__tokenindex >= len(self.__tokenlist): + self.__logexpr() + print(self.__operand_stack.pop(), end='') + + while self.__token.category == Token.COMMA: + self.__advance() + self.__logexpr() + print(self.__operand_stack.pop(), end='') + + # Final newline + print() + + def __letstmt(self): + """Parses a LET statement, + consuming the LET keyword. + """ + self.__advance() # Advance past the LET token + self.__assignmentstmt() + + def __gotostmt(self): + """Parses a GOTO statement + + :return: A FlowSignal containing the target line number + of the GOTO + + """ + self.__advance() # Advance past GOTO token + self.__expr() + + # Set up and return the flow signal + return FlowSignal(ftarget=self.__operand_stack.pop()) + + def __gosubstmt(self): + """Parses a GOSUB statement + + :return: A FlowSignal containing the first line number + of the subroutine + + """ + + self.__advance() # Advance past GOSUB token + self.__expr() + + # Set up and return the flow signal + return FlowSignal(ftarget=self.__operand_stack.pop(), + ftype=FlowSignal.GOSUB) + + def __returnstmt(self): + """Parses a RETURN statement""" + + self.__advance() # Advance past RETURN token + + # Set up and return the flow signal + return FlowSignal(ftype=FlowSignal.RETURN) + + def __stopstmt(self): + """Parses a STOP statement""" + + self.__advance() # Advance past STOP token + + return FlowSignal(ftype=FlowSignal.STOP) + + def __assignmentstmt(self): + """Parses an assignment statement, + placing the corresponding + variable and its value in the symbol + table. + + """ + left = self.__token.lexeme # Save lexeme of + # the current token + self.__advance() + + if self.__token.category == Token.LEFTPAREN: + # We are assiging to an array + self.__arrayassignmentstmt(left) + + else: + # We are assigning to a simple variable + self.__consume(Token.ASSIGNOP) + self.__logexpr() + + # Check that we are using the right variable name format + right = self.__operand_stack.pop() + + if left.endswith('$') and not isinstance(right, str): + raise SyntaxError('Syntax error: Attempt to assign non string to string variable' + + ' in line ' + str(self.__line_number)) + + elif not left.endswith('$') and isinstance(right, str): + raise SyntaxError('Syntax error: Attempt to assign string to numeric variable' + + ' in line ' + str(self.__line_number)) + + self.__symbol_table[left] = right + + def __dimstmt(self): + """Parses DIM statement and creates a symbol + table entry for an array of the specified + dimensions. + + """ + self.__advance() # Advance past DIM keyword + + # Extract the array name, append a suffix so + # that we can distinguish from simple variables + # in the symbol table + name = self.__token.lexeme + '_array' + self.__advance() # Advance past array name + + self.__consume(Token.LEFTPAREN) + + # Extract the dimensions + dimensions = [] + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() + dimensions.append(self.__operand_stack.pop()) + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + dimensions.append(self.__operand_stack.pop()) + + self.__consume(Token.RIGHTPAREN) + + if len(dimensions) > 3: + raise SyntaxError("Maximum number of array dimensions is three " + + "in line " + str(self.__line_number)) + + self.__symbol_table[name] = BASICArray(dimensions) + + def __arrayassignmentstmt(self, name): + """Parses an assignment to an array variable + + :param name: Array name + + """ + self.__consume(Token.LEFTPAREN) + + # Capture the index variables + # Extract the dimensions + indexvars = [] + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() + indexvars.append(self.__operand_stack.pop()) + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + indexvars.append(self.__operand_stack.pop()) + + try: + BASICarray = self.__symbol_table[name + '_array'] + + except KeyError: + raise KeyError('Array could not be found in line ' + + str(self.__line_number)) + + if BASICarray.dims != len(indexvars): + raise IndexError('Incorrect number of indices applied to array ' + + 'in line ' + str(self.__line_number)) + + self.__consume(Token.RIGHTPAREN) + self.__consume(Token.ASSIGNOP) + + self.__logexpr() + + # Check that we are using the right variable name format + right = self.__operand_stack.pop() + + if name.endswith('$') and not isinstance(right, str): + raise SyntaxError('Attempt to assign non string to string array' + + ' in line ' + str(self.__line_number)) + + elif not name.endswith('$') and isinstance(right, str): + raise SyntaxError('Attempt to assign string to numeric array' + + ' in line ' + str(self.__line_number)) + + # Assign to the specified array index + try: + if len(indexvars) == 1: + BASICarray.data[indexvars[0]] = right + + elif len(indexvars) == 2: + BASICarray.data[indexvars[0]][indexvars[1]] = right + + elif len(indexvars) == 3: + BASICarray.data[indexvars[0]][indexvars[1]][indexvars[2]] = right + + except IndexError: + raise IndexError('Array index out of range in line ' + + str(self.__line_number)) + + def __inputstmt(self): + """Parses an input statement, extracts the input + from the user and places the values into the + symbol table + + """ + self.__advance() # Advance past INPUT token + + prompt = '? ' + if self.__token.category == Token.STRING: + # Acquire the input prompt + self.__logexpr() + prompt = self.__operand_stack.pop() + self.__consume(Token.COLON) + + # Acquire the comma separated input variables + variables = [] + if not self.__tokenindex >= len(self.__tokenlist): + if self.__token.category != Token.NAME: + raise ValueError('Expecting NAME in INPUT statement ' + + 'in line ' + str(self.__line_number)) + variables.append(self.__token.lexeme) + self.__advance() # Advance past variable + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + variables.append(self.__token.lexeme) + self.__advance() # Advance past variable + + valid_input = False + while not valid_input: + # Gather input from the user into the variables + inputvals = input(prompt).split(',', (len(variables)-1)) + + for variable in variables: + left = variable + + try: + right = inputvals.pop(0) + + if left.endswith('$'): + self.__symbol_table[left] = str(right) + valid_input = True + + elif not left.endswith('$'): + try: + if '.' in right: + self.__symbol_table[left] = float(right) + + else: + self.__symbol_table[left] = int(right) + + valid_input = True + + except ValueError: + valid_input = False + print('Non-numeric input provided to a numeric variable - redo from start') + break + + except IndexError: + # No more input to process + valid_input = False + print('Not enough values input - redo from start') + break + + def __datastmt(self): + """Parses a DATA statement""" + + self.__advance() # Advance past DATA token + + # Acquire the comma separated values + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() + self.__data_values.append(self.__operand_stack.pop()) + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + self.__data_values.append(self.__operand_stack.pop()) + + def __readstmt(self): + """Parses a READ statement.""" + + self.__advance() # Advance past READ token + + # Acquire the comma separated input variables + variables = [] + if not self.__tokenindex >= len(self.__tokenlist): + variables.append(self.__token.lexeme) + self.__advance() # Advance past variable + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + variables.append(self.__token.lexeme) + self.__advance() # Advance past variable + + # Check that we have enough data values to fill the + # variables + if len(variables) > len(self.__data_values): + raise RuntimeError('Insufficient constants supplied to READ ' + + 'in line ' + str(self.__line_number)) + + # Gather input from the DATA statement into the variables + for variable in variables: + left = variable + right = self.__data_values.pop(0) + + if left.endswith('$'): + # Python inserts quotes around input data + if isinstance(right, int): + raise ValueError('Non-string input provided to a string variable ' + + 'in line ' + str(self.__line_number)) + + else: + self.__symbol_table[left] = right + + elif not left.endswith('$'): + try: + #if '.' in right: + # self.__symbol_table[left] = float(right) + #else: + # self.__symbol_table[left] = int(right) + + numeric = float(right) + if numeric.is_integer(): + numeric = int(numeric) + self.__symbol_table[left] = numeric + + except ValueError: + raise ValueError('Non-numeric input provided to a numeric variable ' + + 'in line ' + str(self.__line_number)) + + def __expr(self): + """Parses a numerical expression consisting + of two terms being added or subtracted, + leaving the result on the operand stack. + + """ + self.__term() # Pushes value of left term + # onto top of stack + + while self.__token.category in [Token.PLUS, Token.MINUS]: + savedcategory = self.__token.category + self.__advance() + self.__term() # Pushes value of right term + # onto top of stack + rightoperand = self.__operand_stack.pop() + leftoperand = self.__operand_stack.pop() + + if savedcategory == Token.PLUS: + self.__operand_stack.append(leftoperand + rightoperand) + + else: + self.__operand_stack.append(leftoperand - rightoperand) + + def __term(self): + """Parses a numerical expression consisting + of two factors being multiplied together, + leaving the result on the operand stack. + + """ + self.__sign = 1 # Initialise sign to keep track of unary + # minuses + self.__factor() # Leaves value of term on top of stack + + while self.__token.category in [Token.TIMES, Token.DIVIDE, Token.MODULO]: + savedcategory = self.__token.category + self.__advance() + self.__sign = 1 # Initialise sign + self.__factor() # Leaves value of term on top of stack + rightoperand = self.__operand_stack.pop() + leftoperand = self.__operand_stack.pop() + + if savedcategory == Token.TIMES: + self.__operand_stack.append(leftoperand * rightoperand) + + elif savedcategory == Token.DIVIDE: + self.__operand_stack.append(leftoperand / rightoperand) + + else: + self.__operand_stack.append(leftoperand % rightoperand) + + def __factor(self): + """Evaluates a numerical expression + and leaves its value on top of the + operand stack. + + """ + if self.__token.category == Token.PLUS: + self.__advance() + self.__factor() + + elif self.__token.category == Token.MINUS: + self.__sign = -self.__sign + self.__advance() + self.__factor() + + elif self.__token.category == Token.UNSIGNEDINT: + self.__operand_stack.append(self.__sign*int(self.__token.lexeme)) + self.__advance() + + elif self.__token.category == Token.UNSIGNEDFLOAT: + self.__operand_stack.append(self.__sign*float(self.__token.lexeme)) + self.__advance() + + elif self.__token.category == Token.STRING: + self.__operand_stack.append(self.__token.lexeme) + self.__advance() + + elif self.__token.category == Token.NAME and \ + self.__token.category not in Token.functions: + # Check if this is a simple or array variable + if (self.__token.lexeme + '_array') in self.__symbol_table: + # Capture the current lexeme + arrayname = self.__token.lexeme + '_array' + + # Array must be processed + # Capture the index variables + self.__advance() # Advance past the array name + + try: + self.__consume(Token.LEFTPAREN) + except RuntimeError: + raise RuntimeError('Array used without index in line ' + + str(self.__line_number)) + + indexvars = [] + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() + indexvars.append(self.__operand_stack.pop()) + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + indexvars.append(self.__operand_stack.pop()) + + BASICarray = self.__symbol_table[arrayname] + arrayval = self.__get_array_val(BASICarray, indexvars) + + if arrayval != None: + self.__operand_stack.append(self.__sign*arrayval) + + else: + raise IndexError('Empty array value returned in line ' + + str(self.__line_number)) + + elif self.__token.lexeme in self.__symbol_table: + # Simple variable must be processed + self.__operand_stack.append(self.__sign*self.__symbol_table[self.__token.lexeme]) + + else: + raise RuntimeError('Name ' + self.__token.lexeme + ' is not defined' + + ' in line ' + str(self.__line_number)) + + self.__advance() + + elif self.__token.category == Token.LEFTPAREN: + self.__advance() + + # Save sign because expr() calls term() which resets + # sign to 1 + savesign = self.__sign + self.__logexpr() # Value of expr is pushed onto stack + + if savesign == -1: + # Change sign of expression + self.__operand_stack[-1] = -self.__operand_stack[-1] + + self.__consume(Token.RIGHTPAREN) + + elif self.__token.category in Token.functions: + self.__operand_stack.append(self.__evaluate_function(self.__token.category)) + + else: + raise RuntimeError('Expecting factor in numeric expression' + + ' in line ' + str(self.__line_number)) + + def __get_array_val(self, BASICarray, indexvars): + """Extracts the value from the given BASICArray at the specified indexes + + :param BASICarray: The BASICArray + :param indexvars: The list of indexes, one for each dimension + + :return: The value at the indexed position in the array + + """ + if BASICarray.dims != len(indexvars): + raise IndexError('Incorrect number of indices applied to array ' + + 'in line ' + str(self.__line_number)) + + # Fetch the value from the array + try: + if len(indexvars) == 1: + arrayval = BASICarray.data[indexvars[0]] + + elif len(indexvars) == 2: + arrayval = BASICarray.data[indexvars[0]][indexvars[1]] + + elif len(indexvars) == 3: + arrayval = BASICarray.data[indexvars[0]][indexvars[1]][indexvars[2]] + + except IndexError: + raise IndexError('Array index out of range in line ' + + str(self.__line_number)) + + return arrayval + + def __compoundstmt(self): + """Parses compound statements, + specifically if-then-else and + loops + + :return: The FlowSignal to indicate to the program + how to branch if necessary, None otherwise + + """ + if self.__token.category == Token.FOR: + return self.__forstmt() + + elif self.__token.category == Token.NEXT: + return self.__nextstmt() + + elif self.__token.category == Token.IF: + return self.__ifstmt() + + elif self.__token.category == Token.ON: + return self.__ongosubstmt() + + def __ifstmt(self): + """Parses if-then-else + statements + + :return: The FlowSignal to indicate to the program + how to branch if necessary, None otherwise + + """ + + self.__advance() # Advance past IF token + self.__logexpr() + + # Save result of expression + saveval = self.__operand_stack.pop() + + # Process the THEN part and save the jump value + self.__consume(Token.THEN) + + if self.__token.category == Token.GOTO: + self.__advance() # Advance past optional GOTO + + self.__expr() + then_jump = self.__operand_stack.pop() + + # Jump if the expression evaluated to True + if saveval: + # Set up and return the flow signal + return FlowSignal(ftarget=then_jump) + + # See if there is an ELSE part + if self.__token.category == Token.ELSE: + self.__advance() + + if self.__token.category == Token.GOTO: + self.__advance() # Advance past optional GOTO + + self.__expr() + + # Set up and return the flow signal + return FlowSignal(ftarget=self.__operand_stack.pop()) + + else: + # No ELSE action + return None + + def __forstmt(self): + """Parses for loops + + :return: The FlowSignal to indicate that + a loop start has been processed + + """ + + # Set up default loop increment value + step = 1 + + self.__advance() # Advance past FOR token + + # Process the loop variable initialisation + loop_variable = self.__token.lexeme # Save lexeme of + # the current token + + if loop_variable.endswith('$'): + raise SyntaxError('Syntax error: Loop variable is not numeric' + + ' in line ' + str(self.__line_number)) + + self.__advance() # Advance past loop variable + self.__consume(Token.ASSIGNOP) + self.__expr() + + # Check that we are using the right variable name format + # for numeric variables + start_val = self.__operand_stack.pop() + + # Advance past the 'TO' keyword + self.__consume(Token.TO) + + # Process the terminating value + self.__expr() + end_val = self.__operand_stack.pop() + + # Check if there is a STEP value + increment = True + if not self.__tokenindex >= len(self.__tokenlist): + self.__consume(Token.STEP) + + # Acquire the step value + self.__expr() + step = self.__operand_stack.pop() + + # Check whether we are decrementing or + # incrementing + if step == 0: + raise IndexError('Zero step value supplied for loop' + + ' in line ' + str(self.__line_number)) + + elif step < 0: + increment = False + + # Now determine the status of the loop + + # Note that we cannot use the presence of the loop variable in + # the symbol table for this test, as the same variable may already + # have been instantiated elsewhere in the program + # + # Need to initialize the loop variable anytime the for + # statement is reached from a statement other than an active NEXT. + + from_next = False + if self.last_flowsignal: + if self.last_flowsignal.ftype == FlowSignal.LOOP_REPEAT: + from_next = True + + if not from_next: + self.__symbol_table[loop_variable] = start_val + + else: + # We need to modify the loop variable + # according to the STEP value + self.__symbol_table[loop_variable] += step + + # If the loop variable has reached the end value, + # remove it from the set of extant loop variables to signal that + # this is the last loop iteration + stop = False + if increment and self.__symbol_table[loop_variable] > end_val: + stop = True + + elif not increment and self.__symbol_table[loop_variable] < end_val: + stop = True + + if stop: + # Loop must terminate + return FlowSignal(ftype=FlowSignal.LOOP_SKIP, + ftarget=loop_variable) + else: + # Set up and return the flow signal + return FlowSignal(ftype=FlowSignal.LOOP_BEGIN) + + def __nextstmt(self): + """Processes a NEXT statement that terminates + a loop + + :return: A FlowSignal indicating that a loop + has been processed + + """ + + self.__advance() # Advance past NEXT token + + return FlowSignal(ftype=FlowSignal.LOOP_REPEAT) + + def __ongosubstmt(self): + """Process the ON-GOSUB statement + + :return: A FlowSignal indicating the subroutine line number + if the condition is true, None otherwise + + """ + + self.__advance() # Advance past ON token + self.__logexpr() + + # Save result of expression + saveval = self.__operand_stack.pop() + + # Process the GOSUB part and save the jump value + # if the condition is met + if saveval: + return self.__gosubstmt() + else: + return None + + def __relexpr(self): + """Parses a relational expression + """ + self.__expr() + + # Since BASIC uses same operator for both + # assignment and equality, we need to check for this + if self.__token.category == Token.ASSIGNOP: + self.__token.category = Token.EQUAL + + if self.__token.category in [Token.LESSER, Token.LESSEQUAL, + Token.GREATER, Token.GREATEQUAL, + Token.EQUAL, Token.NOTEQUAL]: + savecat = self.__token.category + self.__advance() + self.__expr() + + right = self.__operand_stack.pop() + left = self.__operand_stack.pop() + + if savecat == Token.EQUAL: + self.__operand_stack.append(left == right) # Push True or False + + elif savecat == Token.NOTEQUAL: + self.__operand_stack.append(left != right) # Push True or False + + elif savecat == Token.LESSER: + self.__operand_stack.append(left < right) # Push True or False + + elif savecat == Token.GREATER: + self.__operand_stack.append(left > right) # Push True or False + + elif savecat == Token.LESSEQUAL: + self.__operand_stack.append(left <= right) # Push True or False + + elif savecat == Token.GREATEQUAL: + self.__operand_stack.append(left >= right) # Push True or False + + def __logexpr(self): + """Parses a logical expression + """ + self.__notexpr() + + while self.__token.category in [Token.OR, Token.AND]: + savecat = self.__token.category + self.__advance() + self.__notexpr() + + right = self.__operand_stack.pop() + left = self.__operand_stack.pop() + + if savecat == Token.OR: + self.__operand_stack.append(left or right) # Push True or False + + elif savecat == Token.AND: + self.__operand_stack.append(left and right) # Push True or False + + def __notexpr(self): + """Parses a logical not expression + """ + if self.__token.category == Token.NOT: + self.__advance() + self.__relexpr() + right = self.__operand_stack.pop() + self.__operand_stack.append(not right) + else: + self.__relexpr() + + def __evaluate_function(self, category): + """Evaluate the function in the statement + and return the result. + + :return: The result of the function + + """ + + self.__advance() # Advance past function name + + # Process arguments according to function + if category == Token.RND: + return random.random() + + if category == Token.PI: + return math.pi + + if category == Token.RNDINT: + self.__consume(Token.LEFTPAREN) + + self.__expr() + lo = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + hi = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + try: + return random.randint(lo, hi) + + except ValueError: + raise ValueError("Invalid value supplied to RNDINT in line " + + str(self.__line_number)) + + if category == Token.MAX: + self.__consume(Token.LEFTPAREN) + + self.__expr() + value_list = [self.__operand_stack.pop()] + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + value_list.append(self.__operand_stack.pop()) + + self.__consume(Token.RIGHTPAREN) + + try: + return max(*value_list) + + except TypeError: + raise TypeError("Invalid type supplied to MAX in line " + + str(self.__line_number)) + + if category == Token.MIN: + self.__consume(Token.LEFTPAREN) + + self.__expr() + value_list = [self.__operand_stack.pop()] + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + value_list.append(self.__operand_stack.pop()) + + self.__consume(Token.RIGHTPAREN) + + try: + return min(*value_list) + + except TypeError: + raise TypeError("Invalid type supplied to MIN in line " + + str(self.__line_number)) + + if category == Token.POW: + self.__consume(Token.LEFTPAREN) + + self.__expr() + base = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + exponent = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + try: + return math.pow(base, exponent) + + except ValueError: + raise ValueError("Invalid value supplied to POW in line " + + str(self.__line_number)) + + if category == Token.TERNARY: + self.__consume(Token.LEFTPAREN) + + self.__logexpr() + condition = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + whentrue = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + whenfalse = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + return whentrue if condition else whenfalse + + if category == Token.MID: + self.__consume(Token.LEFTPAREN) + + self.__expr() + instring = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + start = self.__operand_stack.pop() + + if self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + end = self.__operand_stack.pop() + else: + end = None + + self.__consume(Token.RIGHTPAREN) + + try: + return instring[start:end] + + except TypeError: + raise TypeError("Invalid type supplied to MID$ in line " + + str(self.__line_number)) + + if category == Token.INSTR: + self.__consume(Token.LEFTPAREN) + + self.__expr() + hackstackstring = self.__operand_stack.pop() + if not isinstance(hackstackstring, str): + raise TypeError("Invalid type supplied to INSTR in line " + + str(self.__line_number)) + + self.__consume(Token.COMMA) + + self.__expr() + needlestring = self.__operand_stack.pop() + + start = end = None + if self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + start = self.__operand_stack.pop() + + if self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + end = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + try: + return hackstackstring.find(needlestring, start, end) + + except TypeError: + raise TypeError("Invalid type supplied to INSTR in line " + + str(self.__line_number)) + + self.__consume(Token.LEFTPAREN) + + self.__expr() + value = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + if category == Token.SQR: + try: + return math.sqrt(value) + + except ValueError: + raise ValueError("Invalid value supplied to SQR in line " + + str(self.__line_number)) + + elif category == Token.ABS: + try: + return abs(value) + + except ValueError: + raise ValueError("Invalid value supplied to ABS in line " + + str(self.__line_number)) + + elif category == Token.ATN: + try: + return math.atan(value) + + except ValueError: + raise ValueError("Invalid value supplied to ATN in line " + + str(self.__line_number)) + + elif category == Token.COS: + try: + return math.cos(value) + + except ValueError: + raise ValueError("Invalid value supplied to COS in line " + + str(self.__line_number)) + + elif category == Token.EXP: + try: + return math.exp(value) + + except ValueError: + raise ValueError("Invalid value supplied to EXP in line " + + str(self.__line_number)) + + elif category == Token.INT: + try: + return math.floor(value) + + except ValueError: + raise ValueError("Invalid value supplied to INT in line " + + str(self.__line_number)) + + elif category == Token.ROUND: + try: + return round(value) + + except TypeError: + raise TypeError("Invalid type supplied to LEN in line " + + str(self.__line_number)) + + elif category == Token.LOG: + try: + return math.log(value) + + except ValueError: + raise ValueError("Invalid value supplied to LOG in line " + + str(self.__line_number)) + + elif category == Token.SIN: + try: + return math.sin(value) + + except ValueError: + raise ValueError("Invalid value supplied to SIN in line " + + str(self.__line_number)) + + elif category == Token.TAN: + try: + return math.tan(value) + + except ValueError: + raise ValueError("Invalid value supplied to TAN in line " + + str(self.__line_number)) + + elif category == Token.CHR: + try: + return chr(value) + + except TypeError: + raise TypeError("Invalid type supplied to CHR$ in line " + + str(self.__line_number)) + + except ValueError: + raise ValueError("Invalid value supplied to CHR$ in line " + + str(self.__line_number)) + + elif category == Token.ASC: + try: + return ord(value) + + except TypeError: + raise TypeError("Invalid type supplied to ASC in line " + + str(self.__line_number)) + + except ValueError: + raise ValueError("Invalid value supplied to ASC in line " + + str(self.__line_number)) + + elif category == Token.STR: + return str(value) + + elif category == Token.VAL: + try: + numeric = float(value) + if numeric.is_integer(): + return int(numeric) + return numeric + + # Like other BASIC variants, non-numeric strings return 0 + except ValueError: + return 0 + + elif category == Token.LEN: + try: + return len(value) + + except TypeError: + raise TypeError("Invalid type supplied to LEN in line " + + str(self.__line_number)) + + elif category == Token.UPPER: + if not isinstance(value, str): + raise TypeError("Invalid type supplied to UPPER$ in line " + + str(self.__line_number)) + + return value.upper() + + elif category == Token.LOWER: + if not isinstance(value, str): + raise TypeError("Invalid type supplied to LOWER$ in line " + + str(self.__line_number)) + + return value.lower() + + else: + raise SyntaxError("Unrecognised function in line " + + str(self.__line_number)) + + def __randomizestmt(self): + """Implements a function to seed the random + number generator + + """ + self.__advance() # Advance past RANDOMIZE token + + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() # Process the seed + seed = self.__operand_stack.pop() + + random.seed(seed) + + else: + random.seed(int(monotonic())) From 25dc57b5690adff27bfb0b947f44d5ce1680541a Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 5 Sep 2021 18:56:57 -0400 Subject: [PATCH 040/183] attempt to fix github diff issue --- basicparser.py | 2686 ++++++++++++++++++++++++------------------------ 1 file changed, 1343 insertions(+), 1343 deletions(-) diff --git a/basicparser.py b/basicparser.py index 2965071..1335f06 100644 --- a/basicparser.py +++ b/basicparser.py @@ -1,1343 +1,1343 @@ -#! /usr/bin/python - -# SPDX-License-Identifier: GPL-3.0-or-later -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from basictoken import BASICToken as Token -from flowsignal import FlowSignal -import math -import random -from time import monotonic - - -"""Implements a BASIC array, which may have up -to three dimensions of fixed size. - -""" -class BASICArray: - - def __init__(self, dimensions): - """Initialises the object with the specified - number of dimensions. Maximum number of - dimensions is three - - :param dimensions: List of array dimensions and their - corresponding sizes - - """ - self.dims = min(3,len(dimensions)) - - if self.dims == 0: - raise SyntaxError("Zero dimensional array specified") - - # Check for invalid sizes and ensure int - for i in range(self.dims): - if dimensions[i] < 0: - raise SyntaxError("Negative array size specified") - # Allow sizes like 1.0f, but not 1.1f - if int(dimensions[i]) != dimensions[i]: - raise SyntaxError("Fractional array size specified") - dimensions[i] = int(dimensions[i]) - - if self.dims == 1: - self.data = [None for x in range(dimensions[0])] - elif self.dims == 2: - self.data = [[None for x in range(dimensions[1])] for x in range(dimensions[0])] - else: - self.data = [[[None for x in range(dimensions[2])] for x in range(dimensions[1])] for x in range(dimensions[0])] - - def pretty_print(self): - print(str(self.data)) - -"""Implements a BASIC parser that parses a single -statement when supplied. - -""" -class BASICParser: - - def __init__(self): - # Symbol table to hold variable names mapped - # to values - self.__symbol_table = {} - - # Stack on which to store operands - # when evaluating expressions - self.__operand_stack = [] - - # List to hold contents of DATA statement - self.__data_values = [] - - # These values will be - # initialised on a per - # statement basis - self.__tokenlist = [] - self.__tokenindex = None - - # Set to keep track of extant loop variables - self. __loop_vars = set() - - def parse(self, tokenlist, line_number): - """Must be initialised with the list of - BTokens to be processed. These tokens - represent a BASIC statement without - its corresponding line number. - - :param tokenlist: The tokenized program statement - :param line_number: The line number of the statement - - :return: The FlowSignal to indicate to the program - how to branch if necessary, None otherwise - - """ - self.__tokenlist = tokenlist - self.__tokenindex = 0 - - # Remember the line number to aid error reporting - self.__line_number = line_number - - # Assign the first token - self.__token = self.__tokenlist[self.__tokenindex] - - return self.__stmt() - - def __advance(self): - """Advances to the next token - - """ - # Move to the next token - self.__tokenindex += 1 - - # Acquire the next token if there any left - if not self.__tokenindex >= len(self.__tokenlist): - self.__token = self.__tokenlist[self.__tokenindex] - - def __consume(self, expected_category): - """Consumes a token from the list - - """ - if self.__token.category == expected_category: - self.__advance() - - else: - raise RuntimeError('Expecting ' + Token.catnames[expected_category] + - ' in line ' + str(self.__line_number)) - - def __stmt(self): - """Parses a program statement - - :return: The FlowSignal to indicate to the program - how to branch if necessary, None otherwise - - """ - if self.__token.category in [Token.FOR, Token.IF, Token.NEXT, - Token.ON]: - return self.__compoundstmt() - - else: - return self.__simplestmt() - - def __simplestmt(self): - """Parses a non-compound program statement - - :return: The FlowSignal to indicate to the program - how to branch if necessary, None otherwise - - """ - if self.__token.category == Token.NAME: - self.__assignmentstmt() - return None - - elif self.__token.category == Token.PRINT: - self.__printstmt() - return None - - elif self.__token.category == Token.LET: - self.__letstmt() - return None - - elif self.__token.category == Token.GOTO: - return self.__gotostmt() - - elif self.__token.category == Token.GOSUB: - return self.__gosubstmt() - - elif self.__token.category == Token.RETURN: - return self.__returnstmt() - - elif self.__token.category == Token.STOP: - return self.__stopstmt() - - elif self.__token.category == Token.INPUT: - self.__inputstmt() - return None - - elif self.__token.category == Token.DIM: - self.__dimstmt() - return None - - elif self.__token.category == Token.RANDOMIZE: - self.__randomizestmt() - return None - - elif self.__token.category == Token.DATA: - self.__datastmt() - return None - - elif self.__token.category == Token.READ: - self.__readstmt() - return None - - else: - # Ignore comments, but raise an error - # for anything else - if self.__token.category != Token.REM: - raise RuntimeError('Expecting program statement in line ' - + str(self.__line_number)) - - def __printstmt(self): - """Parses a PRINT statement, causing - the value that is on top of the - operand stack to be printed on - the screen. - - """ - self.__advance() # Advance past PRINT token - - # Check there are items to print - if not self.__tokenindex >= len(self.__tokenlist): - self.__logexpr() - print(self.__operand_stack.pop(), end='') - - while self.__token.category == Token.COMMA: - self.__advance() - self.__logexpr() - print(self.__operand_stack.pop(), end='') - - # Final newline - print() - - def __letstmt(self): - """Parses a LET statement, - consuming the LET keyword. - """ - self.__advance() # Advance past the LET token - self.__assignmentstmt() - - def __gotostmt(self): - """Parses a GOTO statement - - :return: A FlowSignal containing the target line number - of the GOTO - - """ - self.__advance() # Advance past GOTO token - self.__expr() - - # Set up and return the flow signal - return FlowSignal(ftarget=self.__operand_stack.pop()) - - def __gosubstmt(self): - """Parses a GOSUB statement - - :return: A FlowSignal containing the first line number - of the subroutine - - """ - - self.__advance() # Advance past GOSUB token - self.__expr() - - # Set up and return the flow signal - return FlowSignal(ftarget=self.__operand_stack.pop(), - ftype=FlowSignal.GOSUB) - - def __returnstmt(self): - """Parses a RETURN statement""" - - self.__advance() # Advance past RETURN token - - # Set up and return the flow signal - return FlowSignal(ftype=FlowSignal.RETURN) - - def __stopstmt(self): - """Parses a STOP statement""" - - self.__advance() # Advance past STOP token - - return FlowSignal(ftype=FlowSignal.STOP) - - def __assignmentstmt(self): - """Parses an assignment statement, - placing the corresponding - variable and its value in the symbol - table. - - """ - left = self.__token.lexeme # Save lexeme of - # the current token - self.__advance() - - if self.__token.category == Token.LEFTPAREN: - # We are assiging to an array - self.__arrayassignmentstmt(left) - - else: - # We are assigning to a simple variable - self.__consume(Token.ASSIGNOP) - self.__logexpr() - - # Check that we are using the right variable name format - right = self.__operand_stack.pop() - - if left.endswith('$') and not isinstance(right, str): - raise SyntaxError('Syntax error: Attempt to assign non string to string variable' + - ' in line ' + str(self.__line_number)) - - elif not left.endswith('$') and isinstance(right, str): - raise SyntaxError('Syntax error: Attempt to assign string to numeric variable' + - ' in line ' + str(self.__line_number)) - - self.__symbol_table[left] = right - - def __dimstmt(self): - """Parses DIM statement and creates a symbol - table entry for an array of the specified - dimensions. - - """ - self.__advance() # Advance past DIM keyword - - # Extract the array name, append a suffix so - # that we can distinguish from simple variables - # in the symbol table - name = self.__token.lexeme + '_array' - self.__advance() # Advance past array name - - self.__consume(Token.LEFTPAREN) - - # Extract the dimensions - dimensions = [] - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - dimensions.append(self.__operand_stack.pop()) - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - dimensions.append(self.__operand_stack.pop()) - - self.__consume(Token.RIGHTPAREN) - - if len(dimensions) > 3: - raise SyntaxError("Maximum number of array dimensions is three " + - "in line " + str(self.__line_number)) - - self.__symbol_table[name] = BASICArray(dimensions) - - def __arrayassignmentstmt(self, name): - """Parses an assignment to an array variable - - :param name: Array name - - """ - self.__consume(Token.LEFTPAREN) - - # Capture the index variables - # Extract the dimensions - indexvars = [] - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - indexvars.append(self.__operand_stack.pop()) - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - indexvars.append(self.__operand_stack.pop()) - - try: - BASICarray = self.__symbol_table[name + '_array'] - - except KeyError: - raise KeyError('Array could not be found in line ' + - str(self.__line_number)) - - if BASICarray.dims != len(indexvars): - raise IndexError('Incorrect number of indices applied to array ' + - 'in line ' + str(self.__line_number)) - - self.__consume(Token.RIGHTPAREN) - self.__consume(Token.ASSIGNOP) - - self.__logexpr() - - # Check that we are using the right variable name format - right = self.__operand_stack.pop() - - if name.endswith('$') and not isinstance(right, str): - raise SyntaxError('Attempt to assign non string to string array' + - ' in line ' + str(self.__line_number)) - - elif not name.endswith('$') and isinstance(right, str): - raise SyntaxError('Attempt to assign string to numeric array' + - ' in line ' + str(self.__line_number)) - - # Assign to the specified array index - try: - if len(indexvars) == 1: - BASICarray.data[indexvars[0]] = right - - elif len(indexvars) == 2: - BASICarray.data[indexvars[0]][indexvars[1]] = right - - elif len(indexvars) == 3: - BASICarray.data[indexvars[0]][indexvars[1]][indexvars[2]] = right - - except IndexError: - raise IndexError('Array index out of range in line ' + - str(self.__line_number)) - - def __inputstmt(self): - """Parses an input statement, extracts the input - from the user and places the values into the - symbol table - - """ - self.__advance() # Advance past INPUT token - - prompt = '? ' - if self.__token.category == Token.STRING: - # Acquire the input prompt - self.__logexpr() - prompt = self.__operand_stack.pop() - self.__consume(Token.COLON) - - # Acquire the comma separated input variables - variables = [] - if not self.__tokenindex >= len(self.__tokenlist): - if self.__token.category != Token.NAME: - raise ValueError('Expecting NAME in INPUT statement ' + - 'in line ' + str(self.__line_number)) - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable - - valid_input = False - while not valid_input: - # Gather input from the user into the variables - inputvals = input(prompt).split(',', (len(variables)-1)) - - for variable in variables: - left = variable - - try: - right = inputvals.pop(0) - - if left.endswith('$'): - self.__symbol_table[left] = str(right) - valid_input = True - - elif not left.endswith('$'): - try: - if '.' in right: - self.__symbol_table[left] = float(right) - - else: - self.__symbol_table[left] = int(right) - - valid_input = True - - except ValueError: - valid_input = False - print('Non-numeric input provided to a numeric variable - redo from start') - break - - except IndexError: - # No more input to process - valid_input = False - print('Not enough values input - redo from start') - break - - def __datastmt(self): - """Parses a DATA statement""" - - self.__advance() # Advance past DATA token - - # Acquire the comma separated values - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - self.__data_values.append(self.__operand_stack.pop()) - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - self.__data_values.append(self.__operand_stack.pop()) - - def __readstmt(self): - """Parses a READ statement.""" - - self.__advance() # Advance past READ token - - # Acquire the comma separated input variables - variables = [] - if not self.__tokenindex >= len(self.__tokenlist): - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable - - # Check that we have enough data values to fill the - # variables - if len(variables) > len(self.__data_values): - raise RuntimeError('Insufficient constants supplied to READ ' + - 'in line ' + str(self.__line_number)) - - # Gather input from the DATA statement into the variables - for variable in variables: - left = variable - right = self.__data_values.pop(0) - - if left.endswith('$'): - # Python inserts quotes around input data - if isinstance(right, int): - raise ValueError('Non-string input provided to a string variable ' + - 'in line ' + str(self.__line_number)) - - else: - self.__symbol_table[left] = right - - elif not left.endswith('$'): - try: - #if '.' in right: - # self.__symbol_table[left] = float(right) - #else: - # self.__symbol_table[left] = int(right) - - numeric = float(right) - if numeric.is_integer(): - numeric = int(numeric) - self.__symbol_table[left] = numeric - - except ValueError: - raise ValueError('Non-numeric input provided to a numeric variable ' + - 'in line ' + str(self.__line_number)) - - def __expr(self): - """Parses a numerical expression consisting - of two terms being added or subtracted, - leaving the result on the operand stack. - - """ - self.__term() # Pushes value of left term - # onto top of stack - - while self.__token.category in [Token.PLUS, Token.MINUS]: - savedcategory = self.__token.category - self.__advance() - self.__term() # Pushes value of right term - # onto top of stack - rightoperand = self.__operand_stack.pop() - leftoperand = self.__operand_stack.pop() - - if savedcategory == Token.PLUS: - self.__operand_stack.append(leftoperand + rightoperand) - - else: - self.__operand_stack.append(leftoperand - rightoperand) - - def __term(self): - """Parses a numerical expression consisting - of two factors being multiplied together, - leaving the result on the operand stack. - - """ - self.__sign = 1 # Initialise sign to keep track of unary - # minuses - self.__factor() # Leaves value of term on top of stack - - while self.__token.category in [Token.TIMES, Token.DIVIDE, Token.MODULO]: - savedcategory = self.__token.category - self.__advance() - self.__sign = 1 # Initialise sign - self.__factor() # Leaves value of term on top of stack - rightoperand = self.__operand_stack.pop() - leftoperand = self.__operand_stack.pop() - - if savedcategory == Token.TIMES: - self.__operand_stack.append(leftoperand * rightoperand) - - elif savedcategory == Token.DIVIDE: - self.__operand_stack.append(leftoperand / rightoperand) - - else: - self.__operand_stack.append(leftoperand % rightoperand) - - def __factor(self): - """Evaluates a numerical expression - and leaves its value on top of the - operand stack. - - """ - if self.__token.category == Token.PLUS: - self.__advance() - self.__factor() - - elif self.__token.category == Token.MINUS: - self.__sign = -self.__sign - self.__advance() - self.__factor() - - elif self.__token.category == Token.UNSIGNEDINT: - self.__operand_stack.append(self.__sign*int(self.__token.lexeme)) - self.__advance() - - elif self.__token.category == Token.UNSIGNEDFLOAT: - self.__operand_stack.append(self.__sign*float(self.__token.lexeme)) - self.__advance() - - elif self.__token.category == Token.STRING: - self.__operand_stack.append(self.__token.lexeme) - self.__advance() - - elif self.__token.category == Token.NAME and \ - self.__token.category not in Token.functions: - # Check if this is a simple or array variable - if (self.__token.lexeme + '_array') in self.__symbol_table: - # Capture the current lexeme - arrayname = self.__token.lexeme + '_array' - - # Array must be processed - # Capture the index variables - self.__advance() # Advance past the array name - - try: - self.__consume(Token.LEFTPAREN) - except RuntimeError: - raise RuntimeError('Array used without index in line ' + - str(self.__line_number)) - - indexvars = [] - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - indexvars.append(self.__operand_stack.pop()) - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - indexvars.append(self.__operand_stack.pop()) - - BASICarray = self.__symbol_table[arrayname] - arrayval = self.__get_array_val(BASICarray, indexvars) - - if arrayval != None: - self.__operand_stack.append(self.__sign*arrayval) - - else: - raise IndexError('Empty array value returned in line ' + - str(self.__line_number)) - - elif self.__token.lexeme in self.__symbol_table: - # Simple variable must be processed - self.__operand_stack.append(self.__sign*self.__symbol_table[self.__token.lexeme]) - - else: - raise RuntimeError('Name ' + self.__token.lexeme + ' is not defined' + - ' in line ' + str(self.__line_number)) - - self.__advance() - - elif self.__token.category == Token.LEFTPAREN: - self.__advance() - - # Save sign because expr() calls term() which resets - # sign to 1 - savesign = self.__sign - self.__logexpr() # Value of expr is pushed onto stack - - if savesign == -1: - # Change sign of expression - self.__operand_stack[-1] = -self.__operand_stack[-1] - - self.__consume(Token.RIGHTPAREN) - - elif self.__token.category in Token.functions: - self.__operand_stack.append(self.__evaluate_function(self.__token.category)) - - else: - raise RuntimeError('Expecting factor in numeric expression' + - ' in line ' + str(self.__line_number)) - - def __get_array_val(self, BASICarray, indexvars): - """Extracts the value from the given BASICArray at the specified indexes - - :param BASICarray: The BASICArray - :param indexvars: The list of indexes, one for each dimension - - :return: The value at the indexed position in the array - - """ - if BASICarray.dims != len(indexvars): - raise IndexError('Incorrect number of indices applied to array ' + - 'in line ' + str(self.__line_number)) - - # Fetch the value from the array - try: - if len(indexvars) == 1: - arrayval = BASICarray.data[indexvars[0]] - - elif len(indexvars) == 2: - arrayval = BASICarray.data[indexvars[0]][indexvars[1]] - - elif len(indexvars) == 3: - arrayval = BASICarray.data[indexvars[0]][indexvars[1]][indexvars[2]] - - except IndexError: - raise IndexError('Array index out of range in line ' + - str(self.__line_number)) - - return arrayval - - def __compoundstmt(self): - """Parses compound statements, - specifically if-then-else and - loops - - :return: The FlowSignal to indicate to the program - how to branch if necessary, None otherwise - - """ - if self.__token.category == Token.FOR: - return self.__forstmt() - - elif self.__token.category == Token.NEXT: - return self.__nextstmt() - - elif self.__token.category == Token.IF: - return self.__ifstmt() - - elif self.__token.category == Token.ON: - return self.__ongosubstmt() - - def __ifstmt(self): - """Parses if-then-else - statements - - :return: The FlowSignal to indicate to the program - how to branch if necessary, None otherwise - - """ - - self.__advance() # Advance past IF token - self.__logexpr() - - # Save result of expression - saveval = self.__operand_stack.pop() - - # Process the THEN part and save the jump value - self.__consume(Token.THEN) - - if self.__token.category == Token.GOTO: - self.__advance() # Advance past optional GOTO - - self.__expr() - then_jump = self.__operand_stack.pop() - - # Jump if the expression evaluated to True - if saveval: - # Set up and return the flow signal - return FlowSignal(ftarget=then_jump) - - # See if there is an ELSE part - if self.__token.category == Token.ELSE: - self.__advance() - - if self.__token.category == Token.GOTO: - self.__advance() # Advance past optional GOTO - - self.__expr() - - # Set up and return the flow signal - return FlowSignal(ftarget=self.__operand_stack.pop()) - - else: - # No ELSE action - return None - - def __forstmt(self): - """Parses for loops - - :return: The FlowSignal to indicate that - a loop start has been processed - - """ - - # Set up default loop increment value - step = 1 - - self.__advance() # Advance past FOR token - - # Process the loop variable initialisation - loop_variable = self.__token.lexeme # Save lexeme of - # the current token - - if loop_variable.endswith('$'): - raise SyntaxError('Syntax error: Loop variable is not numeric' + - ' in line ' + str(self.__line_number)) - - self.__advance() # Advance past loop variable - self.__consume(Token.ASSIGNOP) - self.__expr() - - # Check that we are using the right variable name format - # for numeric variables - start_val = self.__operand_stack.pop() - - # Advance past the 'TO' keyword - self.__consume(Token.TO) - - # Process the terminating value - self.__expr() - end_val = self.__operand_stack.pop() - - # Check if there is a STEP value - increment = True - if not self.__tokenindex >= len(self.__tokenlist): - self.__consume(Token.STEP) - - # Acquire the step value - self.__expr() - step = self.__operand_stack.pop() - - # Check whether we are decrementing or - # incrementing - if step == 0: - raise IndexError('Zero step value supplied for loop' + - ' in line ' + str(self.__line_number)) - - elif step < 0: - increment = False - - # Now determine the status of the loop - - # If the loop variable is not in the set of extant - # variables, this is the first time we have entered the loop - # Note that we cannot use the presence of the loop variable in - # the symbol table for this test, as the same variable may already - # have been instantiated elsewhere in the program - if loop_variable not in self.__loop_vars: - self.__symbol_table[loop_variable] = start_val - - # Also add loop variable to set of extant loop - # variables - self.__loop_vars.add(loop_variable) - - else: - # We need to modify the loop variable - # according to the STEP value - self.__symbol_table[loop_variable] += step - - # If the loop variable has reached the end value, - # remove it from the set of extant loop variables to signal that - # this is the last loop iteration - stop = False - if increment and self.__symbol_table[loop_variable] > end_val: - stop = True - - elif not increment and self.__symbol_table[loop_variable] < end_val: - stop = True - - if stop: - # Loop must terminate, so remove loop vriable from set of - # extant loop variables and remove loop variable from - # symbol table - self.__loop_vars.remove(loop_variable) - del self.__symbol_table[loop_variable] - return FlowSignal(ftype=FlowSignal.LOOP_SKIP, - ftarget=loop_variable) - else: - # Set up and return the flow signal - return FlowSignal(ftype=FlowSignal.LOOP_BEGIN) - - def __nextstmt(self): - """Processes a NEXT statement that terminates - a loop - - :return: A FlowSignal indicating that a loop - has been processed - - """ - - self.__advance() # Advance past NEXT token - - return FlowSignal(ftype=FlowSignal.LOOP_REPEAT) - - def __ongosubstmt(self): - """Process the ON-GOSUB statement - - :return: A FlowSignal indicating the subroutine line number - if the condition is true, None otherwise - - """ - - self.__advance() # Advance past ON token - self.__expr() - - # Save result of expression - saveval = self.__operand_stack.pop() - - if self.__token.category == Token.GOTO: - self.__consume(Token.GOTO) - branchtype = 1 - else: - self.__consume(Token.GOSUB) - branchtype = 2 - - branch_values = [] - # Acquire the comma separated values - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - branch_values.append(self.__operand_stack.pop()) - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - branch_values.append(self.__operand_stack.pop()) - - if saveval < 1 or saveval > len(branch_values) or len(branch_values) == 0: - return None - elif branchtype == 1: - return FlowSignal(ftarget=branch_values[saveval-1]) - else: - return FlowSignal(ftarget=branch_values[saveval-1], - ftype=FlowSignal.GOSUB) - - def __relexpr(self): - """Parses a relational expression - """ - self.__expr() - - # Since BASIC uses same operator for both - # assignment and equality, we need to check for this - if self.__token.category == Token.ASSIGNOP: - self.__token.category = Token.EQUAL - - if self.__token.category in [Token.LESSER, Token.LESSEQUAL, - Token.GREATER, Token.GREATEQUAL, - Token.EQUAL, Token.NOTEQUAL]: - savecat = self.__token.category - self.__advance() - self.__expr() - - right = self.__operand_stack.pop() - left = self.__operand_stack.pop() - - if savecat == Token.EQUAL: - self.__operand_stack.append(left == right) # Push True or False - - elif savecat == Token.NOTEQUAL: - self.__operand_stack.append(left != right) # Push True or False - - elif savecat == Token.LESSER: - self.__operand_stack.append(left < right) # Push True or False - - elif savecat == Token.GREATER: - self.__operand_stack.append(left > right) # Push True or False - - elif savecat == Token.LESSEQUAL: - self.__operand_stack.append(left <= right) # Push True or False - - elif savecat == Token.GREATEQUAL: - self.__operand_stack.append(left >= right) # Push True or False - - def __logexpr(self): - """Parses a logical expression - """ - self.__notexpr() - - while self.__token.category in [Token.OR, Token.AND]: - savecat = self.__token.category - self.__advance() - self.__notexpr() - - right = self.__operand_stack.pop() - left = self.__operand_stack.pop() - - if savecat == Token.OR: - self.__operand_stack.append(left or right) # Push True or False - - elif savecat == Token.AND: - self.__operand_stack.append(left and right) # Push True or False - - def __notexpr(self): - """Parses a logical not expression - """ - if self.__token.category == Token.NOT: - self.__advance() - self.__relexpr() - right = self.__operand_stack.pop() - self.__operand_stack.append(not right) - else: - self.__relexpr() - - def __evaluate_function(self, category): - """Evaluate the function in the statement - and return the result. - - :return: The result of the function - - """ - - self.__advance() # Advance past function name - - # Process arguments according to function - if category == Token.RND: - return random.random() - - if category == Token.PI: - return math.pi - - if category == Token.RNDINT: - self.__consume(Token.LEFTPAREN) - - self.__expr() - lo = self.__operand_stack.pop() - - self.__consume(Token.COMMA) - - self.__expr() - hi = self.__operand_stack.pop() - - self.__consume(Token.RIGHTPAREN) - - try: - return random.randint(lo, hi) - - except ValueError: - raise ValueError("Invalid value supplied to RNDINT in line " + - str(self.__line_number)) - - if category == Token.MAX: - self.__consume(Token.LEFTPAREN) - - self.__expr() - value_list = [self.__operand_stack.pop()] - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - value_list.append(self.__operand_stack.pop()) - - self.__consume(Token.RIGHTPAREN) - - try: - return max(*value_list) - - except TypeError: - raise TypeError("Invalid type supplied to MAX in line " + - str(self.__line_number)) - - if category == Token.MIN: - self.__consume(Token.LEFTPAREN) - - self.__expr() - value_list = [self.__operand_stack.pop()] - - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - value_list.append(self.__operand_stack.pop()) - - self.__consume(Token.RIGHTPAREN) - - try: - return min(*value_list) - - except TypeError: - raise TypeError("Invalid type supplied to MIN in line " + - str(self.__line_number)) - - if category == Token.POW: - self.__consume(Token.LEFTPAREN) - - self.__expr() - base = self.__operand_stack.pop() - - self.__consume(Token.COMMA) - - self.__expr() - exponent = self.__operand_stack.pop() - - self.__consume(Token.RIGHTPAREN) - - try: - return math.pow(base, exponent) - - except ValueError: - raise ValueError("Invalid value supplied to POW in line " + - str(self.__line_number)) - - if category == Token.TERNARY: - self.__consume(Token.LEFTPAREN) - - self.__logexpr() - condition = self.__operand_stack.pop() - - self.__consume(Token.COMMA) - - self.__expr() - whentrue = self.__operand_stack.pop() - - self.__consume(Token.COMMA) - - self.__expr() - whenfalse = self.__operand_stack.pop() - - self.__consume(Token.RIGHTPAREN) - - return whentrue if condition else whenfalse - - if category == Token.MID: - self.__consume(Token.LEFTPAREN) - - self.__expr() - instring = self.__operand_stack.pop() - - self.__consume(Token.COMMA) - - self.__expr() - start = self.__operand_stack.pop() - - if self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - end = self.__operand_stack.pop() - else: - end = None - - self.__consume(Token.RIGHTPAREN) - - try: - return instring[start:end] - - except TypeError: - raise TypeError("Invalid type supplied to MID$ in line " + - str(self.__line_number)) - - if category == Token.INSTR: - self.__consume(Token.LEFTPAREN) - - self.__expr() - hackstackstring = self.__operand_stack.pop() - if not isinstance(hackstackstring, str): - raise TypeError("Invalid type supplied to INSTR in line " + - str(self.__line_number)) - - self.__consume(Token.COMMA) - - self.__expr() - needlestring = self.__operand_stack.pop() - - start = end = None - if self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - start = self.__operand_stack.pop() - - if self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - end = self.__operand_stack.pop() - - self.__consume(Token.RIGHTPAREN) - - try: - return hackstackstring.find(needlestring, start, end) - - except TypeError: - raise TypeError("Invalid type supplied to INSTR in line " + - str(self.__line_number)) - - self.__consume(Token.LEFTPAREN) - - self.__expr() - value = self.__operand_stack.pop() - - self.__consume(Token.RIGHTPAREN) - - if category == Token.SQR: - try: - return math.sqrt(value) - - except ValueError: - raise ValueError("Invalid value supplied to SQR in line " + - str(self.__line_number)) - - elif category == Token.ABS: - try: - return abs(value) - - except ValueError: - raise ValueError("Invalid value supplied to ABS in line " + - str(self.__line_number)) - - elif category == Token.ATN: - try: - return math.atan(value) - - except ValueError: - raise ValueError("Invalid value supplied to ATN in line " + - str(self.__line_number)) - - elif category == Token.COS: - try: - return math.cos(value) - - except ValueError: - raise ValueError("Invalid value supplied to COS in line " + - str(self.__line_number)) - - elif category == Token.EXP: - try: - return math.exp(value) - - except ValueError: - raise ValueError("Invalid value supplied to EXP in line " + - str(self.__line_number)) - - elif category == Token.INT: - try: - return math.floor(value) - - except ValueError: - raise ValueError("Invalid value supplied to INT in line " + - str(self.__line_number)) - - elif category == Token.ROUND: - try: - return round(value) - - except TypeError: - raise TypeError("Invalid type supplied to LEN in line " + - str(self.__line_number)) - - elif category == Token.LOG: - try: - return math.log(value) - - except ValueError: - raise ValueError("Invalid value supplied to LOG in line " + - str(self.__line_number)) - - elif category == Token.SIN: - try: - return math.sin(value) - - except ValueError: - raise ValueError("Invalid value supplied to SIN in line " + - str(self.__line_number)) - - elif category == Token.TAN: - try: - return math.tan(value) - - except ValueError: - raise ValueError("Invalid value supplied to TAN in line " + - str(self.__line_number)) - - elif category == Token.CHR: - try: - return chr(value) - - except TypeError: - raise TypeError("Invalid type supplied to CHR$ in line " + - str(self.__line_number)) - - except ValueError: - raise ValueError("Invalid value supplied to CHR$ in line " + - str(self.__line_number)) - - elif category == Token.ASC: - try: - return ord(value) - - except TypeError: - raise TypeError("Invalid type supplied to ASC in line " + - str(self.__line_number)) - - except ValueError: - raise ValueError("Invalid value supplied to ASC in line " + - str(self.__line_number)) - - elif category == Token.STR: - return str(value) - - elif category == Token.VAL: - try: - numeric = float(value) - if numeric.is_integer(): - return int(numeric) - return numeric - - # Like other BASIC variants, non-numeric strings return 0 - except ValueError: - return 0 - - elif category == Token.LEN: - try: - return len(value) - - except TypeError: - raise TypeError("Invalid type supplied to LEN in line " + - str(self.__line_number)) - - elif category == Token.UPPER: - if not isinstance(value, str): - raise TypeError("Invalid type supplied to UPPER$ in line " + - str(self.__line_number)) - - return value.upper() - - elif category == Token.LOWER: - if not isinstance(value, str): - raise TypeError("Invalid type supplied to LOWER$ in line " + - str(self.__line_number)) - - return value.lower() - - else: - raise SyntaxError("Unrecognised function in line " + - str(self.__line_number)) - - def __randomizestmt(self): - """Implements a function to seed the random - number generator - - """ - self.__advance() # Advance past RANDOMIZE token - - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() # Process the seed - seed = self.__operand_stack.pop() - - random.seed(seed) - - else: - random.seed(int(monotonic())) +#! /usr/bin/python + +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from basictoken import BASICToken as Token +from flowsignal import FlowSignal +import math +import random +from time import monotonic + + +"""Implements a BASIC array, which may have up +to three dimensions of fixed size. + +""" +class BASICArray: + + def __init__(self, dimensions): + """Initialises the object with the specified + number of dimensions. Maximum number of + dimensions is three + + :param dimensions: List of array dimensions and their + corresponding sizes + + """ + self.dims = min(3,len(dimensions)) + + if self.dims == 0: + raise SyntaxError("Zero dimensional array specified") + + # Check for invalid sizes and ensure int + for i in range(self.dims): + if dimensions[i] < 0: + raise SyntaxError("Negative array size specified") + # Allow sizes like 1.0f, but not 1.1f + if int(dimensions[i]) != dimensions[i]: + raise SyntaxError("Fractional array size specified") + dimensions[i] = int(dimensions[i]) + + if self.dims == 1: + self.data = [None for x in range(dimensions[0])] + elif self.dims == 2: + self.data = [[None for x in range(dimensions[1])] for x in range(dimensions[0])] + else: + self.data = [[[None for x in range(dimensions[2])] for x in range(dimensions[1])] for x in range(dimensions[0])] + + def pretty_print(self): + print(str(self.data)) + +"""Implements a BASIC parser that parses a single +statement when supplied. + +""" +class BASICParser: + + def __init__(self): + # Symbol table to hold variable names mapped + # to values + self.__symbol_table = {} + + # Stack on which to store operands + # when evaluating expressions + self.__operand_stack = [] + + # List to hold contents of DATA statement + self.__data_values = [] + + # These values will be + # initialised on a per + # statement basis + self.__tokenlist = [] + self.__tokenindex = None + + # Set to keep track of extant loop variables + self. __loop_vars = set() + + def parse(self, tokenlist, line_number): + """Must be initialised with the list of + BTokens to be processed. These tokens + represent a BASIC statement without + its corresponding line number. + + :param tokenlist: The tokenized program statement + :param line_number: The line number of the statement + + :return: The FlowSignal to indicate to the program + how to branch if necessary, None otherwise + + """ + self.__tokenlist = tokenlist + self.__tokenindex = 0 + + # Remember the line number to aid error reporting + self.__line_number = line_number + + # Assign the first token + self.__token = self.__tokenlist[self.__tokenindex] + + return self.__stmt() + + def __advance(self): + """Advances to the next token + + """ + # Move to the next token + self.__tokenindex += 1 + + # Acquire the next token if there any left + if not self.__tokenindex >= len(self.__tokenlist): + self.__token = self.__tokenlist[self.__tokenindex] + + def __consume(self, expected_category): + """Consumes a token from the list + + """ + if self.__token.category == expected_category: + self.__advance() + + else: + raise RuntimeError('Expecting ' + Token.catnames[expected_category] + + ' in line ' + str(self.__line_number)) + + def __stmt(self): + """Parses a program statement + + :return: The FlowSignal to indicate to the program + how to branch if necessary, None otherwise + + """ + if self.__token.category in [Token.FOR, Token.IF, Token.NEXT, + Token.ON]: + return self.__compoundstmt() + + else: + return self.__simplestmt() + + def __simplestmt(self): + """Parses a non-compound program statement + + :return: The FlowSignal to indicate to the program + how to branch if necessary, None otherwise + + """ + if self.__token.category == Token.NAME: + self.__assignmentstmt() + return None + + elif self.__token.category == Token.PRINT: + self.__printstmt() + return None + + elif self.__token.category == Token.LET: + self.__letstmt() + return None + + elif self.__token.category == Token.GOTO: + return self.__gotostmt() + + elif self.__token.category == Token.GOSUB: + return self.__gosubstmt() + + elif self.__token.category == Token.RETURN: + return self.__returnstmt() + + elif self.__token.category == Token.STOP: + return self.__stopstmt() + + elif self.__token.category == Token.INPUT: + self.__inputstmt() + return None + + elif self.__token.category == Token.DIM: + self.__dimstmt() + return None + + elif self.__token.category == Token.RANDOMIZE: + self.__randomizestmt() + return None + + elif self.__token.category == Token.DATA: + self.__datastmt() + return None + + elif self.__token.category == Token.READ: + self.__readstmt() + return None + + else: + # Ignore comments, but raise an error + # for anything else + if self.__token.category != Token.REM: + raise RuntimeError('Expecting program statement in line ' + + str(self.__line_number)) + + def __printstmt(self): + """Parses a PRINT statement, causing + the value that is on top of the + operand stack to be printed on + the screen. + + """ + self.__advance() # Advance past PRINT token + + # Check there are items to print + if not self.__tokenindex >= len(self.__tokenlist): + self.__logexpr() + print(self.__operand_stack.pop(), end='') + + while self.__token.category == Token.COMMA: + self.__advance() + self.__logexpr() + print(self.__operand_stack.pop(), end='') + + # Final newline + print() + + def __letstmt(self): + """Parses a LET statement, + consuming the LET keyword. + """ + self.__advance() # Advance past the LET token + self.__assignmentstmt() + + def __gotostmt(self): + """Parses a GOTO statement + + :return: A FlowSignal containing the target line number + of the GOTO + + """ + self.__advance() # Advance past GOTO token + self.__expr() + + # Set up and return the flow signal + return FlowSignal(ftarget=self.__operand_stack.pop()) + + def __gosubstmt(self): + """Parses a GOSUB statement + + :return: A FlowSignal containing the first line number + of the subroutine + + """ + + self.__advance() # Advance past GOSUB token + self.__expr() + + # Set up and return the flow signal + return FlowSignal(ftarget=self.__operand_stack.pop(), + ftype=FlowSignal.GOSUB) + + def __returnstmt(self): + """Parses a RETURN statement""" + + self.__advance() # Advance past RETURN token + + # Set up and return the flow signal + return FlowSignal(ftype=FlowSignal.RETURN) + + def __stopstmt(self): + """Parses a STOP statement""" + + self.__advance() # Advance past STOP token + + return FlowSignal(ftype=FlowSignal.STOP) + + def __assignmentstmt(self): + """Parses an assignment statement, + placing the corresponding + variable and its value in the symbol + table. + + """ + left = self.__token.lexeme # Save lexeme of + # the current token + self.__advance() + + if self.__token.category == Token.LEFTPAREN: + # We are assiging to an array + self.__arrayassignmentstmt(left) + + else: + # We are assigning to a simple variable + self.__consume(Token.ASSIGNOP) + self.__logexpr() + + # Check that we are using the right variable name format + right = self.__operand_stack.pop() + + if left.endswith('$') and not isinstance(right, str): + raise SyntaxError('Syntax error: Attempt to assign non string to string variable' + + ' in line ' + str(self.__line_number)) + + elif not left.endswith('$') and isinstance(right, str): + raise SyntaxError('Syntax error: Attempt to assign string to numeric variable' + + ' in line ' + str(self.__line_number)) + + self.__symbol_table[left] = right + + def __dimstmt(self): + """Parses DIM statement and creates a symbol + table entry for an array of the specified + dimensions. + + """ + self.__advance() # Advance past DIM keyword + + # Extract the array name, append a suffix so + # that we can distinguish from simple variables + # in the symbol table + name = self.__token.lexeme + '_array' + self.__advance() # Advance past array name + + self.__consume(Token.LEFTPAREN) + + # Extract the dimensions + dimensions = [] + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() + dimensions.append(self.__operand_stack.pop()) + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + dimensions.append(self.__operand_stack.pop()) + + self.__consume(Token.RIGHTPAREN) + + if len(dimensions) > 3: + raise SyntaxError("Maximum number of array dimensions is three " + + "in line " + str(self.__line_number)) + + self.__symbol_table[name] = BASICArray(dimensions) + + def __arrayassignmentstmt(self, name): + """Parses an assignment to an array variable + + :param name: Array name + + """ + self.__consume(Token.LEFTPAREN) + + # Capture the index variables + # Extract the dimensions + indexvars = [] + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() + indexvars.append(self.__operand_stack.pop()) + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + indexvars.append(self.__operand_stack.pop()) + + try: + BASICarray = self.__symbol_table[name + '_array'] + + except KeyError: + raise KeyError('Array could not be found in line ' + + str(self.__line_number)) + + if BASICarray.dims != len(indexvars): + raise IndexError('Incorrect number of indices applied to array ' + + 'in line ' + str(self.__line_number)) + + self.__consume(Token.RIGHTPAREN) + self.__consume(Token.ASSIGNOP) + + self.__logexpr() + + # Check that we are using the right variable name format + right = self.__operand_stack.pop() + + if name.endswith('$') and not isinstance(right, str): + raise SyntaxError('Attempt to assign non string to string array' + + ' in line ' + str(self.__line_number)) + + elif not name.endswith('$') and isinstance(right, str): + raise SyntaxError('Attempt to assign string to numeric array' + + ' in line ' + str(self.__line_number)) + + # Assign to the specified array index + try: + if len(indexvars) == 1: + BASICarray.data[indexvars[0]] = right + + elif len(indexvars) == 2: + BASICarray.data[indexvars[0]][indexvars[1]] = right + + elif len(indexvars) == 3: + BASICarray.data[indexvars[0]][indexvars[1]][indexvars[2]] = right + + except IndexError: + raise IndexError('Array index out of range in line ' + + str(self.__line_number)) + + def __inputstmt(self): + """Parses an input statement, extracts the input + from the user and places the values into the + symbol table + + """ + self.__advance() # Advance past INPUT token + + prompt = '? ' + if self.__token.category == Token.STRING: + # Acquire the input prompt + self.__logexpr() + prompt = self.__operand_stack.pop() + self.__consume(Token.COLON) + + # Acquire the comma separated input variables + variables = [] + if not self.__tokenindex >= len(self.__tokenlist): + if self.__token.category != Token.NAME: + raise ValueError('Expecting NAME in INPUT statement ' + + 'in line ' + str(self.__line_number)) + variables.append(self.__token.lexeme) + self.__advance() # Advance past variable + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + variables.append(self.__token.lexeme) + self.__advance() # Advance past variable + + valid_input = False + while not valid_input: + # Gather input from the user into the variables + inputvals = input(prompt).split(',', (len(variables)-1)) + + for variable in variables: + left = variable + + try: + right = inputvals.pop(0) + + if left.endswith('$'): + self.__symbol_table[left] = str(right) + valid_input = True + + elif not left.endswith('$'): + try: + if '.' in right: + self.__symbol_table[left] = float(right) + + else: + self.__symbol_table[left] = int(right) + + valid_input = True + + except ValueError: + valid_input = False + print('Non-numeric input provided to a numeric variable - redo from start') + break + + except IndexError: + # No more input to process + valid_input = False + print('Not enough values input - redo from start') + break + + def __datastmt(self): + """Parses a DATA statement""" + + self.__advance() # Advance past DATA token + + # Acquire the comma separated values + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() + self.__data_values.append(self.__operand_stack.pop()) + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + self.__data_values.append(self.__operand_stack.pop()) + + def __readstmt(self): + """Parses a READ statement.""" + + self.__advance() # Advance past READ token + + # Acquire the comma separated input variables + variables = [] + if not self.__tokenindex >= len(self.__tokenlist): + variables.append(self.__token.lexeme) + self.__advance() # Advance past variable + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + variables.append(self.__token.lexeme) + self.__advance() # Advance past variable + + # Check that we have enough data values to fill the + # variables + if len(variables) > len(self.__data_values): + raise RuntimeError('Insufficient constants supplied to READ ' + + 'in line ' + str(self.__line_number)) + + # Gather input from the DATA statement into the variables + for variable in variables: + left = variable + right = self.__data_values.pop(0) + + if left.endswith('$'): + # Python inserts quotes around input data + if isinstance(right, int): + raise ValueError('Non-string input provided to a string variable ' + + 'in line ' + str(self.__line_number)) + + else: + self.__symbol_table[left] = right + + elif not left.endswith('$'): + try: + #if '.' in right: + # self.__symbol_table[left] = float(right) + #else: + # self.__symbol_table[left] = int(right) + + numeric = float(right) + if numeric.is_integer(): + numeric = int(numeric) + self.__symbol_table[left] = numeric + + except ValueError: + raise ValueError('Non-numeric input provided to a numeric variable ' + + 'in line ' + str(self.__line_number)) + + def __expr(self): + """Parses a numerical expression consisting + of two terms being added or subtracted, + leaving the result on the operand stack. + + """ + self.__term() # Pushes value of left term + # onto top of stack + + while self.__token.category in [Token.PLUS, Token.MINUS]: + savedcategory = self.__token.category + self.__advance() + self.__term() # Pushes value of right term + # onto top of stack + rightoperand = self.__operand_stack.pop() + leftoperand = self.__operand_stack.pop() + + if savedcategory == Token.PLUS: + self.__operand_stack.append(leftoperand + rightoperand) + + else: + self.__operand_stack.append(leftoperand - rightoperand) + + def __term(self): + """Parses a numerical expression consisting + of two factors being multiplied together, + leaving the result on the operand stack. + + """ + self.__sign = 1 # Initialise sign to keep track of unary + # minuses + self.__factor() # Leaves value of term on top of stack + + while self.__token.category in [Token.TIMES, Token.DIVIDE, Token.MODULO]: + savedcategory = self.__token.category + self.__advance() + self.__sign = 1 # Initialise sign + self.__factor() # Leaves value of term on top of stack + rightoperand = self.__operand_stack.pop() + leftoperand = self.__operand_stack.pop() + + if savedcategory == Token.TIMES: + self.__operand_stack.append(leftoperand * rightoperand) + + elif savedcategory == Token.DIVIDE: + self.__operand_stack.append(leftoperand / rightoperand) + + else: + self.__operand_stack.append(leftoperand % rightoperand) + + def __factor(self): + """Evaluates a numerical expression + and leaves its value on top of the + operand stack. + + """ + if self.__token.category == Token.PLUS: + self.__advance() + self.__factor() + + elif self.__token.category == Token.MINUS: + self.__sign = -self.__sign + self.__advance() + self.__factor() + + elif self.__token.category == Token.UNSIGNEDINT: + self.__operand_stack.append(self.__sign*int(self.__token.lexeme)) + self.__advance() + + elif self.__token.category == Token.UNSIGNEDFLOAT: + self.__operand_stack.append(self.__sign*float(self.__token.lexeme)) + self.__advance() + + elif self.__token.category == Token.STRING: + self.__operand_stack.append(self.__token.lexeme) + self.__advance() + + elif self.__token.category == Token.NAME and \ + self.__token.category not in Token.functions: + # Check if this is a simple or array variable + if (self.__token.lexeme + '_array') in self.__symbol_table: + # Capture the current lexeme + arrayname = self.__token.lexeme + '_array' + + # Array must be processed + # Capture the index variables + self.__advance() # Advance past the array name + + try: + self.__consume(Token.LEFTPAREN) + except RuntimeError: + raise RuntimeError('Array used without index in line ' + + str(self.__line_number)) + + indexvars = [] + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() + indexvars.append(self.__operand_stack.pop()) + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + indexvars.append(self.__operand_stack.pop()) + + BASICarray = self.__symbol_table[arrayname] + arrayval = self.__get_array_val(BASICarray, indexvars) + + if arrayval != None: + self.__operand_stack.append(self.__sign*arrayval) + + else: + raise IndexError('Empty array value returned in line ' + + str(self.__line_number)) + + elif self.__token.lexeme in self.__symbol_table: + # Simple variable must be processed + self.__operand_stack.append(self.__sign*self.__symbol_table[self.__token.lexeme]) + + else: + raise RuntimeError('Name ' + self.__token.lexeme + ' is not defined' + + ' in line ' + str(self.__line_number)) + + self.__advance() + + elif self.__token.category == Token.LEFTPAREN: + self.__advance() + + # Save sign because expr() calls term() which resets + # sign to 1 + savesign = self.__sign + self.__logexpr() # Value of expr is pushed onto stack + + if savesign == -1: + # Change sign of expression + self.__operand_stack[-1] = -self.__operand_stack[-1] + + self.__consume(Token.RIGHTPAREN) + + elif self.__token.category in Token.functions: + self.__operand_stack.append(self.__evaluate_function(self.__token.category)) + + else: + raise RuntimeError('Expecting factor in numeric expression' + + ' in line ' + str(self.__line_number)) + + def __get_array_val(self, BASICarray, indexvars): + """Extracts the value from the given BASICArray at the specified indexes + + :param BASICarray: The BASICArray + :param indexvars: The list of indexes, one for each dimension + + :return: The value at the indexed position in the array + + """ + if BASICarray.dims != len(indexvars): + raise IndexError('Incorrect number of indices applied to array ' + + 'in line ' + str(self.__line_number)) + + # Fetch the value from the array + try: + if len(indexvars) == 1: + arrayval = BASICarray.data[indexvars[0]] + + elif len(indexvars) == 2: + arrayval = BASICarray.data[indexvars[0]][indexvars[1]] + + elif len(indexvars) == 3: + arrayval = BASICarray.data[indexvars[0]][indexvars[1]][indexvars[2]] + + except IndexError: + raise IndexError('Array index out of range in line ' + + str(self.__line_number)) + + return arrayval + + def __compoundstmt(self): + """Parses compound statements, + specifically if-then-else and + loops + + :return: The FlowSignal to indicate to the program + how to branch if necessary, None otherwise + + """ + if self.__token.category == Token.FOR: + return self.__forstmt() + + elif self.__token.category == Token.NEXT: + return self.__nextstmt() + + elif self.__token.category == Token.IF: + return self.__ifstmt() + + elif self.__token.category == Token.ON: + return self.__ongosubstmt() + + def __ifstmt(self): + """Parses if-then-else + statements + + :return: The FlowSignal to indicate to the program + how to branch if necessary, None otherwise + + """ + + self.__advance() # Advance past IF token + self.__logexpr() + + # Save result of expression + saveval = self.__operand_stack.pop() + + # Process the THEN part and save the jump value + self.__consume(Token.THEN) + + if self.__token.category == Token.GOTO: + self.__advance() # Advance past optional GOTO + + self.__expr() + then_jump = self.__operand_stack.pop() + + # Jump if the expression evaluated to True + if saveval: + # Set up and return the flow signal + return FlowSignal(ftarget=then_jump) + + # See if there is an ELSE part + if self.__token.category == Token.ELSE: + self.__advance() + + if self.__token.category == Token.GOTO: + self.__advance() # Advance past optional GOTO + + self.__expr() + + # Set up and return the flow signal + return FlowSignal(ftarget=self.__operand_stack.pop()) + + else: + # No ELSE action + return None + + def __forstmt(self): + """Parses for loops + + :return: The FlowSignal to indicate that + a loop start has been processed + + """ + + # Set up default loop increment value + step = 1 + + self.__advance() # Advance past FOR token + + # Process the loop variable initialisation + loop_variable = self.__token.lexeme # Save lexeme of + # the current token + + if loop_variable.endswith('$'): + raise SyntaxError('Syntax error: Loop variable is not numeric' + + ' in line ' + str(self.__line_number)) + + self.__advance() # Advance past loop variable + self.__consume(Token.ASSIGNOP) + self.__expr() + + # Check that we are using the right variable name format + # for numeric variables + start_val = self.__operand_stack.pop() + + # Advance past the 'TO' keyword + self.__consume(Token.TO) + + # Process the terminating value + self.__expr() + end_val = self.__operand_stack.pop() + + # Check if there is a STEP value + increment = True + if not self.__tokenindex >= len(self.__tokenlist): + self.__consume(Token.STEP) + + # Acquire the step value + self.__expr() + step = self.__operand_stack.pop() + + # Check whether we are decrementing or + # incrementing + if step == 0: + raise IndexError('Zero step value supplied for loop' + + ' in line ' + str(self.__line_number)) + + elif step < 0: + increment = False + + # Now determine the status of the loop + + # If the loop variable is not in the set of extant + # variables, this is the first time we have entered the loop + # Note that we cannot use the presence of the loop variable in + # the symbol table for this test, as the same variable may already + # have been instantiated elsewhere in the program + if loop_variable not in self.__loop_vars: + self.__symbol_table[loop_variable] = start_val + + # Also add loop variable to set of extant loop + # variables + self.__loop_vars.add(loop_variable) + + else: + # We need to modify the loop variable + # according to the STEP value + self.__symbol_table[loop_variable] += step + + # If the loop variable has reached the end value, + # remove it from the set of extant loop variables to signal that + # this is the last loop iteration + stop = False + if increment and self.__symbol_table[loop_variable] > end_val: + stop = True + + elif not increment and self.__symbol_table[loop_variable] < end_val: + stop = True + + if stop: + # Loop must terminate, so remove loop vriable from set of + # extant loop variables and remove loop variable from + # symbol table + self.__loop_vars.remove(loop_variable) + del self.__symbol_table[loop_variable] + return FlowSignal(ftype=FlowSignal.LOOP_SKIP, + ftarget=loop_variable) + else: + # Set up and return the flow signal + return FlowSignal(ftype=FlowSignal.LOOP_BEGIN) + + def __nextstmt(self): + """Processes a NEXT statement that terminates + a loop + + :return: A FlowSignal indicating that a loop + has been processed + + """ + + self.__advance() # Advance past NEXT token + + return FlowSignal(ftype=FlowSignal.LOOP_REPEAT) + + def __ongosubstmt(self): + """Process the ON-GOSUB statement + + :return: A FlowSignal indicating the subroutine line number + if the condition is true, None otherwise + + """ + + self.__advance() # Advance past ON token + self.__expr() + + # Save result of expression + saveval = self.__operand_stack.pop() + + if self.__token.category == Token.GOTO: + self.__consume(Token.GOTO) + branchtype = 1 + else: + self.__consume(Token.GOSUB) + branchtype = 2 + + branch_values = [] + # Acquire the comma separated values + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() + branch_values.append(self.__operand_stack.pop()) + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + branch_values.append(self.__operand_stack.pop()) + + if saveval < 1 or saveval > len(branch_values) or len(branch_values) == 0: + return None + elif branchtype == 1: + return FlowSignal(ftarget=branch_values[saveval-1]) + else: + return FlowSignal(ftarget=branch_values[saveval-1], + ftype=FlowSignal.GOSUB) + + def __relexpr(self): + """Parses a relational expression + """ + self.__expr() + + # Since BASIC uses same operator for both + # assignment and equality, we need to check for this + if self.__token.category == Token.ASSIGNOP: + self.__token.category = Token.EQUAL + + if self.__token.category in [Token.LESSER, Token.LESSEQUAL, + Token.GREATER, Token.GREATEQUAL, + Token.EQUAL, Token.NOTEQUAL]: + savecat = self.__token.category + self.__advance() + self.__expr() + + right = self.__operand_stack.pop() + left = self.__operand_stack.pop() + + if savecat == Token.EQUAL: + self.__operand_stack.append(left == right) # Push True or False + + elif savecat == Token.NOTEQUAL: + self.__operand_stack.append(left != right) # Push True or False + + elif savecat == Token.LESSER: + self.__operand_stack.append(left < right) # Push True or False + + elif savecat == Token.GREATER: + self.__operand_stack.append(left > right) # Push True or False + + elif savecat == Token.LESSEQUAL: + self.__operand_stack.append(left <= right) # Push True or False + + elif savecat == Token.GREATEQUAL: + self.__operand_stack.append(left >= right) # Push True or False + + def __logexpr(self): + """Parses a logical expression + """ + self.__notexpr() + + while self.__token.category in [Token.OR, Token.AND]: + savecat = self.__token.category + self.__advance() + self.__notexpr() + + right = self.__operand_stack.pop() + left = self.__operand_stack.pop() + + if savecat == Token.OR: + self.__operand_stack.append(left or right) # Push True or False + + elif savecat == Token.AND: + self.__operand_stack.append(left and right) # Push True or False + + def __notexpr(self): + """Parses a logical not expression + """ + if self.__token.category == Token.NOT: + self.__advance() + self.__relexpr() + right = self.__operand_stack.pop() + self.__operand_stack.append(not right) + else: + self.__relexpr() + + def __evaluate_function(self, category): + """Evaluate the function in the statement + and return the result. + + :return: The result of the function + + """ + + self.__advance() # Advance past function name + + # Process arguments according to function + if category == Token.RND: + return random.random() + + if category == Token.PI: + return math.pi + + if category == Token.RNDINT: + self.__consume(Token.LEFTPAREN) + + self.__expr() + lo = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + hi = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + try: + return random.randint(lo, hi) + + except ValueError: + raise ValueError("Invalid value supplied to RNDINT in line " + + str(self.__line_number)) + + if category == Token.MAX: + self.__consume(Token.LEFTPAREN) + + self.__expr() + value_list = [self.__operand_stack.pop()] + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + value_list.append(self.__operand_stack.pop()) + + self.__consume(Token.RIGHTPAREN) + + try: + return max(*value_list) + + except TypeError: + raise TypeError("Invalid type supplied to MAX in line " + + str(self.__line_number)) + + if category == Token.MIN: + self.__consume(Token.LEFTPAREN) + + self.__expr() + value_list = [self.__operand_stack.pop()] + + while self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + value_list.append(self.__operand_stack.pop()) + + self.__consume(Token.RIGHTPAREN) + + try: + return min(*value_list) + + except TypeError: + raise TypeError("Invalid type supplied to MIN in line " + + str(self.__line_number)) + + if category == Token.POW: + self.__consume(Token.LEFTPAREN) + + self.__expr() + base = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + exponent = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + try: + return math.pow(base, exponent) + + except ValueError: + raise ValueError("Invalid value supplied to POW in line " + + str(self.__line_number)) + + if category == Token.TERNARY: + self.__consume(Token.LEFTPAREN) + + self.__logexpr() + condition = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + whentrue = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + whenfalse = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + return whentrue if condition else whenfalse + + if category == Token.MID: + self.__consume(Token.LEFTPAREN) + + self.__expr() + instring = self.__operand_stack.pop() + + self.__consume(Token.COMMA) + + self.__expr() + start = self.__operand_stack.pop() + + if self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + end = self.__operand_stack.pop() + else: + end = None + + self.__consume(Token.RIGHTPAREN) + + try: + return instring[start:end] + + except TypeError: + raise TypeError("Invalid type supplied to MID$ in line " + + str(self.__line_number)) + + if category == Token.INSTR: + self.__consume(Token.LEFTPAREN) + + self.__expr() + hackstackstring = self.__operand_stack.pop() + if not isinstance(hackstackstring, str): + raise TypeError("Invalid type supplied to INSTR in line " + + str(self.__line_number)) + + self.__consume(Token.COMMA) + + self.__expr() + needlestring = self.__operand_stack.pop() + + start = end = None + if self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + start = self.__operand_stack.pop() + + if self.__token.category == Token.COMMA: + self.__advance() # Advance past comma + self.__expr() + end = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + try: + return hackstackstring.find(needlestring, start, end) + + except TypeError: + raise TypeError("Invalid type supplied to INSTR in line " + + str(self.__line_number)) + + self.__consume(Token.LEFTPAREN) + + self.__expr() + value = self.__operand_stack.pop() + + self.__consume(Token.RIGHTPAREN) + + if category == Token.SQR: + try: + return math.sqrt(value) + + except ValueError: + raise ValueError("Invalid value supplied to SQR in line " + + str(self.__line_number)) + + elif category == Token.ABS: + try: + return abs(value) + + except ValueError: + raise ValueError("Invalid value supplied to ABS in line " + + str(self.__line_number)) + + elif category == Token.ATN: + try: + return math.atan(value) + + except ValueError: + raise ValueError("Invalid value supplied to ATN in line " + + str(self.__line_number)) + + elif category == Token.COS: + try: + return math.cos(value) + + except ValueError: + raise ValueError("Invalid value supplied to COS in line " + + str(self.__line_number)) + + elif category == Token.EXP: + try: + return math.exp(value) + + except ValueError: + raise ValueError("Invalid value supplied to EXP in line " + + str(self.__line_number)) + + elif category == Token.INT: + try: + return math.floor(value) + + except ValueError: + raise ValueError("Invalid value supplied to INT in line " + + str(self.__line_number)) + + elif category == Token.ROUND: + try: + return round(value) + + except TypeError: + raise TypeError("Invalid type supplied to LEN in line " + + str(self.__line_number)) + + elif category == Token.LOG: + try: + return math.log(value) + + except ValueError: + raise ValueError("Invalid value supplied to LOG in line " + + str(self.__line_number)) + + elif category == Token.SIN: + try: + return math.sin(value) + + except ValueError: + raise ValueError("Invalid value supplied to SIN in line " + + str(self.__line_number)) + + elif category == Token.TAN: + try: + return math.tan(value) + + except ValueError: + raise ValueError("Invalid value supplied to TAN in line " + + str(self.__line_number)) + + elif category == Token.CHR: + try: + return chr(value) + + except TypeError: + raise TypeError("Invalid type supplied to CHR$ in line " + + str(self.__line_number)) + + except ValueError: + raise ValueError("Invalid value supplied to CHR$ in line " + + str(self.__line_number)) + + elif category == Token.ASC: + try: + return ord(value) + + except TypeError: + raise TypeError("Invalid type supplied to ASC in line " + + str(self.__line_number)) + + except ValueError: + raise ValueError("Invalid value supplied to ASC in line " + + str(self.__line_number)) + + elif category == Token.STR: + return str(value) + + elif category == Token.VAL: + try: + numeric = float(value) + if numeric.is_integer(): + return int(numeric) + return numeric + + # Like other BASIC variants, non-numeric strings return 0 + except ValueError: + return 0 + + elif category == Token.LEN: + try: + return len(value) + + except TypeError: + raise TypeError("Invalid type supplied to LEN in line " + + str(self.__line_number)) + + elif category == Token.UPPER: + if not isinstance(value, str): + raise TypeError("Invalid type supplied to UPPER$ in line " + + str(self.__line_number)) + + return value.upper() + + elif category == Token.LOWER: + if not isinstance(value, str): + raise TypeError("Invalid type supplied to LOWER$ in line " + + str(self.__line_number)) + + return value.lower() + + else: + raise SyntaxError("Unrecognised function in line " + + str(self.__line_number)) + + def __randomizestmt(self): + """Implements a function to seed the random + number generator + + """ + self.__advance() # Advance past RANDOMIZE token + + if not self.__tokenindex >= len(self.__tokenlist): + self.__expr() # Process the seed + seed = self.__operand_stack.pop() + + random.seed(seed) + + else: + random.seed(int(monotonic())) From 8db116bcb761367978e6c746403c4d795b48780e Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 5 Sep 2021 18:58:17 -0400 Subject: [PATCH 041/183] text version of test program --- ontest.bas | Bin 1053 -> 204 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ontest.bas b/ontest.bas index e97617a688e65e577a8f90aab71360345af154dc..43cdf19410f6dc761b2b63bd628adcfbee11ba3b 100644 GIT binary patch literal 204 zcmZ9_u?hk)37?gn0>38ftr#gOSsTcC11(D@nB|VEz@_ bw~G8~D(s)oUz@o1@@*kGF8+#hie55rbv`RE literal 1053 zcmZvbTXWK25Qd=vO3`X*sai{Gs#Vh#D+iDDP0eIPcRlS z8nI9))b1V*j$A~`S|cux&A#)Y+xw1Kd48B5I^Ucl2jh(>r=@D`z(Rb`>WE~5kT5#Y z@RW_p$BFw+}2q0*CtDM=SNol$8C z$wA90r)*3c!vkjsDS0Wnx|d zX~Kd4lMGBVpfQlkX0fR9a|B)TDb5YmVg^epJx{nGX>z1G&(+H+zeuVS2^RU!Os2;p`=J4?ul%CM31 zRCz+dR|N)fO_{C}ZU`Wgl0nF%co13K6w!Mdw>;5p!iI2>e|oy3Om_+Qgh((UZ-|W4 zbRFCmk!xeq6Fneo`JzBa+sd;;*cCwT7U-xbqF**1dZInTBVQDpQc0N}6L@*Sj;ya; zJQ2}v8x>DfC78Y_&{0i!o)YT+?P#Axl}y-%<(Uo$&wNv`rRPf3AiNlDNfu{ydX9sa s;`*c6c;&fX6Ko-)JchIyXT4a#8)bS+c;`1IACW9>*Q^%NWYJ*#FDo1fmjD0& From adf5c00f5ac46faa2b219b04252b7f8f470a46e6 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 5 Sep 2021 19:29:48 -0400 Subject: [PATCH 042/183] Cleanup loops and unnecessary variables --- lexer.py | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/lexer.py b/lexer.py index 02f8385..3c44723 100644 --- a/lexer.py +++ b/lexer.py @@ -56,9 +56,6 @@ def tokenize(self, stmt): # Establish a list of tokens to be # derived from the statement tokenlist = [] - firstToken = False - firstNumber = True - commentStmt = False # Process every character until we # reach the end of the statement string @@ -73,22 +70,9 @@ def tokenize(self, stmt): # incremented token = Token(self.__column - 1, None, '') - # Remark Statments - process rest of statement without checks - if commentStmt: - token.category = Token.REM - firstToken = False - - while True: - token.lexeme += c # Append the current char to the lexeme - c = self.__get_next_char() - - if c == '': - break - # Process strings - elif c == '"': + if c == '"': token.category = Token.STRING - firstToken = False # Consume all of the characters # until we reach the terminating @@ -118,9 +102,6 @@ def tokenize(self, stmt): elif c.isdigit(): token.category = Token.UNSIGNEDINT found_point = False - if firstNumber: - firstToken = True - firstNumber = False # Consume all of the digits, including any decimal point while True: @@ -162,17 +143,20 @@ def tokenize(self, stmt): if token.lexeme in Token.keywords: token.category = Token.keywords[token.lexeme] - if firstToken and token.lexeme == "REM": - commentStmt = True - firstToken = False - else: token.category = Token.NAME - firstToken = False + + # Remark Statments - process rest of statement without checks + if token.lexeme == "REM": + token.lexeme += c + c = self.__get_next_char() + + while c!= '': + token.lexeme += c # Append the current char to the lexeme + c = self.__get_next_char() # Process operator symbols elif c in Token.smalltokens: - firstToken = False save = c c = self.__get_next_char() # c might be '' (end of stmt) twochar = save + c From cff4b5d703efef5adf46e62089f2c6ea17039fca Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 5 Sep 2021 19:33:57 -0400 Subject: [PATCH 043/183] removing more unnecessary code --- lexer.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/lexer.py b/lexer.py index 3c44723..1eac07a 100644 --- a/lexer.py +++ b/lexer.py @@ -148,9 +148,6 @@ def tokenize(self, stmt): # Remark Statments - process rest of statement without checks if token.lexeme == "REM": - token.lexeme += c - c = self.__get_next_char() - while c!= '': token.lexeme += c # Append the current char to the lexeme c = self.__get_next_char() From 92a25d8112122fa2a91e3f995fffff18d8c5e7ce Mon Sep 17 00:00:00 2001 From: brickbots Date: Sun, 5 Sep 2021 18:33:09 -0700 Subject: [PATCH 044/183] Add LIST X Y functionality --- interpreter.py | 18 +++++++++++++++++- program.py | 38 ++++++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/interpreter.py b/interpreter.py index dc14a64..a13f405 100644 --- a/interpreter.py +++ b/interpreter.py @@ -87,7 +87,23 @@ def main(): # List the program elif tokenlist[0].category == Token.LIST: - program.list() + if len(tokenlist) == 2: + program.list(int(tokenlist[1].lexeme),int(tokenlist[1].lexeme)) + elif len(tokenlist) == 3: + # if we have 3 tokens, it might be LIST x y for a range + # or LIST -y or list x- for a start to y, or x to end + if tokenlist[1].lexeme == "-": + program.list(None, int(tokenlist[2].lexeme)) + elif tokenlist[2].lexeme == "-": + program.list(int(tokenlist[1].lexeme), None) + else: + program.list(int(tokenlist[1].lexeme),int(tokenlist[2].lexeme)) + elif len(tokenlist) == 4: + # if we have 4, assume LIST x-y or some other + # delimiter for a range + program.list(int(tokenlist[1].lexeme),int(tokenlist[3].lexeme)) + else: + program.list() # Save the program to disk elif tokenlist[0].category == Token.SAVE: diff --git a/program.py b/program.py index 0d95bd3..ce4681e 100644 --- a/program.py +++ b/program.py @@ -47,22 +47,36 @@ def __str__(self): line_numbers = self.line_numbers() for line_number in line_numbers: - program_text += str(line_number) + " " + program_text += self.str_statement(line_number) - statement = self.__program[line_number] - for token in statement: - # Add in quotes for strings - if token.category == Token.STRING: - program_text += '"' + token.lexeme + '" ' - - else: - program_text += token.lexeme + " " - program_text += "\n" return program_text - def list(self): + def str_statement(self, line_number): + line_text = str(line_number) + " " + + statement = self.__program[line_number] + for token in statement: + # Add in quotes for strings + if token.category == Token.STRING: + line_text += '"' + token.lexeme + '" ' + + else: + line_text += token.lexeme + " " + line_text += "\n" + return line_text + + def list(self, start_line=None, end_line=None): """Lists the program""" - print(str(self), end="") + line_numbers = self.line_numbers() + if not start_line: + start_line = int(line_numbers[0]) + + if not end_line: + end_line = int(line_numbers[-1]) + + for line_number in line_numbers: + if int(line_number) >= start_line and int(line_number) <= end_line: + print(self.str_statement(line_number), end="") def save(self, file): """Save the program From c95e2b0d639cfa92bd4e3207b18d77313f56227d Mon Sep 17 00:00:00 2001 From: brickbots Date: Sun, 5 Sep 2021 18:40:52 -0700 Subject: [PATCH 045/183] Add new list function to readme --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 4606d88..8418164 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,15 @@ Programs may be listed using the **LIST** command: > ``` +The list command can take arguments to refine the line selection listed + +`LIST 50` Lists only line 50 +`LIST 50-100` Lists lines 50 through 100 inclusive +`LIST 50 100` Also Lists lines 50 through 100 inclusive, almost any delimiter +works here +`LIST -100` Lists from the start of the program through line 100 inclusive +`LIST 50-` Lists from line 50 to the end of the program + A program is executed using the **RUN** command: ``` From 02c338942a73d02fe1f6aefba6ae21db6625b8f4 Mon Sep 17 00:00:00 2001 From: brickbots Date: Sun, 5 Sep 2021 18:41:54 -0700 Subject: [PATCH 046/183] Fix readme formatting --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 8418164..5e901ca 100644 --- a/README.md +++ b/README.md @@ -67,12 +67,17 @@ Programs may be listed using the **LIST** command: The list command can take arguments to refine the line selection listed `LIST 50` Lists only line 50 + `LIST 50-100` Lists lines 50 through 100 inclusive + `LIST 50 100` Also Lists lines 50 through 100 inclusive, almost any delimiter works here + `LIST -100` Lists from the start of the program through line 100 inclusive + `LIST 50-` Lists from line 50 to the end of the program + A program is executed using the **RUN** command: ``` From 9f0b95e59b1d50ed310451f1019ddfebfb1bdfb5 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 6 Sep 2021 15:57:42 -0400 Subject: [PATCH 047/183] Add files via upload --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ff238d..b96857d 100644 --- a/README.md +++ b/README.md @@ -791,7 +791,7 @@ be omitted to get the rest of the string. If *start-position* or *end-position* **MIN**(*expression-list*) - Returns the lowest value in *expression-list* -**ON** *expression* **GOSUB** *line-number* - Conditional subroutine call +**ON** *expression* **GOSUB|GOTO** *line-number1,line-number2,...* - Conditional subroutine call|branch - Program flow will be transferred either through a *GOSUB* subroutine call or a *GOTO* branch to the line number in the list of line numbers corresponding to the ordinal value of the evaluated expr. The first line number corresponds with an expr value of 1. "expr" must evaluate to an integer value. **PI** - Returns the value of pi From 0ea219915fe3554ef1d6952ad1be4c2b76d4d482 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 6 Sep 2021 16:02:07 -0400 Subject: [PATCH 048/183] Add files via upload --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b96857d..8872294 100644 --- a/README.md +++ b/README.md @@ -791,7 +791,7 @@ be omitted to get the rest of the string. If *start-position* or *end-position* **MIN**(*expression-list*) - Returns the lowest value in *expression-list* -**ON** *expression* **GOSUB|GOTO** *line-number1,line-number2,...* - Conditional subroutine call|branch - Program flow will be transferred either through a *GOSUB* subroutine call or a *GOTO* branch to the line number in the list of line numbers corresponding to the ordinal value of the evaluated expr. The first line number corresponds with an expr value of 1. "expr" must evaluate to an integer value. +**ON** *expression* **GOSUB|GOTO** *line-number1,line-number2,...* - Conditional subroutine call|branch - Program flow will be transferred either through a **GOSUB** subroutine call or a **GOTO** branch to the line number in the list of line numbers corresponding to the ordinal value of the evaluated expr. The first line number corresponds with an *expr* value of 1. *expr* must evaluate to an integer value. **PI** - Returns the value of pi From c10b62aee98094cc2ad38bb5651a6c0a01fc6999 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 6 Sep 2021 16:03:40 -0400 Subject: [PATCH 049/183] Add files via upload --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8872294..8018b39 100644 --- a/README.md +++ b/README.md @@ -791,7 +791,7 @@ be omitted to get the rest of the string. If *start-position* or *end-position* **MIN**(*expression-list*) - Returns the lowest value in *expression-list* -**ON** *expression* **GOSUB|GOTO** *line-number1,line-number2,...* - Conditional subroutine call|branch - Program flow will be transferred either through a **GOSUB** subroutine call or a **GOTO** branch to the line number in the list of line numbers corresponding to the ordinal value of the evaluated expr. The first line number corresponds with an *expr* value of 1. *expr* must evaluate to an integer value. +**ON** *expression* **GOSUB|GOTO** *line-number1,line-number2,...* - Conditional subroutine call|branch - Program flow will be transferred either through a **GOSUB** subroutine call or a **GOTO** branch to the line number in the list of line numbers corresponding to the ordinal value of the evaluated *expr*. The first line number corresponds with an *expr* value of 1. *expr* must evaluate to an integer value. **PI** - Returns the value of pi From d78139ebca7079523edab717bd4d2b1fc0550aae Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 8 Sep 2021 22:49:48 -0400 Subject: [PATCH 050/183] Delete ontest.bas --- ontest.bas | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 ontest.bas diff --git a/ontest.bas b/ontest.bas deleted file mode 100644 index 43cdf19..0000000 --- a/ontest.bas +++ /dev/null @@ -1,10 +0,0 @@ -10 INPUT "Enter 1, 2 or 3: " : I -20 ON I GOTO 100 , 200 , 300 -30 PRINT "nope!" -40 GOTO 1000 -100 PRINT "One" -110 GOTO 1000 -200 PRINT "Two" -210 GOTO 1000 -300 PRINT "Three" -1000 REM DONE From f38cc2680e196516d13c211e96283474cfb140c0 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 8 Sep 2021 22:49:58 -0400 Subject: [PATCH 051/183] Delete testloop.bas --- testloop.bas | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 testloop.bas diff --git a/testloop.bas b/testloop.bas deleted file mode 100644 index b9c7a1e..0000000 --- a/testloop.bas +++ /dev/null @@ -1,11 +0,0 @@ -5 N = 0 -6 I = 7 -10 PRINT "hello world" -20 PRINT "before loop i: " , I -100 FOR I = 1 TO 10 -110 PRINT I -115 IF I = 5 THEN GOTO 130 -120 NEXT I -130 N = N + 1 -140 IF N < 2 THEN GOTO 10 -150 PRINT "i: " , I , " n: " , N \ No newline at end of file From bea061a861b0571efce8ec867249fa70d81bf6e3 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 8 Sep 2021 22:50:27 -0400 Subject: [PATCH 052/183] Add files via upload --- basicparser.py | 250 ++++++++++++++++++++++++++++++++++++++++++------- basictoken.py | 20 +++- interpreter.py | 4 +- program.py | 125 ++++++++++++++++++++++++- regression.bas | 70 +++++++++++--- 5 files changed, 415 insertions(+), 54 deletions(-) diff --git a/basicparser.py b/basicparser.py index 3191c25..3b5a7ee 100644 --- a/basicparser.py +++ b/basicparser.py @@ -67,7 +67,7 @@ def pretty_print(self): """ class BASICParser: - def __init__(self): + def __init__(self, basicdata): # Symbol table to hold variable names mapped # to values self.__symbol_table = {} @@ -76,7 +76,9 @@ def __init__(self): # when evaluating expressions self.__operand_stack = [] - # List to hold contents of DATA statement + # BasicDATA structure containing program DATA Statements + self.__data = basicdata + # List to hold values read from DATA statements self.__data_values = [] # These values will be @@ -85,9 +87,13 @@ def __init__(self): self.__tokenlist = [] self.__tokenindex = None - # Set to keep track of extant loop variables + # Previous flowsignal used to determine initializion of + # loop variable self.last_flowsignal = None + #file handle list + self.__file_handles = {} + def parse(self, tokenlist, line_number): """Must be initialised with the list of BTokens to be processed. These tokens @@ -199,6 +205,21 @@ def __simplestmt(self): self.__readstmt() return None + elif self.__token.category == Token.RESTORE: + self.__restorestmt() + return None + + elif self.__token.category == Token.OPEN: + return self.__openstmt() + + elif self.__token.category == Token.CLOSE: + self.__closestmt() + return None + + elif self.__token.category == Token.FSEEK: + self.__fseekstmt() + return None + else: # Ignore comments, but raise an error # for anything else @@ -215,18 +236,45 @@ def __printstmt(self): """ self.__advance() # Advance past PRINT token + fileIO = False + if self.__token.category == Token.HASH: + fileIO = True + + # Process the # keyword + self.__consume(Token.HASH) + + # Acquire the file number + self.__expr() + filenum = self.__operand_stack.pop() + + if self.__file_handles.get(filenum) == None: + raise RuntimeError("PRINT: file #"+str(filenum)+" not opened in line " + str(self.__line_number)) + + # Process the comma + if self.__tokenindex < len(self.__tokenlist) and self.__token.category != Token.COLON: + self.__consume(Token.COMMA) + # Check there are items to print if not self.__tokenindex >= len(self.__tokenlist): self.__logexpr() - print(self.__operand_stack.pop(), end='') + if fileIO: + self.__file_handles[filenum].write('%s' %(self.__operand_stack.pop())) + else: + print(self.__operand_stack.pop(), end='') while self.__token.category == Token.COMMA: self.__advance() self.__logexpr() - print(self.__operand_stack.pop(), end='') + if fileIO: + self.__file_handles[filenum].write('%s' %(self.__operand_stack.pop())) + else: + print(self.__operand_stack.pop(), end='') # Final newline - print() + if fileIO: + self.__file_handles[filenum].write("\n") + else: + print() def __letstmt(self): """Parses a LET statement, @@ -276,6 +324,10 @@ def __stopstmt(self): self.__advance() # Advance past STOP token + for handles in self.__file_handles: + self.__file_handles[handles].close() + self.__file_handles.clear() + return FlowSignal(ftype=FlowSignal.STOP) def __assignmentstmt(self): @@ -408,6 +460,122 @@ def __arrayassignmentstmt(self, name): raise IndexError('Array index out of range in line ' + str(self.__line_number)) + def __openstmt(self): + """Parses an open statement, opens the indicated file and + places the file handle into handle table + """ + + self.__advance() # Advance past OPEN token + + # Acquire the filename + self.__logexpr() + filename = self.__operand_stack.pop() + + # Process the FOR keyword + self.__consume(Token.FOR) + + if self.__token.category == Token.INPUT: + accessMode = "r" + elif self.__token.category == Token.APPEND: + accessMode = "r+" + elif self.__token.category == Token.OUTPUT: + accessMode = "w+" + else: + raise SyntaxError('Invalid Open access mode in line ' + str(self.__line_number)) + + self.__advance() # Advance past acess type + + if self.__token.lexeme != "AS": + raise SyntaxError('Expecting AS in line ' + str(self.__line_number)) + + self.__advance() # Advance past AS keyword + + # Process the # keyword + self.__consume(Token.HASH) + + # Acquire the file number + self.__expr() + filenum = self.__operand_stack.pop() + + branchOnError = False + if self.__token.category == Token.ELSE: + branchOnError = True + self.__advance() # Advance past ELSE + + if self.__token.category == Token.GOTO: + self.__advance() # Advance past optional GOTO + + self.__expr() + + if self.__file_handles.get(filenum) != None: + if branchOnError: + return FlowSignal(ftarget=self.__operand_stack.pop()) + else: + raise RuntimeError("File #",filenum," already opened in line " + str(self.__line_number)) + + try: + self.__file_handles[filenum] = open(filename,accessMode) + + except: + if branchOnError: + return FlowSignal(ftarget=self.__operand_stack.pop()) + else: + raise RuntimeError('File '+filename+' could not be opened in line ' + str(self.__line_number)) + + if accessMode == "r+": + self.__file_handles[filenum].seek(0) + filelen = 0 + for lines in self.__file_handles[filenum]: + filelen += (len(lines)+(0 if implementation.name.upper() == 'MICROPYTHON' else 1)) + + self.__file_handles[filenum].seek(filelen) + + return None + + def __closestmt(self): + """Parses a close, closes the file and removes + the file handle from the handle table + """ + + self.__advance() # Advance past CLOSE token + + # Process the # keyword + self.__consume(Token.HASH) + + # Acquire the file number + self.__expr() + filenum = self.__operand_stack.pop() + + if self.__file_handles.get(filenum) == None: + raise RuntimeError("CLOSE: file #"+str(filenum)+" not opened in line " + str(self.__line_number)) + + self.__file_handles[filenum].close() + self.__file_handles.pop(filenum) + + def __fseekstmt(self): + """Parses an fseek statement, seeks the indicated file position + """ + + self.__advance() # Advance past FSEEK token + + # Process the # keyword + self.__consume(Token.HASH) + + # Acquire the file number + self.__expr() + filenum = self.__operand_stack.pop() + + if self.__file_handles.get(filenum) == None: + raise RuntimeError("FSEEK: file #"+str(filenum)+" not opened in line " + str(self.__line_number)) + + # Process the comma + self.__consume(Token.COMMA) + + # Acquire the file position + self.__expr() + + self.__file_handles[filenum].seek(self.__operand_stack.pop()) + def __inputstmt(self): """Parses an input statement, extracts the input from the user and places the values into the @@ -416,8 +584,29 @@ def __inputstmt(self): """ self.__advance() # Advance past INPUT token + fileIO = False + if self.__token.category == Token.HASH: + fileIO = True + + # Process the # keyword + self.__consume(Token.HASH) + + # Acquire the file number + self.__expr() + filenum = self.__operand_stack.pop() + + if self.__file_handles.get(filenum) == None: + raise RuntimeError("INPUT: file #"+str(filenum)+" not opened in line " + str(self.__line_number)) + + # Process the comma + self.__consume(Token.COMMA) + prompt = '? ' if self.__token.category == Token.STRING: + if fileIO: + raise SyntaxError('Input prompt specified for file I/O ' + + 'in line ' + str(self.__line_number)) + # Acquire the input prompt self.__logexpr() prompt = self.__operand_stack.pop() @@ -440,7 +629,11 @@ def __inputstmt(self): valid_input = False while not valid_input: # Gather input from the user into the variables - inputvals = input(prompt).split(',', (len(variables)-1)) + if fileIO: + inputvals = ((self.__file_handles[filenum].readline().replace("\n","")).replace("\r","")).split(',', (len(variables)-1)) + valid_input = True + else: + inputvals = input(prompt).split(',', (len(variables)-1)) for variable in variables: left = variable @@ -463,30 +656,30 @@ def __inputstmt(self): valid_input = True except ValueError: - valid_input = False + if not fileIO: + valid_input = False print('Non-numeric input provided to a numeric variable - redo from start') break except IndexError: # No more input to process - valid_input = False + if not fileIO: + valid_input = False print('Not enough values input - redo from start') break - def __datastmt(self): - """Parses a DATA statement""" + def __restorestmt(self): - self.__advance() # Advance past DATA token + self.__advance() # Advance past RESTORE token - # Acquire the comma separated values - if not self.__tokenindex >= len(self.__tokenlist): - self.__expr() - self.__data_values.append(self.__operand_stack.pop()) + # Acquire the line number + self.__expr() - while self.__token.category == Token.COMMA: - self.__advance() # Advance past comma - self.__expr() - self.__data_values.append(self.__operand_stack.pop()) + self.__data_values.clear() + self.__data.restore(self.__operand_stack.pop()) + + def __datastmt(self): + """Parses a DATA statement""" def __readstmt(self): """Parses a READ statement.""" @@ -504,14 +697,12 @@ def __readstmt(self): variables.append(self.__token.lexeme) self.__advance() # Advance past variable - # Check that we have enough data values to fill the - # variables - if len(variables) > len(self.__data_values): - raise RuntimeError('Insufficient constants supplied to READ ' + - 'in line ' + str(self.__line_number)) - # Gather input from the DATA statement into the variables for variable in variables: + + if len(self.__data_values) < 1: + self.__data_values = self.__data.readData(self.__line_number) + left = variable right = self.__data_values.pop(0) @@ -526,11 +717,6 @@ def __readstmt(self): elif not left.endswith('$'): try: - #if '.' in right: - # self.__symbol_table[left] = float(right) - #else: - # self.__symbol_table[left] = int(right) - numeric = float(right) if numeric.is_integer(): numeric = int(numeric) @@ -1339,4 +1525,4 @@ def __randomizestmt(self): random.seed(seed) else: - random.seed(int(monotonic())) + random.seed(int(monotonic())) \ No newline at end of file diff --git a/basictoken.py b/basictoken.py index 873946c..15b74d3 100644 --- a/basictoken.py +++ b/basictoken.py @@ -108,6 +108,13 @@ class BASICToken: NOT = 75 # NOT operator PI = 76 # PI constant RNDINT = 77 # RNDINT function + OPEN = 78 # OPEN keyword + HASH = 79 # "#" + CLOSE = 80 # CLOSE keyword + FSEEK = 81 # FSEEK keyword + RESTORE = 82 # RESTORE keyword + APPEND = 83 # APPEND keyword + OUTPUT = 84 # OUTPUT keyword # Displayable names for each token category catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN', @@ -124,14 +131,16 @@ class BASICToken: 'CHR', 'ASC', 'STR', 'MID', 'MODULO', 'TERNARY', 'VAL', 'LEN', 'UPPER', 'LOWER', 'ROUND', 'MAX', 'MIN', 'INSTR', 'AND', 'OR', 'NOT', 'PI', - 'RNDINT'] + 'RNDINT', 'OPEN', 'HASH', 'CLOSE', 'FSEEK', 'APPEND', + 'OUTPUT', 'RESTORE'] smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN, '+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE, '\n': NEWLINE, '<': LESSER, '>': GREATER, '<>': NOTEQUAL, '<=': LESSEQUAL, '>=': GREATEQUAL, ',': COMMA, - ':': COLON, '%': MODULO, '!=': NOTEQUAL} + ':': COLON, '%': MODULO, '!=': NOTEQUAL, '#': HASH} + # Dictionary of BASIC reserved words keywords = {'LET': LET, 'LIST': LIST, 'PRINT': PRINT, @@ -155,7 +164,11 @@ class BASICToken: 'ROUND': ROUND, 'MAX': MAX, 'MIN': MIN, 'INSTR': INSTR, 'END': STOP, 'AND': AND, 'OR': OR, 'NOT': NOT, - 'PI': PI, 'RNDINT': RNDINT} + 'PI': PI, 'RNDINT': RNDINT, 'OPEN': OPEN, + 'CLOSE': CLOSE, 'FSEEK': FSEEK, + 'APPEND': APPEND, 'OUTPUT':OUTPUT, + 'RESTORE': RESTORE} + # Functions functions = {ABS, ATN, COS, EXP, INT, LOG, POW, RND, SIN, SQR, TAN, @@ -178,4 +191,3 @@ def pretty_print(self): def print_lexeme(self): print(self.lexeme, end=' ') - diff --git a/interpreter.py b/interpreter.py index a13f405..d81814d 100644 --- a/interpreter.py +++ b/interpreter.py @@ -34,7 +34,7 @@ def main(): banner = ( """ PPPP Y Y BBBB AAA SSSS I CCC - P P Y Y B B A A S I C + P P Y Y B B A A S I C P P Y Y B B A A S I C PPPP Y BBBB AAAAA SSSS I C P Y B B A A S I C @@ -133,4 +133,4 @@ def main(): if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/program.py b/program.py index 4161381..50d82aa 100644 --- a/program.py +++ b/program.py @@ -27,6 +27,115 @@ from lexer import Lexer +class BASICData: + + def __init__(self): + # array of line numbers to represent data statements + self.__datastmts = {} + + # Data pointer + self.__next_data = 0 + + + def delete(self): + self.__datastmts.clear() + self.__next_data = 0 + + def delData(self,line_number): + if self.__datastmts.get(line_number) != None: + del self.__datastmts[line_number] + + def addData(self,line_number,tokenlist): + """ + Adds the supplied token list + to the program's DATA store. If a token list with the + same line number already exists, this is + replaced. + + line_number: Basic program line number of DATA statement + + """ + + try: + self.__datastmts[line_number] = tokenlist + + except TypeError as err: + raise TypeError("Invalid line number: " + str(err)) + + + def getTokens(self,line_number): + """ + returns the tokens from the program DATA statement + + line_number: Basic program line number of DATA statement + + """ + + return self.__datastmts.get(line_number) + + def readData(self,read_line_number): + + if len(self.__datastmts) == 0: + raise RuntimeError('No DATA statements available to READ ' + + 'in line ' + str(read_line_number)) + + data_values = [] + + line_numbers = list(self.__datastmts.keys()) + line_numbers.sort() + + if self.__next_data == 0: + self.__next_data = line_numbers[0] + elif line_numbers.index(self.__next_data) < len(line_numbers)-1: + self.__next_data = line_numbers[line_numbers.index(self.__next_data)+1] + else: + raise RuntimeError('No DATA statements available to READ ' + + 'in line ' + str(read_line_number)) + + tokenlist = self.__datastmts[self.__next_data] + + sign = 1 + for token in tokenlist[1:]: + if token.category != Token.COMMA: + #data_values.append(token.lexeme) + + if token.category == Token.STRING: + data_values.append(token.lexeme) + elif token.category == Token.UNSIGNEDINT: + data_values.append(sign*int(token.lexeme)) + elif token.category == Token.UNSIGNEDFLOAT: + data_values.append(sign*eval(token.lexeme)) + elif token.category == Token.MINUS: + sign = -1 + #else: + #data_values.append(token.lexeme) + else: + sign = 1 + + + return data_values + + def restore(self,restoreLineNo): + if restoreLineNo == 0 or restoreLineNo in self.__datastmts: + + if restoreLineNo == 0: + self.__next_data = restoreLineNo + else: + + line_numbers = list(self.__datastmts.keys()) + line_numbers.sort() + + indexln = line_numbers.index(restoreLineNo) + + if indexln == 0: + self.__next_data = 0 + else: + self.__next_data = line_numbers[indexln-1] + else: + raise RuntimeError('Attempt to RESTORE but no DATA ' + + 'statement at line ' + str(restoreLineNo)) + + class Program: def __init__(self): @@ -41,6 +150,9 @@ def __init__(self): # and loop returns self.__return_stack = [] + # Setup DATA object + self.__data = BASICData() + def __str__(self): program_text = "" @@ -55,6 +167,8 @@ def str_statement(self, line_number): line_text = str(line_number) + " " statement = self.__program[line_number] + if statement[0].category == Token.DATA: + statement = self.__data.getTokens(line_number) for token in statement: # Add in quotes for strings if token.category == Token.STRING: @@ -130,7 +244,11 @@ def add_stmt(self, tokenlist): """ try: line_number = int(tokenlist[0].lexeme) - self.__program[line_number] = tokenlist[1:] + if tokenlist[1].lexeme == "DATA": + self.__data.addData(line_number,tokenlist[1:]) + self.__program[line_number] = [tokenlist[1],] + else: + self.__program[line_number] = tokenlist[1:] except TypeError as err: raise TypeError("Invalid line number: " + @@ -174,7 +292,8 @@ def __execute(self, line_number): def execute(self): """Execute the program""" - self.__parser = BASICParser() + self.__parser = BASICParser(self.__data) + self.__data.restore(0) # reset data pointer line_numbers = self.line_numbers() @@ -323,6 +442,7 @@ def execute(self): def delete(self): """Deletes the program by emptying the dictionary""" self.__program.clear() + self.__data.delete() def delete_statement(self, line_number): """Deletes a statement from the program with @@ -331,6 +451,7 @@ def delete_statement(self, line_number): :param line_number: The line number to be deleted """ + self.__data.delData(line_number) try: del self.__program[line_number] diff --git a/regression.bas b/regression.bas index 1bf56d7..b842c91 100644 --- a/regression.bas +++ b/regression.bas @@ -21,10 +21,10 @@ 210 PRINT "Should not print this line" 220 PRINT "*** Testing subroutine behaviour ***" 230 PRINT "Calling subroutine" -240 GOSUB 540 +240 GOSUB 1630 250 PRINT "Exited subroutine" 260 PRINT "Now testing nested subroutines" -270 GOSUB 600 +270 GOSUB 1660 280 PRINT "*** Testing loops ***" 290 PRINT "This loop should count to 5 in increments of 1:" 300 FOR I = 1 TO 5 @@ -49,15 +49,57 @@ 490 NEXT I 500 PRINT "This should print 555" 510 PRINT A(0, 0), A(1, 1), A(2, 2) -520 PRINT "*** Finished ***" -530 STOP -540 REM A SUBROUTINE TEST -550 PRINT "Executing the subroutine" -560 RETURN -600 REM AN OUTER SUBROUTINE -610 GOSUB 700 -620 PRINT "This should be printed second" -630 RETURN -700 REM A NESTED SUBROUTINE -710 PRINT "This should be printed first" -720 RETURN +520 PRINT "*** Testing file i/o ***" +530 OPEN "REGRESSION.TXT" FOR OUTPUT AS #1 +540 PRINT #1,"0123456789Hello World!" +550 CLOSE #1 +560 OPEN "REGRESSION.TXT" FOR INPUT AS #2 +570 PRINT "The next line should say 'Hello World!'" +580 FSEEK #2,10 +590 INPUT #2,A$ +600 PRINT A$ +620 N = 0 +630 I = 7 +640 PRINT "This loop should count to 5 in increments of 1 twice:" +650 FOR I = 1 TO 10 +660 PRINT I +670 IF I = 5 THEN GOTO 690 +680 NEXT I +690 N = N + 1 +700 IF N < 2 THEN GOTO 650 +810 PRINT "The loop variable I should be equal to 5, I=",I +815 DATA "DATA Statement tests..." +820 READ A$ +825 PRINT "The next line should read: DATA Statement tests..." +830 PRINT A$ +840 DATA 1 , 2 , 3 +850 DATA 4 , 5 , 6 +855 DATA 1.5 , 2 , "test" +858 PRINT "The next three lines should be: 12 34 56" +860 FOR I = 1 TO 3 +870 READ J , K +880 PRINT J , K +890 NEXT I +900 RESTORE 840 +910 READ I , J , K +920 PRINT "the next line should print 123" +930 PRINT I , J , K +970 RESTORE 855 +980 READ A1 , B , C$ +990 PRINT "Float: " , A1 , " Int: " , B , " String: " , C$ +1000 RESTORE 850 +1010 READ I , J , K , L +1020 PRINT "the next line should print 4561.5:" +1030 PRINT I , J , K , L +1610 PRINT "*** Finished ***" +1620 STOP +1630 REM A SUBROUTINE TEST +1640 PRINT "Executing the subroutine" +1650 RETURN +1660 REM AN OUTER SUBROUTINE +1670 GOSUB 1700 +1680 PRINT "This should be printed second" +1690 RETURN +1700 REM A NESTED SUBROUTINE +1710 PRINT "This should be printed first" +1720 RETURN From 66ca8bdb5e69d52629bf48c13aa7e1ba2853a576 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Thu, 9 Sep 2021 00:30:43 -0400 Subject: [PATCH 053/183] Updates for recent language changes --- factorial.bas | 7 ++++--- rock_scissors_paper.bas | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/factorial.bas b/factorial.bas index 1e288e0..fcd0da6 100644 --- a/factorial.bas +++ b/factorial.bas @@ -1,8 +1,9 @@ 10 REM A SHORT PROGRAM TO CALCULATE FACTORIAL OF 20 REM SUPPLIED NUMBER N 30 INPUT "Please provide N: ": N -40 OUTPUT = 1 +35 REM OUTPUT IS NOW A RESERVED KEYWORD +40 OUTPUTVAL = 1 50 FOR I = 1 TO N -60 OUTPUT = OUTPUT * I +60 OUTPUTVAL = OUTPUTVAL * I 70 NEXT I -80 PRINT "Factorial of N is ", OUTPUT +80 PRINT "Factorial of N is ", OUTPUTVAL diff --git a/rock_scissors_paper.bas b/rock_scissors_paper.bas index f6b7d04..f8d64e8 100644 --- a/rock_scissors_paper.bas +++ b/rock_scissors_paper.bas @@ -5,13 +5,14 @@ 50 YOURSCORE = 0 60 PRINT "Let's play rock-scissors-paper!" 70 INPUT "(R)ock, (S)cissors, (P)aper or e(X)it: ": YOURGUESS$ +75 IF YOURGUESS$ = "X" THEN 160 80 RANDOM = RND 90 GOSUB 190 100 PRINT "Your guess: ", YOURGUESS$, ", My guess: ", MYGUESS$ -110 ON YOURGUESS$ = "R" GOSUB 300 -120 ON YOURGUESS$ = "S" GOSUB 500 -130 ON YOURGUESS$ = "P" GOSUB 700 -140 IF YOURGUESS$ = "X" THEN 160 +110 REM ON YOURGUESS$ = "R" GOSUB 300 +120 REM ON YOURGUESS$ = "S" GOSUB 500 +130 REM ON YOURGUESS$ = "P" GOSUB 700 +135 ON INSTR("RSP",YOURGUESS$)+1 GOSUB 300,500,700 150 GOTO 70 160 PRINT "My score: ", MYSCORE, " Your score: ", YOURSCORE 170 STOP From 0aaae46dfa90edd7150b1efb0a47161b29217206 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Thu, 9 Sep 2021 17:28:20 -0400 Subject: [PATCH 054/183] Test program startrek.bas I ported the startrek program over so that it will run under the current richpl version PyBasic. I threw together a quick python script to separate the compound lines which caused a few line number bugs, but I think I found most of those. Since PyBasic doesn't yet support the trailing delimiter to suppress the end of line carriage return some of the displays are off but I did rebuild the printouts of most of the primary control displays. --- PyBStartrek.bas | 1062 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1062 insertions(+) create mode 100644 PyBStartrek.bas diff --git a/PyBStartrek.bas b/PyBStartrek.bas new file mode 100644 index 0000000..10533ee --- /dev/null +++ b/PyBStartrek.bas @@ -0,0 +1,1062 @@ +520 REM +530 REM +540 REM **** **** STAR TREK **** **** +550 REM **** Simulation of a mission of the starship ENTERPRISE +560 REM **** as seen on the Star Trek tv show +570 REM **** Original program in Creative Computing +580 REM **** Basic Computer Games by Dave Ahl +590 REM **** Modifications by Bob Fritz and Sharon Fritz +600 REM **** for the IBM Personal Computer, October-November 1981 +610 REM **** Bob Fritz, 9915 Caninito Cuadro, San Diego, Ca., 92129 +620 REM **** (714) 484-2955 +630 REM **** +640 for i = 1 to 10 +644 print +648 next i +650 E1$= " ,-----------------, _" +660 E2$= " `---------- ----',-----/ \----," +670 E3$= " | | '- --------'" +680 E4$= " ,--' '------/ /--," +690 E5$= " '-----------------'" +691 PRINT +700 E6$= " THE USS ENTERPRISE --- NCC-1701" +710 rem E8$=CHR$(15) +720 PRINT E1$ +730 PRINT E2$ +740 PRINT E3$ +750 PRINT E4$ +760 PRINT E5$ +770 PRINT +780 PRINT E6$ +790 for i = 1 to 7 +794 print +798 next i +800 rem CLEAR 600 +810 rem RANDOMIZE 120*(VAL(RIGHT$(TIME$,2)) + VAL(MID$(TIME$,3,5)) ) +811 RANDOMIZE +820 REM Z$=" " +830 GOSUB 840 +831 GOTO 960 +840 REM ------- set function keys for game ------- +850 rem KEY 1,"NAV"+CHR$(13) +860 rem KEY 2,"SRS"+CHR$(13) +870 rem KEY 3,"LRS"+CHR$(13) +880 rem KEY 4,"PHASERS"+CHR$(13) +890 rem KEY 5,"TORPEDO"+CHR$(13) +900 rem KEY 6,"SHIELDS"+CHR$(13) +910 rem KEY 7,"DAMAGE REPORT"+CHR$(13) +920 rem KEY 8,"COMPUTER"+CHR$(13) +930 rem KEY 9,"RESIGN"+CHR$(13) +940 rem KEY 10,"" +950 RETURN +960 dim D(9) +961 dim C(10,3) +962 dim K(4,4) +963 dim N(4) +965 DIM G(9,9) +966 DIM Z(9,9) +970 T=INT(RND*20+20)*100 +971 T0=T +972 T9=25+INT(RND*10) +973 D0=0 +974 E=3000 +975 E0=E +980 P=10 +981 P0=P +982 S9=200 +983 S=0 +984 B9=0 +985 K9=0 +986 X$="" +987 X0$=" is " +990 rem DEF FND(D)=SQR((K(I,1)-S1)^2+(K(I,2)-S2)^2) +1000 rem DEF FNR(R)=INT(RND(R)*7.98+1.01) +1010 REM initialize enterprise's position +1020 Q1=INT(RND*7.98+1.01) +1021 Q2=INT(RND*7.98+1.01) +1022 S1=INT(RND*7.98+1.01) +1023 S2=INT(RND*7.98+1.01) +1030 FOR I=1 TO 9 +1031 C(I,1)=0 +1032 C(I,2)=0 +1033 NEXT I +1040 C(3,1)=-1 +1041 C(2,1)=-1 +1042 C(4,1)=-1 +1043 C(4,2)=-1 +1044 C(5,2)=-1 +1045 C(6,2)=-1 +1050 C(1,2)=1 +1051 C(2,2)=1 +1052 C(6,1)=1 +1053 C(7,1)=1 +1054 C(8,1)=1 +1055 C(8,2)=1 +1056 C(9,2)=1 +1060 FOR I=1 TO 8 +1061 D(I)=0 +1062 NEXT I +1070 A1$="NAVSRSLRSPHATORSHIDAMCOMRES" +1080 REM set up what exists in galaxy +1090 REM k3=#klingons b3=#starbases s3=#stars +1100 FOR I=1 TO 8 +1101 FOR J=1 TO 8 +1102 K3=0 +1103 Z(I,J)=0 +1104 R1=RND +1110 IF R1<=0.9799999 THEN goto 1120 +1111 K3=3 +1112 K9=K9+3 +1113 GOTO 1140 +1120 IF R1<=0.95 THEN goto 1130 +1121 K3=2 +1122 K9=K9+2 +1123 GOTO 1140 +1130 IF R1<=0.8 THEN goto 1140 +1131 K3=1 +1132 K9=K9+1 +1140 B3=0 +1141 IF RND<=0.96 THEN goto 1150 +1142 B3=1 +1143 B9=B9+1 +1150 G(I,J)=K3*100+B3*10+INT(RND*7.98+1.01) +1151 NEXT J +1152 NEXT I +1153 IF K9<=T9 THEN goto 1160 +1154 T9=K9+1 +1160 IF B9<>0 THEN 1190 +1170 IF G(Q1,Q2)>=200 THEN 1180 +1171 G(Q1,Q2)=G(Q1,Q2)+100 +1172 K9=K9+1 +1180 B9=1 +1181 G(Q1,Q2)=G(Q1,Q2)+10 +1182 Q1=INT(RND*7.98+1.01) +1183 Q2=INT(RND*7.98+1.01) +1190 K7=K9 +1191 IF B9=1 THEN 1200 +1192 X$="s" +1193 X0$=" are " +1200 PRINT" Your orders are as follows: " +1210 PRINT" Destroy the ",K9," Klingon warships which have invaded" +1220 PRINT" the galaxy before they can attack Federation headquarters" +1230 PRINT" on stardate ",T0+T9,". This gives you ",T9," days. there",X0$ +1240 PRINT" ",B9," starbase",X$," in the galaxy for resupplying your ship" +1250 rem PRINT PRINT "hit any key except return when ready to accept command" +1260 rem I=RND IF INP(1)=13 THEN 1260 +1261 PRINT +1262 input "hit return when ready to accept command":i$ +1270 REM here any time new quadrant entered +1280 Z4=Q1 +1281 Z5=Q2 +1282 K3=0 +1283 B3=0 +1284 S3=0 +1285 G5=0 +1286 D4=0.5*RND +1287 Z(Q1,Q2)=G(Q1,Q2) +1290 IF Q1<1 OR Q1>8 OR Q2<1 OR Q2>8 THEN 1410 +1300 GOSUB 5040 +1301 PRINT +1302 IF T0 <>T THEN 1330 +1310 PRINT"Your mission begins with your starship located" +1320 PRINT"in the galactic quadrant, '",G2$,"'." +1321 GOTO 1340 +1330 PRINT"Now entering ",G2$," quadrant. . ." +1340 PRINT +1341 K3=INT(G(Q1,Q2)*0.01) +1342 B3=INT(G(Q1,Q2)*0.1)-10*K3 +1350 S3=G(Q1,Q2)-100*K3-10*B3 +1351 IF K3=0 THEN 1400 +1360 PRINT "COMBAT AREA!! Condition RED" +1370 rem PRINT " RED ": +1371 rem PRINT +1380 GOSUB 5290 +1381 IF S>200 THEN 1400 +1390 PRINT" SHIELDS DANGEROUSLY LOW" +1391 rem print +1392 rem PRINT SPC(53) +1400 FOR I=1 TO 3 +1401 K(I,1)=0 +1402 K(I,2)=0 +1403 NEXT I +1410 FOR I=1 TO 3 +1411 K(I,3)=0 +1412 NEXT I +1413 Q$=" "*192 +1420 REM position enterprise in quadrant, then place "k3" klingons,& +1430 REM "b3" starbases & "s3" stars elsewhere. +1440 A$="\e/" +1441 Z1=S1 +1442 Z2=S2 +1443 GOSUB 4830 +1445 IF K3<1 THEN 1470 +1450 FOR I=1 TO K3 +1451 GOSUB 4800 +1452 A$=chr$(187)+"K"+chr$(171) +1453 Z1=R1 +1454 Z2=R2 +1460 GOSUB 4830 +1461 K(I,1)=R1 +1462 K(I,2)=R2 +1463 K(I,3)=S9*(0.5+RND) +1464 NEXT I +1470 IF B3<1 THEN 1500 +1480 GOSUB 4800 +1481 A$="("+chr$(174)+")" +1482 Z1=R1 +1483 B4=R1 +1484 Z2=R2 +1485 B5=R2 +1490 GOSUB 4830 +1500 FOR I=1 TO S3 +1502 GOSUB 4800 +1503 A$=" * " +1504 Z1=R1 +1505 Z2=R2 +1506 GOSUB 4830 +1507 NEXT I +1510 GOSUB 3720 +1520 IF S+E<=10 THEN 1530 +1521 IF E>10 OR D(7)>=0 THEN 1580 +1530 PRINT"*** FATAL ERROR ***" +1531 GOSUB 5290 +1540 PRINT"You've just stranded your ship in space" +1551 PRINT"You have insufficient maneuvering energy, and shield control" +1561 PRINT"is presently incapable of cross-circuiting to engine room!!" +1571 GOTO 3480 +1580 INPUT"command ":A$ +1590 FOR I=1 TO 9 +1591 IF mid$(A$,0,3)<> MID$(A1$,3*I-3,3*I) THEN 1610 +1600 ON I GOTO 1720,1510,2440,2530,2750,3090,3180,3980,3510 +1610 NEXT I +1611 PRINT"Enter one of the following:" +1620 PRINT" NAV (to set course)" +1630 PRINT" SRS (for short range sensor scan)" +1640 PRINT" LRS (for long range sensor scan)" +1650 PRINT" PHA (to fire phasers)" +1660 PRINT" TOR (to fire photon torpedoes)" +1670 PRINT" SHI (to raise or lower shields)" +1680 PRINT" DAM (for damage control reports)" +1690 PRINT" COM (to call on library-computer)" +1700 PRINT" RES (to resign your command)" +1702 PRINT +1703 GOTO 1520 +1710 REM course control begins here +1720 INPUT"Course (1-9) ":C2$ +1721 C1=VAL(C2$) +1723 IF C1<>9 THEN 1730 +1724 C1=1 +1730 IF C1>=1 AND C1<9 THEN 1750 +1740 PRINT" Lt. Sulu reports, 'Incorrect course data, sir!'" +1741 GOTO 1520 +1750 X$="8" +1751 IF D(1)>=0 THEN 1760 +1752 X$="0.2" +1760 PRINT"Warp factor(0-",X$,") " +1761 INPUT C2$ +1762 W1=VAL(C2$) +1763 IF D(1)<0 AND W1>0.2 THEN 1810 +1770 IF W1>0 AND W1<8 THEN 1820 +1780 IF W1=0 THEN 1520 +1790 PRINT" Chief Engineer Scott reports 'The engines won't take warp ",W1,"!" +1801 GOTO 1520 +1810 PRINT"Warp engines are damaged. Maximum speed = warp 0.2" +1811 GOTO 1520 +1820 N1=INT(W1*8+0.5) +1821 IF E-N1>=0 THEN 1900 +1830 PRINT"Engineering reports 'Insufficient energy available" +1840 PRINT" for maneuvering at warp ",W1,"!'" +1850 IF S=0 THEN 1990 +1950 D(I)=min(0,D(I)+D6) +1951 IF D(I)<=-0.1 or D(I)>=0 THEN 1960 +1952 D(I)=-0.1 +1953 GOTO 1990 +1960 IF D(I)<0 THEN 1990 +1970 IF D1=1 THEN 1980 +1971 D1=1 +1972 PRINT"DAMAGE CONTROL REPORT: " +1980 PRINT " "*8 +1981 R1=I +1982 GOSUB 4890 +1983 PRINT G2$," Repair completed." +1990 NEXT I +1991 IF RND>0.2 THEN 2070 +2000 R1=INT(RND*7.98+1.01) +2001 IF RND>=0.6 THEN 2040 +2010 IF K3=0 THEN 2070 +2020 D(R1)=D(R1)-(RND*5+1) +2021 PRINT"DAMAGE CONTROL REPORT: " +2030 GOSUB 4890 +2031 PRINT G2$," damaged" +2032 PRINT +2033 GOTO 2070 +2040 D(R1)=min(0,D(R1)+RND*3+1) +2041 PRINT"DAMAGE CONTROL REPORT: " +2050 GOSUB 4890 +2051 PRINT G2$," State of repair improved" +2052 PRINT +2060 REM begin moving starship +2070 A$=" " +2071 Z1=INT(S1) +2072 Z2=INT(S2) +2073 GOSUB 4830 +2078 Z1=INT(C1) +2079 C1=C1-Z1 +2080 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1 +2081 X=S1 +2082 Y=S2 +2090 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1 +2091 Q4=Q1 +2092 Q5=Q2 +2100 FOR I=1 TO N1 +2101 S1=S1+X1 +2102 S2=S2+X2 +2103 IF S1<1 OR S1>=9 OR S2<1 OR S2>=9 THEN 2220 +2110 S8=INT(S1)*24+INT(S2)*3-26 +2111 IF MID$(Q$,S8,S8+2)=" " THEN 2140 +2120 S1=INT(S1-X1) +2121 S2=INT(S2-X2) +2122 PRINT"Warp engines shut down at sector ",S1,",",S2," due to bad navigation." +2131 GOTO 2150 +2140 NEXT I +2141 S1=INT(S1) +2142 S2=INT(S2) +2150 A$="\e/" +2160 Z1=INT(S1) +2161 Z2=INT(S2) +2162 GOSUB 4830 +2163 GOSUB 2390 +2164 T8=1 +2170 IF W1>=1 THEN 2180 +2171 T8=0.1*INT(10*W1) +2180 T=T+T8 +2181 IF T>T0+T9 THEN 3480 +2190 REM see if docked then get command +2200 GOTO 1510 +2210 REM exceeded quadrant limits +2220 X=8*Q1+X+N1*X1 +2221 Y=8*Q2+Y+N1*X2 +2222 Q1=INT(X/8) +2223 Q2=INT(Y/8) +2224 S1=INT(X-Q1*8) +2230 S2=INT(Y-Q2*8) +2231 IF S1<>0 THEN 2240 +2232 Q1=Q1-1 +2233 S1=8 +2240 IF S2<>0 THEN 2249 +2241 Q2=Q2-1 +2242 S2=8 +2249 X5=0 +2250 IF Q1>=1 THEN 2260 +2251 X5=1 +2252 Q1=1 +2253 S1=1 +2260 IF Q1<=8 THEN 2270 +2261 X5=1 +2262 Q1=8 +2263 S1=8 +2270 IF Q2>=1 THEN 2280 +2271 X5=1 +2272 Q2=1 +2273 S2=1 +2280 IF Q2<=8 THEN 2290 +2281 X5=1 +2282 Q2=8 +2283 S2=8 +2290 IF X5=0 THEN 2360 +2300 PRINT"Lt. Uhura reports message from Starfleet Command:" +2310 PRINT" 'Permission to attempt crossing of galactic perimeter" +2320 PRINT" is hereby *DENIED*. Shut down your engines.'" +2330 PRINT"Chief Engineer Scott reports 'Warp engines shut down" +2340 PRINT" at sector ",S1,",",S2," of quadrant ",Q1,",",Q2".'" +2350 IF T>T0 THEN 3480 +2360 IF 8*Q1+Q2=8*Q4+Q5 THEN 2150 +2370 T=T+1 +2371 GOSUB 2390 +2372 GOTO 1280 +2380 REM maneuver energy s/r ** +2390 E=E-N1-10 +2391 IF E<=0 THEN 2400 +2392 RETURN +2400 PRINT"Shield control supplies energy to complete the maneuver." +2410 S=S+E +2411 E=0 +2412 IF S<=0 THEN 2420 +2413 S=0 +2420 RETURN +2430 REM long range sensor scan code +2440 IF D(3)>=0 THEN 2450 +2441 PRINT"Long Range Sensors are inoperable" +2442 GOTO 1520 +2450 PRINT"Long Range Scan for quadrant ",Q1,",",Q2 +2460 PRINT "-"*19 +2461 LINE$ = "" +2470 FOR I=Q1-1 TO Q1+1 +2471 N(1)=-1 +2472 N(2)=-2 +2473 N(3)=-3 +2474 FOR J=Q2-1 TO Q2+1 +2480 IF I<=0 or I>=9 or J<=0 or J>=9 THEN 2490 +2481 N(J-Q2+2)=G(I,J) +2482 REM added so long range sensor scans are added to computer database +2483 z(i,j)=g(i,j) +2490 NEXT J +2491 FOR L=1 TO 3 +2492 REM PRINT"| " +2493 LINE$ = LINE$ + "| " +2494 IF N(L)>=0 THEN 2500 +2495 REM PRINT"*** " +2496 LINE$ = LINE$ + "*** " +2497 GOTO 2510 +2500 REM PRINT mid$(STR$(N(L)+1000),1,4)," " +2501 LINE$ = LINE$ + mid$(STR$(N(L)+1000),1,4) + " " +2510 NEXT L +2511 PRINT LINE$ + "|" +2512 PRINT "-"*19 +2513 LINE$ = "" +2514 NEXT I +2515 GOTO 1520 +2520 REM phaser control code begins here +2530 IF D(4)>=0 THEN 2540 +2531 PRINT"Phasers Inoperative" +2532 GOTO 1520 +2540 IF K3>0 THEN 2570 +2550 PRINT"Science Officer Spock reports 'Sensors show no enemy ships" +2560 PRINT" in this quadrant'" +2561 GOTO 1520 +2570 IF D(8)>=0 THEN 2580 +2571 PRINT"Computer failure hampers accuracy" +2580 PRINT"Phasers locked on target " +2590 PRINT"Energy available = ",E," units" +2600 INPUT"Numbers of units to fire ":X +2601 IF X<=0 THEN 1520 +2610 IF E-X<0 THEN 2590 +2620 E=E-X +2621 GOSUB 5420 +2622 IF D(7)<0 THEN 2630 +2623 X=X*RND +2624 rem print "Energy * Rnd: ",x +2630 H1=INT(X/K3) +2631 FOR I=1 TO 3 +2632 IF K(I,3)<=0 THEN 2730 +2640 KSQ1 = (K(I,1)-S1)*(K(I,1)-S1) +2641 KSQ2 = (K(I,2)-S2)*(K(I,2)-S2) +2642 H= SQR( KSQ1 + KSQ2 ) +2643 rem print "distance to enemy #",i,": ",H +2646 H = H1 / H +2647 H = INT(H * (rnd+2)) +2648 IF H>0.15*K(I,3) THEN 2660 +2650 PRINT"Sensors show no damage to enemy at ",K(I,1),",",K(I,2) +2651 GOTO 2730 +2660 K(I,3)=K(I,3)-H +2661 PRINT H,"Unit hit on Klingon at sector ",K(I,1),"," +2670 PRINT K(I,2) +2671 IF K(I,3)> 0 THEN GOTO 2700 +2680 PRINT "**** KLINGON DESTROYED ****" +2690 GOTO 2710 +2700 PRINT" (Sensors show ",K(I,3)," units remaining)" +2701 GOTO 2730 +2710 K3=K3-1 +2711 K9=K9-1 +2712 Z1=K(I,1) +2713 Z2=K(I,2) +2714 A$=" " +2715 GOSUB 4830 +2720 K(I,3)=0 +2721 G(Q1,Q2)=G(Q1,Q2)-100 +2722 Z(Q1,Q2)=G(Q1,Q2) +2723 IF K9<=0 THEN 3680 +2730 NEXT I +2731 GOSUB 3350 +2732 GOTO 1520 +2740 REM photon torpedo code begins here +2750 IF P>0 THEN 2760 +2751 PRINT"All photon torpedoes expended" +2752 GOTO 1520 +2760 IF D(5)>=0 THEN 2770 +2761 PRINT"Photon tubes are not operational" +2762 GOTO 1520 +2770 INPUT"Photon torpedo course (1-9) ":C2$ +2771 C1=VAL(C2$) +2772 IF C1<>9 THEN 2780 +2773 C1=1 +2780 IF C1>=1 AND C1<9 THEN 2810 +2790 PRINT"Ensign Chekov reports, 'Incorrect course data, sir!'" +2800 GOTO 1520 +2810 Z1=INT(C1) +2811 C1=C1-Z1 +2812 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1 +2813 E=E-2 +2814 P=P-1 +2820 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1 +2821 X=S1 +2822 Y=S2 +2823 GOSUB 5360 +2830 PRINT"Torpedo track:" +2840 X=X+X1 +2841 Y=Y+X2 +2842 X3=INT(X+0.5) +2843 Y3=INT(Y+0.5) +2850 IF X3<1 OR X3>8 OR Y3<1 OR Y3>8 THEN 3070 +2860 PRINT" ",X3,",",Y3 +2861 A$=" " +2862 Z1=X +2863 Z2=Y +2864 GOSUB 4990 +2870 IF Z3<>0 THEN 2840 +2880 A$=chr$(187)+"K"+chr$(171) +2881 Z1=X +2882 Z2=Y +2883 GOSUB 4990 +2884 IF Z3=0 THEN 2940 +2890 PRINT"**** KLINGON DESTROYED ****" +2900 K3=K3-1 +2901 K9=K9-1 +2902 IF K9<=0 THEN 3680 +2910 FOR I=1 TO 3 +2911 IF X3=K(I,1) AND Y3=K(I,2) THEN 2930 +2920 NEXT I +2921 I=3 +2930 K(I,3)=0 +2931 GOTO 3050 +2940 A$=" * " +2941 Z1=X +2942 Z2=Y +2943 GOSUB 4990 +2944 IF Z3=0 THEN 2960 +2950 PRINT"Star at ",X3,",",Y3," absorbed torpedo energy." +2951 GOSUB 3350 +2952 GOTO 1520 +2960 A$="("+chr$(174)+")" +2961 Z1=X +2962 Z2=Y +2963 GOSUB 4990 +2964 IF Z3<>0 THEN 2970 +2965 PRINT "Torpedo absorbed by unknown object at ",x3,",",y3 +2966 goto 1520 +2970 PRINT"*** STARBASE DESTROYED ***" +2980 B3=B3-1 +2981 B9=B9-1 +2990 IF B9>0 OR K9>T-T0-T9 THEN 3030 +3000 PRINT"THAT DOES IT, CAPTAIN!! You are hereby relieved of command" +3010 PRINT"and sentenced to 99 stardates of hard labor on CYGNUS 12!!" +3020 GOTO 3510 +3030 PRINT"Starfleet reviewing your record to consider" +3040 PRINT"court martial!" +3041 D0=0 +3050 Z1=X +3051 Z2=Y +3052 A$=" " +3053 GOSUB 4830 +3060 G(Q1,Q2)=K3*100+B3*10+S3 +3061 Z(Q1,Q2)=G(Q1,Q2) +3062 GOSUB 3350 +3063 GOTO 1520 +3070 PRINT"Torpedo missed" +3071 GOSUB 3350 +3072 GOTO 1520 +3080 REM shield control +3090 IF D(7)>=0 THEN 3100 +3091 PRINT"Shield control inoperable" +3092 GOTO 1520 +3100 PRINT"Energy available = ",E+S +3101 INPUT "Number of units to shields? ":X +3110 IF X>=0 and S<>X THEN 3120 +3111 PRINT"" +3112 GOTO 1520 +3120 IF X" +3141 goto 1990 +3150 E=E+S-X +3151 S=X +3152 PRINT "Deflector Control Room report" +3160 PRINT" 'Shields now at ",INT(S)," units per your command.'" +3161 GOTO 1520 +3170 REM damage control +3180 IF D(6)>=0 THEN 3290 +3190 PRINT"Damage control report not available" +3191 IF D0=0 THEN 1520 +3200 D3=0 +3201 FOR I=1 TO 8 +3202 IF D(I)>=0 THEN 3210 +3203 D3=D3+1 +3210 NEXT I +3211 IF D3=0 THEN 1520 +3220 PRINT +3221 D3=D3+D4 +3222 IF D3<1 THEN 3230 +3223 D3=0.9 +3230 PRINT"Technicians standing by to effect repairs to your ship:" +3240 PRINT"estimated time to repair: ",0.01*INT(100*D3)," stardates" +3250 INPUT"Will you authorize the repair order (Y/N)? ":A$ +3260 IF A$<>"y" AND A$<> "Y" THEN 1520 +3270 FOR I=1 TO 8 +3271 IF D(I)>=0 THEN 3280 +3272 D(I)=0 +3280 NEXT I +3281 T=T+D3+0.1 +3290 PRINT +3291 PRINT"Device state of repair" +3292 FOR R1=1 TO 8 +3300 GOSUB 4890 +3301 PRINT G2$ +3310 GG2=INT(D(R1)*100)*0.01 +3311 PRINT GG2 +3320 NEXT R1 +3321 PRINT +3322 IF D0<>0 THEN 3200 +3330 GOTO 1520 +3340 REM klingons shooting +3350 IF K3>0 THEN 3360 +3351 RETURN +3360 IF D0=0 THEN 3370 +3361 PRINT"Starbase shields protect the ENTERPRISE" +3362 RETURN +3370 FOR I=1 TO 3 +3371 IF K(I,3)<=0 THEN 3460 +3380 ksq1 = (K(I,1)-S1)*(K(I,1)-S1) +3381 ksq2 = (K(I,2)-S2)*(K(I,2)-S2) +3382 H=( K(I,3) / SQR( ksq1 + ksq2 ) )*(2+RND) +3383 h = int(h) +3384 S=S-H +3385 K(I,3)=K(I,3)/(3+RND) +3390 PRINT "ENTERPRISE HIT!" +3400 GOSUB 5480 +3401 PRINT H," Unit hit on ENTERPRISE from sector ",K(I,1),",",K(I,2) +3410 IF S<=0 THEN 3490 +3420 PRINT" " +3421 IF H<20 THEN 3460 +3430 IF RND>0.6 OR H/S<=0.02 THEN 3460 +3440 R1=INT(RND*7.98+1.01) +3441 D(R1)=D(R1)-H/S-0.5*RND +3442 GOSUB 4890 +3450 PRINT"Damage control reports '",G2$," damaged by the hit'" +3460 NEXT I +3461 RETURN +3470 REM end of game +3480 PRINT"It is stardate",T +3481 GOTO 3510 +3490 PRINT +3491 PRINT"the ENTERPRISE has been destroyed. The Federation will be conquered" +3501 GOTO 3480 +3510 PRINT"There were ",K9," Klingon battle cruisers left at" +3520 PRINT"the end of your mission" +3530 PRINT +3531 PRINT +3532 IF B9=0 THEN 3670 +3540 PRINT"The Federation is in need of a new starship commander" +3550 PRINT"for a similar mission -- if there is a volunteer," +3560 INPUT"let him or her step forward and enter 'AYE' ":X$ +3561 IF X$="AYE" THEN 520 +3570 rem KEY 1,"LIST " +3580 rem KEY 2,"RUN"+CHR$(13) +3590 rem KEY 3,"LOAD"+CHR$(34) +3600 rem KEY 4, "SAVE"+CHR$(34) +3610 rem KEY 5, "CONT"+CHR$(13) +3620 rem KEY 6,","+CHR$(34)+"LPT1:"+CHR$(34)+CHR$(13) +3630 rem KEY 7, "TRON"+CHR$(13) +3640 rem KEY 8, "TROFF"+CHR$(13) +3650 rem KEY 9, "KEY " +3660 rem KEY 10,"SCREEN 0,0,0"+CHR$(13) +3670 END +3680 PRINT"Congratulations, Captain! the last Klingon battle cruiser" +3690 PRINT"menacing the Federation has been destroyed." +3691 PRINT +3700 cc1 = k7/(t-t0) +3701 PRINT"Your efficiency rating is ",1000*cc1*cc1 +3703 GOTO 3530 +3710 REM short range sensor scan & startup subroutine +3720 A$="("+chr$(174)+")" +3721 Z3=0 +3722 FOR I=S1-1 TO S1+1 +3723 FOR J=S2-1 TO S2+1 +3730 IF INT(I+0.5)<1 OR INT(I+0.5)>8 OR INT(J+0.5)<1 OR INT(J+0.5)>8 or Z3=1 THEN 3760 +3750 Z1=I +3751 Z2=J +3752 GOSUB 4990 +3760 NEXT J +3761 NEXT I +3762 IF Z3=1 THEN 3770 +3763 D0=0 +3764 GOTO 3790 +3770 D0=1 +3771 CC$="docked" +3772 E=E0 +3773 P=P0 +3780 PRINT"Shields dropped for docking purposes" +3781 S=0 +3782 GOTO 3810 +3790 IF K3<=0 THEN 3800 +3791 C$="*red*" +3792 GOTO 3810 +3800 C$="GREEN" +3801 IF E>=E0*0.1 THEN 3810 +3802 C$="YELLOW" +3810 IF D(2)>=0 THEN 3830 +3820 PRINT +3821 PRINT"*** Short Range Sensors are out ***" +3822 PRINT +3823 RETURN +3830 PRINT "-"*33 +3831 LINE$ = "" +3832 FOR I=1 TO 8 +3840 FOR J=(I-1)*24 TO (I-1)*24+21 STEP 3 +3850 IF MID$(Q$,J,J+3)<>" " THEN 3860 +3851 REM PRINT " . " +3852 LINE$ = LINE$ + " . " +3853 GOTO 3862 +3860 REM PRINT " ",MID$(Q$,J,J+3) +3861 LINE$ = LINE$ + " " + MID$(Q$,J,J+3) +3862 NEXT J +3870 ON I GOTO 3880,3900,3910,3920,3930,3940,3950,3960 +3880 REM PRINT" Stardate " +3881 LINE$ = LINE$ + " Stardate " +3890 TT= T*10 +3891 TT=INT(TT)*0.1 +3892 PRINT LINE$,TT +3894 GOTO 3970 +3900 PRINT LINE$ + " Condition ",C$ +3901 GOTO 3970 +3910 PRINT LINE$+" Quadrant ",Q1,",",Q2 +3911 GOTO 3970 +3920 PRINT LINE$+" Sector ",S1,",",S2 +3921 GOTO 3970 +3930 PRINT LINE$+" Photon torpedoes ",INT(P) +3931 GOTO 3970 +3940 PRINT LINE$+" Total energy ",INT(E+S) +3941 GOTO 3970 +3950 PRINT LINE$+" Shields ",INT(S) +3951 GOTO 3970 +3960 PRINT LINE$+" Klingons remaining ",INT(K9) +3970 LINE$ = "" +3971 NEXT I +3972 PRINT "-"*33 +3973 RETURN +3980 REM library computer code +3990 CM1$="GALSTATORBASDIRREG" +4000 IF D(8)>=0 THEN 4010 +4001 PRINT"Computer Disabled" +4002 GOTO 1520 +4010 rem KEY 1, "GAL RCD"+CHR$(13) +4020 rem KEY 2, "STATUS"+CHR$(13) +4030 rem KEY 3, "TOR DATA"+CHR$(13) +4040 rem KEY 4, "BASE NAV"+CHR$(13) +4050 rem KEY 5, "DIR/DIST"+CHR$(13) +4060 rem KEY 6, "REG MAP"+CHR$(13) +4070 rem KEY 7,CHR$(13):KEY 8,CHR$(13):KEY 9,CHR$(13):KEY 10,CHR$(13) +4074 gosub 4130 +4080 INPUT"Computer active and awaiting command ":CM$ +4081 H8=1 +4090 FOR K1= 1 TO 6 +4100 IF mid$(CM$,0,3)<>MID$(CM1$,3*K1-3,3*K1) THEN 4120 +4110 rem ON K1 GOTO 4230,4400,4490,4750,4550,4210 +4111 if k1=1 then 4230 +4112 if k1=2 then 4400 +4113 if k1=3 then 4490 +4114 if k1=4 then 4750 +4115 if k1=5 then 4550 +4116 if k1=6 then 4210 +4120 NEXT K1 +4121 gosub 4130 +4122 goto 4080 +4130 PRINT"Functions available from library-computer:" +4140 PRINT" GAL = Cumulative galactic record" +4150 PRINT" STA = Status report" +4160 PRINT" TOR = Photon torpedo data" +4170 PRINT" BAS = Starbase nav data" +4180 PRINT" DIR = Direction/distance calculator" +4190 PRINT" REG = Galaxy 'region name' map" +4191 PRINT +4192 return +4200 REM setup to change cum gal record to galaxy map +4210 GOSUB 840 +4211 H8=0 +4212 G5=1 +4213 PRINT" the galaxy" +4214 GOTO 4290 +4220 REM cum galactic record +4230 rem 'INPUT"Do you want a hardcopy? Is the TTY on (Y/N) ":A$ +4240 rem 'IF A$="y" THEN POKE 1229,2 POKE 1237,3 NULL 1 +4250 GOSUB 840 +4260 PRINT +4261 PRINT" " +4270 PRINT "Computer record of galaxy for quadrant ",Q1,",",Q2 +4280 PRINT +4290 PRINT" 1 2 3 4 5 6 7 8" +4300 O1$=" ----- ----- ----- ----- ----- ----- ----- -----" +4310 PRINT O1$ +4312 LINE$ = "" +4313 FOR I=1 TO 8 +4314 REM PRINT I," " +4315 LINE$ = LINE$ + STR$(I)+ " " +4316 IF H8=0 THEN 4350 +4320 FOR J=1 TO 8 +4321 REM PRINT" " +4322 LINE$ = LINE$ + " " +4323 IF Z(I,J)<>0 THEN 4330 +4324 REM PRINT"***" +4325 LINE$ = LINE$ + "***" +4326 GOTO 4340 +4330 ZLEN = len(str$(z(i,j)+1000) +4331 REM PRINT mid$(STR$(Z(I,J)+1000),zlen-3,zlen) +4332 LINE$ = LINE$ + mid$(STR$(Z(I,J)+1000),zlen-3,zlen) +4340 NEXT J +4341 GOTO 4370 +4350 Z4=I +4351 Z5=1 +4352 GOSUB 5040 +4353 J0=INT(15-0.5*LEN(G2$)) +4354 PRINT G2$ +4360 Z5=5 +4361 GOSUB 5040 +4362 J0=INT(40-0.5*LEN(G2$)) +4363 PRINT G2$ +4370 PRINT LINE$ +4371 LINE$ = "" +4372 PRINT O1$ +4373 NEXT I +4374 PRINT +4375 rem 'POKE 1229,0 POKE 1237,1 +4380 GOTO 1520 +4390 REM status report +4400 GOSUB 840 +4401 PRINT" Status Report" +4402 X$="" +4403 IF K9<=1 THEN 4410 +4404 X$="s" +4410 PRINT"Klingon",X$," left: ",K9 +4420 PRINT"Mission must be completed in ",0.1*INT((T0+T9-T)*10)," stardates" +4430 X$="s" +4431 IF B9>=2 THEN 4440 +4432 X$="" +4433 IF B9<1 THEN 4460 +4440 PRINT"The federation is maintaining ",B9," starbase",X$," in the galaxy" +4450 GOTO 3180 +4460 PRINT"Your stupidity has left you on your own in" +4470 PRINT" the galaxy -- you have no starbases left!" +4471 GOTO 3180 +4480 REM torpedo, base nav, d/d calculator +4490 GOSUB 840 +4491 IF K3<=0 THEN 2550 +4500 X$="" +4501 IF K3<=1 THEN 4510 +4502 X$="s" +4510 PRINT"From ENTERPRISE to Klingon battle cruiser",X$ +4520 H8=0 +4521 FOR I=1 TO 3 +4522 IF K(I,3)<=0 THEN 4740 +4530 W1=K(I,1) +4531 X=K(I,2) +4540 C1=S1 +4541 A=S2 +4542 GOTO 4590 +4550 GOSUB 840 +4551 PRINT"Direction/Distance Calculator:" +4560 PRINT"You are at quadrant ",Q1,",",Q2," sector ",S1,",",S2 +4570 PRINT"Please enter " +4571 INPUT" initial coordinates (x,y) ":C1,A +4580 INPUT" Final coordinates (x,y) ":W1,X +4590 X=X-A +4591 A=C1-W1 +4592 aa=abs(a) +4593 ax=abs(x) +4594 IF X<0 THEN 4670 +4600 IF A<0 THEN 4690 +4610 IF X>0 THEN 4630 +4620 IF A<>0 THEN 4630 +4621 C1=5 +4622 GOTO 4640 +4630 C1=1 +4640 IF AA<=AX THEN 4660 +4650 PRINT"Direction1 = " +4651 cc1=(AA-AX+AA)/AA +4652 print c1+cc1 +4653 GOTO 4730 +4660 PRINT"Direction2 = " +4661 cc1=C1+(AA/AX) +4662 print cc1 +4663 GOTO 4730 +4670 IF A<=0 THEN 4680 +4671 C1=3 +4672 GOTO 4700 +4680 IF X=0 THEN 4690 +4681 C1=5 +4682 GOTO 4640 +4690 C1=7 +4700 IF AA>=AX THEN 4720 +4710 PRINT"Direction3 = " +4711 cc1=(AX-AA+AX)/AX +4712 print c1+cc1 +4713 GOTO 4730 +4720 PRINT"Direction4 = " +4721 CC1=C1+(AX/AA) +4722 PRINT CC1 +4730 PRINT"Distance = " +4731 cc1=SQR(x*X+A*A) +4732 print cc1 +4733 IF H8=1 THEN 1520 +4740 NEXT I +4741 GOTO 1520 +4750 GOSUB 840 +4751 IF B3=0 THEN 4770 +4752 PRINT "From ENTERPRISE to Starbase" +4754 W1=B4 +4755 X=B5 +4760 GOTO 4540 +4770 PRINT"Mr. Spock reports, 'Sensors show no starbases in this" +4780 PRINT"quadrant.'" +4781 GOTO 1520 +4790 REM find empty place in quadrant (for things) +4800 R1=INT(RND*7.98+1.01) +4801 R2=INT(RND*7.98+1.01) +4802 A$=" " +4803 Z1=R1 +4804 Z2=R2 +4805 GOSUB 4990 +4807 IF Z3=0 THEN 4800 +4810 RETURN +4820 REM insert in string array for quadrant +4830 S8=INT(Z2-0.5)*3+INT(Z1-0.5)*24+1 +4840 IF LEN(A$)=3 THEN 4850 +4841 PRINT"ERROR" +4842 STOP +4850 IF S8<>1 THEN 4860 +4851 Q$=A$+mid$(Q$,3,192) +4852 RETURN +4860 IF S8<>190 THEN 4870 +4861 Q$=mid$(Q$,0,189)+A$ +4862 RETURN +4870 Q$=mid$(Q$,0,S8-1)+A$+mid$(Q$,s8+2,192) +4871 RETURN +4880 REM prints device name +4890 ON R1 GOTO 4900,4910,4920,4930,4940,4950,4960,4970 +4900 G2$="Warp Engines" +4901 RETURN +4910 G2$="Short Range Sensors" +4911 RETURN +4920 G2$="Long Range Sensors" +4921 RETURN +4930 G2$="Phaser Control" +4931 RETURN +4940 G2$="Photon Tubes" +4941 RETURN +4950 G2$="Damage Control" +4951 RETURN +4960 G2$="Shield Control" +4961 RETURN +4970 G2$="Library-Computer" +4971 RETURN +4980 REM string comparison in quadrant array +4990 Z1=INT(Z1+0.5) +4991 Z2=INT(Z2+0.5) +4992 S8=(Z2-1)*3+(Z1-1)*24 +4993 Z3=0 +5000 IF MID$(Q$,S8,s8+3)=A$ THEN 5010 +5001 RETURN +5010 Z3=1 +5011 RETURN +5020 REM quadrant name in g2$ from z4,z5 (=q1,q2) +5030 REM call with g5=1 to get region name only +5040 IF Z5<=4 THEN 5140 +5041 ON Z4 GOTO 5060,5070,5080,5090,5100,5110,5120,5130 +5050 GOTO 5140 +5060 G2$="Antares" +5061 GOTO 5230 +5070 G2$="Rigel" +5071 GOTO 5230 +5080 G2$="Procyon" +5081 GOTO 5230 +5090 G2$="Vega" +5091 GOTO 5230 +5100 G2$="Canopus" +5101 GOTO 5230 +5110 G2$="Altair" +5111 GOTO 5230 +5120 G2$="Sagittarius" +5121 GOTO 5230 +5130 G2$="Pollux" +5131 GOTO 5230 +5140 ON Z4 GOTO 5150,5160,5170,5180,5180,5200,5210,5220 +5150 G2$="Sirius" +5151 GOTO 5230 +5160 G2$="Deneb" +5161 GOTO 5230 +5170 G2$="Capella" +5171 GOTO 5230 +5180 G2$="Betelgeuse" +5181 GOTO 5230 +5190 G2$="Aldebaran" +5191 GOTO 5230 +5200 G2$="Regulus" +5201 GOTO 5230 +5210 G2$="Arcturus" +5211 GOTO 5230 +5220 G2$="Spica" +5230 IF G5=1 THEN 5240 +5231 ON Z5 GOTO 5250,5260,5270,5280,5250,5260,5270,5280 +5240 RETURN +5250 G2$=G2$+" i" +5251 RETURN +5260 G2$=G2$+" ii" +5261 RETURN +5270 G2$=G2$+" iii" +5271 RETURN +5280 G2$=G2$+" iv" +5281 RETURN +5290 rem red alert sound +5291 return +5300 FOR J= 1 TO 4 +5310 FOR K=1000 TO 2000 STEP 20 +5320 rem SOUND K,0.01*18.2 +5330 NEXT K +5340 NEXT J +5350 RETURN +5360 rem torpedo sound +5361 return +5370 FOR J = 1500 TO 100 STEP -20 +5380 rem SOUND J,0.01*18.2 +5390 rem SOUND 3600-J,.01*18.2 +5400 NEXT J +5410 RETURN +5420 rem phaser sound +5421 return +5430 FOR J= 1 TO 40 +5440 rem SOUND 800,.01*18.2 +5450 rem SOUND 2500,.008*18.2 +5460 NEXT J +5470 RETURN +5480 rem alarm sound +5481 return +5490 FOR SI = 1 TO 3 +5500 FOR J= 800 TO 1500 STEP 20 +5510 rem SOUND J,.01 *18.2 +5520 NEXT J +5530 FOR K = 1500 TO 800 STEP -20 +5540 rem SOUND K, .01 *18.2 +5550 NEXT K +5560 NEXT SI +5570 RETURN From 1f74e7f9e08d3dd2f63de6f5aab5b19143fa9f16 Mon Sep 17 00:00:00 2001 From: richpl Date: Fri, 10 Sep 2021 14:12:40 +0100 Subject: [PATCH 055/183] Reflects addition of ON-GOTO --- README.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/README.md b/README.md index de728bd..0ff238d 100644 --- a/README.md +++ b/README.md @@ -64,20 +64,6 @@ Programs may be listed using the **LIST** command: > ``` -The list command can take arguments to refine the line selection listed - -`LIST 50` Lists only line 50 - -`LIST 50-100` Lists lines 50 through 100 inclusive - -`LIST 50 100` Also Lists lines 50 through 100 inclusive, almost any delimiter -works here - -`LIST -100` Lists from the start of the program through line 100 inclusive - -`LIST 50-` Lists from line 50 to the end of the program - - A program is executed using the **RUN** command: ``` @@ -805,7 +791,7 @@ be omitted to get the rest of the string. If *start-position* or *end-position* **MIN**(*expression-list*) - Returns the lowest value in *expression-list* -**ON** *expression* **GOSUB|GOTO** *line-number1,line-number2,...* - Conditional subroutine call|branch - Program flow will be transferred either through a **GOSUB** subroutine call or a **GOTO** branch to the line number in the list of line numbers corresponding to the ordinal value of the evaluated *expr*. The first line number corresponds with an *expr* value of 1. *expr* must evaluate to an integer value. +**ON** *expression* **GOSUB** *line-number* - Conditional subroutine call **PI** - Returns the value of pi From 17af6e1ffcb829f3cb67ed223d58c45ed6f37113 Mon Sep 17 00:00:00 2001 From: richpl Date: Fri, 10 Sep 2021 14:17:35 +0100 Subject: [PATCH 056/183] Reflects addition of ON-GOTO From d1078587c57494a9d692c1aea9bcb70b35e5d397 Mon Sep 17 00:00:00 2001 From: richpl Date: Fri, 10 Sep 2021 14:18:46 +0100 Subject: [PATCH 057/183] Reflects addition of ON-GOTO --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ff238d..e2719fc 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,20 @@ Programs may be listed using the **LIST** command: > ``` +The list command can take arguments to refine the line selection listed + +`LIST 50` Lists only line 50 + +`LIST 50-100` Lists lines 50 through 100 inclusive + +`LIST 50 100` Also Lists lines 50 through 100 inclusive, almost any delimiter +works here + +`LIST -100` Lists from the start of the program through line 100 inclusive + +`LIST 50-` Lists from line 50 to the end of the program + + A program is executed using the **RUN** command: ``` @@ -499,6 +513,8 @@ I is greater than J > ``` +It is also possible to use **ON-GOTO** to perform a conditional jump. + Allowable relational operators are: * '=' (equal, note that in BASIC the same operator is used for assignment) @@ -732,6 +748,8 @@ calculate the corresponding factorial *N!*. * *rock_scissors_paper.bas* - A BASIC implementation of the rock-paper-scissors game. +* *PyBStartrek.bas* - A port of the 1971 Star Trek text based strategy game. + ## Informal grammar definition **ABS**(*numerical-expression*) - Calculates the absolute value of the result of *numerical-expression* @@ -791,7 +809,7 @@ be omitted to get the rest of the string. If *start-position* or *end-position* **MIN**(*expression-list*) - Returns the lowest value in *expression-list* -**ON** *expression* **GOSUB** *line-number* - Conditional subroutine call +**ON** *expression* **GOSUB|GOTO** *line-number1,line-number2,...* - Conditional subroutine call|branch - Program flow will be transferred either through a **GOSUB** subroutine call or a **GOTO** branch to the line number in the list of line numbers corresponding to the ordinal value of the evaluated *expr*. The first line number corresponds with an *expr* value of 1. *expr* must evaluate to an integer value. **PI** - Returns the value of pi From cde8505a033f31c2731a6d12ef68377ee9e532a1 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 13:36:46 -0400 Subject: [PATCH 058/183] remove implementation code Somehow a segment of my micropython code slipped through of the fileio APPEND routines. :/ --- basicparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/basicparser.py b/basicparser.py index 3b5a7ee..d89146a 100644 --- a/basicparser.py +++ b/basicparser.py @@ -526,7 +526,7 @@ def __openstmt(self): self.__file_handles[filenum].seek(0) filelen = 0 for lines in self.__file_handles[filenum]: - filelen += (len(lines)+(0 if implementation.name.upper() == 'MICROPYTHON' else 1)) + filelen += len(lines)+1 self.__file_handles[filenum].seek(filelen) From 622d334f80b5813d43c8329022489e07b6a56042 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 15:51:44 -0400 Subject: [PATCH 059/183] Updates to README.md --- README.md | 122 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index e2719fc..5994c56 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ but this can be changed with parentheses. > 50 PRINT 15 MOD 10 > RUN 6 -2 +2.0 20 0 5 @@ -260,22 +260,37 @@ and are declared in a comma separated list: These values can then later be assigned to variables using the **READ** statement. Note that the type of the value (string or numeric) must match the type of the variable, otherwise an error message will be triggered. Therefore, -attention should be paid to the relative ordering of constants and variables. Further, -there must be enough constants to fill all of the variables defined in the **READ** statement, or else an -error will be given. This is to ensure that the program is not left in a state where a variable has not been -assigned a value, but nevertheless an attempt to use that variable is made later on in the program. +attention should be paid to the relative ordering of constants and variables. Once the constants on a **DATA** +statement are used to by a **READ** statement, the next variable on the **READ** statement or subsequent **READ** +statements will move to the **DATA** statement with the next higher line number, if there are no more **DATA** +statements before the end of the program an error will be given. This is to ensure that the program is not left +in a state where a variable has not been assigned a value, but nevertheless an attempt to use that variable is +made later on in the program. + +Normally each **DATA** statement is consumed sequently by **READ** statements however, the **RESTORE** statment can +be used to override this order and set the line number of the **DATA** statement that will be used by the next +**READ** statement. -The constants defined in the **DATA** statement may be consumed using several **READ** statements: +The constants defined in the **DATA** statement may be consumed using several **READ** statements or several **DATA** +statements may be consumed by a single **READ** statement.: ``` > 10 DATA 56, "Hello", 78 -> 20 READ FIRSTNUM, STR$ -> 30 PRINT FIRSTNUM, " ", STR$ +> 20 READ FIRSTNUM, S$ +> 30 PRINT FIRSTNUM, " ", S$ > 40 READ SECONDNUM > 50 PRINT SECONDNUM +> 60 DATA "Another " +> 70 DATA "Line " +> 80 DATA "of " +> 90 DATA "Data" +> 100 RESTORE 10 +> 110 READ FIRSTNUM, S$, SECONDNUM, A$, B$, C$, D$ +> 120 PRINT S$," ",A$,B$,C$,D$ > RUN 56 Hello 78 +Hello Another Line of Data > ``` @@ -305,8 +320,6 @@ The **REM** statement is used to indicate a comment, and occupies an entire stat > 10 REM THIS IS A COMMENT ``` -Note that comments will be automatically normalised to upper case. - ### Stopping a program The **STOP** statement may be used to cease program execution. The command **END** has the same effect. @@ -357,7 +370,7 @@ of dimensions, and attempts to assign to an array using an out of range index, w ### Printing to standard output -The **PRINT** statement is used to print to the screen: +The **PRINT** statement is used to print to the screen or to a file (see File I/O below): ``` > 10 PRINT 2 * 4 @@ -472,6 +485,9 @@ Note that the start value, end value and step value need not be integers, but ca point numbers as well. If the loop variable was previously assigned in the program, its value will be replaced by the start value, it will not be evaluated. +After the completion of the loop, the loop variable value will be the end value + step value (unless +the loop is exited using a **GOTO** statement). + ### Conditional branching Conditional branches are implemented using the **IF-THEN-ELSE** statement. The expression is evaluated and the appropriate jump @@ -496,15 +512,37 @@ expression evaluates to true, otherwise the following statement is executed. You can optionally give the **GOTO** keyword before your line numbers. This is for compatibility with other BASIC dialects. e.g. `40 IF I > J THEN GOTO 50 ELSE GOTO 70` -It is also possible to call a subroutine depending upon the result of a conditional expression -using the **ON-GOSUB** statement. If the expression evaluates to true, then the subroutine is -called, otherwise execution continues to the next statement without making the call: +It is also possible to call a subroutine or branch to a line number using the **ON GOTO|GOSUB** +statement. The subroutine or line number branched to is determined by evaluating the expression and selecting the line number from the list of +line numbers. If the expression evaluates to less than 1 or greater than the number of provided line numbers execution continues to the next +statement without making a subroutine call or branch: + +``` +> 20 LET J = 2 +> 30 ON J GOSUB 100,200,300 +> 40 STOP +> 100 REM THE 1ST SUBROUTINE +> 110 PRINT "J is ONE" +> 120 RETURN +> 200 REM THE 2ND SUBROUTINE +> 210 PRINT "J is TWO" +> 220 RETURN +> 300 REM THE 3RD SUBROUTINE +> 310 PRINT "J is THREE" +> 320 RETURN +> RUN +J is TWO +> +``` + +It is also possible to use **ON GOSUB** to perform a conditional subroutine call of sorts (see Ternary Functions below). ``` > 10 LET I = 10 > 20 LET J = 5 -> 30 ON I > J GOSUB 100 -> 40 STOP +> 30 K = IFF (I > J, 1, 0) +> 40 ON K GOSUB 100 +> 50 STOP > 100 REM THE SUBROUTINE > 110 PRINT "I is greater than J" > 120 RETURN @@ -513,7 +551,7 @@ I is greater than J > ``` -It is also possible to use **ON-GOTO** to perform a conditional jump. + Allowable relational operators are: @@ -580,7 +618,7 @@ I is greater than J ### User input -The **INPUT** statement is used to solicit input from the user: +The **INPUT** statement is used to solicit input from the user or read input from a file (see File I/O below): ``` > 10 INPUT A @@ -625,6 +663,43 @@ to re-input the values again. It is a limitation of this BASIC dialect that it is not possible to assign constants directly to array variables within an **INPUT** statement, only simple variables. +### File I/O + +Data can be read from or written to files using the **OPEN**, **FSEEK**, **INPUT**, **PRINT** and **CLOSE** statments. + +When a file is opened using the syntax **OPEN** "*filename*" **FOR INPUT|OUTPUT|APPEND AS** *#filenum* [**ELSE** *linenum*] a +file number (*#filenum*) is assigned to the file which if specfied as the first argument of an **INPUT** or **PRINT** +statment will direct the input or output to the file. + +If there is an error opening a file and the optional **ELSE** option has been specified with a line number, program control +will be branched to that line number, if the **ELSE** has not been provided an error message will be displayed. + +If a file is opened for **OUTPUT" which does not exist, the file will be created, if the file does exist, it's contents will +be erased and any new **PRINT** output will replace it. If a file is opened for **APPEND** an error will occur if the file +doesn't exist (or the **ELSE** branch will occur if specified) and if it does, any **PRINT** statments will add to the end +of the file. + +If an input prompt is specfied on an **INPUT** statement being used for file I/O (i.e. *#filenum* is specified) an error +will be displayed. + +The **FSEEK** *#filenum*,*filepos* statement will position the file pointer for the next **INPUT** statement. + +The **CLOSE** *#filenum* statment will close the file. + +... +> 10 OPEN "FILE.TXT" FOR OUTPUT AS #1 +> 20 PRINT #1,"0123456789Hello World!" +> 30 CLOSE #1 +> 40 OPEN "FILE.TXT" FOR INPUT AS #2 +> 50 FSEEK #2,10 +> 60 INPUT #2,A$ +> 70 PRINT A$ +> RUN +Hello World! +> +... + + ### Numeric functions Selected numeric functions are provided, and may be used with any numeric expression. For example, @@ -762,6 +837,8 @@ calculate the corresponding factorial *N!*. **COS**(*numerical-expression*) - Calculates the cosine value of the result of *numerical-expression* +**CLOSE** *#filenum* - Closes an open file + **DATA**(*expression-list*) - Defines a list of string or numerical values **DIM** *array-variable*(*dimensions*) - Defines a new array variable @@ -772,6 +849,8 @@ calculate the corresponding factorial *N!*. **FOR** *loop-variable* = *start-value* **TO** *end-value* [**STEP** *increment*] - Bounded loop +**FSEEK** *#filenum*,*filepos* - Positions the file input pointer to the specified location within the open file + **GOSUB** *line-number* - Subroutine call **GOTO** *line-number* - Unconditional branch @@ -782,7 +861,7 @@ calculate the corresponding factorial *N!*. **IF$**(*expression*, *string-expression*, *string-expression*) - Evaluates *expression* and returns the value of the result of the first *string-expression* if true, or the second if false. -**INPUT** [*input-prompt*:] *simple-variable-list* - Processes user input presented as a comma separated list +**INPUT** [*#filenum*,]|[*input-prompt*:] *simple-variable-list* - Processes user or file input presented as a comma separated list **INSTR**(*hackstack-string-expression*, *needle-string-expression*[, *start-numeric-expression*[, *end-numeric-expression*]]) - Returns position of first *needle-string-expression* inside first *hackstack-string-expression*, optionally start searching at position given by *start-numeric-expression* and optionally ending at position given by *end-numeric-expression*. Returns -1 if no match found. @@ -811,11 +890,14 @@ be omitted to get the rest of the string. If *start-position* or *end-position* **ON** *expression* **GOSUB|GOTO** *line-number1,line-number2,...* - Conditional subroutine call|branch - Program flow will be transferred either through a **GOSUB** subroutine call or a **GOTO** branch to the line number in the list of line numbers corresponding to the ordinal value of the evaluated *expr*. The first line number corresponds with an *expr* value of 1. *expr* must evaluate to an integer value. +**OPEN** "*filename*" **FOR INPUT|OUTPUT|APPEND AS** *#filenum* [**ELSE** *linenum*] - Opens the specified file. Program control is transferred to *linenum* if an error occurs otherwise continues +on the next line. + **PI** - Returns the value of pi **POW**(*base*, *exponent*) - Calculates the result of raising the base to the power of the exponent -**PRINT** *print-list* - Prints a comma separated list of literals or variables +**PRINT** [*#filenum*,]*print-list* - Prints a comma separated list of literals or variables to the screen or to a file **RANDOMIZE** [*numeric-expression*] - Resets random number generator to an unpredictable sequence. With optional seed (*numeric expression*), the sequence is predictable. From 6e1d3036fe50049162906589a03f8771d5d57d43 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 15:55:55 -0400 Subject: [PATCH 060/183] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5994c56..14cc0fa 100644 --- a/README.md +++ b/README.md @@ -261,9 +261,9 @@ and are declared in a comma separated list: These values can then later be assigned to variables using the **READ** statement. Note that the type of the value (string or numeric) must match the type of the variable, otherwise an error message will be triggered. Therefore, attention should be paid to the relative ordering of constants and variables. Once the constants on a **DATA** -statement are used to by a **READ** statement, the next variable on the **READ** statement or subsequent **READ** -statements will move to the **DATA** statement with the next higher line number, if there are no more **DATA** -statements before the end of the program an error will be given. This is to ensure that the program is not left +statement are used by a **READ** statement, the next **READ** +statement will move to the **DATA** statement with the next higher line number, if there are no more **DATA** +statements before the end of the program an error will be displayed. This is to ensure that the program is not left in a state where a variable has not been assigned a value, but nevertheless an attempt to use that variable is made later on in the program. From 1682ac74f0e4635f51398a81b0028adde248de10 Mon Sep 17 00:00:00 2001 From: brickbots Date: Fri, 10 Sep 2021 12:58:11 -0700 Subject: [PATCH 061/183] Fully merged, working out bug in StarTrek basic --- PyBStartrek.bas | 170 ++++++++++++++++++++-------------------- basictoken.py | 2 +- factorial.bas | 4 +- rock_scissors_paper.bas | 10 +-- 4 files changed, 93 insertions(+), 93 deletions(-) diff --git a/PyBStartrek.bas b/PyBStartrek.bas index 10533ee..a7fbd08 100644 --- a/PyBStartrek.bas +++ b/PyBStartrek.bas @@ -55,9 +55,9 @@ 963 dim N(4) 965 DIM G(9,9) 966 DIM Z(9,9) -970 T=INT(RND*20+20)*100 +970 T=INT(RND(1)*20+20)*100 971 T0=T -972 T9=25+INT(RND*10) +972 T9=25+INT(RND(1)*10) 973 D0=0 974 E=3000 975 E0=E @@ -72,10 +72,10 @@ 990 rem DEF FND(D)=SQR((K(I,1)-S1)^2+(K(I,2)-S2)^2) 1000 rem DEF FNR(R)=INT(RND(R)*7.98+1.01) 1010 REM initialize enterprise's position -1020 Q1=INT(RND*7.98+1.01) -1021 Q2=INT(RND*7.98+1.01) -1022 S1=INT(RND*7.98+1.01) -1023 S2=INT(RND*7.98+1.01) +1020 Q1=INT(RND(1)*7.98+1.01) +1021 Q2=INT(RND(1)*7.98+1.01) +1022 S1=INT(RND(1)*7.98+1.01) +1023 S2=INT(RND(1)*7.98+1.01) 1030 FOR I=1 TO 9 1031 C(I,1)=0 1032 C(I,2)=0 @@ -103,7 +103,7 @@ 1101 FOR J=1 TO 8 1102 K3=0 1103 Z(I,J)=0 -1104 R1=RND +1104 R1=RND(1) 1110 IF R1<=0.9799999 THEN goto 1120 1111 K3=3 1112 K9=K9+3 @@ -116,10 +116,10 @@ 1131 K3=1 1132 K9=K9+1 1140 B3=0 -1141 IF RND<=0.96 THEN goto 1150 +1141 IF RND(1)<=0.96 THEN goto 1150 1142 B3=1 1143 B9=B9+1 -1150 G(I,J)=K3*100+B3*10+INT(RND*7.98+1.01) +1150 G(I,J)=K3*100+B3*10+INT(RND(1)*7.98+1.01) 1151 NEXT J 1152 NEXT I 1153 IF K9<=T9 THEN goto 1160 @@ -130,8 +130,8 @@ 1172 K9=K9+1 1180 B9=1 1181 G(Q1,Q2)=G(Q1,Q2)+10 -1182 Q1=INT(RND*7.98+1.01) -1183 Q2=INT(RND*7.98+1.01) +1182 Q1=INT(RND(1)*7.98+1.01) +1183 Q2=INT(RND(1)*7.98+1.01) 1190 K7=K9 1191 IF B9=1 THEN 1200 1192 X$="s" @@ -142,9 +142,9 @@ 1230 PRINT" on stardate ",T0+T9,". This gives you ",T9," days. there",X0$ 1240 PRINT" ",B9," starbase",X$," in the galaxy for resupplying your ship" 1250 rem PRINT PRINT "hit any key except return when ready to accept command" -1260 rem I=RND IF INP(1)=13 THEN 1260 +1260 rem I=RND(1) IF INP(1)=13 THEN 1260 1261 PRINT -1262 input "hit return when ready to accept command":i$ +1262 input "hit return when ready to accept command";i$ 1270 REM here any time new quadrant entered 1280 Z4=Q1 1281 Z5=Q2 @@ -152,16 +152,16 @@ 1283 B3=0 1284 S3=0 1285 G5=0 -1286 D4=0.5*RND +1286 D4=0.5*RND(1) 1287 Z(Q1,Q2)=G(Q1,Q2) 1290 IF Q1<1 OR Q1>8 OR Q2<1 OR Q2>8 THEN 1410 1300 GOSUB 5040 1301 PRINT 1302 IF T0 <>T THEN 1330 1310 PRINT"Your mission begins with your starship located" -1320 PRINT"in the galactic quadrant, '",G2$,"'." +1320 PRINT"in the galactic quadrant, '";G2$;"'." 1321 GOTO 1340 -1330 PRINT"Now entering ",G2$," quadrant. . ." +1330 PRINT"Now entering ";G2$;" quadrant. . ." 1340 PRINT 1341 K3=INT(G(Q1,Q2)*0.01) 1342 B3=INT(G(Q1,Q2)*0.1)-10*K3 @@ -198,7 +198,7 @@ 1460 GOSUB 4830 1461 K(I,1)=R1 1462 K(I,2)=R2 -1463 K(I,3)=S9*(0.5+RND) +1463 K(I,3)=S9*(0.5+RND(1)) 1464 NEXT I 1470 IF B3<1 THEN 1500 1480 GOSUB 4800 @@ -224,7 +224,7 @@ 1551 PRINT"You have insufficient maneuvering energy, and shield control" 1561 PRINT"is presently incapable of cross-circuiting to engine room!!" 1571 GOTO 3480 -1580 INPUT"command ":A$ +1580 INPUT"command ";A$ 1590 FOR I=1 TO 9 1591 IF mid$(A$,0,3)<> MID$(A1$,3*I-3,3*I) THEN 1610 1600 ON I GOTO 1720,1510,2440,2530,2750,3090,3180,3980,3510 @@ -242,7 +242,7 @@ 1702 PRINT 1703 GOTO 1520 1710 REM course control begins here -1720 INPUT"Course (1-9) ":C2$ +1720 INPUT"Course (1-9) ";C2$ 1721 C1=VAL(C2$) 1723 IF C1<>9 THEN 1730 1724 C1=1 @@ -252,22 +252,22 @@ 1750 X$="8" 1751 IF D(1)>=0 THEN 1760 1752 X$="0.2" -1760 PRINT"Warp factor(0-",X$,") " +1760 PRINT"Warp factor(0-";X$;") " 1761 INPUT C2$ 1762 W1=VAL(C2$) 1763 IF D(1)<0 AND W1>0.2 THEN 1810 1770 IF W1>0 AND W1<8 THEN 1820 1780 IF W1=0 THEN 1520 -1790 PRINT" Chief Engineer Scott reports 'The engines won't take warp ",W1,"!" +1790 PRINT" Chief Engineer Scott reports 'The engines won't take warp ";W1;"!" 1801 GOTO 1520 1810 PRINT"Warp engines are damaged. Maximum speed = warp 0.2" 1811 GOTO 1520 1820 N1=INT(W1*8+0.5) 1821 IF E-N1>=0 THEN 1900 1830 PRINT"Engineering reports 'Insufficient energy available" -1840 PRINT" for maneuvering at warp ",W1,"!'" +1840 PRINT" for maneuvering at warp ";W1;"!'" 1850 IF S0.2 THEN 2070 -2000 R1=INT(RND*7.98+1.01) -2001 IF RND>=0.6 THEN 2040 +1991 IF RND(1)>0.2 THEN 2070 +2000 R1=INT(RND(1)*7.98+1.01) +2001 IF RND(1)>=0.6 THEN 2040 2010 IF K3=0 THEN 2070 -2020 D(R1)=D(R1)-(RND*5+1) +2020 D(R1)=D(R1)-(RND(1)*5+1) 2021 PRINT"DAMAGE CONTROL REPORT: " 2030 GOSUB 4890 -2031 PRINT G2$," damaged" +2031 PRINT G2$;" damaged" 2032 PRINT 2033 GOTO 2070 -2040 D(R1)=min(0,D(R1)+RND*3+1) +2040 D(R1)=min(0,D(R1)+RND(1)*3+1) 2041 PRINT"DAMAGE CONTROL REPORT: " 2050 GOSUB 4890 -2051 PRINT G2$," State of repair improved" +2051 PRINT G2$;" State of repair improved" 2052 PRINT 2060 REM begin moving starship -2070 A$=" " +2070 A$=" " 2071 Z1=INT(S1) 2072 Z2=INT(S2) 2073 GOSUB 4830 @@ -339,7 +339,7 @@ 2111 IF MID$(Q$,S8,S8+2)=" " THEN 2140 2120 S1=INT(S1-X1) 2121 S2=INT(S2-X2) -2122 PRINT"Warp engines shut down at sector ",S1,",",S2," due to bad navigation." +2122 PRINT"Warp engines shut down at sector ";S1;",";S2;" due to bad navigation." 2131 GOTO 2150 2140 NEXT I 2141 S1=INT(S1) @@ -391,7 +391,7 @@ 2310 PRINT" 'Permission to attempt crossing of galactic perimeter" 2320 PRINT" is hereby *DENIED*. Shut down your engines.'" 2330 PRINT"Chief Engineer Scott reports 'Warp engines shut down" -2340 PRINT" at sector ",S1,",",S2," of quadrant ",Q1,",",Q2".'" +2340 PRINT" at sector ";S1;",";S2;" of quadrant ";Q1;",";Q2".'" 2350 IF T>T0 THEN 3480 2360 IF 8*Q1+Q2=8*Q4+Q5 THEN 2150 2370 T=T+1 @@ -411,7 +411,7 @@ 2440 IF D(3)>=0 THEN 2450 2441 PRINT"Long Range Sensors are inoperable" 2442 GOTO 1520 -2450 PRINT"Long Range Scan for quadrant ",Q1,",",Q2 +2450 PRINT"Long Range Scan for quadrant ";Q1;",";Q2 2460 PRINT "-"*19 2461 LINE$ = "" 2470 FOR I=Q1-1 TO Q1+1 @@ -450,14 +450,14 @@ 2570 IF D(8)>=0 THEN 2580 2571 PRINT"Computer failure hampers accuracy" 2580 PRINT"Phasers locked on target " -2590 PRINT"Energy available = ",E," units" -2600 INPUT"Numbers of units to fire ":X +2590 PRINT"Energy available = ";E;" units" +2600 INPUT"Numbers of units to fire ";X 2601 IF X<=0 THEN 1520 2610 IF E-X<0 THEN 2590 2620 E=E-X 2621 GOSUB 5420 2622 IF D(7)<0 THEN 2630 -2623 X=X*RND +2623 X=X*RND(1) 2624 rem print "Energy * Rnd: ",x 2630 H1=INT(X/K3) 2631 FOR I=1 TO 3 @@ -469,15 +469,15 @@ 2646 H = H1 / H 2647 H = INT(H * (rnd+2)) 2648 IF H>0.15*K(I,3) THEN 2660 -2650 PRINT"Sensors show no damage to enemy at ",K(I,1),",",K(I,2) +2650 PRINT"Sensors show no damage to enemy at ";K(I,1);",";K(I,2) 2651 GOTO 2730 2660 K(I,3)=K(I,3)-H -2661 PRINT H,"Unit hit on Klingon at sector ",K(I,1),"," +2661 PRINT H,"Unit hit on Klingon at sector ";K(I,1);"," 2670 PRINT K(I,2) 2671 IF K(I,3)> 0 THEN GOTO 2700 2680 PRINT "**** KLINGON DESTROYED ****" 2690 GOTO 2710 -2700 PRINT" (Sensors show ",K(I,3)," units remaining)" +2700 PRINT" (Sensors show ";K(I,3);" units remaining)" 2701 GOTO 2730 2710 K3=K3-1 2711 K9=K9-1 @@ -499,7 +499,7 @@ 2760 IF D(5)>=0 THEN 2770 2761 PRINT"Photon tubes are not operational" 2762 GOTO 1520 -2770 INPUT"Photon torpedo course (1-9) ":C2$ +2770 INPUT"Photon torpedo course (1-9) ";C2$ 2771 C1=VAL(C2$) 2772 IF C1<>9 THEN 2780 2773 C1=1 @@ -521,7 +521,7 @@ 2842 X3=INT(X+0.5) 2843 Y3=INT(Y+0.5) 2850 IF X3<1 OR X3>8 OR Y3<1 OR Y3>8 THEN 3070 -2860 PRINT" ",X3,",",Y3 +2860 PRINT" ";X3;",";Y3 2861 A$=" " 2862 Z1=X 2863 Z2=Y @@ -547,7 +547,7 @@ 2942 Z2=Y 2943 GOSUB 4990 2944 IF Z3=0 THEN 2960 -2950 PRINT"Star at ",X3,",",Y3," absorbed torpedo energy." +2950 PRINT"Star at ";X3;",";Y3;" absorbed torpedo energy." 2951 GOSUB 3350 2952 GOTO 1520 2960 A$="("+chr$(174)+")" @@ -555,7 +555,7 @@ 2962 Z2=Y 2963 GOSUB 4990 2964 IF Z3<>0 THEN 2970 -2965 PRINT "Torpedo absorbed by unknown object at ",x3,",",y3 +2965 PRINT "Torpedo absorbed by unknown object at ";x3;",";y3 2966 goto 1520 2970 PRINT"*** STARBASE DESTROYED ***" 2980 B3=B3-1 @@ -582,8 +582,8 @@ 3090 IF D(7)>=0 THEN 3100 3091 PRINT"Shield control inoperable" 3092 GOTO 1520 -3100 PRINT"Energy available = ",E+S -3101 INPUT "Number of units to shields? ":X +3100 PRINT"Energy available = ";E+S +3101 INPUT "Number of units to shields? ";X 3110 IF X>=0 and S<>X THEN 3120 3111 PRINT"" 3112 GOTO 1520 @@ -594,7 +594,7 @@ 3150 E=E+S-X 3151 S=X 3152 PRINT "Deflector Control Room report" -3160 PRINT" 'Shields now at ",INT(S)," units per your command.'" +3160 PRINT" 'Shields now at ";INT(S);" units per your command.'" 3161 GOTO 1520 3170 REM damage control 3180 IF D(6)>=0 THEN 3290 @@ -611,8 +611,8 @@ 3222 IF D3<1 THEN 3230 3223 D3=0.9 3230 PRINT"Technicians standing by to effect repairs to your ship:" -3240 PRINT"estimated time to repair: ",0.01*INT(100*D3)," stardates" -3250 INPUT"Will you authorize the repair order (Y/N)? ":A$ +3240 PRINT"estimated time to repair: ";0.01*INT(100*D3);" stardates" +3250 INPUT"Will you authorize the repair order (Y/N)? ";A$ 3260 IF A$<>"y" AND A$<> "Y" THEN 1520 3270 FOR I=1 TO 8 3271 IF D(I)>=0 THEN 3280 @@ -640,37 +640,37 @@ 3371 IF K(I,3)<=0 THEN 3460 3380 ksq1 = (K(I,1)-S1)*(K(I,1)-S1) 3381 ksq2 = (K(I,2)-S2)*(K(I,2)-S2) -3382 H=( K(I,3) / SQR( ksq1 + ksq2 ) )*(2+RND) +3382 H=( K(I,3) / SQR( ksq1 + ksq2 ) )*(2+RND(1)) 3383 h = int(h) 3384 S=S-H -3385 K(I,3)=K(I,3)/(3+RND) +3385 K(I,3)=K(I,3)/(3+RND(1)) 3390 PRINT "ENTERPRISE HIT!" 3400 GOSUB 5480 -3401 PRINT H," Unit hit on ENTERPRISE from sector ",K(I,1),",",K(I,2) +3401 PRINT H," Unit hit on ENTERPRISE from sector ";K(I,1);",";K(I,2) 3410 IF S<=0 THEN 3490 -3420 PRINT" " +3420 PRINT" " 3421 IF H<20 THEN 3460 -3430 IF RND>0.6 OR H/S<=0.02 THEN 3460 -3440 R1=INT(RND*7.98+1.01) -3441 D(R1)=D(R1)-H/S-0.5*RND +3430 IF RND(1)>0.6 OR H/S<=0.02 THEN 3460 +3440 R1=INT(RND(1)*7.98+1.01) +3441 D(R1)=D(R1)-H/S-0.5*RND(1) 3442 GOSUB 4890 -3450 PRINT"Damage control reports '",G2$," damaged by the hit'" +3450 PRINT"Damage control reports '";G2$;" damaged by the hit'" 3460 NEXT I 3461 RETURN 3470 REM end of game -3480 PRINT"It is stardate",T +3480 PRINT"It is stardate";T 3481 GOTO 3510 3490 PRINT 3491 PRINT"the ENTERPRISE has been destroyed. The Federation will be conquered" 3501 GOTO 3480 -3510 PRINT"There were ",K9," Klingon battle cruisers left at" +3510 PRINT"There were ";K9;" Klingon battle cruisers left at" 3520 PRINT"the end of your mission" 3530 PRINT 3531 PRINT 3532 IF B9=0 THEN 3670 3540 PRINT"The Federation is in need of a new starship commander" 3550 PRINT"for a similar mission -- if there is a volunteer," -3560 INPUT"let him or her step forward and enter 'AYE' ":X$ +3560 INPUT"let him or her step forward and enter 'AYE' ";X$ 3561 IF X$="AYE" THEN 520 3570 rem KEY 1,"LIST " 3580 rem KEY 2,"RUN"+CHR$(13) @@ -687,7 +687,7 @@ 3690 PRINT"menacing the Federation has been destroyed." 3691 PRINT 3700 cc1 = k7/(t-t0) -3701 PRINT"Your efficiency rating is ",1000*cc1*cc1 +3701 PRINT"Your efficiency rating is ";1000*cc1*cc1 3703 GOTO 3530 3710 REM short range sensor scan & startup subroutine 3720 A$="("+chr$(174)+")" @@ -729,7 +729,7 @@ 3851 REM PRINT " . " 3852 LINE$ = LINE$ + " . " 3853 GOTO 3862 -3860 REM PRINT " ",MID$(Q$,J,J+3) +3860 REM PRINT " ";MID$(Q$,J,J+3) 3861 LINE$ = LINE$ + " " + MID$(Q$,J,J+3) 3862 NEXT J 3870 ON I GOTO 3880,3900,3910,3920,3930,3940,3950,3960 @@ -737,28 +737,28 @@ 3881 LINE$ = LINE$ + " Stardate " 3890 TT= T*10 3891 TT=INT(TT)*0.1 -3892 PRINT LINE$,TT +3892 PRINT LINE$;TT 3894 GOTO 3970 -3900 PRINT LINE$ + " Condition ",C$ +3900 PRINT LINE$ + " Condition ";C$ 3901 GOTO 3970 -3910 PRINT LINE$+" Quadrant ",Q1,",",Q2 +3910 PRINT LINE$+" Quadrant ";Q1;",";Q2 3911 GOTO 3970 -3920 PRINT LINE$+" Sector ",S1,",",S2 +3920 PRINT LINE$+" Sector ";S1;",";S2 3921 GOTO 3970 -3930 PRINT LINE$+" Photon torpedoes ",INT(P) +3930 PRINT LINE$+" Photon torpedoes ";INT(P) 3931 GOTO 3970 -3940 PRINT LINE$+" Total energy ",INT(E+S) +3940 PRINT LINE$+" Total energy ";INT(E+S) 3941 GOTO 3970 -3950 PRINT LINE$+" Shields ",INT(S) +3950 PRINT LINE$+" Shields ";INT(S) 3951 GOTO 3970 -3960 PRINT LINE$+" Klingons remaining ",INT(K9) +3960 PRINT LINE$+" Klingons remaining ";INT(K9) 3970 LINE$ = "" 3971 NEXT I 3972 PRINT "-"*33 3973 RETURN 3980 REM library computer code 3990 CM1$="GALSTATORBASDIRREG" -4000 IF D(8)>=0 THEN 4010 +4000 IF D(8)>=0 THEN 4010 4001 PRINT"Computer Disabled" 4002 GOTO 1520 4010 rem KEY 1, "GAL RCD"+CHR$(13) @@ -769,7 +769,7 @@ 4060 rem KEY 6, "REG MAP"+CHR$(13) 4070 rem KEY 7,CHR$(13):KEY 8,CHR$(13):KEY 9,CHR$(13):KEY 10,CHR$(13) 4074 gosub 4130 -4080 INPUT"Computer active and awaiting command ":CM$ +4080 INPUT"Computer active and awaiting command ";CM$ 4081 H8=1 4090 FOR K1= 1 TO 6 4100 IF mid$(CM$,0,3)<>MID$(CM1$,3*K1-3,3*K1) THEN 4120 @@ -804,14 +804,14 @@ 4250 GOSUB 840 4260 PRINT 4261 PRINT" " -4270 PRINT "Computer record of galaxy for quadrant ",Q1,",",Q2 +4270 PRINT "Computer record of galaxy for quadrant ";Q1;",";Q2 4280 PRINT 4290 PRINT" 1 2 3 4 5 6 7 8" 4300 O1$=" ----- ----- ----- ----- ----- ----- ----- -----" 4310 PRINT O1$ 4312 LINE$ = "" 4313 FOR I=1 TO 8 -4314 REM PRINT I," " +4314 REM PRINT I;" " 4315 LINE$ = LINE$ + STR$(I)+ " " 4316 IF H8=0 THEN 4350 4320 FOR J=1 TO 8 @@ -848,13 +848,13 @@ 4402 X$="" 4403 IF K9<=1 THEN 4410 4404 X$="s" -4410 PRINT"Klingon",X$," left: ",K9 -4420 PRINT"Mission must be completed in ",0.1*INT((T0+T9-T)*10)," stardates" +4410 PRINT"Klingon";X$;" left: ";K9 +4420 PRINT"Mission must be completed in ";0.1*INT((T0+T9-T)*10);" stardates" 4430 X$="s" 4431 IF B9>=2 THEN 4440 4432 X$="" 4433 IF B9<1 THEN 4460 -4440 PRINT"The federation is maintaining ",B9," starbase",X$," in the galaxy" +4440 PRINT"The federation is maintaining ";B9;" starbase";X$;" in the galaxy" 4450 GOTO 3180 4460 PRINT"Your stupidity has left you on your own in" 4470 PRINT" the galaxy -- you have no starbases left!" @@ -865,7 +865,7 @@ 4500 X$="" 4501 IF K3<=1 THEN 4510 4502 X$="s" -4510 PRINT"From ENTERPRISE to Klingon battle cruiser",X$ +4510 PRINT"From ENTERPRISE to Klingon battle cruiser";X$ 4520 H8=0 4521 FOR I=1 TO 3 4522 IF K(I,3)<=0 THEN 4740 @@ -876,10 +876,10 @@ 4542 GOTO 4590 4550 GOSUB 840 4551 PRINT"Direction/Distance Calculator:" -4560 PRINT"You are at quadrant ",Q1,",",Q2," sector ",S1,",",S2 +4560 PRINT"You are at quadrant ";Q1;",";Q2;" sector ";S1;",";S2 4570 PRINT"Please enter " -4571 INPUT" initial coordinates (x,y) ":C1,A -4580 INPUT" Final coordinates (x,y) ":W1,X +4571 INPUT" initial coordinates (x,y) ";C1,A +4580 INPUT" Final coordinates (x,y) ";W1,X 4590 X=X-A 4591 A=C1-W1 4592 aa=abs(a) @@ -931,8 +931,8 @@ 4780 PRINT"quadrant.'" 4781 GOTO 1520 4790 REM find empty place in quadrant (for things) -4800 R1=INT(RND*7.98+1.01) -4801 R2=INT(RND*7.98+1.01) +4800 R1=INT(RND(1)*7.98+1.01) +4801 R2=INT(RND(1)*7.98+1.01) 4802 A$=" " 4803 Z1=R1 4804 Z2=R2 diff --git a/basictoken.py b/basictoken.py index 986d2c2..7aba639 100644 --- a/basictoken.py +++ b/basictoken.py @@ -144,7 +144,7 @@ class BASICToken: '\n': NEWLINE, '<': LESSER, '>': GREATER, '<>': NOTEQUAL, '<=': LESSEQUAL, '>=': GREATEQUAL, ',': COMMA, - ':': COLON, '%': MODULO, '!=': NOTEQUAL, '#': HASH} + ':': COLON, '%': MODULO, '!=': NOTEQUAL, '#': HASH, ';': SEMICOLON} diff --git a/factorial.bas b/factorial.bas index fcd0da6..1f443fd 100644 --- a/factorial.bas +++ b/factorial.bas @@ -1,9 +1,9 @@ 10 REM A SHORT PROGRAM TO CALCULATE FACTORIAL OF 20 REM SUPPLIED NUMBER N -30 INPUT "Please provide N: ": N +30 INPUT "Please provide N: "; N 35 REM OUTPUT IS NOW A RESERVED KEYWORD 40 OUTPUTVAL = 1 50 FOR I = 1 TO N 60 OUTPUTVAL = OUTPUTVAL * I 70 NEXT I -80 PRINT "Factorial of N is ", OUTPUTVAL +80 PRINT "Factorial of N is "; OUTPUTVAL diff --git a/rock_scissors_paper.bas b/rock_scissors_paper.bas index f8d64e8..39e7482 100644 --- a/rock_scissors_paper.bas +++ b/rock_scissors_paper.bas @@ -4,17 +4,17 @@ 40 MYSCORE = 0 50 YOURSCORE = 0 60 PRINT "Let's play rock-scissors-paper!" -70 INPUT "(R)ock, (S)cissors, (P)aper or e(X)it: ": YOURGUESS$ +70 INPUT "(R)ock, (S)cissors, (P)aper or e(X)it: "; YOURGUESS$ 75 IF YOURGUESS$ = "X" THEN 160 -80 RANDOM = RND +80 RANDOM = RND(1) 90 GOSUB 190 -100 PRINT "Your guess: ", YOURGUESS$, ", My guess: ", MYGUESS$ +100 PRINT "Your guess: "; YOURGUESS$; ", My guess: "; MYGUESS$ 110 REM ON YOURGUESS$ = "R" GOSUB 300 120 REM ON YOURGUESS$ = "S" GOSUB 500 130 REM ON YOURGUESS$ = "P" GOSUB 700 135 ON INSTR("RSP",YOURGUESS$)+1 GOSUB 300,500,700 150 GOTO 70 -160 PRINT "My score: ", MYSCORE, " Your score: ", YOURSCORE +160 PRINT "My score: "; MYSCORE; " Your score: "; YOURSCORE 170 STOP 190 REM RANDOMLY ASSIGN MYGUESS$ 200 IF RANDOM < 0.3 THEN 210 ELSE 230 @@ -60,4 +60,4 @@ 780 IF MYGUESS$ = "S" THEN 790 ELSE 810 790 PRINT "I won!" 800 MYSCORE = MYSCORE + 1 -810 RETURN \ No newline at end of file +810 RETURN From eca50891bacd2bc22fdea01165672923a458d03d Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 16:05:20 -0400 Subject: [PATCH 062/183] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 14cc0fa..0ec4ba0 100644 --- a/README.md +++ b/README.md @@ -671,10 +671,10 @@ When a file is opened using the syntax **OPEN** "*filename*" **FOR INPUT|OUTPUT| file number (*#filenum*) is assigned to the file which if specfied as the first argument of an **INPUT** or **PRINT** statment will direct the input or output to the file. -If there is an error opening a file and the optional **ELSE** option has been specified with a line number, program control +If there is an error opening a file and the optional **ELSE** option has been specified, program control will be branched to that line number, if the **ELSE** has not been provided an error message will be displayed. -If a file is opened for **OUTPUT" which does not exist, the file will be created, if the file does exist, it's contents will +If a file is opened for **OUTPUT** which does not exist, the file will be created, if the file does exist, it's contents will be erased and any new **PRINT** output will replace it. If a file is opened for **APPEND** an error will occur if the file doesn't exist (or the **ELSE** branch will occur if specified) and if it does, any **PRINT** statments will add to the end of the file. @@ -908,6 +908,8 @@ optional seed (*numeric expression*), the sequence is predictable. **RETURN** - Return from a subroutine +**RESTORE** *#filenum*,*filepos* - positions the input file position such that the next **INPUT** *#filenum* will read starting at file position *filepos* + **RND** - Generates a pseudo random number N, where 0 <= N < 1 **RNDINT**(*lo-numerical-expression*, *hi-numerical-expression*) - Generates a pseudo random integer N, where *lo-numerical-expression* <= N <= *hi-numerical-expression* From 35df8fd98f293f13063bce175f8da5c9b71625fa Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 16:15:27 -0400 Subject: [PATCH 063/183] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0ec4ba0..073feb9 100644 --- a/README.md +++ b/README.md @@ -370,7 +370,7 @@ of dimensions, and attempts to assign to an array using an out of range index, w ### Printing to standard output -The **PRINT** statement is used to print to the screen or to a file (see File I/O below): +The **PRINT** statement is used to print to the screen (or to a file, see File I/O below): ``` > 10 PRINT 2 * 4 @@ -618,7 +618,7 @@ I is greater than J ### User input -The **INPUT** statement is used to solicit input from the user or read input from a file (see File I/O below): +The **INPUT** statement is used to solicit input from the user (or read input from a file, see File I/O below): ``` > 10 INPUT A @@ -663,7 +663,7 @@ to re-input the values again. It is a limitation of this BASIC dialect that it is not possible to assign constants directly to array variables within an **INPUT** statement, only simple variables. -### File I/O +### File Input/Output Data can be read from or written to files using the **OPEN**, **FSEEK**, **INPUT**, **PRINT** and **CLOSE** statments. From 4d524ad38d8a075c327581af31bffbadf52a5212 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 16:17:16 -0400 Subject: [PATCH 064/183] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 073feb9..4cb9c24 100644 --- a/README.md +++ b/README.md @@ -686,7 +686,7 @@ The **FSEEK** *#filenum*,*filepos* statement will position the file pointer for The **CLOSE** *#filenum* statment will close the file. -... +``` > 10 OPEN "FILE.TXT" FOR OUTPUT AS #1 > 20 PRINT #1,"0123456789Hello World!" > 30 CLOSE #1 @@ -697,7 +697,7 @@ The **CLOSE** *#filenum* statment will close the file. > RUN Hello World! > -... +``` ### Numeric functions From 7693de9e927d2c40aa6700bd812f5225c6dead34 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 16:40:05 -0400 Subject: [PATCH 065/183] single line conditional ongosub --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4cb9c24..17efe57 100644 --- a/README.md +++ b/README.md @@ -540,9 +540,8 @@ It is also possible to use **ON GOSUB** to perform a conditional subroutine call ``` > 10 LET I = 10 > 20 LET J = 5 -> 30 K = IFF (I > J, 1, 0) -> 40 ON K GOSUB 100 -> 50 STOP +> 30 ON IFF (I > J, 1, 0) GOSUB 100 +> 40 STOP > 100 REM THE SUBROUTINE > 110 PRINT "I is greater than J" > 120 RETURN From 8521dd06be449e973a425a12d6b462f0246e89c6 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 16:44:34 -0400 Subject: [PATCH 066/183] minor change to README wording --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 17efe57..bdeed90 100644 --- a/README.md +++ b/README.md @@ -535,7 +535,7 @@ J is TWO > ``` -It is also possible to use **ON GOSUB** to perform a conditional subroutine call of sorts (see Ternary Functions below). +It is also possible to use **ON GOSUB** to perform a conditional subroutine call (see Ternary Functions below). ``` > 10 LET I = 10 From fa8f3513a68d44be46c25cfb4ba4ce896e917420 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 16:47:17 -0400 Subject: [PATCH 067/183] Rewording.... --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bdeed90..745d30e 100644 --- a/README.md +++ b/README.md @@ -535,7 +535,7 @@ J is TWO > ``` -It is also possible to use **ON GOSUB** to perform a conditional subroutine call (see Ternary Functions below). +The **IFF** statement (see Ternary Functions below) can be used with **ON GOSUB** to perform a conditional subroutine call. ``` > 10 LET I = 10 From 4647c06b4d1000a4b41ac3468ab89b87e3e994dc Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 16:49:48 -0400 Subject: [PATCH 068/183] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 745d30e..e97b039 100644 --- a/README.md +++ b/README.md @@ -535,7 +535,7 @@ J is TWO > ``` -The **IFF** statement (see Ternary Functions below) can be used with **ON GOSUB** to perform a conditional subroutine call. +The **IFF** function (see Ternary Functions below) can be used with **ON GOSUB** to perform a conditional subroutine call. ``` > 10 LET I = 10 From 3a0a01d6e452b7a4064650103031f30a9e196b20 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 17:08:36 -0400 Subject: [PATCH 069/183] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e97b039..30dd5d7 100644 --- a/README.md +++ b/README.md @@ -667,15 +667,15 @@ within an **INPUT** statement, only simple variables. Data can be read from or written to files using the **OPEN**, **FSEEK**, **INPUT**, **PRINT** and **CLOSE** statments. When a file is opened using the syntax **OPEN** "*filename*" **FOR INPUT|OUTPUT|APPEND AS** *#filenum* [**ELSE** *linenum*] a -file number (*#filenum*) is assigned to the file which if specfied as the first argument of an **INPUT** or **PRINT** -statment will direct the input or output to the file. +file number (*#filenum*) is assigned to the file, which if specfied as the first argument of an **INPUT** or **PRINT** +statment, will direct the input or output to the file. If there is an error opening a file and the optional **ELSE** option has been specified, program control -will be branched to that line number, if the **ELSE** has not been provided an error message will be displayed. +will branch to the specified line number, if the **ELSE** has not been provided an error message will be displayed. -If a file is opened for **OUTPUT** which does not exist, the file will be created, if the file does exist, it's contents will +If a file is opened for **OUTPUT** which does not exist, the file will be created, if the file does exist, its contents will be erased and any new **PRINT** output will replace it. If a file is opened for **APPEND** an error will occur if the file -doesn't exist (or the **ELSE** branch will occur if specified) and if it does, any **PRINT** statments will add to the end +doesn't exist (or the **ELSE** branch will occur if specified). If the file does exist, any **PRINT** statments will add to the end of the file. If an input prompt is specfied on an **INPUT** statement being used for file I/O (i.e. *#filenum* is specified) an error From 26fe2411a75dbc93fb3c626ab1417fc6ab81d4a1 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 17:13:26 -0400 Subject: [PATCH 070/183] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 30dd5d7..1b726db 100644 --- a/README.md +++ b/README.md @@ -834,10 +834,10 @@ calculate the corresponding factorial *N!*. **CHR$**(*numerical-expression*) - Returns the character specified by character code of the result of *numerical-expression*. -**COS**(*numerical-expression*) - Calculates the cosine value of the result of *numerical-expression* - **CLOSE** *#filenum* - Closes an open file +**COS**(*numerical-expression*) - Calculates the cosine value of the result of *numerical-expression* + **DATA**(*expression-list*) - Defines a list of string or numerical values **DIM** *array-variable*(*dimensions*) - Defines a new array variable From f4d2e6b86b6537a545ca43131f8e6ed1c14849a3 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 10 Sep 2021 22:24:11 -0400 Subject: [PATCH 071/183] Corrected RESTORE syntax + misc edits --- README.md | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 1b726db..e35e836 100644 --- a/README.md +++ b/README.md @@ -269,7 +269,8 @@ made later on in the program. Normally each **DATA** statement is consumed sequently by **READ** statements however, the **RESTORE** statment can be used to override this order and set the line number of the **DATA** statement that will be used by the next -**READ** statement. +**READ** statement. If the *line-number* used in a **RESTORE** statement does not refer to a **DATA** statement an +error will be displayed. The constants defined in the **DATA** statement may be consumed using several **READ** statements or several **DATA** statements may be consumed by a single **READ** statement.: @@ -294,21 +295,6 @@ Hello Another Line of Data > ``` -The supply of constants may be refreshed by defining more **DATA** statements: - -``` -> 10 DATA 20 -> 20 READ NUM -> 30 PRINT NUM -> 40 DATA 30 -> 50 READ NUM -> 60 PRINT NUM -> RUN -20 -30 -> -``` - It is a limitation of this BASIC dialect that it is not possible to assign constants directly to array variables within a **READ** statement, only simple variables. @@ -512,9 +498,9 @@ expression evaluates to true, otherwise the following statement is executed. You can optionally give the **GOTO** keyword before your line numbers. This is for compatibility with other BASIC dialects. e.g. `40 IF I > J THEN GOTO 50 ELSE GOTO 70` -It is also possible to call a subroutine or branch to a line number using the **ON GOTO|GOSUB** -statement. The subroutine or line number branched to is determined by evaluating the expression and selecting the line number from the list of -line numbers. If the expression evaluates to less than 1 or greater than the number of provided line numbers execution continues to the next +The **ON GOTO|GOSUB** *expr* *line1,line2,...* statement will call a subroutine or branch to a line number in the list of line numbers corresponding to the ordinal +value of the evaluated *expr*. The first line number corresponds with an *expr* value of 1. *expr* must evaluate to an integer value. + If *expr* evaluates to less than 1 or greater than the number of provided line numbers execution continues on the next statement without making a subroutine call or branch: ``` @@ -535,7 +521,9 @@ J is TWO > ``` -The **IFF** function (see Ternary Functions below) can be used with **ON GOSUB** to perform a conditional subroutine call. +It is also possible to call a subroutine depending upon the result of a conditional expression using the **IFF** function (see Ternary Functions below). In +the example below, if the expression evaluates to true, **IFF** returns a 1 and the subroutine is called, otherwise **IFF** returns a 0 and execution +continues to the next statement without making the call: ``` > 10 LET I = 10 @@ -848,7 +836,8 @@ calculate the corresponding factorial *N!*. **FOR** *loop-variable* = *start-value* **TO** *end-value* [**STEP** *increment*] - Bounded loop -**FSEEK** *#filenum*,*filepos* - Positions the file input pointer to the specified location within the open file +**FSEEK** *#filenum*,*filepos* - Positions the file input pointer to the specified location within the open file, the next **INPUT** *#filenum* +will read starting at file position *filepos* **GOSUB** *line-number* - Subroutine call @@ -860,7 +849,7 @@ calculate the corresponding factorial *N!*. **IF$**(*expression*, *string-expression*, *string-expression*) - Evaluates *expression* and returns the value of the result of the first *string-expression* if true, or the second if false. -**INPUT** [*#filenum*,]|[*input-prompt*:] *simple-variable-list* - Processes user or file input presented as a comma separated list +**INPUT** [*#filenum*,|*input-prompt*:] *simple-variable-list* - Processes user or file input presented as a comma separated list **INSTR**(*hackstack-string-expression*, *needle-string-expression*[, *start-numeric-expression*[, *end-numeric-expression*]]) - Returns position of first *needle-string-expression* inside first *hackstack-string-expression*, optionally start searching at position given by *start-numeric-expression* and optionally ending at position given by *end-numeric-expression*. Returns -1 if no match found. @@ -907,7 +896,7 @@ optional seed (*numeric expression*), the sequence is predictable. **RETURN** - Return from a subroutine -**RESTORE** *#filenum*,*filepos* - positions the input file position such that the next **INPUT** *#filenum* will read starting at file position *filepos* +**RESTORE** *line-number* - sets the line number that the next **READ** will start loading constants from. *line-number* must refer to a **DATA** statement **RND** - Generates a pseudo random number N, where 0 <= N < 1 From 1985eb61c800218c3197684d3c8e9e3f4b33b6ff Mon Sep 17 00:00:00 2001 From: brickbots Date: Sat, 11 Sep 2021 11:11:00 -0700 Subject: [PATCH 072/183] Working Startrek --- PyBStartrek.bas | 1041 +++++++++++++++++------------------------------ 1 file changed, 366 insertions(+), 675 deletions(-) diff --git a/PyBStartrek.bas b/PyBStartrek.bas index a7fbd08..2582256 100644 --- a/PyBStartrek.bas +++ b/PyBStartrek.bas @@ -10,9 +10,7 @@ 610 REM **** Bob Fritz, 9915 Caninito Cuadro, San Diego, Ca., 92129 620 REM **** (714) 484-2955 630 REM **** -640 for i = 1 to 10 -644 print -648 next i +640 PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT 650 E1$= " ,-----------------, _" 660 E2$= " `---------- ----',-----/ \----," 670 E3$= " | | '- --------'" @@ -20,7 +18,6 @@ 690 E5$= " '-----------------'" 691 PRINT 700 E6$= " THE USS ENTERPRISE --- NCC-1701" -710 rem E8$=CHR$(15) 720 PRINT E1$ 730 PRINT E2$ 740 PRINT E3$ @@ -28,71 +25,25 @@ 760 PRINT E5$ 770 PRINT 780 PRINT E6$ -790 for i = 1 to 7 -794 print -798 next i -800 rem CLEAR 600 -810 rem RANDOMIZE 120*(VAL(RIGHT$(TIME$,2)) + VAL(MID$(TIME$,3,5)) ) +790 PRINT:PRINT: PRINT:PRINT:PRINT:PRINT:PRINT 811 RANDOMIZE -820 REM Z$=" " -830 GOSUB 840 -831 GOTO 960 -840 REM ------- set function keys for game ------- -850 rem KEY 1,"NAV"+CHR$(13) -860 rem KEY 2,"SRS"+CHR$(13) -870 rem KEY 3,"LRS"+CHR$(13) -880 rem KEY 4,"PHASERS"+CHR$(13) -890 rem KEY 5,"TORPEDO"+CHR$(13) -900 rem KEY 6,"SHIELDS"+CHR$(13) -910 rem KEY 7,"DAMAGE REPORT"+CHR$(13) -920 rem KEY 8,"COMPUTER"+CHR$(13) -930 rem KEY 9,"RESIGN"+CHR$(13) -940 rem KEY 10,"" -950 RETURN -960 dim D(9) -961 dim C(10,3) -962 dim K(4,4) -963 dim N(4) -965 DIM G(9,9) -966 DIM Z(9,9) -970 T=INT(RND(1)*20+20)*100 -971 T0=T -972 T9=25+INT(RND(1)*10) -973 D0=0 -974 E=3000 -975 E0=E -980 P=10 -981 P0=P -982 S9=200 -983 S=0 -984 B9=0 -985 K9=0 -986 X$="" -987 X0$=" is " +960 dim D(8) +961 dim C(9,2) +962 dim K(3,3) +963 dim N(3) +965 DIM G(8,8) +966 DIM Z(8,8) +970 T=INT(RND(1)*20+20)*100:T0=T:T9=25+INT(RND(1)*10):D0=0:E=3000:E0=E +980 P=10:P0=P:S9=200:S=0:B9=0:K9=0:X$="":X0$=" is " 990 rem DEF FND(D)=SQR((K(I,1)-S1)^2+(K(I,2)-S2)^2) -1000 rem DEF FNR(R)=INT(RND(R)*7.98+1.01) +1000 rem DEF FNR(R)=INT(RND(1)(R)*7.98+1.01) 1010 REM initialize enterprise's position -1020 Q1=INT(RND(1)*7.98+1.01) -1021 Q2=INT(RND(1)*7.98+1.01) -1022 S1=INT(RND(1)*7.98+1.01) -1023 S2=INT(RND(1)*7.98+1.01) +1020 Q1=INT(RND(1)*7.98+1.01):Q2=INT(RND(1)*7.98+1.01):S1=INT(RND(1)*7.98+1.01):S2=INT(RND(1)*7.98+1.01) 1030 FOR I=1 TO 9 -1031 C(I,1)=0 -1032 C(I,2)=0 -1033 NEXT I -1040 C(3,1)=-1 -1041 C(2,1)=-1 -1042 C(4,1)=-1 -1043 C(4,2)=-1 -1044 C(5,2)=-1 -1045 C(6,2)=-1 -1050 C(1,2)=1 -1051 C(2,2)=1 -1052 C(6,1)=1 -1053 C(7,1)=1 -1054 C(8,1)=1 -1055 C(8,2)=1 -1056 C(9,2)=1 +1031 C(I,1)=0:C(I,2)=0 +1032 NEXT I +1040 C(3,1)=-1:C(2,1)=-1:C(4,1)=-1:C(4,2)=-1:C(5,2)=-1:C(6,2)=-1 +1050 C(1,2)=1:C(2,2)=1:C(6,1)=1:C(7,1)=1:C(8,1)=1:C(8,2)=1:C(9,2)=1 1060 FOR I=1 TO 8 1061 D(I)=0 1062 NEXT I @@ -101,24 +52,15 @@ 1090 REM k3=#klingons b3=#starbases s3=#stars 1100 FOR I=1 TO 8 1101 FOR J=1 TO 8 -1102 K3=0 -1103 Z(I,J)=0 -1104 R1=RND(1) +1102 K3=0:Z(I,J)=0:R1=RND(1) 1110 IF R1<=0.9799999 THEN goto 1120 -1111 K3=3 -1112 K9=K9+3 -1113 GOTO 1140 +1111 K3=3:K9=K9+3: GOTO 1140 1120 IF R1<=0.95 THEN goto 1130 -1121 K3=2 -1122 K9=K9+2 -1123 GOTO 1140 +1121 K3=2:K9=K9+2: GOTO 1140 1130 IF R1<=0.8 THEN goto 1140 -1131 K3=1 -1132 K9=K9+1 -1140 B3=0 -1141 IF RND(1)<=0.96 THEN goto 1150 -1142 B3=1 -1143 B9=B9+1 +1131 K3=1:K9=K9+1 +1140 B3=0:IF RND(1)<=0.96 THEN goto 1150 +1141 B3=1:B9=B9+1 1150 G(I,J)=K3*100+B3*10+INT(RND(1)*7.98+1.01) 1151 NEXT J 1152 NEXT I @@ -126,108 +68,80 @@ 1154 T9=K9+1 1160 IF B9<>0 THEN 1190 1170 IF G(Q1,Q2)>=200 THEN 1180 -1171 G(Q1,Q2)=G(Q1,Q2)+100 -1172 K9=K9+1 -1180 B9=1 -1181 G(Q1,Q2)=G(Q1,Q2)+10 -1182 Q1=INT(RND(1)*7.98+1.01) -1183 Q2=INT(RND(1)*7.98+1.01) -1190 K7=K9 -1191 IF B9=1 THEN 1200 -1192 X$="s" -1193 X0$=" are " +1171 G(Q1,Q2)=G(Q1,Q2)+100:K9=K9+1 +1180 B9=1:G(Q1,Q2)=G(Q1,Q2)+10:Q1=INT(RND(1)*7.98+1.01):Q2=INT(RND(1)*7.98+1.01) +1190 K7=K9:IF B9=1 THEN 1200 +1191 X$="s":X0$=" are " 1200 PRINT" Your orders are as follows: " -1210 PRINT" Destroy the ",K9," Klingon warships which have invaded" +1210 PRINT" Destroy the ";K9;" Klingon warships which have invaded" 1220 PRINT" the galaxy before they can attack Federation headquarters" -1230 PRINT" on stardate ",T0+T9,". This gives you ",T9," days. there",X0$ -1240 PRINT" ",B9," starbase",X$," in the galaxy for resupplying your ship" -1250 rem PRINT PRINT "hit any key except return when ready to accept command" -1260 rem I=RND(1) IF INP(1)=13 THEN 1260 -1261 PRINT -1262 input "hit return when ready to accept command";i$ +1230 PRINT" on stardate ";T0+T9;". This gives you ";T9;" days. there";X0$ +1240 PRINT" ";B9;" starbase";X$;" in the galaxy for resupplying your ship" +1261 PRINT:INPUT"hit return when ready to accept command";i$ 1270 REM here any time new quadrant entered -1280 Z4=Q1 -1281 Z5=Q2 -1282 K3=0 -1283 B3=0 -1284 S3=0 -1285 G5=0 -1286 D4=0.5*RND(1) -1287 Z(Q1,Q2)=G(Q1,Q2) +1280 Z4=Q1:Z5=Q2:K3=0:B3=0:S3=0:G5=0:D4=0.5*RND(1):Z(Q1,Q2)=G(Q1,Q2) 1290 IF Q1<1 OR Q1>8 OR Q2<1 OR Q2>8 THEN 1410 1300 GOSUB 5040 -1301 PRINT -1302 IF T0 <>T THEN 1330 +1301 PRINT:IF T0 <>T THEN 1330 1310 PRINT"Your mission begins with your starship located" -1320 PRINT"in the galactic quadrant, '";G2$;"'." -1321 GOTO 1340 +1320 PRINT"in the galactic quadrant, '";G2$;"'.":GOTO 1340 1330 PRINT"Now entering ";G2$;" quadrant. . ." -1340 PRINT -1341 K3=INT(G(Q1,Q2)*0.01) -1342 B3=INT(G(Q1,Q2)*0.1)-10*K3 -1350 S3=G(Q1,Q2)-100*K3-10*B3 -1351 IF K3=0 THEN 1400 -1360 PRINT "COMBAT AREA!! Condition RED" -1370 rem PRINT " RED ": -1371 rem PRINT +1340 PRINT:K3=INT(G(Q1,Q2)*0.01):B3=INT(G(Q1,Q2)*0.1)-10*K3 +1350 S3=G(Q1,Q2)-100*K3-10*B3:IF K3=0 THEN 1400 +1360 PRINT "COMBAT AREA!! Condition"; +1370 PRINT " RED "; : PRINT 1380 GOSUB 5290 1381 IF S>200 THEN 1400 -1390 PRINT" SHIELDS DANGEROUSLY LOW" -1391 rem print -1392 rem PRINT SPC(53) +1390 PRINT" SHIELDS DANGEROUSLY LOW"; : PRINT 1400 FOR I=1 TO 3 -1401 K(I,1)=0 -1402 K(I,2)=0 -1403 NEXT I +1401 K(I,1)=0:K(I,2)=0 +1402 NEXT I 1410 FOR I=1 TO 3 1411 K(I,3)=0 1412 NEXT I 1413 Q$=" "*192 1420 REM position enterprise in quadrant, then place "k3" klingons,& 1430 REM "b3" starbases & "s3" stars elsewhere. -1440 A$="\e/" -1441 Z1=S1 -1442 Z2=S2 -1443 GOSUB 4830 -1445 IF K3<1 THEN 1470 +1440 A$="\e/":Z1=S1:Z2=S2 +1441 GOSUB 4830 +1442 IF K3<1 THEN 1470 1450 FOR I=1 TO K3 1451 GOSUB 4800 -1452 A$=chr$(187)+"K"+chr$(171) -1453 Z1=R1 -1454 Z2=R2 +1452 A$=chr$(187)+"K"+chr$(171):Z1=R1:Z2=R2 1460 GOSUB 4830 -1461 K(I,1)=R1 -1462 K(I,2)=R2 -1463 K(I,3)=S9*(0.5+RND(1)) -1464 NEXT I +1461 K(I,1)=R1:K(I,2)=R2:K(I,3)=S9*(0.5+RND(1)) +1462 NEXT I 1470 IF B3<1 THEN 1500 1480 GOSUB 4800 -1481 A$="("+chr$(174)+")" -1482 Z1=R1 -1483 B4=R1 -1484 Z2=R2 -1485 B5=R2 +1481 A$="("+chr$(174)+")":Z1=R1:B4=R1:Z2=R2:B5=R2 1490 GOSUB 4830 1500 FOR I=1 TO S3 -1502 GOSUB 4800 -1503 A$=" * " -1504 Z1=R1 -1505 Z2=R2 -1506 GOSUB 4830 -1507 NEXT I +1501 GOSUB 4800 +1502 A$=" * ":Z1=R1:Z2=R2 +1503 GOSUB 4830 +1504 NEXT I 1510 GOSUB 3720 1520 IF S+E<=10 THEN 1530 1521 IF E>10 OR D(7)>=0 THEN 1580 -1530 PRINT"*** FATAL ERROR ***" +1530 PRINT"*** FATAL ERROR ***"; 1531 GOSUB 5290 -1540 PRINT"You've just stranded your ship in space" -1551 PRINT"You have insufficient maneuvering energy, and shield control" -1561 PRINT"is presently incapable of cross-circuiting to engine room!!" -1571 GOTO 3480 +1540 PRINT"You've just stranded your ship in " +1550 PRINT"space":PRINT"You have insufficient maneuvering energy,"; +1560 PRINT" and shield control":PRINT"is presently incapable of cross"; +1570 PRINT"-circuiting to engine room!!":GOTO 3480 1580 INPUT"command ";A$ 1590 FOR I=1 TO 9 -1591 IF mid$(A$,0,3)<> MID$(A1$,3*I-3,3*I) THEN 1610 -1600 ON I GOTO 1720,1510,2440,2530,2750,3090,3180,3980,3510 +1591 IF MID$(A$,1,3)<> MID$(A1$,3*I-2,3) THEN 1610 +1600 rem ON I GOTO 1720,1510,2440,2530,2750,3090,3180,3980,3510 +1601 if i = 1 THEN 1720 +1602 if i = 2 THEN 1510 +1603 if i = 3 THEN 2440 +1604 if i = 4 THEN 2530 +1605 if i = 5 THEN 2750 +1606 if i = 6 THEN 3090 +1607 if i = 7 THEN 3180 +1608 if i = 8 THEN 3980 +1609 if i = 9 THEN 3510 1610 NEXT I 1611 PRINT"Enter one of the following:" 1620 PRINT" NAV (to set course)" @@ -238,32 +152,21 @@ 1670 PRINT" SHI (to raise or lower shields)" 1680 PRINT" DAM (for damage control reports)" 1690 PRINT" COM (to call on library-computer)" -1700 PRINT" RES (to resign your command)" -1702 PRINT -1703 GOTO 1520 +1700 PRINT" RES (to resign your command)":PRINT:GOTO 1520 1710 REM course control begins here -1720 INPUT"Course (1-9) ";C2$ -1721 C1=VAL(C2$) -1723 IF C1<>9 THEN 1730 -1724 C1=1 +1720 INPUT"Course (1-9) ";C2$:C1=VAL(C2$):IF C1<>9 THEN 1730 +1721 C1=1 1730 IF C1>=1 AND C1<9 THEN 1750 -1740 PRINT" Lt. Sulu reports, 'Incorrect course data, sir!'" -1741 GOTO 1520 -1750 X$="8" -1751 IF D(1)>=0 THEN 1760 -1752 X$="0.2" -1760 PRINT"Warp factor(0-";X$;") " -1761 INPUT C2$ -1762 W1=VAL(C2$) -1763 IF D(1)<0 AND W1>0.2 THEN 1810 +1740 PRINT" Lt. Sulu reports, 'Incorrect course data, sir!'":GOTO 1520 +1750 X$="8":IF D(1)>=0 THEN 1760 +1751 X$="0.2" +1760 PRINT"Warp factor(0-";X$;") ";:INPUT C2$:W1=VAL(C2$):IF D(1)<0 AND W1>0.2 THEN 1810 1770 IF W1>0 AND W1<8 THEN 1820 1780 IF W1=0 THEN 1520 -1790 PRINT" Chief Engineer Scott reports 'The engines won't take warp ";W1;"!" -1801 GOTO 1520 -1810 PRINT"Warp engines are damaged. Maximum speed = warp 0.2" -1811 GOTO 1520 -1820 N1=INT(W1*8+0.5) -1821 IF E-N1>=0 THEN 1900 +1790 PRINT" Chief Engineer Scott reports 'The engines won't take"; +1800 PRINT" warp ";W1;"!":GOTO 1520 +1810 PRINT"Warp engines are damaged. Maximum speed = warp 0.2":GOTO 1520 +1820 N1=INT(W1*8+0.5):IF E-N1>=0 THEN 1900 1830 PRINT"Engineering reports 'Insufficient energy available" 1840 PRINT" for maneuvering at warp ";W1;"!'" 1850 IF S=0 THEN 1990 -1950 D(I)=min(0,D(I)+D6) -1951 IF D(I)<=-0.1 or D(I)>=0 THEN 1960 -1952 D(I)=-0.1 -1953 GOTO 1990 +1950 D(I)=min(0,D(I)+D6):IF D(I)<=-0.1 or D(I)>=0 THEN 1960 +1951 D(I)=-0.1 +1952 GOTO 1990 1960 IF D(I)<0 THEN 1990 1970 IF D1=1 THEN 1980 -1971 D1=1 -1972 PRINT"DAMAGE CONTROL REPORT: " -1980 PRINT TAB(8) -1981 R1=I -1982 GOSUB 4890 -1983 PRINT G2$;" Repair completed." +1971 D1=1:PRINT"DAMAGE CONTROL REPORT: "; +1980 PRINT TAB(8);:R1=I +1981 GOSUB 4890 +1982 PRINT G2$;" Repair completed." 1990 NEXT I 1991 IF RND(1)>0.2 THEN 2070 -2000 R1=INT(RND(1)*7.98+1.01) -2001 IF RND(1)>=0.6 THEN 2040 +2000 R1=INT(RND(1)*7.98+1.01):IF RND(1)>=0.6 THEN 2040 2010 IF K3=0 THEN 2070 -2020 D(R1)=D(R1)-(RND(1)*5+1) -2021 PRINT"DAMAGE CONTROL REPORT: " +2020 D(R1)=D(R1)-(RND(1)*5+1):PRINT"DAMAGE CONTROL REPORT: "; 2030 GOSUB 4890 -2031 PRINT G2$;" damaged" -2032 PRINT -2033 GOTO 2070 -2040 D(R1)=min(0,D(R1)+RND(1)*3+1) -2041 PRINT"DAMAGE CONTROL REPORT: " +2031 PRINT G2$;" damaged":PRINT:GOTO 2070 +2040 D(R1)=min(0,D(R1)+RND(1)*3+1):PRINT"DAMAGE CONTROL REPORT: "; 2050 GOSUB 4890 -2051 PRINT G2$;" State of repair improved" -2052 PRINT +2051 PRINT G2$;" State of repair improved":PRINT 2060 REM begin moving starship -2070 A$=" " -2071 Z1=INT(S1) -2072 Z2=INT(S2) -2073 GOSUB 4830 -2078 Z1=INT(C1) -2079 C1=C1-Z1 -2080 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1 -2081 X=S1 -2082 Y=S2 -2090 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1 -2091 Q4=Q1 -2092 Q5=Q2 +2070 A$=" " :Z1=INT(S1):Z2=INT(S2) +2071 GOSUB 4830 +2079 Z1=INT(C1):C1=C1-Z1 +2080 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1:X=S1:Y=S2 +2090 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1:Q4=Q1:Q5=Q2 2100 FOR I=1 TO N1 -2101 S1=S1+X1 -2102 S2=S2+X2 -2103 IF S1<1 OR S1>=9 OR S2<1 OR S2>=9 THEN 2220 -2110 S8=INT(S1)*24+INT(S2)*3-26 -2111 IF MID$(Q$,S8,S8+2)=" " THEN 2140 -2120 S1=INT(S1-X1) -2121 S2=INT(S2-X2) -2122 PRINT"Warp engines shut down at sector ";S1;",";S2;" due to bad navigation." -2131 GOTO 2150 +2101 S1=S1+X1:S2=S2+X2:IF S1<1 OR S1>=9 OR S2<1 OR S2>=9 THEN 2220 +2110 S8=INT(S1)*24+INT(S2)*3-26:IF MID$(Q$,S8+1,2)=" " THEN 2140 +2120 S1=INT(S1-X1):S2=INT(S2-X2):PRINT"Warp engines shut down at "; +2130 PRINT "sector ";S1;",";S2;" due to bad navigation.":GOTO 2150 2140 NEXT I -2141 S1=INT(S1) -2142 S2=INT(S2) +2141 S1=INT(S1):S2=INT(S2) 2150 A$="\e/" -2160 Z1=INT(S1) -2161 Z2=INT(S2) -2162 GOSUB 4830 -2163 GOSUB 2390 -2164 T8=1 +2160 Z1=INT(S1):Z2=INT(S2) +2161 GOSUB 4830 +2162 GOSUB 2390 +2163 T8=1 2170 IF W1>=1 THEN 2180 2171 T8=0.1*INT(10*W1) -2180 T=T+T8 -2181 IF T>T0+T9 THEN 3480 +2180 T=T+T8:IF T>T0+T9 THEN 3480 2190 REM see if docked then get command 2200 GOTO 1510 2210 REM exceeded quadrant limits -2220 X=8*Q1+X+N1*X1 -2221 Y=8*Q2+Y+N1*X2 -2222 Q1=INT(X/8) -2223 Q2=INT(Y/8) -2224 S1=INT(X-Q1*8) -2230 S2=INT(Y-Q2*8) -2231 IF S1<>0 THEN 2240 -2232 Q1=Q1-1 -2233 S1=8 -2240 IF S2<>0 THEN 2249 -2241 Q2=Q2-1 -2242 S2=8 -2249 X5=0 -2250 IF Q1>=1 THEN 2260 -2251 X5=1 -2252 Q1=1 -2253 S1=1 +2220 X=8*Q1+X+N1*X1:Y=8*Q2+Y+N1*X2:Q1=INT(X/8):Q2=INT(Y/8):S1=INT(X-Q1*8) +2230 S2=INT(Y-Q2*8):IF S1<>0 THEN 2240 +2231 Q1=Q1-1:S1=8 +2240 IF S2<>0 THEN 2250 +2241 Q2=Q2-1:S2=8 +2250 X5=0:IF Q1>=1 THEN 2260 +2251 X5=1:Q1=1:S1=1 2260 IF Q1<=8 THEN 2270 -2261 X5=1 -2262 Q1=8 -2263 S1=8 +2261 X5=1:Q1=8:S1=8 2270 IF Q2>=1 THEN 2280 -2271 X5=1 -2272 Q2=1 -2273 S2=1 +2271 X5=1:Q2=1:S2=1 2280 IF Q2<=8 THEN 2290 -2281 X5=1 -2282 Q2=8 -2283 S2=8 +2281 X5=1:Q2=8:S2=8 2290 IF X5=0 THEN 2360 2300 PRINT"Lt. Uhura reports message from Starfleet Command:" 2310 PRINT" 'Permission to attempt crossing of galactic perimeter" @@ -398,219 +255,151 @@ 2371 GOSUB 2390 2372 GOTO 1280 2380 REM maneuver energy s/r ** -2390 E=E-N1-10 -2391 IF E<=0 THEN 2400 -2392 RETURN +2390 E=E-N1-10:IF E<=0 THEN 2400 +2391 RETURN 2400 PRINT"Shield control supplies energy to complete the maneuver." -2410 S=S+E -2411 E=0 -2412 IF S<=0 THEN 2420 -2413 S=0 +2410 S=S+E:E=0:IF S<=0 THEN 2420 +2411 S=0 2420 RETURN 2430 REM long range sensor scan code 2440 IF D(3)>=0 THEN 2450 -2441 PRINT"Long Range Sensors are inoperable" -2442 GOTO 1520 +2441 PRINT"Long Range Sensors are inoperable":GOTO 1520 2450 PRINT"Long Range Scan for quadrant ";Q1;",";Q2 2460 PRINT "-"*19 -2461 LINE$ = "" 2470 FOR I=Q1-1 TO Q1+1 -2471 N(1)=-1 -2472 N(2)=-2 -2473 N(3)=-3 -2474 FOR J=Q2-1 TO Q2+1 +2471 N(1)=-1:N(2)=-2:N(3)=-3 +2472 FOR J=Q2-1 TO Q2+1 2480 IF I<=0 or I>=9 or J<=0 or J>=9 THEN 2490 2481 N(J-Q2+2)=G(I,J) 2482 REM added so long range sensor scans are added to computer database 2483 z(i,j)=g(i,j) 2490 NEXT J 2491 FOR L=1 TO 3 -2492 REM PRINT"| " -2493 LINE$ = LINE$ + "| " -2494 IF N(L)>=0 THEN 2500 -2495 REM PRINT"*** " -2496 LINE$ = LINE$ + "*** " -2497 GOTO 2510 -2500 REM PRINT mid$(STR$(N(L)+1000),1,4)," " -2501 LINE$ = LINE$ + mid$(STR$(N(L)+1000),1,4) + " " +2492 PRINT"| "; +2493 IF N(L)>=0 THEN 2500 +2494 PRINT"*** ";:GOTO 2510 +2500 PRINT MID$(STR$(N(L)+1000),2,3);" "; 2510 NEXT L -2511 PRINT LINE$ + "|" +2511 PRINT"|" 2512 PRINT "-"*19 -2513 LINE$ = "" -2514 NEXT I -2515 GOTO 1520 +2513 NEXT I +2514 GOTO 1520 2520 REM phaser control code begins here 2530 IF D(4)>=0 THEN 2540 -2531 PRINT"Phasers Inoperative" -2532 GOTO 1520 +2531 PRINT"Phasers Inoperative":GOTO 1520 2540 IF K3>0 THEN 2570 2550 PRINT"Science Officer Spock reports 'Sensors show no enemy ships" -2560 PRINT" in this quadrant'" -2561 GOTO 1520 +2560 PRINT" in this quadrant'":GOTO 1520 2570 IF D(8)>=0 THEN 2580 2571 PRINT"Computer failure hampers accuracy" -2580 PRINT"Phasers locked on target " +2580 PRINT"Phasers locked on target "; 2590 PRINT"Energy available = ";E;" units" -2600 INPUT"Numbers of units to fire ";X -2601 IF X<=0 THEN 1520 +2600 INPUT"Numbers of units to fire ";X:IF X<=0 THEN 1520 2610 IF E-X<0 THEN 2590 2620 E=E-X 2621 GOSUB 5420 2622 IF D(7)<0 THEN 2630 2623 X=X*RND(1) -2624 rem print "Energy * Rnd: ",x 2630 H1=INT(X/K3) 2631 FOR I=1 TO 3 2632 IF K(I,3)<=0 THEN 2730 2640 KSQ1 = (K(I,1)-S1)*(K(I,1)-S1) 2641 KSQ2 = (K(I,2)-S2)*(K(I,2)-S2) 2642 H= SQR( KSQ1 + KSQ2 ) -2643 rem print "distance to enemy #",i,": ",H 2646 H = H1 / H -2647 H = INT(H * (rnd+2)) +2647 H = INT(H * (RND(1)+2)) 2648 IF H>0.15*K(I,3) THEN 2660 -2650 PRINT"Sensors show no damage to enemy at ";K(I,1);",";K(I,2) -2651 GOTO 2730 -2660 K(I,3)=K(I,3)-H -2661 PRINT H,"Unit hit on Klingon at sector ";K(I,1);"," -2670 PRINT K(I,2) -2671 IF K(I,3)> 0 THEN GOTO 2700 +2650 PRINT"Sensors show no damage to enemy at ";K(I,1);",";K(I,2):GOTO 2730 +2660 K(I,3)=K(I,3)-H:PRINT H;"Unit hit on Klingon at sector ";K(I,1);","; +2670 PRINT K(I,2):IF K(I,3)> 0 THEN GOTO 2700 2680 PRINT "**** KLINGON DESTROYED ****" 2690 GOTO 2710 -2700 PRINT" (Sensors show ";K(I,3);" units remaining)" -2701 GOTO 2730 -2710 K3=K3-1 -2711 K9=K9-1 -2712 Z1=K(I,1) -2713 Z2=K(I,2) -2714 A$=" " -2715 GOSUB 4830 -2720 K(I,3)=0 -2721 G(Q1,Q2)=G(Q1,Q2)-100 -2722 Z(Q1,Q2)=G(Q1,Q2) -2723 IF K9<=0 THEN 3680 +2700 PRINT" (Sensors show ";K(I,3);" units remaining)":GOTO 2730 +2710 K3=K3-1:K9=K9-1:Z1=K(I,1):Z2=K(I,2):A$=" " +2711 GOSUB 4830 +2720 K(I,3)=0:G(Q1,Q2)=G(Q1,Q2)-100:Z(Q1,Q2)=G(Q1,Q2):IF K9<=0 THEN 3680 2730 NEXT I 2731 GOSUB 3350 2732 GOTO 1520 2740 REM photon torpedo code begins here 2750 IF P>0 THEN 2760 -2751 PRINT"All photon torpedoes expended" -2752 GOTO 1520 +2751 PRINT"All photon torpedoes expended":GOTO 1520 2760 IF D(5)>=0 THEN 2770 -2761 PRINT"Photon tubes are not operational" -2762 GOTO 1520 -2770 INPUT"Photon torpedo course (1-9) ";C2$ -2771 C1=VAL(C2$) -2772 IF C1<>9 THEN 2780 -2773 C1=1 +2761 PRINT"Photon tubes are not operational":GOTO 1520 +2770 INPUT"Photon torpedo course (1-9) ";C2$:C1=VAL(C2$):IF C1<>9 THEN 2780 +2771 C1=1 2780 IF C1>=1 AND C1<9 THEN 2810 2790 PRINT"Ensign Chekov reports, 'Incorrect course data, sir!'" 2800 GOTO 1520 -2810 Z1=INT(C1) -2811 C1=C1-Z1 -2812 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1 -2813 E=E-2 -2814 P=P-1 -2820 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1 -2821 X=S1 -2822 Y=S2 -2823 GOSUB 5360 +2810 Z1=INT(C1):C1=C1-Z1 +2811 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1:E=E-2:P=P-1 +2820 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1:X=S1:Y=S2 +2821 GOSUB 5360 2830 PRINT"Torpedo track:" -2840 X=X+X1 -2841 Y=Y+X2 -2842 X3=INT(X+0.5) -2843 Y3=INT(Y+0.5) +2840 X=X+X1:Y=Y+X2:X3=INT(X+0.5):Y3=INT(Y+0.5) 2850 IF X3<1 OR X3>8 OR Y3<1 OR Y3>8 THEN 3070 -2860 PRINT" ";X3;",";Y3 -2861 A$=" " -2862 Z1=X -2863 Z2=Y -2864 GOSUB 4990 +2860 PRINT" ";X3;",";Y3:A$=" ":Z1=X:Z2=Y +2861 GOSUB 4990 2870 IF Z3<>0 THEN 2840 -2880 A$=chr$(187)+"K"+chr$(171) -2881 Z1=X -2882 Z2=Y -2883 GOSUB 4990 -2884 IF Z3=0 THEN 2940 +2880 A$=chr$(187)+"K"+chr$(171):Z1=X:Z2=Y +2881 GOSUB 4990 +2882 IF Z3=0 THEN 2940 2890 PRINT"**** KLINGON DESTROYED ****" -2900 K3=K3-1 -2901 K9=K9-1 -2902 IF K9<=0 THEN 3680 +2900 K3=K3-1:K9=K9-1:IF K9<=0 THEN 3680 2910 FOR I=1 TO 3 2911 IF X3=K(I,1) AND Y3=K(I,2) THEN 2930 2920 NEXT I 2921 I=3 -2930 K(I,3)=0 -2931 GOTO 3050 -2940 A$=" * " -2941 Z1=X -2942 Z2=Y -2943 GOSUB 4990 -2944 IF Z3=0 THEN 2960 +2930 K(I,3)=0:GOTO 3050 +2940 A$=" * ":Z1=X:Z2=Y +2941 GOSUB 4990 +2942 IF Z3=0 THEN 2960 2950 PRINT"Star at ";X3;",";Y3;" absorbed torpedo energy." 2951 GOSUB 3350 2952 GOTO 1520 -2960 A$="("+chr$(174)+")" -2961 Z1=X -2962 Z2=Y -2963 GOSUB 4990 -2964 IF Z3<>0 THEN 2970 -2965 PRINT "Torpedo absorbed by unknown object at ";x3;",";y3 -2966 goto 1520 +2960 A$="("+chr$(174)+")":Z1=X:Z2=Y +2961 GOSUB 4990 +2962 IF Z3<>0 THEN 2970 +2963 PRINT "Torpedo absorbed by unknown object at ";x3;",";y3 +2964 goto 1520 2970 PRINT"*** STARBASE DESTROYED ***" -2980 B3=B3-1 -2981 B9=B9-1 +2980 B3=B3-1 : B9=B9-1 2990 IF B9>0 OR K9>T-T0-T9 THEN 3030 3000 PRINT"THAT DOES IT, CAPTAIN!! You are hereby relieved of command" 3010 PRINT"and sentenced to 99 stardates of hard labor on CYGNUS 12!!" 3020 GOTO 3510 3030 PRINT"Starfleet reviewing your record to consider" -3040 PRINT"court martial!" -3041 D0=0 -3050 Z1=X -3051 Z2=Y -3052 A$=" " -3053 GOSUB 4830 -3060 G(Q1,Q2)=K3*100+B3*10+S3 -3061 Z(Q1,Q2)=G(Q1,Q2) -3062 GOSUB 3350 -3063 GOTO 1520 +3040 PRINT"court martial!":D0=0 +3050 Z1=X:Z2=Y:A$=" " +3051 GOSUB 4830 +3060 G(Q1,Q2)=K3*100+B3*10+S3:Z(Q1,Q2)=G(Q1,Q2) +3061 GOSUB 3350 +3062 GOTO 1520 3070 PRINT"Torpedo missed" 3071 GOSUB 3350 3072 GOTO 1520 3080 REM shield control 3090 IF D(7)>=0 THEN 3100 -3091 PRINT"Shield control inoperable" -3092 GOTO 1520 -3100 PRINT"Energy available = ";E+S -3101 INPUT "Number of units to shields? ";X +3091 PRINT"Shield control inoperable":GOTO 1520 +3100 PRINT"Energy available = ";E+S :INPUT "Number of units to shields? ";X 3110 IF X>=0 and S<>X THEN 3120 -3111 PRINT"" -3112 GOTO 1520 +3111 PRINT"":GOTO 1520 3120 IF X" -3141 goto 1990 -3150 E=E+S-X -3151 S=X -3152 PRINT "Deflector Control Room report" -3160 PRINT" 'Shields now at ";INT(S);" units per your command.'" -3161 GOTO 1520 +3140 PRINT"":goto 1990 +3150 E=E+S-X:S=X:PRINT"Deflector Control Room report:" +3160 PRINT" 'Shields now at ";INT(S);" units per your command.'":GOTO 1520 3170 REM damage control 3180 IF D(6)>=0 THEN 3290 -3190 PRINT"Damage control report not available" -3191 IF D0=0 THEN 1520 -3200 D3=0 -3201 FOR I=1 TO 8 -3202 IF D(I)>=0 THEN 3210 -3203 D3=D3+1 +3190 PRINT"Damage control report not available":IF D0=0 THEN 1520 +3200 D3=0:FOR I=1 TO 8 +3201 IF D(I)>=0 THEN 3210 +3202 D3=D3+1 3210 NEXT I 3211 IF D3=0 THEN 1520 -3220 PRINT -3221 D3=D3+D4 -3222 IF D3<1 THEN 3230 -3223 D3=0.9 -3230 PRINT"Technicians standing by to effect repairs to your ship:" +3220 PRINT:D3=D3+D4:IF D3<1 THEN 3230 +3221 D3=0.9 +3230 PRINT"Technicians standing by to effect repairs to your ship;" 3240 PRINT"estimated time to repair: ";0.01*INT(100*D3);" stardates" 3250 INPUT"Will you authorize the repair order (Y/N)? ";A$ 3260 IF A$<>"y" AND A$<> "Y" THEN 1520 @@ -619,16 +408,13 @@ 3272 D(I)=0 3280 NEXT I 3281 T=T+D3+0.1 -3290 PRINT -3291 PRINT"Device state of repair" -3292 FOR R1=1 TO 8 +3290 PRINT:PRINT"Device state of repair" +3291 FOR R1=1 TO 8 3300 GOSUB 4890 -3301 PRINT G2$ -3310 GG2=INT(D(R1)*100)*0.01 -3311 PRINT GG2 +3301 PRINT G2$;tab(25); +3310 GG2=INT(D(R1)*100)*0.01:PRINT GG2 3320 NEXT R1 -3321 PRINT -3322 IF D0<>0 THEN 3200 +3321 PRINT:IF D0<>0 THEN 3200 3330 GOTO 1520 3340 REM klingons shooting 3350 IF K3>0 THEN 3360 @@ -642,36 +428,28 @@ 3381 ksq2 = (K(I,2)-S2)*(K(I,2)-S2) 3382 H=( K(I,3) / SQR( ksq1 + ksq2 ) )*(2+RND(1)) 3383 h = int(h) -3384 S=S-H -3385 K(I,3)=K(I,3)/(3+RND(1)) +3384 S=S-H:K(I,3)=K(I,3)/(3+RND(1)) 3390 PRINT "ENTERPRISE HIT!" 3400 GOSUB 5480 -3401 PRINT H," Unit hit on ENTERPRISE from sector ";K(I,1);",";K(I,2) +3401 PRINT H;" Unit hit on ENTERPRISE from sector ";K(I,1);",";K(I,2) 3410 IF S<=0 THEN 3490 -3420 PRINT" " -3421 IF H<20 THEN 3460 +3420 PRINT" ":IF H<20 THEN 3460 3430 IF RND(1)>0.6 OR H/S<=0.02 THEN 3460 -3440 R1=INT(RND(1)*7.98+1.01) -3441 D(R1)=D(R1)-H/S-0.5*RND(1) -3442 GOSUB 4890 +3440 R1=INT(RND(1)*7.98+1.01):D(R1)=D(R1)-H/S-0.5*RND(1) +3441 GOSUB 4890 3450 PRINT"Damage control reports '";G2$;" damaged by the hit'" 3460 NEXT I 3461 RETURN 3470 REM end of game -3480 PRINT"It is stardate";T -3481 GOTO 3510 -3490 PRINT -3491 PRINT"the ENTERPRISE has been destroyed. The Federation will be conquered" -3501 GOTO 3480 +3480 PRINT"It is stardate";T:GOTO 3510 +3490 PRINT:PRINT"the ENTERPRISE has been destroyed. The Federation "; +3500 PRINT"will be conquered":GOTO 3480 3510 PRINT"There were ";K9;" Klingon battle cruisers left at" 3520 PRINT"the end of your mission" -3530 PRINT -3531 PRINT -3532 IF B9=0 THEN 3670 +3530 PRINT:PRINT:IF B9=0 THEN 3670 3540 PRINT"The Federation is in need of a new starship commander" 3550 PRINT"for a similar mission -- if there is a volunteer," -3560 INPUT"let him or her step forward and enter 'AYE' ";X$ -3561 IF X$="AYE" THEN 520 +3560 INPUT"let him or her step forward and enter 'AYE' ";X$:IF X$="AYE" THEN 520 3570 rem KEY 1,"LIST " 3580 rem KEY 2,"RUN"+CHR$(13) 3590 rem KEY 3,"LOAD"+CHR$(34) @@ -684,83 +462,60 @@ 3660 rem KEY 10,"SCREEN 0,0,0"+CHR$(13) 3670 END 3680 PRINT"Congratulations, Captain! the last Klingon battle cruiser" -3690 PRINT"menacing the Federation has been destroyed." -3691 PRINT -3700 cc1 = k7/(t-t0) -3701 PRINT"Your efficiency rating is ";1000*cc1*cc1 -3703 GOTO 3530 +3690 PRINT"menacing the Federation has been destroyed.":PRINT +3700 PRINT"Your efficiency rating is ";:cc1 = k7/(t-t0):PRINT 1000*cc1*cc1:GOTO 3530 3710 REM short range sensor scan & startup subroutine -3720 A$="("+chr$(174)+")" -3721 Z3=0 -3722 FOR I=S1-1 TO S1+1 -3723 FOR J=S2-1 TO S2+1 +3720 A$="("+chr$(174)+")":Z3=0 +3721 FOR I=S1-1 TO S1+1 +3722 FOR J=S2-1 TO S2+1 3730 IF INT(I+0.5)<1 OR INT(I+0.5)>8 OR INT(J+0.5)<1 OR INT(J+0.5)>8 or Z3=1 THEN 3760 -3750 Z1=I -3751 Z2=J -3752 GOSUB 4990 +3750 Z1=I:Z2=J +3751 GOSUB 4990 3760 NEXT J 3761 NEXT I 3762 IF Z3=1 THEN 3770 -3763 D0=0 -3764 GOTO 3790 -3770 D0=1 -3771 CC$="docked" -3772 E=E0 -3773 P=P0 -3780 PRINT"Shields dropped for docking purposes" -3781 S=0 -3782 GOTO 3810 +3763 D0=0:GOTO 3790 +3770 D0=1:CC$="docked":E=E0:P=P0 +3780 PRINT"Shields dropped for docking purposes":S=0:GOTO 3810 3790 IF K3<=0 THEN 3800 -3791 C$="*red*" -3792 GOTO 3810 -3800 C$="GREEN" -3801 IF E>=E0*0.1 THEN 3810 -3802 C$="YELLOW" +3791 C$="*red*":GOTO 3810 +3800 C$="GREEN":IF E>=E0*0.1 THEN 3810 +3801 C$="YELLOW" 3810 IF D(2)>=0 THEN 3830 -3820 PRINT -3821 PRINT"*** Short Range Sensors are out ***" -3822 PRINT -3823 RETURN +3820 PRINT:PRINT"*** Short Range Sensors are out ***":PRINT +3821 RETURN 3830 PRINT "-"*33 -3831 LINE$ = "" 3832 FOR I=1 TO 8 3840 FOR J=(I-1)*24 TO (I-1)*24+21 STEP 3 -3850 IF MID$(Q$,J,J+3)<>" " THEN 3860 -3851 REM PRINT " . " -3852 LINE$ = LINE$ + " . " -3853 GOTO 3862 -3860 REM PRINT " ";MID$(Q$,J,J+3) -3861 LINE$ = LINE$ + " " + MID$(Q$,J,J+3) -3862 NEXT J -3870 ON I GOTO 3880,3900,3910,3920,3930,3940,3950,3960 -3880 REM PRINT" Stardate " -3881 LINE$ = LINE$ + " Stardate " -3890 TT= T*10 -3891 TT=INT(TT)*0.1 -3892 PRINT LINE$;TT -3894 GOTO 3970 -3900 PRINT LINE$ + " Condition ";C$ -3901 GOTO 3970 -3910 PRINT LINE$+" Quadrant ";Q1;",";Q2 -3911 GOTO 3970 -3920 PRINT LINE$+" Sector ";S1;",";S2 -3921 GOTO 3970 -3930 PRINT LINE$+" Photon torpedoes ";INT(P) -3931 GOTO 3970 -3940 PRINT LINE$+" Total energy ";INT(E+S) -3941 GOTO 3970 -3950 PRINT LINE$+" Shields ";INT(S) -3951 GOTO 3970 -3960 PRINT LINE$+" Klingons remaining ";INT(K9) -3970 LINE$ = "" -3971 NEXT I -3972 PRINT "-"*33 -3973 RETURN +3850 IF MID$(Q$,J+1,3)<>" " THEN 3860 +3851 PRINT " . ";:GOTO 3861 +3860 PRINT " ";MID$(Q$,J+1,3); +3861 NEXT J +3870 rem ON I GOTO 3880,3900,3910,3920,3930,3940,3950,3960 +3871 if i=1 then 3880 +3872 if i=2 then 3900 +3873 if i=3 then 3910 +3874 if i=4 then 3920 +3875 if i=5 then 3930 +3876 if i=6 then 3940 +3877 if i=7 then 3950 +3878 if i=8 then 3960 +3880 PRINT" Stardate "; +3890 TT= T*10 : TT=INT(TT)*0.1:PRINT TT :GOTO 3970 +3900 PRINT" Condition ";C$:GOTO 3970 +3910 PRINT" Quadrant ";Q1;",";Q2:GOTO 3970 +3920 PRINT" Sector ";S1;",";S2:GOTO 3970 +3930 PRINT" Photon torpedoes ";INT(P):GOTO 3970 +3940 PRINT" Total energy ";INT(E+S):GOTO 3970 +3950 PRINT" Shields ";INT(S):GOTO 3970 +3960 PRINT" Klingons remaining ";INT(K9) +3970 NEXT I +3971 PRINT "-"*33 +3972 RETURN 3980 REM library computer code 3990 CM1$="GALSTATORBASDIRREG" -4000 IF D(8)>=0 THEN 4010 -4001 PRINT"Computer Disabled" -4002 GOTO 1520 +4000 IF D(8)>=0 THEN 4010 +4001 PRINT"Computer Disabled":GOTO 1520 4010 rem KEY 1, "GAL RCD"+CHR$(13) 4020 rem KEY 2, "STATUS"+CHR$(13) 4030 rem KEY 3, "TOR DATA"+CHR$(13) @@ -768,11 +523,10 @@ 4050 rem KEY 5, "DIR/DIST"+CHR$(13) 4060 rem KEY 6, "REG MAP"+CHR$(13) 4070 rem KEY 7,CHR$(13):KEY 8,CHR$(13):KEY 9,CHR$(13):KEY 10,CHR$(13) -4074 gosub 4130 -4080 INPUT"Computer active and awaiting command ";CM$ -4081 H8=1 +4071 gosub 4130 +4080 INPUT"Computer active and awaiting command ";CM$:H8=1 4090 FOR K1= 1 TO 6 -4100 IF mid$(CM$,0,3)<>MID$(CM1$,3*K1-3,3*K1) THEN 4120 +4100 IF MID$(CM$,1,3)<>MID$(CM1$,3*K1-2,3) THEN 4120 4110 rem ON K1 GOTO 4230,4400,4490,4750,4550,4210 4111 if k1=1 then 4230 4112 if k1=2 then 4400 @@ -789,244 +543,181 @@ 4160 PRINT" TOR = Photon torpedo data" 4170 PRINT" BAS = Starbase nav data" 4180 PRINT" DIR = Direction/distance calculator" -4190 PRINT" REG = Galaxy 'region name' map" -4191 PRINT -4192 return +4190 PRINT" REG = Galaxy 'region name' map":PRINT +4191 return 4200 REM setup to change cum gal record to galaxy map 4210 GOSUB 840 -4211 H8=0 -4212 G5=1 -4213 PRINT" the galaxy" -4214 GOTO 4290 -4220 REM cum galactic record -4230 rem 'INPUT"Do you want a hardcopy? Is the TTY on (Y/N) ":A$ -4240 rem 'IF A$="y" THEN POKE 1229,2 POKE 1237,3 NULL 1 +4211 H8=0:G5=1:PRINT" the galaxy":GOTO 4290 4250 GOSUB 840 -4260 PRINT -4261 PRINT" " +4260 PRINT:PRINT" "; 4270 PRINT "Computer record of galaxy for quadrant ";Q1;",";Q2 4280 PRINT 4290 PRINT" 1 2 3 4 5 6 7 8" 4300 O1$=" ----- ----- ----- ----- ----- ----- ----- -----" 4310 PRINT O1$ -4312 LINE$ = "" -4313 FOR I=1 TO 8 -4314 REM PRINT I;" " -4315 LINE$ = LINE$ + STR$(I)+ " " -4316 IF H8=0 THEN 4350 +4311 FOR I=1 TO 8 +4312 PRINT I," ";:IF H8=0 THEN 4350 4320 FOR J=1 TO 8 -4321 REM PRINT" " -4322 LINE$ = LINE$ + " " -4323 IF Z(I,J)<>0 THEN 4330 -4324 REM PRINT"***" -4325 LINE$ = LINE$ + "***" -4326 GOTO 4340 +4321 PRINT" ";:IF Z(I,J)<>0 THEN 4330 +4322 PRINT"***";:GOTO 4340 4330 ZLEN = len(str$(z(i,j)+1000) -4331 REM PRINT mid$(STR$(Z(I,J)+1000),zlen-3,zlen) -4332 LINE$ = LINE$ + mid$(STR$(Z(I,J)+1000),zlen-3,zlen) +4331 PRINT MID$(STR$(Z(I,J)+1000),zlen-2,3); 4340 NEXT J 4341 GOTO 4370 -4350 Z4=I -4351 Z5=1 -4352 GOSUB 5040 -4353 J0=INT(15-0.5*LEN(G2$)) -4354 PRINT G2$ +4350 Z4=I:Z5=1 +4351 GOSUB 5040 +4352 J0=INT(15-0.5*LEN(G2$)):PRINT TAB(J0);G2$; 4360 Z5=5 4361 GOSUB 5040 -4362 J0=INT(40-0.5*LEN(G2$)) -4363 PRINT G2$ -4370 PRINT LINE$ -4371 LINE$ = "" -4372 PRINT O1$ -4373 NEXT I -4374 PRINT -4375 rem 'POKE 1229,0 POKE 1237,1 +4362 J0=INT(40-0.5*LEN(G2$)):PRINT TAB(J0);G2$; +4370 PRINT:PRINT O1$ +4371 NEXT I +4372 PRINT +4373 rem 'POKE 1229,0 POKE 1237,1 4380 GOTO 1520 4390 REM status report 4400 GOSUB 840 -4401 PRINT" Status Report" -4402 X$="" -4403 IF K9<=1 THEN 4410 -4404 X$="s" +4401 PRINT" Status Report":X$="":IF K9<=1 THEN 4410 +4402 X$="s" 4410 PRINT"Klingon";X$;" left: ";K9 4420 PRINT"Mission must be completed in ";0.1*INT((T0+T9-T)*10);" stardates" -4430 X$="s" -4431 IF B9>=2 THEN 4440 -4432 X$="" -4433 IF B9<1 THEN 4460 +4430 X$="s":IF B9>=2 THEN 4440 +4431 X$="":IF B9<1 THEN 4460 4440 PRINT"The federation is maintaining ";B9;" starbase";X$;" in the galaxy" 4450 GOTO 3180 4460 PRINT"Your stupidity has left you on your own in" -4470 PRINT" the galaxy -- you have no starbases left!" -4471 GOTO 3180 +4470 PRINT" the galaxy -- you have no starbases left!":GOTO 3180 4480 REM torpedo, base nav, d/d calculator 4490 GOSUB 840 4491 IF K3<=0 THEN 2550 -4500 X$="" -4501 IF K3<=1 THEN 4510 -4502 X$="s" +4500 X$="":IF K3<=1 THEN 4510 +4501 X$="s" 4510 PRINT"From ENTERPRISE to Klingon battle cruiser";X$ -4520 H8=0 -4521 FOR I=1 TO 3 -4522 IF K(I,3)<=0 THEN 4740 -4530 W1=K(I,1) -4531 X=K(I,2) -4540 C1=S1 -4541 A=S2 -4542 GOTO 4590 +4520 H8=0:FOR I=1 TO 3 +4521 IF K(I,3)<=0 THEN 4740 +4530 W1=K(I,1):X=K(I,2) +4540 C1=S1:A=S2:GOTO 4590 4550 GOSUB 840 4551 PRINT"Direction/Distance Calculator:" 4560 PRINT"You are at quadrant ";Q1;",";Q2;" sector ";S1;",";S2 -4570 PRINT"Please enter " -4571 INPUT" initial coordinates (x,y) ";C1,A +4570 PRINT"Please enter ":INPUT" initial coordinates (x,y) ";C1,A 4580 INPUT" Final coordinates (x,y) ";W1,X -4590 X=X-A -4591 A=C1-W1 -4592 aa=abs(a) -4593 ax=abs(x) -4594 IF X<0 THEN 4670 +4590 X=X-A:A=C1-W1:aa=abs(a):ax=abs(x):IF X<0 THEN 4670 4600 IF A<0 THEN 4690 4610 IF X>0 THEN 4630 4620 IF A<>0 THEN 4630 -4621 C1=5 -4622 GOTO 4640 +4621 C1=5:GOTO 4640 4630 C1=1 4640 IF AA<=AX THEN 4660 -4650 PRINT"Direction1 = " -4651 cc1=(AA-AX+AA)/AA -4652 print c1+cc1 -4653 GOTO 4730 -4660 PRINT"Direction2 = " -4661 cc1=C1+(AA/AX) -4662 print cc1 -4663 GOTO 4730 +4650 PRINT"Direction1 = ";:cc1=(AA-AX+AA)/AA:PRINT c1+cc1:GOTO 4730 +4660 PRINT"Direction2 = ";:cc1=C1+(AA/AX):PRINT cc1:GOTO 4730 4670 IF A<=0 THEN 4680 -4671 C1=3 -4672 GOTO 4700 +4671 C1=3:GOTO 4700 4680 IF X=0 THEN 4690 -4681 C1=5 -4682 GOTO 4640 +4681 C1=5:GOTO 4640 4690 C1=7 4700 IF AA>=AX THEN 4720 -4710 PRINT"Direction3 = " -4711 cc1=(AX-AA+AX)/AX -4712 print c1+cc1 -4713 GOTO 4730 -4720 PRINT"Direction4 = " -4721 CC1=C1+(AX/AA) -4722 PRINT CC1 -4730 PRINT"Distance = " -4731 cc1=SQR(x*X+A*A) -4732 print cc1 -4733 IF H8=1 THEN 1520 +4710 PRINT"Direction3 = ";:cc1=(AX-AA+AX)/AX:PRINT c1+cc1:GOTO 4730 +4720 PRINT"Direction4 = ";:CC1=C1+(AX/AA):PRINT CC1 +4730 PRINT"Distance = ";:cc1=SQR(x*X+A*A):PRINT cc1:IF H8=1 THEN 1520 4740 NEXT I 4741 GOTO 1520 4750 GOSUB 840 4751 IF B3=0 THEN 4770 -4752 PRINT "From ENTERPRISE to Starbase" -4754 W1=B4 -4755 X=B5 +4752 PRINT"From ENTERPRISE to Starbase:":W1=B4:X=B5 4760 GOTO 4540 -4770 PRINT"Mr. Spock reports, 'Sensors show no starbases in this" -4780 PRINT"quadrant.'" -4781 GOTO 1520 +4770 PRINT"Mr. Spock reports, 'Sensors show no starbases in this"; +4780 PRINT"quadrant.'":GOTO 1520 4790 REM find empty place in quadrant (for things) -4800 R1=INT(RND(1)*7.98+1.01) -4801 R2=INT(RND(1)*7.98+1.01) -4802 A$=" " -4803 Z1=R1 -4804 Z2=R2 -4805 GOSUB 4990 -4807 IF Z3=0 THEN 4800 +4800 R1=INT(RND(1)*7.98+1.01):R2=INT(RND(1)*7.98+1.01):A$=" ":Z1=R1:Z2=R2 +4801 GOSUB 4990 +4802 IF Z3=0 THEN 4800 4810 RETURN 4820 REM insert in string array for quadrant 4830 S8=INT(Z2-0.5)*3+INT(Z1-0.5)*24+1 4840 IF LEN(A$)=3 THEN 4850 -4841 PRINT"ERROR" -4842 STOP +4841 PRINT"ERROR":STOP 4850 IF S8<>1 THEN 4860 -4851 Q$=A$+mid$(Q$,3,192) -4852 RETURN +4851 Q$=A$+MID$(Q$,4,189):RETURN 4860 IF S8<>190 THEN 4870 -4861 Q$=mid$(Q$,0,189)+A$ -4862 RETURN -4870 Q$=mid$(Q$,0,S8-1)+A$+mid$(Q$,s8+2,192) -4871 RETURN +4861 Q$=MID$(Q$,1,189)+A$:RETURN +4870 Q$=MID$(Q$,1,S8-1)+A$+MID$(Q$,s8+3,192-s8-2):RETURN 4880 REM prints device name -4890 ON R1 GOTO 4900,4910,4920,4930,4940,4950,4960,4970 -4900 G2$="Warp Engines" -4901 RETURN -4910 G2$="Short Range Sensors" -4911 RETURN -4920 G2$="Long Range Sensors" -4921 RETURN -4930 G2$="Phaser Control" -4931 RETURN -4940 G2$="Photon Tubes" -4941 RETURN -4950 G2$="Damage Control" -4951 RETURN -4960 G2$="Shield Control" -4961 RETURN -4970 G2$="Library-Computer" -4971 RETURN +4890 rem ON R1 GOTO 4900,4910,4920,4930,4940,4950,4960,4970 +4891 if r1 = 1 then 4900 +4892 if r1 = 2 then 4910 +4893 if r1 = 3 then 4920 +4894 if r1 = 4 then 4930 +4895 if r1 = 5 then 4940 +4896 if r1 = 6 then 4950 +4897 if r1 = 7 then 4960 +4898 if r1 = 8 then 4970 +4900 G2$="Warp Engines":RETURN +4910 G2$="Short Range Sensors":RETURN +4920 G2$="Long Range Sensors":RETURN +4930 G2$="Phaser Control":RETURN +4940 G2$="Photon Tubes":RETURN +4950 G2$="Damage Control":RETURN +4960 G2$="Shield Control":RETURN +4970 G2$="Library-Computer":RETURN 4980 REM string comparison in quadrant array -4990 Z1=INT(Z1+0.5) -4991 Z2=INT(Z2+0.5) -4992 S8=(Z2-1)*3+(Z1-1)*24 -4993 Z3=0 -5000 IF MID$(Q$,S8,s8+3)=A$ THEN 5010 +4990 Z1=INT(Z1+0.5):Z2=INT(Z2+0.5):S8=(Z2-1)*3+(Z1-1)*24:Z3=0 +5000 IF MID$(Q$,S8+1,3)=A$ THEN 5010 5001 RETURN -5010 Z3=1 -5011 RETURN +5010 Z3=1:RETURN 5020 REM quadrant name in g2$ from z4,z5 (=q1,q2) 5030 REM call with g5=1 to get region name only 5040 IF Z5<=4 THEN 5140 -5041 ON Z4 GOTO 5060,5070,5080,5090,5100,5110,5120,5130 +5041 rem ON Z4 GOTO 5060,5070,5080,5090,5100,5110,5120,5130 +5042 if z4 = 1 then 5060 +5043 if z4 = 2 then 5070 +5044 if z4 = 3 then 5080 +5045 if z4 = 4 then 5090 +5046 if z4 = 5 then 5100 +5047 if z4 = 6 then 5110 +5048 if z4 = 7 then 5120 +5049 if z4 = 8 then 5130 5050 GOTO 5140 -5060 G2$="Antares" -5061 GOTO 5230 -5070 G2$="Rigel" -5071 GOTO 5230 -5080 G2$="Procyon" -5081 GOTO 5230 -5090 G2$="Vega" -5091 GOTO 5230 -5100 G2$="Canopus" -5101 GOTO 5230 -5110 G2$="Altair" -5111 GOTO 5230 -5120 G2$="Sagittarius" -5121 GOTO 5230 -5130 G2$="Pollux" -5131 GOTO 5230 -5140 ON Z4 GOTO 5150,5160,5170,5180,5180,5200,5210,5220 -5150 G2$="Sirius" -5151 GOTO 5230 -5160 G2$="Deneb" -5161 GOTO 5230 -5170 G2$="Capella" -5171 GOTO 5230 -5180 G2$="Betelgeuse" -5181 GOTO 5230 -5190 G2$="Aldebaran" -5191 GOTO 5230 -5200 G2$="Regulus" -5201 GOTO 5230 -5210 G2$="Arcturus" -5211 GOTO 5230 +5060 G2$="Antares":GOTO 5230 +5070 G2$="Rigel":GOTO 5230 +5080 G2$="Procyon":GOTO 5230 +5090 G2$="Vega":GOTO 5230 +5100 G2$="Canopus":GOTO 5230 +5110 G2$="Altair":GOTO 5230 +5120 G2$="Sagittarius":GOTO 5230 +5130 G2$="Pollux":GOTO 5230 +5140 rem ON Z4 GOTO 5150,5160,5170,5180,5180,5200,5210,5220 +5142 if z4 = 1 then 5150 +5143 if z4 = 2 then 5160 +5144 if z4 = 3 then 5170 +5145 if z4 = 4 then 5180 +5146 if z4 = 5 then 5190 +5147 if z4 = 6 then 5200 +5148 if z4 = 7 then 5210 +5149 if z4 = 8 then 5220 +5150 G2$="Sirius":GOTO 5230 +5160 G2$="Deneb":GOTO 5230 +5170 G2$="Capella":GOTO 5230 +5180 G2$="Betelgeuse":GOTO 5230 +5190 G2$="Aldebaran":GOTO 5230 +5200 G2$="Regulus":GOTO 5230 +5210 G2$="Arcturus":GOTO 5230 5220 G2$="Spica" 5230 IF G5=1 THEN 5240 -5231 ON Z5 GOTO 5250,5260,5270,5280,5250,5260,5270,5280 +5231 rem ON Z5 GOTO 5250,5260,5270,5280,5250,5260,5270,5280 +5232 if z4 = 1 then 5250 +5233 if z4 = 2 then 5260 +5234 if z4 = 3 then 5270 +5235 if z4 = 4 then 5280 +5236 if z4 = 5 then 5250 +5237 if z4 = 6 then 5260 +5238 if z4 = 7 then 5270 +5239 if z4 = 8 then 5280 5240 RETURN -5250 G2$=G2$+" i" -5251 RETURN -5260 G2$=G2$+" ii" -5261 RETURN -5270 G2$=G2$+" iii" -5271 RETURN -5280 G2$=G2$+" iv" -5281 RETURN +5250 G2$=G2$+" i":RETURN +5260 G2$=G2$+" ii":RETURN +5270 G2$=G2$+" iii":RETURN +5280 G2$=G2$+" iv":RETURN 5290 rem red alert sound 5291 return 5300 FOR J= 1 TO 4 From 7ac55f7281e13f8cf25843e43dc6708283c62f00 Mon Sep 17 00:00:00 2001 From: brickbots Date: Sat, 11 Sep 2021 12:13:37 -0700 Subject: [PATCH 073/183] Adjust INST in readme --- README.md | 2 +- adventure-fast.bas | 1112 ++++++++++++++++++++++++++++++++++++++++++++ bagels.bas | 83 ++++ basicparser.py | 8 +- regression.bas | 1 + 5 files changed, 1203 insertions(+), 3 deletions(-) create mode 100644 adventure-fast.bas create mode 100644 bagels.bas diff --git a/README.md b/README.md index c65b9ad..9ba39c5 100644 --- a/README.md +++ b/README.md @@ -730,7 +730,7 @@ The functions are: Note that despite the name, this function can return codes outside the ASCII range. * **CHR$**(x) - Returns the character specified by character code *x*. * **INSTR**(x$, y$[, start[, end]]) - Returns position of *y$* inside *x$*, optionally start searching -at position *start* and end at *end*. Returns -1 if no match found. +at position *start* and end at *end*. Returns 0 if no match found. * **LEN**(x$) - Returns the length of *x$*. * **LOWER$**(x$) - Returns a lower-case version of *x$*. * **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y*. If z is provided, that number of characters is returned, if omitted the entire rest of the string is returned diff --git a/adventure-fast.bas b/adventure-fast.bas new file mode 100644 index 0000000..c9d06d5 --- /dev/null +++ b/adventure-fast.bas @@ -0,0 +1,1112 @@ +10 rem ADVENTURE/3000 VERSION 3.2 27 FEB 1979 AT 5:30 PM +11 rem THIS PROGRAM IS RELATIVELY BUG-FREE, BUT ONE STILL MUST +12 rem realize that murphy 'S LAW STILL PREVAILS! +13 rem +14 rem ADVENTURE: PROGRAMMED IN HP/3000 BASIC BY BENJAMIN MOSER +15 rem JAMES MADISON HIGH SCHOOL, VIENNA, VIRGINIA. THE BASIC LAYOUT OF THE +16 rem GAME WAS CONCEIVED BY DON WOODS & WILLIE CROWTHER, BOTH OF M.I.T. +17 rem +18 rem ADVENTURE WAS PORTED TO THE MACINTOSH PLUS BY THE ELIZABETH AND DAVID HUNTER +19 rem IN MARCH 1998 AND THEN TO PYBASIC FOR THE RASPBERRY RP2040 IN JUNE 2021 +20 rem PRINT "Adventure 3.2 on ";date$;" at ";time$ +21 PRINT "Adventure 3.2" +22 open "AMESSAGE" for INPUT as #3:open "AMOVING" for INPUT as #4 +23 open "ADESCRIP" for INPUT as #1:open "AITEMS" for INPUT as #2 +44 rem dirs is an array of possible room directions, it replaces file AMOVING +45 dim dirs(100,10) +46 dim indx(303) +47 dim fraindx(10) +50 dim s(99) +51 dim v(100) +52 dim k(2) +53 dim o(15) +54 string$ = "100 N 101 NE 102 E 103 SE 104 S 105 SW 106 W 107 NW 108 U 109 D 110 PLUGH 111 XYZZY 112 PLOVER 113 CROSS 114 CLIMB 115 JUMP 116 FILL 117 EMPTY 117 POUR 118 LOOK 118 L 119 LIGHT 119 ON 120 EXTINGUISH 120 OFF 121 IN 121 ENTER 122 LEAVE 122 OUT 123 INVENTORY 123 I 124 GET 124 CATCH 124 TAKE 125 DROP 125 DUMP 047 ALL 047 EVERYTHING 126 THROW 127 ATTACK 127 KILL 128 FEED 129 WATER 130 LOCK 131 UNLOCK 132 FREE 132 RELEASE 133 WAVE 134 OPEN 135 CLOSE 136 OIL 137 EAT 138 DRINK 139 FEE FIE FOE FOO 140 SHORT 141 LONG 142 BRIEF 143 QUIT 143 STOP 143 END 144 SCORE 145 SAVE 146 LOAD 147 READ 147 EXAMINE 148 YES 148 Y 149 BUG 001 GOLD 001 NUGGET 002 BARS 002 SILVER 003 JEWELRY 004 COINS 005 DIAMONDS 006 MING 006 VASE 007 PEARL 008 EGGS 008 NEST 009 TRIDENT 010 EMERALD 011 PLATINUM 011 PYRAMID 012 CHAIN 013 SPICES 014 PERSIAN 014 RUG 015 TREASURE 015 CHEST 016 WATER 017 OIL 018 LAMP 018 LANTERN 019 KEYS 020 FOOD 021 BOTTLE 022 CAGE 023 ROD 023 WAND 024 CLAM 025 MAGAZINE 026 BEAR 027 AXE 028 VELVET 028 PILLOW 029 SHARDS 030 OYSTER 031 BIRD 032 TROLL 033 DRAGON 034 SNAKE 035 DWARF 036 ROCK 036 BOULDER 037 STAIRS 038 STEPS 039 HOUSE 039 BUILDING 040 GRATE 041 STREAM 042 ROOM 043 BRIDGE 044 PIT 045 VOLCANO 046 ROAD 100 NORTH 101 NORTHEAST 102 EAST 103 SOUTHEAST 104 SOUTH 105 SOUTHWEST 106 WEST 107 NORTHWEST 108 UP 109 DOWN " +70 PRINT:PRINT "Initializing."; +110 rem initialize +130 rem total rooms, items,and keywords +150 l1 = int(RND(1)*4)+1 : l2 = l1 +160 g = 0 : b0 = 1 : sn = 1 : d1 = 1 : d2 = 0 : t = 1 : b1 = 0 : b2 = 0 : p1 = 0: dead = 0: lastc$="" +161 l = 0 : c = 0 : d3 = 0 : b3 = 0 : d0 = 2 : t1 = 100 : t2 = 35 : t3 = 149 : r0 = 0 : c0 = 0: c$="" +162 KC = 1.03 +170 for ii = 1 to 99 +171 s(ii) = 0 : v(ii) = 0 +172 next ii +175 PRINT ".";:v(100) = 0 +182 indx(1) = -1:indx(2)=-1 +190 rem read in possible movement direction array +192 for z2 = 1 to 100 +193 INPUT #4,dx1,dx2,dx3,dx4,dx5,dx6,dx7,dx8,dx9,dx0 +194 dirs(z2,1)=dx1:dirs(z2,2)=dx2:dirs(z2,3)=dx3:dirs(z2,4)=dx4:dirs(z2,5)=dx5 +195 dirs(z2,6)=dx6:dirs(z2,7)=dx7:dirs(z2,8)=dx8:dirs(z2,9)=dx9:dirs(z2,10)=dx0 +196 PRINT "."; +197 next z2 +198 close #4 +200 restore 230 +210 rem read in locations of items +220 for z2 = 1 to T2 step 5 +221 read dx1,dx2,dx3,dx4,dx5:s(z2)=dx1:s(z2+1)=dx2:s(z2+2)=dx3:s(z2+3)=dx4:s(z2+4)=dx5 +222 PRINT "."; +228 next z2 +230 data 18,25,23,24,21 +231 data 52,0,71,74,58 +232 data 59,69,66,82,100 +233 data 7,49,7,7,7 +234 data 7,12,13,40,38 +235 data 69,0,46,0,0 +236 data 15,60,82,22,250 +240 for ii = 1 to 15 step 5 +241 read dx1,dx2,dx3,dx4,dx5:o(ii)=dx1:o(ii+1)=dx2:o(ii+2)=dx3:o(ii+3)=dx4:o(ii+4)=dx5 +242 PRINT "."; +243 next ii +245 data 1,2,2,2,2 +246 data 3,4,3,3,2 +247 data 5,3,2,3,3 +260 rem ASK IF HE WANTS DIRECTIONS +263 z59 = 301:gosub 7620 +264 gosub 9860 +266 if z0 then 267 else 320 +267 z59 = 302:gosub 7620 +280 rem command INPUT routine +300 rem PRINT room, items +310 rem If it's dark don't let him see anything +320 if l1 < 13 or l1 = 58 then 350 +321 if l = 1 and (s(18) = l1 or s(18) = -1) then 350 +330 z59 = 45:gosub 7620 +340 goto 400 +350 on d0+1 gosub 6780,7990,8180 +360 v(l1) = 1 +370 gosub 6680 +375 if dead = 1 then 9540 +380 gosub 7800 +390 rem INPUT LOOP --- MULTIPLE COMMAANDS, REMOVE JUNK CHARACTERS +400 rem if len(c$) > 0 then 500 +410 INPUT ">";c$:c$=upper$(c$) +420 if c$ = "" then 410 +425 PRINT:PRINT:NKEY=0 +431 if c$ <> lastc$ then 433 +432 c$ = "":goto 900 +433 numkeys=1:lastc$ = c$:a$="" +449 rem 65-90 = A-Z 48-57 = 0-9 46 = . +450 for x = 1 to len(c$) +460 z5 = asc(mid$(c$,x,1)) +470 if (z5 > 64 and z5 < 91) or (z5 > 47 and z5 < 58) or z5 = 46 then 480 else 471 +471 c$ = mid$(c$,1,x-1)+" "+mid$(c$,x+1) +480 next x +490 if instr(c$," ")=0 then 520 +500 numkeys=2:a$ = mid$(c$,instr(c$," "),len(c$)-instr(c$," ")+1)+" " +510 c$ = mid$(c$,1,instr(c$," ")-1) +520 c$ = " "+c$+" " +800 k(1)=0:k(2)=0:z3 = 0 +870 a1 = instr(string$,c$):if a1=0 then 872 +871 c$ = mid$(string$,a1-3,3):k(1)=int(val(c$)) +872 if numkeys=1 then 900 +873 a1 = instr(string$,a$):if a1=0 then 900 +874 b$ = mid$(string$,a1-3,3):k(2)=int(val(b$)) +890 rem EXOTIC WORDS +900 z0 = 36 +910 if k(1) >= z0 and k(1) <=46 then 930 +920 if k(2) >= z0 and k(2) <=46 then 930 else 980 +930 gosub 8390 +940 PRINT "What do you want to do with the ";d$;"?" +950 goto 410 +980 if k(1) >= 110 and k(1) <= T3 then 1950 +990 if k(2) >= 110 and k(2) <= T3 then 1950 +1000 rem THEN IS IT A DIRECTION +1010 for d = 1 to 10 +1020 if k(1) = d+99 or k(2) = d+99 then 1070 +1030 next d +1040 rem COMMAND NOT A DIRECTION +1050 goto 1950 +1060 rem CAN HE MOVE THAT WAY? +1070 z2 = dirs(L1,d) +1130 if z2 = 255 then 1470 +1140 if z2 < 1 or z2 > 254 then 1220 +1150 rem NORMAL MOVING +1160 rem CHECK FOR SPECIAL MOVE CONDITIONS +1170 goto 1260 +1180 l2 = l1 : l1 = z2 +1190 if s(35) = l2 then 1191 else 1200 +1191 s(35) = l1 +1200 goto 9780 +1220 z59 = 1 +1221 gosub 7620 +1230 goto 400 +1240 rem SPECIAL ROOM DIRECTIONS +1250 rem GRATE +1260 if L1 = 10 and (d = 10 or d = 5) THEN 1280 +1261 IF L1 = 11 and (d = 9 or d = 3) then 1280 else 1320 +1270 rem IF GRATE IS OPEN (G=0) MOVE HIM +1280 if g = 1 then 1180 +1290 z59 = 10:gosub 7620 +1300 goto 400 +1310 rem CAN'T TAKE NUGGET UPPSTAIRS +1320 if not (l1 = 17 and d = 9 and s(1) = -1) then 1360 +1330 z59 = 38:gosub 7620 +1340 goto 400 +1350 rem CRYSTAL BRIDGE AND FISSURE +1360 if not (l1 = 19 and d = 7 or l1 = 20 and d = 3) then 1410 +1370 if b2 then 1180 +1380 z59 = 3:gosub 7620 +1390 goto 400 +1400 rem MT. KING & SNAKE +1410 if not (l1 = 22 and d <> 3 and d <> 9) then 1690 +1420 if sn = 0 then 1180 +1430 z59 = 50:gosub 7620 +1440 goto 400 +1460 rem bedquilt and random directiosn +1470 if l1 <> 44 then 1590 +1480 if RND(1) > 0.5 then 1510 +1490 z59 = 52:gosub 7620 +1500 goto 400 +1510 restore 1530 +1520 rem ROOMS TO JU +1530 data 33,37,45,92,76 +1540 for z3 = 1 to int(RND(1)*5)+1 +1550 read z2 +1560 next z3 +1570 goto 1180 +1580 rem WITT's END +1590 if l1 <> 39 then 1220 +1600 rem SHOULD WE LET HIM OUT? +1610 if RND(1) < 0.15 then 1650 +1620 rem NO +1630 z59 = 52:gosub 7620 +1640 goto 400 +1650 rem YES +1660 z2 = 38 +1670 goto 1180 +1680 rem Narrow Tunnel +1690 if not (l1 = 57 or l1 = 58) then 1780 +1691 if k(1) <> 102 and k(2) <> 102 and k(1) <> 106 and k(2) <> 106 then 1780 +1700 for z3 = 1 to t2 +1710 if z3 = 10 then 1750 +1720 if s(z3) <> -1 then 1750 +1730 z59 = 53:gosub 7620 +1740 goto 400 +1750 next z3 +1760 goto 1180 +1770 rem TROLL +1780 IF L1=60 AND D=2 THEN 1790 +1781 IF L1=61 AND D=6 THEN 1790 ELSE 1860 +1790 on t+1 goto 1180,1800,1820,1840 +1800 z59 = 55:gosub 7620 +1810 goto 400 +1820 z59 = 56:gosub 7620 +1821 z59 = 55:gosub 7620 +1830 T=1:goto 400 +1840 t = 2 +1850 goto 1180 +1860 if not (l1 = 73 and d = 1 and d2 = 0) then 1890 +1870 z59 = 57:gosub 7620 +1880 goto 400 +1890 if not (l1 = 82 and s(33) = l1 and d = 1) then 1180 +1900 rem DRAGON +1910 z59 = 51:gosub 7620 +1920 goto 400 +1940 rem OTHER COMMANDS +1950 z1=k(1):if k(1) >= 110 and k(1) <= T3 then 2090 +1960 z1=k(2):if k(2) >= 110 and k(2) <= T3 then 2090 +1980 rem ITEM BO NO VERB? +1990 if k(1) >= 1 and k(1) <= 35 then 2000 +1991 if k(2) >= 1 and k(2) <= 35 then 2000 else 2040 +2000 for x = 1 to 35 +2020 if k(1) <> x and k(2) <> x then 2030 +2021 restore 9960+x +2022 read d$ +2023 goto 940 +2030 next x +2040 restore 2070 +2050 for x = 1 to int(RND(1)*4)+1 +2051 read b$ +2052 next x +2060 PRINT b$ +2070 data "What?","I don't understand.","I can't understand that.","I don't know that word." +2080 goto 400 +2090 z1 = z1-109 +2095 on z1 goto 2120,2220,2300,2390,2570,2640,2680,2860,2930,3000,3080,3130,3290,3450,3590,3920,4160,4430,4630,4800,4950,5060,5190,5410,5560,5750,5810,5920,6000,6090,6230,6270,6310,6350,6410,8970,9080,9220,4490,9330 +2110 goto 2040 +2120 REM *** PLUGH *** +2130 IF L1<>7 THEN 2170 +2140 IF S(35)=L1 THEN S(35)=0 +2150 Z2=26 +2160 GOTO 1180 +2170 IF L1<>26 THEN 2200 +2180 Z2=7 +2190 GOTO 1180 +2200 z59 = 2:gosub 7620 +2210 GOTO 400 +2220 REM *** XYZZY *** +2230 IF L1<>7 THEN 2270 +2240 IF S(35)=L1 THEN S(35)=0 +2250 Z2=13 +2260 GOTO 1180 +2270 IF L1<>13 THEN 2200 +2280 Z2=7 +2290 GOTO 1180 +2300 REM *** PLOVER *** (CAN'T BRING EMERALD WITH HIM) +2310 IF L1>26 THEN 2360 +2320 IF S(35)<>L1 THEN 2330 +2325 S(35) = 0 +2330 IF S(10)<>-1 THEN 2340 +2335 S(10) = L1 +2340 Z2 = 58 +2350 GOTO 1180 +2360 IF L1<>58 THEN 2200 +2370 Z2=26 +2380 GOTO 1180 +2390 REM *** CROSS *** +2400 IF L1<>19 THEN 2470 +2410 IF B2<>0 THEN 2440 +2420 z59 = 3:gosub 7620 +2430 GOTO 400 +2440 D=7 +2450 REM JUST GIVE NEW DIRECTION, USE MOVE ROUTINE +2460 GOTO 1070 +2470 IF L1<>20 THEN 2510 +2480 IF B2=0 THEN 2420 +2490 D=3 +2500 GOTO 1070 +2510 IF L1<>60 THEN 2540 +2520 D=2 +2530 GOTO 1070 +2540 IF L1<>61 THEN 2200 +2550 D=6 +2560 GOTO 1070 +2570 REM *** CLIMB *** +2580 IF L1<>50 THEN 2200 +2590 REM CAN HE CLIMB BEANSTALK? +2600 IF P1<2 THEN 2200 +2610 REM YES +2620 Z2=70 +2630 GOTO 1180 +2640 REM *** JUMP *** STRICTLY SUICIDAL +2650 IF L1<>16 AND L1<>19 AND L1<>20 AND L1<>27 THEN 2200 +2660 z59 = 4:gosub 7620 +2670 GOTO 9550 +2680 REM FILL +2690 IF S(21)=-1 THEN 2730 +2700 B$="bottle" +2710 PRINT "You don't have the ";b$ +2720 goto 410 +2730 IF B0=0 THEN 2760 +2740 z59 = 5:gosub 7620 +2750 GOTO 410 +2760 IF L1<>7 AND L1<>8 AND L1<>9 AND L1<>35 AND L1<>74 AND L1<>81 THEN 2790 +2770 B0=1:S(16)=-1 +2780 GOTO 2840 +2790 IF L1=49 THEN 2830 +2800 B$="oil" +2810 PRINT "I see no ";B$;" here." +2820 GOTO 400 +2830 B0=2:S(17)=-1 +2840 PRINT "The bottle is now filled." +2850 GOTO 400 +2860 REM *** EMPTY *** +2870 IF S(21)=-1 THEN 2890 +2880 GOTO 2700 +2890 REM EMPTY BOTTLE (ASSUMED FULL) +2900 S(B0+15)=0:B0=0 +2910 PRINT "Emptied" +2920 GOTO 400 +2930 rem *** LOOK *** +2940 if l1 < 13 or l1 = 58 then 2970 +2941 if l = 1 and (s(18) = l1 or s(18) = -1) then 2970 +2950 z59 = 45:gosub 7620 +2960 goto 400 +2970 gosub 8050 +2980 gosub 6680 +2990 goto 400 +3000 rem *** LIGHT *** +3010 if s(18) = -1 then 3040 +3020 b$ = "lamp" +3030 goto 2710 +3040 l = 1 +3050 b$ = "on" +3060 PRINT "The lamp is now ";b$ +3070 goto 2940 +3080 REM *** OFF (EXTINGUSIH) *** +3090 IF S(18)=-1 THEN 3110 +3100 GOTO 3020 +3110 L=0:B$="off" +3120 GOTO 3060 +3130 REM *** ENTER *** +3140 IF L1<>6 THEN 3180 +3150 REM TO HOUSE +3160 D=3 +3170 GOTO 1070 +3180 IF L1<>68 THEN 3240 +3190 REM TO BARREN ROOM +3200 D=3 +3210 GOTO 1070 +3240 FOR D=10 TO 1 step -1 +3250 Z2 = DIRS(L1,D) +3260 IF Z2>0 AND Z2<101 THEN 1150 +3270 NEXT D +3280 GOTO 2200 +3290 REM ** LEAVE *** +3300 IF L1<>7 THEN 3340 +3310 REM LEAVE HOUSE +3320 D=7 +3330 GOTO 1070 +3340 IF L1<>69 THEN 3400 +3350 REM LEAVE BARREN ROOM +3360 D = 7 +3370 GOTO 1070 +3400 FOR D = 1 TO 10 +3410 Z2 = DIRS(L1,D) +3420 IF Z2>0 AND Z2<101 THEN 1150 +3430 NEXT D +3440 GOTO 2200 +3450 REM *** INVENTORY *** +3470 Z0=0 +3480 PRINT "You are carrying:"; +3490 FOR X=1 TO T2 +3510 IF S(X)<>-1 THEN 3540 +3511 restore 9960+x:read b$ +3520 PRINT B$ +3530 Z0 = Z0 + 1 +3540 NEXT X +3550 IF Z0=0 THEN 3551 else 3560 +3551 PRINT "nothing." +3560 PRINT +3570 GOTO 400 +3590 rem *** GET *** +3630 if k(1) = 47 or k(2) = 47 then 3680 +3640 gosub 8280 +3650 if z8 > 0 then 3680 +3660 PRINT "Get what?" +3670 goto 2040 +3680 for z3 = 1 to t2 +3710 if k(1) = 47 or k(2) = 47 then 3730 +3720 if k(1) <> z3 and k(2) <> z3 then 3900 +3730 if s(z3) <> l1 then 3750 +3740 if s(z3) = l1 then 3790 +3750 if k(1) = 47 or k(2) = 47 then 3900 +3760 restore 9960+z3:read a$:PRINT a$;" not here." +3770 goto 3900 +3780 rem MUST CHECK NOW FOR LEGALITY OF TAKING ITEM +3790 z8 = 0 +3800 for x = 1 to t2 +3810 if s(x) <> -1 then 3820 +3811 z8 = z8+1 +3820 next x +3830 if z8 < 7 then 3870 +3840 rem CARRYING TOO MUCH +3850 z59 = 54:gosub 7620 +3860 goto 410 +3870 goto 6880 +3880 s(z3) = -1 +3890 restore 9960+z3:read a$:PRINT a$;":taken." +3900 next z3 +3910 goto 400 +3920 REM *** DROP *** +3940 if k(1) = 47 or k(2) = 47 then 4000 +3950 gosub 8280 +3960 IF Z8>0 THEN 4000 +3970 PRINT "Drop what?" +3980 GOTO 2040 +4000 FOR Z3=1 TO T2 +4030 IF K(1) = 47 or k(2) = 47 THEN 4060 +4040 IF K(1) <> Z3 and k(2) <> z3 THEN 4140 +4050 IF S(Z3)=0 THEN 4140 +4060 IF S(Z3)=-1 THEN 4100 +4070 IF K(1)=47 or k(2)=47 THEN 4140 +4080 restore 9960+z3:read b$:PRINT "You don't have the ";B$ +4090 GOTO 4140 +4100 REM STILL NEED TO ELABORATE ON DROP (BIRD IN CAGE, BOTTLE) +4110 GOTO 7380 +4120 restore 9960+z3:read b$:PRINT B$;":dropped." +4130 S(Z3)=L1 +4140 NEXT Z3 +4150 GOTO 400 +4160 REM *** THROW *** +4170 GOSUB 8280 +4180 IF Z8>0 THEN 4210 +4190 PRINT "Throw what?" +4200 GOTO 2040 +4210 IF S(Z3)<>-1 THEN 2710 +4220 IF NOT (Z3<16 AND S(32)=L1) THEN 4260 +4230 REM THROW TREASURE TO TROLL +4240 z59 = 27:gosub 7620 +4241 S(Z3)=0:T=3:GOTO 400 +4260 IF NOT (Z3=27 AND S(32)=L1) THEN 4300 +4270 REM TRYING TO BUTCHER TROLL? +4280 z59 = 26:gosub 7620 +4281 S(27)=L1:GOTO 400 +4300 IF NOT (Z3=27 AND S(35)=L1) THEN 4380 +4310 REM TRYING TO KILL DWARF +4320 IF RND(1)>0.5 THEN 4360 +4330 z59 = 29:gosub 7620 +4340 GOSUB 8650 +4350 GOTO 4410 +4360 z59 = 30:gosub 7620 +4361 S(35)=0:GOTO 4410 +4380 REM NOTHING SPECIAL, JUST DROP ITEM +4390 IF S(35)<>L1 THEN 4400 +4391 GOSUB 8550 +4400 PRINT "Thrown." +4410 S(Z3) = L1 +4415 if dead = 1 then 9540 +4420 GOTO 400 +4430 REM *** ATTACK *** +4440 GOSUB 8280 +4450 IF NOT (Z3=33 AND S(Z3)=L1 AND L1=82) THEN 4520 +4460 REM HE CAN KILL DRAGON +4470 z59 = 68:gosub 7620 +4480 GOTO 410 +4490 IF L1<>82 THEN 2040 +4500 z59 = 69:gosub 7620 +4501 S(33)=0:D1=0:GOTO 400 +4520 IF S(32)<>L1 THEN 4560 +4530 REM TRYING TO MUNGE TROLL +4540 Z9=FNA(25):GOTO 400 +4560 IF NOT (Z3=26 OR Z3>30) THEN 4600 +4570 REM DANGEROUS TO ATTACK THESE +4580 z59 = 70:gosub 7620 +4590 GOTO 400 +4600 REM NOTHING TO ATTACK +4610 z59 = 71:gosub 7620 +4620 GOTO 400 +4630 REM *** FEED *** +4640 GOSUB 8280 +4650 IF Z3<>35 THEN 4690 +4660 REM CAN'T FEED DWARF! +4670 z59 = 24:gosub 7620 +4680 GOTO 400 +4690 IF S(20) = -1 THEN 4720 +4700 B$ = "FOOD":GOTO 2710 +4720 IF L1=69 THEN 4760 +4730 PRINT "I can't feed it." +4740 z59 = 23:gosub 7620 +4750 GOTO 400 +4760 IF S(20)=L1 THEN 7600 +4770 B1=1:S(20)=0:z59 = 6:gosub 7620 +4790 GOTO 400 +4800 REM *** WATER *** +4810 IF S(16) = -1 THEN 4840 +4820 B$ = "water":GOTO 2710 +4840 IF L1<>50 THEN 2200 +4850 REM GOTO P1+1 OF 4860,4890,4920 +4851 IF P1 = 0 THEN 4860 +4852 IF P1 = 1 THEN 4890 +4853 IF P1 = 2 THEN 4920 +4860 z59 = 7:gosub 7620 +4870 P1=1:S(16)=0:B0=0:GOTO 400 +4890 z59 = 8:gosub 7620 +4900 P1=2:S(16)=0:B0=0:GOTO 400 +4920 z59 = 9:gosub 7620 +4930 P1=0:S(16)=0:B0=0:GOTO 400 +4950 REM *** LOCK *** +4960 IF L1=10 OR L1=11 THEN 4990 +4970 REM NOTHING LOCKABLE +4980 GOTO 2200 +4990 IF S(19)=-1 THEN 5020 +5000 B$="keys":goto 2710 +5020 G=0:z59 = 10:gosub 7620 +5040 GOTO 400 +5060 REM *** UNLOCK *** +5070 IF S(19)<>-1 THEN 5000 +5080 IF L1<>10 AND L1<>11 THEN 5120 +5090 G=1:z59 = 11:gosub 7620 +5110 GOTO 400 +5120 IF L1<>69 THEN 2200 +5130 IF B1>0 THEN 5160 +5140 z59 = 12:gosub 7620 +5150 goto 400 +5160 IF C<>0 THEN 5170 +5165 C=1:B1=2 +5170 z59 = 13:gosub 7620 +5180 GOTO 400 +5190 REM *** FREE *** +5200 IF K(1) = 31 or k(2) = 31 THEN 5240 +5210 REM CAN'T FREE ANYTHING BUT BIRD +5220 z59 = 2:gosub 7620 +5230 GOTO 410 +5240 IF S(31)<>-1 THEN 5220 +5250 S(31) = L1:B3=0 +5260 PRINT "Freed." +5270 IF L1<>22 THEN 5350 +5280 IF SN<>1 THEN 400 +5290 B$="snake" +5300 PRINT "The little bird attacks the green ";B$;" and" +5310 IF L1=82 THEN 5380 +5320 PRINT "drives it off" +5330 SN=0:S(34)=0:GOTO 400 +5350 IF L1<>82 THEN 400 +5360 B$="dragon":GOTO 5300 +5380 PRINT "gets burned to a crisp" +5390 S(31)=0 +5400 GOTO 400 +5410 REM *** WAVE *** +5420 IF K(1) <> 23 and k(2) <> 23 THEN 2200 +5430 IF S(23)=-1 THEN 5460 +5440 B$="rod":GOTO 2710 +5460 REM IS HERE NEAR FISSURE +5470 IF L1<>19 AND L1<>20 THEN 2200 +5480 REM yes +5490 REM GOTO B2+1 OF 5500,5530 +5491 IF B2=0 THEN 5500 +5492 IF B2=1 THEN 5530 +5500 z59 = 14:gosub 7620 +5510 B2=1:GOTO 400 +5530 z59 = 15:gosub 7620 +5540 B2=0:GOTO 400 +5560 REM *** OPEN *** +5570 GOSUB 8280 +5580 IF Z3>0 THEN 5610 +5590 PRINT "Open ";:goto 2040 +5610 IF Z3=40 THEN 5070 +5620 IF S(Z3)=L1 THEN 5650 +5630 PRINT "I see no ";b$;" here.":goto 400 +5650 if z3=24 THEN 5680 +5660 PRINT "I don't know how to open a ";B$:GOTO 400 +5680 IF S(9)=-1 THEN 5710 +5690 z59 = 16:gosub 7620 +5700 GOTO 400 +5710 IF S(Z3) = 0 THEN 2200 +5720 REM HE'S OPENED CLAM, SO PRINT DESCRIPTION OF THIS +5730 REM PUT PEARL IN CUL-DE-SAC +5740 S(7)=43:S(24)=0:S(30)=L1:z59 = 17:gosub 7620 +5750 GOTO 400 +5760 REM *** CLOSE *** +5770 GOSUB 8280 +5780 IF Z3=40 THEN 4960 +5790 z59 = 18:gosub 7620 +5800 GOTO 400 +5810 REM OIL +5820 IF K(1) <> 17 and k(2) <> 17 THEN 2200 +5830 IF S(17)=-1 THEN 5860 +5840 B$="oil":GOTO 5630 +5860 IF L1<>73 THEN 2200 +5870 REM IS DOOR STILL RUSTED +5880 IF D2=1 THEN 2200 +5890 D2=1:S(17)=0:B0=0:z59 = 19:gosub 7620 +5900 GOTO 400 +5910 REM *** EAT *** +5920 IF K(1) = 20 or k(2) = 20 THEN 5950 +5930 z59 = 20:gosub 7620 +5940 GOTO 410 +5950 Z3=20:GOSUB 8490 +5970 IF Z5=0 THEN 410 +5980 z59 = 73:gosub 7620 +5381 S(20)=0:B0=0:GOTO 400 +6000 REM *** DRINK *** +6010 IF K(1) = 16 or k(2) =16 THEN 6040 +6020 z59 = 21:gosub 7620 +6030 GOTO 410 +6040 Z3=16:GOSUB 8490 +6060 IF Z5=0 THEN 410 +6070 z59 = 22:gosub 7620 +6071 S(17)=0:B0=0:GOTO 400 +6090 REM *** FEE FIE FOE FOO *** +6100 IF L1=71 THEN 6130 +6110 z59 = 2:gosub 7620 +6120 GOTO 410 +6130 IF S(8)<>L1 THEN 6180 +6140 REM MAKE NEST VANISH +6150 z59 = 79:gosub 7620 +6160 S(8)=0:GOTO 400 +6180 REM IF S(8)=0 THEN 6110 +6190 S(8)=L1 +6200 rem MAKE NEST RE-APPEAR +6210 z59 = 81:gosub 7620 +6220 GOTO 400 +6230 REM *** SHORT *** +6240 PRINT "Short descriptions" +6250 D0=0:GOTO 400 +6270 REM *** LONG *** +6280 PRINT "Long descriptions" +6290 D0=1:GOTO 400 +6310 REM *** BRIEF *** +6320 PRINT "OK, I'll only describe the room in detail the first time." +6330 D0=2:GOTO 400 +6350 REM *** QUIT *** +6360 PRINT "Save game"; +6370 GOSUB 9860 +6380 IF Z0=1 THEN 8970 +6390 GOTO 9750 +6400 REM SCORE *** +6410 GOSUB 6430 +6420 GOTO 400 +6430 REM PRINT OUT SCORE DATA +6440 GOSUB 6510 +6450 PRINT "Your score is now ";S0 +6451 PRINT "You have explored ";(Z9/T1)*T1;"% of the cave." +6460 RESTORE 6470 +6470 DATA "beginner","novice","experienced","advanced","expert" +6480 Z9 = INT((S0-1)/100) +6481 IF Z9 <= 4 THEN 6483 +6482 Z9=4 +6483 FOR Z0=0 TO Z9 +6484 READ D$ +6485 NEXT Z0 +6490 PRINT "That makes you a ";D$;" adventurer." +6500 RETURN +6510 REM COMPUTE CURRENT SCORE +6520 RESTORE 230 +6530 Z9=0:S0=0 +6540 FOR Z0=1 TO 15 +6550 READ Z1 +6560 IF Z1=0 THEN 6590 +6570 IF V(Z1)<>1 THEN 6580 +6575 S0=S0+4*O(Z0) +6580 IF S(Z0)<>7 THEN 6590 +6585 S0=S0+4*O(Z0) +6590 NEXT Z0 +6600 S0=(G=1)*10 + S0:S0=(SN=0)*20 + S0:S0=(D1=0)*30 + S0:S0=(T=0)*30 + S0:S0=(B1=2)*20 + S0 +6605 S0=(B2=1)*20 + S0:S0=(P1=2)*20 + S0:S0=(D2=1)*20 + S0:S0=(C=1)*20 + S0 +6610 FOR Z0 = 1 TO T1 +6620 IF V(Z0)<>1 THEN 6630 +6621 S0=S0+1:Z9=Z9+1 +6630 NEXT Z0 +6640 RETURN +6660 rem list items at location l1 +6680 fseek #2,0 +6690 for z1 = 1 to t2 +6700 INPUT #2,a$ +6710 if s(z1) <> l1 then 6720 +6711 PRINT a$ +6720 next z1 +6721 IF S(26)<>-1 THEN 6730 +6722 z59 = 67:gosub 7620 +6730 rem CHECK FOR DWARF,PIRATE +6740 gosub 8560 +6745 if dead = 1 then 6770 +6750 gosub 8800 +6760 PRINT +6770 return +6775 rem Print Short room description +6780 fseek #1,0 +6820 for z1 = 1 to l1 +6830 INPUT #1,a$ +6840 next z1 +6850 v(l1) = 1 +6860 PRINT a$ +6870 return +6880 rem SPECIAL GETS +6890 if not (z3 = 24 or z3 = 30 or z3 > 31) then 6930 +6900 rem CAN'T GET THESE FOR SOME REASON +6910 z59 = 61:gosub 7620 +6920 goto 400 +6930 if not (z3 = 12 and c = 0) then 6970 +6940 rem CHAIN +6950 z59 = 58:gosub 7620 +6960 goto 3900 +6970 rem BEAR IS HE FED? UNLOCKED? +6980 if not (z3 = 26 and b1 <> 2) then 7010 +6990 z59 = 61:gosub 7620 +7000 goto 3900 +7010 if not (z3 = 14 and d1 = 1) then 7050 +7020 rem DRAGON AND RUG +7030 z59 = 59:gosub 7620 +7040 goto 3900 +7050 if not (z3 = 16 or z3 = 17) then 7090 +7060 rem OIL AND WATER DO SAME AS FILL +7070 PRINT "Why not say 'fill'?" +7080 goto 3900 +7090 if not (z3 = 22 and b3) then 7140 +7100 rem TAKE BIRD SINCE IT'S IN CAGE +7110 s(31) = -1:PRINT "Bird and ";:goto 3880 +7140 if z3 <> 31 then 7310 +7150 rem GETTING BIRD +7160 if b3 <> 1 then 7210 +7170 rem TAKE CAGE, SINCE BIRD IS IN IT +7180 PRINT "Cage and ";:s(22) = -1:goto 3880 +7210 if s(22) = -1 then 7240 +7220 b$ = "cage":goto 2810 +7240 if s(23) = -1 then 7280 +7250 rem OK TO TAKE BIRD +7260 s(31) = -1 : b3 = 1:goto 3890 +7280 rem ROD SCARES BIRD +7290 z59 = 37:gosub 7620 +7300 goto 3900 +7310 rem BOTTLE FULL? IF SO, GET CONTENTS +7320 if not (z3 = 21 and b0) then 7360 +7330 PRINT "Contents and the "; +7340 s(b0+15) = -1 +7360 goto 3880 +7370 rem SPECIAL "DROP" +7380 IF Z3<>31 THEN 7440 +7390 REM BIRD IN CAGE +7400 S(31)=L1:S(22)=L1:B3=1 +7410 IF Z3<>31 THEN 7420 +7415 PRINT "Cage and "; +7420 IF Z3=22 THEN 7430 +7425 PRINT "Bird and "; +7430 goto 4120 +7440 if z3=22 and b3=1 then 7400 +7450 IF Z3<>21 THEN 7520 +7460 REM BOTTLE +7470 IF B0=0 THEN 4120 +7480 REM BOTTLE IS FULL, DO DROP CONTENTS TOO +7490 PRINT "Contents and "; +7500 S(15+B0)=L1:GOTO 4120 +7520 IF NOT (Z3=16 OR Z3=17) THEN 7541 +7530 PRINT "Try saying 'empty'":goto 4140 +7541 IF Z3<>26 OR T<>1 OR (L1<>60 AND L1<>61) THEN 7550 +7542 z59 = 28:gosub 7620 +7543 T=0:S(26)=L1:S(32)=0:GOTO 400 +7550 IF Z3<>6 THEN 4120 +7560 IF S(28)=L1 THEN 7600 +7570 REM GOODBYE, FRAGILE VASE! +7580 z59 = 43:gosub 7620 +7581 S(6)=0:S(29)=L1:GOTO 400 +7600 z59 = 60:gosub 7620 +7610 GOTO 4120 +7619 rem PRINT MESSAGE +7620 if indx(1) >= 0 then 7640 +7630 gosub 12500 +7640 z59 = int(z59):xtmp = indx(z59) +7645 if z59 <> 2 and z59 <> 61 then 7660 +7650 xtmp = fraindx(xtmp+min(int(RND(1)*5),5)) +7660 fseek #3,xtmp +7670 INPUT #3,b1$ +7672 if len(b1$) = 0 then 7673 else 7690 +7673 b1$ = " " +7690 if instr(b1$,"#") = 0 then 7670 +7700 z4 = val(mid$(b1$,2)) +7705 if int(z4) = z59 then 7720 +7710 if int(z4) < z59 then 7670 +7712 if int(z4) > z59 then 7760 +7720 INPUT #3,b1$ +7730 if mid$(b1$,1,1) = "#" then 7770 +7740 PRINT b1$ +7750 goto 7720 +7760 PRINT "NO DESC. # ";z59;" IN FILE AMESSAGE" +7770 return +7800 rem +7810 rem SITUATION DESCRIPTIONS +7820 rem +7830 rem GRATE +7840 if l1 = 10 or l1 = 11 then 7842 else 7860 +7842 z59 = (g+10):gosub 7620 +7850 rem CRYSTAL BRIDGE +7860 if (l1 = 19 or l1 = 20) and b2 = 1 then 7861 else 7880 +7861 z59 = 14:gosub 7620 +7870 rem PLUGH NOISE +7880 if l1 = 26 and RND(1) > 0.3 then 7881 else 7900 +7881 z59 = 41:gosub 7620 +7890 rem IRON DOOR +7900 if l1 = 73 and d2 = 0 then 7901 else 7920 +7901 z59 = 57:gosub 7620 +7910 rem TROLL +7920 if (l1 = 60 or l1 = 61) and t = 1 then 7921 else 7940 +7921 z59 = 63:gosub 7620 +7930 rem BEAR +7940 if l1 = 69 and b1 = 0 then 7941 else 7950 +7941 z59 = 64:gosub 7620 +7950 if l1 = 69 and b1 = 1 then 7951 else 7970 +7951 z59 = 66:gosub 7620 +7960 rem PLANT IN PIT +7970 if l1 = 48 or l1 = 50 then 7971 else 7980 +7971 z59 = 47+p1:gosub 7620 +7980 return +7990 rem +8000 rem PRINT long room description from "amessage" file +8010 rem description is noormally l1+200 except for +8020 rem maze or forest +8030 rem set v(l1)=1 so as not to repeat long desc(brief mode +8040 v(l1) = 1 +8050 if l1 > 4 then 8080 +8060 z59 = 200:gosub 7620 +8070 goto 8130 +8080 if not (l1 > 88 and l1 < 98 or l1 = 99) then 8110 +8090 z59 = 288:gosub 7620 +8100 goto 8130 +8110 rem normal description +8120 z59 = 200+l1:gosub 7620 +8130 return +8180 rem always give long descriptio nfor forest and maze +8190 if l1 < 5 or (l1 > 88 and l1 < 98) or l1 = 99 then 8220 +8200 if v(l1)=1 then 8201 else 8220 +8201 gosub 6780 +8202 goto 8235 +8210 rem he hassn't seen this room, so give a long desc. +8220 v(l1) = 1 +8230 gosub 7990 +8235 return +8240 rem +8250 rem fetch first item code in k(1 to t2) +8260 rem z8=total # of items found in list +8270 rem z3=item code first found +8280 z8 = 0 : z3 = 0: d$ = "" +8300 for z5 = 1 to 45 +8320 if k(1) <> z5 and k(2) <> z5 then 8360 +8330 z8 = z8+1 +8340 restore 9960+z5:read b$:d$ = b$ +8350 if k(1) = z5 and z8 = 1 then 8352 +8351 if k(2) = z5 and z8 = 1 then 8352 else 8360 +8352 z3 = z5 +8360 next z5 +8370 b$ = d$ +8380 return +8390 rem FIND FIRST ITEM AT ROOM +8400 x1 = 0 +8410 FOR Z1=1 TO 47 +8415 if x1=1 then 8440 +8430 IF K(1) = Z1 OR K(2) = Z1 THEN 8431 else 8440 +8431 restore 9960+z1:read d$:x1=1 +8440 NEXT Z1 +8450 RETURN +8460 REM +8470 REM MAKE SURE HE'S CARRYING ITEM * Z3 +8480 REM +8490 IF S(Z3)=-1 THEN 8530 +8500 PRINT "You don't have the ";A$ +8510 Z5=0 +8520 RETURN +8530 Z5=1 +8540 RETURN +8550 rem *** DWARF *** +8560 if d3 <> 0 then 8640 +8570 rem SHOULD DWARF GIVE AWAY AXE? +8580 if l1 < 13 then 8790 +8590 if RND(1) > 0.05 then 8790 +8600 rem GIVE AWAY AXE +8610 z59 = 80:gosub 7620 +8620 s(27) = l1 : d3 = 1 +8630 goto 8790 +8640 rem SHOULD DWARF ATTACK? +8650 if L1 >= 13 then 8660 +8652 s(35) = 0:goto 8790 +8660 if s(35) <> L1 then 8770 +8670 if RND(1) > 0.5 then 8790 +8680 rem YES! +8690 z59 = 32:gosub 7620 +8700 rem DOES THE KNIFE KILL THE PLAYER? +8705 KC = KC - 0.02 +8706 IF KC >= 0.5 THEN 8710 +8707 KC = 0.5 +8710 if RND(1) <= KC then 8750 +8720 rem YES +8730 PRINT "It gets you!" +8740 dead = 1 : goto 8790 +8750 PRINT "It misses!" +8760 goto 8790 +8770 rem SHOULD WE PUT A DWARF HERE? +8780 if RND(1) >= 0.1 then 8790 +8785 s(35) = l1 +8786 z59=31:gosub 7620 +8790 return +8800 rem *** PIRATE *** +8810 rem FIRST, DOES HE HAVE ANYTHING WORTH STEALING? +8820 z3 = 0 +8830 if l1 < 13 then 8960 +8840 for x = 1 to 15 +8850 if s(x) <> -1 then 8860 +8855 z3 = z3+1 +8860 next x +8870 if z3 < int(RND(1)*4)+1 then 8960 +8880 rem SHOULD WE RIP OFF HIS VALUABLES? +8890 if RND(1) < 0.05 then 8920 +8900 z59 = 34:gosub 7620 +8910 goto 8960 +8920 z59 = 33:gosub 7620 +8930 for x = 1 to 15 +8940 if s(x) <> -1 then 8950 +8945 s(x) = 100 +8950 next x +8960 return +8970 REM *** SAVE GAME *** +8980 INPUT "What do you want to call the save file? ";A$ +8990 OPEN A$ FOR OUTPUT AS #5 ELSE 9010 +9000 GOTO 9030 +9010 PRINT "File ";a$;" not created" +9020 GOTO 410 +9030 PRINT #5,T1;",";T2;",";T3;",";L1;",";L2;",";G;",";B0;",";SN;",";D1;",";D2;",";D0;",";T;",";B1;",";B2;",";P1;",";L;",";C;",";D3;",";B3;",";R0;",";KC +9040 FOR X=1 TO 99 +9041 PRINT #5,S(X);",";V(X) +9042 NEXT X +9043 PRINT #5,V(100) +9044 CLOSE #5 +9050 PRINT "Game saved" +9051 C0 = 0 +9060 if k(1) = 143 OR K(2) = 143 then 9750 +9070 GOTO 410 +9080 REM *** LOAD OLD GAME *** +9090 IF C0=0 THEN 9120 +9100 PRINT "You already have a loaded game!" +9110 GOTO 410 +9120 INPUT "Save file name? ";A$ +9130 OPEN A$ FOR INPUT AS #5 ELSE 9150 +9140 GOTO 9170 +9150 PRINT "Unable to use file ";A$ +9160 GOTO 410 +9170 INPUT #5,T1,T2,T3,L1,L2,G,B0,SN,D1,D2,D0,T,B1,B2,P1,L,C,D3,B3,R0,KCX$ +9171 kc = val(kcx$) +9180 FOR X=1 TO 99 +9191 INPUT #5,SX,VX:s(x)=sx:v(x)=vx +9192 NEXT X +9193 INPUT #5,vx:v(100)=vx +9194 CLOSE #5 +9200 C0=1 +9210 GOTO 300 +9220 rem *** READ THE MAGAZINE *** +9230 GOSUB 8280 +9240 IF Z3=25 THEN 9270 +9250 z59 = 74:gosub 7620 +9260 GOTO 410 +9270 IF S(25)=-1 THEN 9300 +9280 B$="magazine" +9290 GOTO 2710 +9300 REM OK, LET HIM READ IT +9310 z59 = 303:gosub 7620 +9320 GOTO 400 +9330 REM *** BUG *** +9340 A$ = "ADVBUGS.TXT" +9341 OPEN A$ FOR APPEND AS #5 ELSE 9150 +9390 INPUT "Your name: ";A$ +9400 A$=A$+" "+DATE$ +9410 PRINT #5,A$ +9420 PRINT "Enter your gripe in up to five lines (hit return to quit):" +9430 FOR Z0=1 TO 5 +9440 PRINT Z0; +9450 INPUT A$ +9460 IF A$="" THEN 9490 +9470 PRINT #5,A$ +9480 NEXT Z0 +9490 PRINT "Message recorded. Thank you!" +9500 CLOSE #5 +9510 GOTO 410 +9540 REM REINCARNATE HIM +9550 R0=R0+1 +9560 IF R0=1 THEN 9580 +9561 IF R0=2 THEN 9610 +9562 IF R0=3 THEN 9740 +9570 REM ASK HIM IF HE WANTS TO BE REINCARNATED +9580 z59 = 75:gosub 7620 +9590 GOSUB 9860 +9600 GOTO 9630 +9610 z59 = 77:gosub 7620 +9620 GOTO 9580 +9630 IF Z0=0 THEN 9750 +9640 z59 = 76:gosub 7620 +9650 REM PUT HIM BACK IN HOUSE, REARRANGE HIS STUFF +9660 S(18)=7:L=0:DEAD=0:KC=1.03 +9670 FOR X=1 TO T2 +9680 IF S(X)<>-1 THEN 9690 +9685 S(X)=L1 +9690 NEXT X +9700 REM WE'VE PUT THE LAMP IN HOUSE AND OTHER ITEMS WHERE HE DIED +9710 L1=INT(RND(1)*4)+1:L2=L1 +9720 GOTO 320 +9730 REM THIRD DEATH--END OF GAME +9740 z59 = 78:gosub 7620 +9750 PRINT "Oh well..." +9760 GOSUB 6430 +9762 close #1:close #2:close #3 +9770 STOP +9780 rem *** PITS *** +9790 if l1 < 13 THEN 300 +9791 IF l = 1 and (s(18) = -1 or s(18) = l1) then 300 +9800 rem IS HE GOING TO FALL INTO A PIT? +9810 if l1 = 16 or l1 = 17 or l1 = 19 or l1 = 20 or l1 = 25 or l1 = 47 or l1 = 48 or l1 = 59 or l1 = 60 or l1 = 61 or l1 = 75 or l1 = 76 or l1 = 98 then 9840 +9820 goto 300 +9830 rem he fell into a pit +9840 z59 = 44:gosub 7620 +9850 goto 9540 +9860 rem *** SEEK A "YES" OR "NO" +9870 INPUT a$ +9875 if len(a$) <> 0 then 9880 +9877 a$ = " " +9880 a$ = LOWER$(mid$(a$,1,1)) +9890 if a$ <> "y" and a$ <> "n" then 9930 +9900 if a$ = "y" then 9901 else 9910 +9901 z0 = 1 +9910 if a$ = "n" then 9911 else 9920 +9911 z0 = 0 +9920 goto 9945 +9930 PRINT "Yes or No-"; +9940 goto 9870 +9945 return +9950 rem ---- SHORT NAMES FOR STUFF ---- +9961 data "large gold nugget" +9962 data "bars of silver" +9963 data "precious jewelry" +9964 data "many coins" +9965 data "several diamonds" +9966 data "fragile ming vase" +9967 data "glistening pearl" +9968 data "nest of golden eggs" +9969 data "jewel-encrusted trident" +9970 data "egg-sized emerald" +9971 data "platinum pyramid" +9972 data "golden chain" +9973 data "rare spices" +9974 data "persian rug" +9975 data "treasure chest" +9976 data "water" +9977 data "oil" +9978 data "brass lamp" +9979 data "keys" +9980 data "food" +9981 data "bottle" +9982 data "wicker cage" +9983 data "3-foot black rod" +9984 data "clam" +9985 data "magazine" +9986 data "bear" +9987 data "axe" +9988 data "velvet pillow" +9989 data "shards of pottery" +9990 data "oyster" +9991 data "bird" +9992 data "troll" +9993 data "dragon" +9994 data "snake" +9995 data "dwarf" +9996 data "rock" +9997 data "stairs" +9998 data "steps" +9999 data "house" +10000 data "grate" +10001 data "stream" +10002 data "room" +10003 data "bridge" +10004 data "pit" +10005 data "volcano" +10006 data "road" +10007 data "everything" +12500 rem INITIALIZE MESSAGE INDEX +12501 open "AMESSAGE.IDX" for INPUT as #6 else 12505 +12502 goto 12610 +12504 rem Message indx file doesn't exist, create it +12505 OPEN "AMESSAGE.IDX" FOR OUTPUT AS #6 +12506 fpos = -2:b$ = "":fracnt= 0:lastfra=-1:fseek #3,0 +12520 fpos = fpos+len(b$)+2 +12522 INPUT #3,b$ +12530 if len(b$) = 0 then 12531 else 12540 +12531 b$ = " " : fpos = fpos-1 +12540 if b$="#" then 12591 +12550 if instr(b$,"#") = 0 then 12520 +12560 z4 = val(mid$(b$,2)) +12570 if int(z4) = z4 then 12580 +12571 fracnt = fracnt + 1 +12572 if lastfra = int(z4) then 12574 +12573 indx(int(z4)) = fracnt:lastfra = int(z4):PRINT #6,int(z4);",";FRACNT +12574 fraindx(fracnt) = fpos +12576 goto 12585 +12580 indx(int(z4)) = fpos +12581 PRINT #6,int(z4);",";FPOS +12585 PRINT "*"; +12590 goto 12520 +12591 PRINT #6,-999;",";-999 +12592 FOR I = 1 TO FRACNT +12593 PRINT #6,FRAINDX(I) +12594 NEXT I +12595 PRINT #6,-999 +12596 close #3 +12597 open "AMESSAGE" for INPUT as #3 +12600 GOTO 12670 +12604 REM READ AMESSAGE.IDX FILE INTO ARRAYS +12610 INPUT #6,II,I +12612 PRINT "*"; +12615 IF I=-999 THEN 12630 +12620 INDX(II)=I:GOTO 12610 +12630 II = 0 +12640 INPUT #6,I +12641 PRINT "*"; +12650 IF I=-999 THEN 12670 +12660 II = II +1:FRAINDX(II)=I:GOTO 12640 +12670 CLOSE #6:PRINT:PRINT +12680 RETURN diff --git a/bagels.bas b/bagels.bas new file mode 100644 index 0000000..7168dc7 --- /dev/null +++ b/bagels.bas @@ -0,0 +1,83 @@ +5 PRINT TAB(33);"BAGELS" +10 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY":PRINT:PRINT +15 REM *** BAGLES NUMBER GUESSING GAME +20 REM *** ORIGINAL SOURCE UNKNOWN BUT SUSPECTED TO BE +25 REM *** LAWRENCE HALL OF SCIENCE UC BERKELY +30 DIM A1(6),A(3),B(3) +40 Y=0:T=255 +50 PRINT:PRINT:PRINT +70 INPUT "WOULD YOU LIKE THE RULES (YES OR NO)";A$ +90 IF LEFT$(A$,1)="N" THEN 150 +100 PRINT:PRINT "I AM THINKING OF A THREE-DIGIT NUMBER. TRY TO GUESS" +110 PRINT "MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:" +120 PRINT " PICO - ONE DIGIT CORRECT BUT IN THE WRONG POSITION" +130 PRINT " FERMI - ONE DIGIT CORRECT AND IN THE RIGHT POSITION" +140 PRINT " BAGELS - NO DIGITS CORRECT" +150 FOR I=1 TO 3 +160 A(I)=INT(10*RND(1)) +165 IF I-1=0 THEN 200 +170 FOR J=1 TO I-1 +180 IF A(I)=A(J) THEN 160 +190 NEXT J +200 NEXT I +210 PRINT:PRINT "O.K. I HAVE A NUMBER IN MIND." +220 FOR I=1 TO 20 +230 PRINT "GUESS #";I +240 INPUT A$ +245 IF LEN(A$)<>3 THEN 630 +250 FOR Z=1 TO 3 +251 A1(Z)=ASC(MID$(A$,Z,1)) +252 NEXT Z +260 FOR J=1 TO 3 +270 IF A1(J)<48 THEN 300 +280 IF A1(J)>57 THEN 300 +285 B(J)=A1(J)-48 +290 NEXT J +295 GOTO 320 +300 PRINT "WHAT?" +310 GOTO 230 +320 IF B(1)=B(2) THEN 650 +330 IF B(2)=B(3) THEN 650 +340 IF B(3)=B(1) THEN 650 +350 C=0:D=0 +360 FOR J=1 TO 2 +370 IF A(J)<>B(J+1) THEN 390 +380 C=C+1 +390 IF A(J+1)<>B(J) THEN 410 +400 C=C+1 +410 NEXT J +420 IF A(1)<>B(3) THEN 440 +430 C=C+1 +440 IF A(3)<>B(1) THEN 460 +450 C=C+1 +460 FOR J=1 TO 3 +470 IF A(J)<>B(J) THEN 490 +480 D=D+1 +490 NEXT J +500 IF D=3 THEN 680 +505 IF C=0 THEN 545 +520 FOR J=1 TO C +530 PRINT "PICO "; +540 NEXT J +545 IF D=0 THEN 580 +550 FOR J=1 TO D +560 PRINT "FERMI "; +570 NEXT J +580 IF C+D<>0 THEN 600 +590 PRINT "BAGELS"; +600 PRINT +605 NEXT I +610 PRINT "OH WELL." +615 PRINT "THAT'S TWNETY GUESSES. MY NUMBER WAS";100*A(1)+10*A(2)+A(3) +620 GOTO 700 +630 PRINT "TRY GUESSING A THREE-DIGIT NUMBER.":GOTO 230 +650 PRINT "OH, I FORGOT TO TELL YOU THAT THE NUMBER I HAVE IN MIND" +660 PRINT "HAS NO TWO DIGITS THE SAME.":GOTO 230 +680 PRINT "YOU GOT IT!!!":PRINT +690 Y=Y+1 +700 INPUT "PLAY AGAIN (YES OR NO)";A$ +720 IF LEFT$(A$,1)="YES" THEN 150 +730 IF Y=0 THEN 750 +740 PRINT:PRINT "A";Y;"POINT BAGELS BUFF!!" +750 PRINT "HOPE YOU HAD FUN. BYE." +999 END diff --git a/basicparser.py b/basicparser.py index 6251337..fc7b71d 100644 --- a/basicparser.py +++ b/basicparser.py @@ -1463,7 +1463,11 @@ def __evaluate_function(self, category): self.__consume(Token.RIGHTPAREN) try: - return hackstackstring.find(needlestring, start, end) + # Older basis dialets are 1 based, so the return value + # here needs to be incremented by one. ALSO + # this moves the -1 not found value to 0 + # which indicated not found in most dialects + return hackstackstring.find(needlestring, start, end) + 1 except TypeError: raise TypeError("Invalid type supplied to INSTR in line " + @@ -1642,4 +1646,4 @@ def __randomizestmt(self): random.seed(seed) else: - random.seed(int(monotonic())) \ No newline at end of file + random.seed(int(monotonic())) diff --git a/regression.bas b/regression.bas index eabeee0..3ce9284 100644 --- a/regression.bas +++ b/regression.bas @@ -51,6 +51,7 @@ 520 PRINT "*** Testing file i/o ***" 530 OPEN "REGRESSION.TXT" FOR OUTPUT AS #1 540 PRINT #1,"0123456789Hello World!" +545 PRINT #1,"This is second line for testing" 550 CLOSE #1 560 OPEN "REGRESSION.TXT" FOR INPUT AS #2 570 PRINT "The next line should say 'Hello World!'" From 775c62a59b11ff9bb045bce21f534fd48564dbf9 Mon Sep 17 00:00:00 2001 From: brickbots Date: Sat, 11 Sep 2021 12:19:08 -0700 Subject: [PATCH 074/183] Remaining adventure files --- ADESCRIP | 100 ++++++++++ AITEMS | 35 ++++ AMESSAGE | 573 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ AMOVING | 100 ++++++++++ BITEMS | 137 +++++++++++++ 5 files changed, 945 insertions(+) create mode 100644 ADESCRIP create mode 100644 AITEMS create mode 100644 AMESSAGE create mode 100644 AMOVING create mode 100644 BITEMS diff --git a/ADESCRIP b/ADESCRIP new file mode 100644 index 0000000..7924165 --- /dev/null +++ b/ADESCRIP @@ -0,0 +1,100 @@ +You're in the forest. +You're in the forest. +You're in the forest. +You're in the forest. +You're at the hill in the road. +You're at the end of the road again. +You're inside the Building. +You're in the valley. +You're at slit in the streambed. +You're directly above a grate in a dry streambed. +You're below the grate. +You're in the cobble crawl. +You're in the debris room. +You are in an awkward sloping east/west canyon. +You're in the bird chamber. +You're at the top of the small pit. +You're in the Hall of Mists. +You're in the nugget of gold room. +You're on the east bank of the fissure. +You are on the west side of the fissure in the Hall of Mists. +You're at west end of the Hall of Mists. +You're in the Hall of the Mountain King. +You are in the south side chamber. +You are in the west side chamber of the Hall of the Mountain King. +You are at a N/S passage at a hole in the floor. +You're at Y2 . +You're at the window near the pit. +The passage here is blocked by a recent cave-in. +You're at the east end of the Long Hall. +You're at west end of the Long Hall. +You are at a crossover of a high N/S passage and a low E/W one. +Dead end +You are in a dirty broken passage. +You're at the top of the small pit. +You are in a little pit which has a stream flowing through it. +You're in the dusty rock room. +You're at a complex junction. +You're in the anteroom. +You are at Witt's end. Passages lead off in *all* directions. +You're in the shell room. +You're in the arched hall. +You're in a long sloping corridor with ragged sharp walls. +You are in a cul-de-sac about eight feet across. How's your french? +You are in bedquilt, a long east/west passage with holes everywhere. +You're in the swiss cheese room. +You're in the soft room. +You're at the east end of the Twopit room. +You're at the west end of the Twopit room. +You're in the east pit. +You're in the west pit. +You're in the slab room. +You're in the Oriental room. +You are in a large low room. +You're in a sloping corridor. +Dead end crawl. +You're in the misty cavern. +You're in the alcove. +You're in the Plover room. +You're in the dark-room. +You're on the SW side of the chasm. +You're on the NE side of the chasm. +You're in a corridor. +You're at the fork in the path. +You're in the limestone passage. +You're at a junction with warm walls. +You're in the chamber of boulders. +You're at a breath-taking view. +You're in front of the barren room. +You're in the barren room. +You're in a narrow corridor. +You're in the Giant room. +The passage here is blocked by a recent cave-in. +You are at one end of an immense north/south passage. +You're in the cavern with a waterfall +You're at a steep incline above a large room. +You are in a secret N/S canyon above a sizable passage. +You're at the top of the stalactite. +You're at a junction of three secret canyons. +You are in a secret N/S canyon above a large room. +You're in mirror canyon. +You're at a reservoir. +You are in a secret canyon which exits to the north and east. +You're in the secret E/W canyon above a tight canyon. +You are at a wide place in a very tight N/S canyon. +The canyon here becomes too tight to go on. +You are in a tall E/W canyon. +The canyon runs into a mass of boulders -- dead end. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are at a huge orange stalactite in the maze. +You are in a maze of twisty little passages, all alike. +You are at a dead end. \ No newline at end of file diff --git a/AITEMS b/AITEMS new file mode 100644 index 0000000..906760a --- /dev/null +++ b/AITEMS @@ -0,0 +1,35 @@ +There is a large sparking nugget of gold here! +There are bars of silver here! +There is precious jewelry here! +There are many coins here! +There are several diamonds here! +There is a delicate, precious ming vase here! +To one side lies a glistening Pearl! +There is a nest here, full of golden eggs! +There is a jewel-encrusted trident here! +There is an emerald the size of a plover's egg here! +There is a platinum pyramid here, eight inches on a side! +There is a golden chain here! +There are rare spices here! +There is a valuable persian rug here! +There is a chest here, full of various treasures! +There is a bottle of water here. +There is a small pool of oil here. +There is a brass lantern here. +There is a set of keys here. +There is food here. +There is a glass bottle here. +There is a small wicker cage here. +There is a 3-foot black rod here. +There is an enormous clam here with its shell tightly shut. +There is a recent issue of 'Spelunker Today' here. +There is a bear nearby. +There is a little axe here. +There is a purple velvet pillow here. +There are shards of pottery scattered about. +There is an enormous oyster here with its shell tightly shut. +There is a little bird here. +A burly troll stands in front of you. +A huge fierce green dragon bars the way! +A huge fierce green snake bars the way! +There is a threatening little dwarf in the room with you! \ No newline at end of file diff --git a/AMESSAGE b/AMESSAGE new file mode 100644 index 0000000..2a1c352 --- /dev/null +++ b/AMESSAGE @@ -0,0 +1,573 @@ +#1 +You can't move that way. +#2.1 +Nothing happens. +#2.2 +Nothing seems to happen. +#2.3 +I don't think that had any affect on anything. +#2.4 +I don't know how to apply that word here. +#2.5 +That had little effect, if any at all. +#3 +The mist is quite thick here, and the fissure is too wide to jump. +#4 +You are at the bottom of the pit with a broken neck. +#5 +The bottle is already filled! +#6 +The bear eagerly wolfs down your food, after which he seems to calm +down considerably and even becomes rather friendly. burp +#7 +The plant surges upwards, reaching halfway to the hole above you +#8 +The plant grows explosively, almost filling the bottom of the pit. +#9 +You overwatered the plant, you fool! +#10 +The grate is locked. +#11 +The grate is unlocked. +#12 +The bear is quite ferocious, and will no doubt tear you to +shreds if you approach it! +#13 +The chain is unlocked and the bear is free. +#14 +A crystal bridge now spans the fissure. +#15 +The bridge vanishes. +#16 +It is beyond your power to do that. +#17 +The clam opens momenntarily, and a glistening pearl falls out +and rolls away. Goodness, this must really have been an oyster. +(I never was any good at identifying bivalves, anyway.) +#18 +I can't close that! +#19 +The oil has freed up the hinges so that the door will now move, +although it requires some effort. +#20 +I can't eat. +#21 +How can I drink that? +#22 +"Glug, glug, glug, Belch!" +#23 +It doesn't want to eat anything (except maybe you!) +#24 +You fool! Dwarves only eat coal! Now you've made him +*** REALLY MAD ****! +#25 +Trolls are close relatives with the rocks and have skin as tough as +that of a rhinoceros. The troll fends off your blows effortlessly. +#26 +The troll deftly catches the axe, examines it carefully, and tosses it +back, declaring, "Good workmanship, but it's not valuable enough." +#27 +The troll catches your treasure and scurries away out of sight. +#28 +The bear lumbers toward the troll, who lets out a startled shriek and +scurries away. The bear soon gives up the pursuit and wanders back. +#29 +You attack a little dwarf, but he dodges out of the way. +#30 +You killed a little dwarf. The body vanishes in a cloud of greasy +black smoke. +#31 +There is a threatening little dwarf in the room with you! +#32 +One sharp nasty knife is thrown at you! +#33 +Out from the shadows behind you pounces a bearded pirate! "Har, har," +he chortles, "I'll just take all this booty and hide it away with me +chest deep in the maze!" he snatches your treasure and vanishes into +the gloom. +#34 +There are faint rustling noises from the darkness behind you. +#35 +The grate is open. +#36 +The grate is locked. +#37 +The bird was unafraid when you entered, but as you approach it becomes +disturbed and you cannot catch it. +#38 +The dome is unclimbable. +#39 +A crystal bridge now spans the fissure. +#40 +A huge green fierce snake bars the way! Hisssssss +#41 +A hollow voice says "Plugh". +#42 +The vase is now resting, delicately, on a velvet pillow. +#43 +Crash! Tinkle! The ming vase shatters into worthless crockery. +#44 +You fell into a pit and broke every bone in your body! +#45 +It is now pitch dark. If you proceed you will likely fall into a pit. +#46 +The nest of golden eggs has vanished! +#47 +There is a tiny little plant in the pit, murmuring "water, water, ..." +#48 +There is a 12-foot-tall beanstalk stretching up out of the pit, +bellowing "WATER!! WATER!!" +#49 +There is a gigantic beanstalk stretching all the way up to the hole. +#50 +You can't get by the snake. He hates your guts. +#51 +The dragon has a rather "volatile" personality, and he will +probably incinerate you if you get any closer. +#52 +You have crawled around in some little holes and wound up back in the +main passage. +#53 +Something you're carrying won't fit through the tunnel with you. +#54 +Your load is too heavy. You'd best take inventory and drop +something first. +#55 +A burly troll stands by the bridge and insists you throw him a +treasure before you may cross. +#56 +The troll pops from under the bridge and blocks your way. +#57 +The way north is barred by a massive, rusty, iron door. +#58 +The bear will bite off your hand. Besides, the chain is locked to the wall. +#59 +It's rather difficult to move a twenty-ton dragon, considering +he is lying on the rug. +#60 +The vase is now resting, delicately, on a velvet pillow. +#61.1 +A valliant attempt! +#61.2 +Nice try! +#61.3 +A valliant attempt, but you're not that strong! +#61.4 +Don't be silly! +#61.5 +Do you have any idea on how to do that? +#62 +A huge green fierce dragon blocks the way. The dragon is lying +on a persian rug! +#63 +A burly troll stands beside the bridge, blocking your way. +#64 +There is a ferocious cave bear eyeing you from the far end of the room! +#65 +The bear is locked to the wall with a golden chain! +#66 +There is a calm, friendly bear locked to the wall. +#67 +You are being followed by a very large, tame bear. +#68 +With what? Your bare hands? +#69 +Congratulations! You have just vanquished a dragon with your bare hands +(hard to believe ain't it?). +#70 +Attacking it is both dangerous and doesn't work. +#71 +There is nothing here to attack. +#72 +How can you do that to more than one thing at a time? +#73 +Thank you, it was delicious! +#74 +I see nothing special about that. +#75 +Oh dear, you seem to have gotten yourself killed. I might be able to +help you out, but I've never really done this before. Do you want me +to try to reincarnate you? +#76 +All right. But don't blame me if something goes wr...... + --- POOF!! --- +You are engulfed in a cloud of orange smoke. Coughing and gasping, +you emerge from the smoke and find.... +#77 +You clumsy oaf, you've done it again! I don't know how long I can +keep this up. Do you want me to try reincarnating you again? +#78 +Now you've really done it! I'm out of orange smoke! You don't expect +me to do a decent reincarnation without any orange smoke, do you? +Better luck next time! +#79 +The nest of golden eggs has vanished! +#80 +A little dwarf just walked around a corner, saw you, threw a little +axe at you which missed, cursed and ran away. +#81 +The nest of golden eggs has re-appeared! +#200 +You are in a forest, with trees all around you. +#205 +You have walked up a hill, still in the forest. The road slopes back +down the other side of the hill. There is a building in the distance. +#206 +You are standing at the end of a road before a small brick building. +Around you is a forest. A small stream flows out of the building and +down a gully. +#207 +You are inside a building, a well house for a large spring. +#208 +You are in a valley in the forest beside a stream tumbling along a +rocky bed. +#209 +At your feet all the water of the stream splashes into a 2-inch slit +in the rock. Downstream the streambed is bare rock. +#210 +You are in a 20-foot depression floored with bare dirt. Set into the +dirt is a strong steel grate mounted in concrete. A dry streambed +leads into the depression. +#211 +You are in a small chamber beneath a 3x3 steel grate to the surface. +A low crawl over cobbles leads inward to the west. +#212 +You are crawling over cobbles in a low passage. There is a dim light +at the east end of the passage. +#213 +You are in a debris room filled with stuff washed in from the surface. +A low wide passage with cobbles becomes plugged with mud and debris +here, but an awkward canyon leads upward and west. A note on the wall +says "magic word XYZZY." +#214 +You are in an awkward sloping east/west canyon. +#215 +You are in a splendid chamber thirty feet high. The walls are frozen +rivers of orange stone. An awkward canyon and a good passage exit +from east and west sides of the chamber. +#216 +At your feet is a small pit breathing traces of white mist. An east +passage ends here except for a small crack leading on. + +Rough stone steps lead down the pit. +#217 +You are at one end of a vast hall stretching forward out of sight to +the west. There are openings to either side. Nearby, a wide stone +staircase leads downward. The hall is filled with wisps of white mist +swaying to and fro almost as if alive. A cold wind blows up the +staircase. There is a passage at the top of a dome behind you. + +Rough stone steps lead to the top of the dome. +#218 +This is a low room with a crude note on the wall. The note says, +"you can't get it up the steps." +#219 +You are on the east bank of a fissure slicing clear across the hall. +The mist is quite thick here, and the fissure is too wide to jump. +#220 +You are on the west side of the fissure in the Hall of Mists. +#221 +You are at the west end of Hall of Mists. A low wide crawl continues +west and another goes north. To the south is a little passage 6 feet +off the floor. +#222 +You are in the Hall of the Mountain King, with passages off in all +directions. +#223 +You are in the south side chamber. +#224 +You are in the west side chamber of the Hall of the Mountain King. +A passage continues west and up here. +#225 +You are in a low N/S passage at a hole in the floor. The hole goes +down to an E/W passage. +#226 +You are in a large room, with a passage to the south, a passage to the +west, and a wall of broken rock to the east. There is a large "Y2" on +a rock in the room's center. +#227 +You're at a low window overlooking a huge pit, which extends up out of +sight. A floor is indistinctly visible over 50 feet below. Traces of +white mist cover the floor of the pit, becoming thicker to the right. +Marks in the dust around the window would seem to indicate that +someone has been here recently. Directly across the pit from you and +25 feet away there is a similar window looking into a lighted room. A +shadowy figure can be seen there peering back at you. + +The shadowy figure seems to be trying to attract your attention. +#228 +The passage here is blocked by a recent cave-in. +#229 +You are at the east end of a very long hall apparently without side +chambers. To the east a low wide crawl slants up. To the north a +round two foot hole slants down. +#230 +You are at the west end of a very long featureless hall. The hall +joins up with a narrow north/south passage. +#231 +You are at a crossover of a high N/S passage and a low E/W one. +#232 +Dead end +#233 +You are in a dirty broken passage. To the east is a crawl. To the +west is a large passage. Above you is a hole to another passage. +#234 +You are on the brink of a small clean climbable pit. A crawl leads +west. +#235 +You are in the bottom of a small pit with a little stream, which +enters and exits through tiny slits. +#236 +You are in a large room full of dusty rocks. There is a big hole in +the floor. There are cracks everywhere, and a passage leading east. +#237 +You are at a complex junction. A low hands and knees passage from the +north joins a higher crawl from the east to make a walking passage +going west. There is also a large room above. The air is damp here. +#238 +You are in an anteroom leading to a large passage to the east. Small +passages go west and up. The remnants of recent digging are evident. +a sign in midair here says "Cave under construction beyond this point. +proceed at own risk. "[Witt Construction Company]" +#239 +You are at Witt's end. Passages lead off in *all* directions. +#240 +You're in a large room carved out of sedimentary rock. The floor and +walls are littered with bits of shells embedded in the stone. A +shallow passage proceeds downward, and a somewhat steeper one leads +up. A low hands and knees passage enters from the south. +#241 +You are in an arched hall. A coral passage once continued up and east +from here, but is now blocked by debris. The air smells of sea water. +#242 +You are in a long sloping corridor with ragged sharp walls. +#243 +You are in a cul-de-sac about eight feet across. How's your french? +#244 +You are in bedquilt, a long east/west passage with holes everywhere. +To explore at random, select north, south, up or down. +#245 +You are in a room whose walls resemble swiss cheese. Obvious passages +go west, east, NE, and NW. Part of the room is occupied by a large +bedrock block. +#246 +You are in the soft room. The walls are covered with heavy curtains, +the floor with a thick pile carpet. Moss covers the ceiling. +#247 +You are at the east end of the Twopit room. The floor here is +littered with thin rock slabs, which make it easy to descend the pits. +There is a path here bypassing the pits to connect passages from east +and west. There are holes all over, but the only big one is on the +wall directly over the west pit where you can't get to it. +#248 +You are at the west end of the Twopit room. There is a large hole in +the wall above the pit at this end of the room. +#249 +You are at the bottom of the eastern pit in the Twopit room. There is +a small pool of oil in one corner of the pit. +#250 +You are at the bottom of the western pit in the Twopit room. There is +a large hole in the wall about 25 feet above you. +#251 +You are in a large low circular chamber whose floor is an immense slab +fallen from the ceiling (slab room). East and west there once were +large passages, but they are now filled with boulders. Low small +passages go north and south, and the south one quickly bends west +around the boulders. +#252 +This is the oriental room. Ancient oriental cave drawings cover the +walls. A gently sloping passage leads upward to the north, another +passage leads SE, and a hands and knees crawl leads west. +#253 +You are in a large low room. Crawls lead north, SE, and SW. +#254 +You are in a long winding corridor sloping out of sight in both +directions. +#255 +Dead end crawl. +#256 +You are following a wide path around the outer edge of a large cavern. +Far below, through a heavy white mist, strange splashing noises can be +heard. The mist rises up through a fissure in the ceiling. The path +exits to the south and west. +#257 +You are in an alcove. A small NW path seems to widen after a short +distance. An extremely tight tunnel leads east. It looks like a very +tight squeeze. An eerie light can be seen at the other end. +#258 +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads NE. +#259 +You're in the dark-room. A corridor leading south is the only exit. +A massive stone tablet embedded in the wall reads: +"Congratulations on bringing light into the dark-room!" +#260 +You are on one side of a large, deep chasm. A heavy white mist rising +up from below obscures all view of the far side. A SW path leads away. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A sign posted on the bridge reads, "STOP! Pay Troll!" +#261 +You are on the far side of the chasm. A NE path leads away from the +chasm on this side. + +A rickety wooden bridge extends across the chasm, vanishing into the +gloom. A sign posted on the bridge reads: "Stop! Pay troll!" +#262 +You're in a long east/west corridor. A faint rumbling noise can be +heard in the distance. +#263 +The path forks here. The left fork leads northeast. A dull rumbling +seems to get louder in that direction. The right fork leads southeast +down a gentle slope. The main corridor enters from the west. +#264 +You are walking along a gently sloping north/south passage lined with +oddly shaped limestone formations. +#265 +The walls are quite warm here. From the north can be heard a steady +roar, so loud that the entire cave seems to be trembling. Another +passage leads south, and a low crawl goes east. +#266 +You are in a small chamber filled with large boulders. The walls are +very warm, causing the air in the room to be almost stifling from the +heat. The only exit is a crawl heading west, through which is coming +a low rumbling. +#267 +You are on the edge of a breath-taking view. Far below you is an +active volcano, from which great gouts of molten lava come surging +out, cascading back down into the depths. The glowing rock fills the +farthest reaches of the cavern with a blood-red glare, giving every- +thing an eerie, macabre appearance. The air is filled with flickering +sparks of ash and a heavy smell of brimstone. The walls are hot to +the touch, and the thundering of the volcano drowns out all other +sounds. Embedded in the jagged roof far overhead are myriad twisted +formations composed of pure white alabaster, which scatter the murky +light into sinister apparitions upon the walls. To one side is a deep +gorge, filled with a bizarre chaos of tortured rock which seems to +have been crafted by the devil himself. An immense river of fire +crashes out from the depths of the volcano, burns its way through the +gorge, and plummets into a bottomless pit far off to your left. To +the right, an immense geyser of blistering steam erupts continuously +from a barren island in the center of a sulfurous lake, which bubbles +ominously. The far right wall is aflame with an incandescence of its +own, which lends an additional infernal splendor to the already +hellish scene. A dark, foreboding passage exits to the south. +#268 +You are standing at the entrance to a large, barren room. A sign +posted above the entrance reads: "Caution! Bear in room!" +#269 +You are inside a barren room. The center of the room is completely +empty except for some dust. Marks in the dust lead away toward the +far end of the room. The only exit is the way you came in. +#270 +You are in a long, narrow corridor stretching out of sight to the +west. At the eastern end is a hole through which you can see a +profusion of leaves. +#271 +You are in the giant room. The ceiling here is too high up for your +lamp to show it. Cavernous passages lead east, north, and south. On +the west wall is scrawled the inscription, "FEE FIE FOE FOO" [sic]. +#272 +The passage here is blocked by a recent cavein. +#273 +You are at one end of an immense north/south passage. +#274 +You are in a magnificent cavern with a rushing stream, which cascades +over a sparkling waterfall into a roaring whirlpool which disappears +through a hole in the floor. Passages exit to the south and west. +#275 +You are at the top of a steep incline above a large room. You could +climb down here, but you would not be able to climb up. There is a +passage leading back to the north. +#276 +You are in a secret N/S canyon above a sizable passage. +#277 +A large stalactite extends from the roof and almost reaches the floor +below. You could climb down it, and jump from it to the floor, but +having done so you would be unable to reach it to climb back up. + +The maze continues at this level. +#278 +You are in a secret canyon at a junction of three canyons, bearing +north, south, and SE. The north one is as tall as the other two +combined. +#279 +You are in a secret N/S canyon above a large room. +#280 +You are in a north/south canyon about 25 feet across. The floor is +covered by white mist seeping in from the north. The walls extend +upward for well over 100 feet. Suspended from some unseen point far +above you, an enormous two-sided mirror is hanging parallel to and +midway between the canyon walls. (The mirror is obviously provided +for the use of the dwarves, who as you know, are extremely vain.) A +small window can be seen in either wall, some fifty feet up. +#281 +You are at the edge of a large underground reservoir. An opaque cloud +of white mist fills the room and rises rapidly upward. The lake is +fed by a stream, which tumbles out of a hole in the wall about 10 feet +overhead and splashes noisily into the water somewhere within the +mist. The only passage goes back toward the south. +#282 +You are in a secret canyon which exits to the north and east. +#283 +You're in the secret E/W canyon above a tight canyon. +#284 +You are at a wide place in a very tight N/S canyon. +#285 +The canyon here becomes too tight to go on. +#286 +You are in a tall E/W canyon. A low tight crawl goes 3 feet north and +seems to open up. +#287 +The canyon runs into a maze of boulders -- dead end. +#288 +You are in a maze of twisty little passages, all alike. +#298 +You are near a large pit beside a large stalactite in the maze. +The stalactite is about 30 feet long, allowing you to descend +it. However, due to the smoothness of the stalactite you will +probably be unable to climb back up again. +#300 +Dead end. +#301 +Welcome to adventure.! Would you like instructions? +#302 +Somewhere nearby is Colossal Cave, where others have found fortunes in +treasure and gold, though it is rumored that some who enter are never +seen again. Magic is said to work in the cave. I will be your eyes +and hands. Direct me with commands such as get, take, or look. Since you +need to move around, you must usually enter compass directions +(N,NE,E,SE,S,SW,W,NW or up or down), although you may sometimes use +more vague verbs with with to move. I know of several objects in this +game, such as a lamp and a bottle. I also know of special objects in the +cave. Some of these objects have side effects, ie there is a rod in the +cave that scares a little bird. +----------------------------------------------------------------- +The object of this game is to gather as many treasures as you can and +put them back in the house. You also run the risk of getting robbed or +killed by some rather unfriendly inhabitants of the cave. +---------------------------------------------------------------- +There are some useful commands that you should know about: +--- Brief, Long, and Short - these commands control the amount of +--- detail you get in descriptions. +--- Look (or L) - gives a detailled descriptio nof your surroundings. +--- Inventory (or I) tells you what you are carrying. +--- Quit, Stop or End - these are self-explanatory. +--- Save - by typing this, you may save your game and continue it at +--- a later time. +--- Continue - lets you continue an old game. +--- Score - tells you hw well you're doing. + +Other helpful functions include: +* multiple commands on one input line, ie: +"Go south, get car, keys, Start car." (example only) +** oops, the PyBasic version doesn't support multiple commands :( +-------------------------------------------------------------- +"Adventure" is a version of the game created by Willy Crowther and +Dan Woods at M.I.T. +-------------------------------------------------------------- +#303 +I'm sorry, but the magazine is written in Dwarvish. +# +# +# \ No newline at end of file diff --git a/AMOVING b/AMOVING new file mode 100644 index 0000000..408591f --- /dev/null +++ b/AMOVING @@ -0,0 +1,100 @@ +3,0,6,0,4,0,2,0,0,0 +1,0,8,0,2,0,4,0,0,0 +2,0,1,0,4,0,7,0,0,0 +1,0,3,0,5,0,2,0,0,0 +2,0,6,0,4,0,1,0,0,0 +2,0,7,0,8,0,5,0,0,0 +0,0,0,0,0,0,6,0,0,0 +6,0,3,0,9,0,4,0,0,0 +8,0,3,0,10,0,4,0,0,0 +9,0,0,0,11,0,0,0,0,11 +0,0,10,0,0,0,12,0,10,0 +0,0,11,0,0,0,13,0,0,0 +0,0,12,0,0,0,14,0,14,0 +0,0,13,0,0,0,15,0,15,13 +0,0,14,0,0,0,16,0,0,0 +0,0,15,0,0,0,0,0,0,17 +22,0,0,0,18,0,19,0,16,22 +17,0,0,0,0,0,0,0,0,0 +0,0,17,0,0,0,20,0,0,0 +0,0,19,0,0,0,21,0,0,0 +20,0,20,0,88,0,29,0,0,0 +25,0,17,0,23,83,24,0,17,0 +22,0,0,0,0,0,0,0,0,0 +0,0,22,0,0,0,31,0,31,0 +26,0,0,0,22,0,0,0,0,33 +0,0,28,0,25,0,27,0,0,0 +0,0,26,0,0,0,0,0,0,0 +0,0,0,0,0,26,0,0,0,0 +31,0,21,0,0,0,30,0,21,31 +31,0,29,0,94,0,0,0,0,0 +32,0,24,0,30,0,29,0,0,0 +31,0,0,0,0,0,0,0,0,0 +0,0,34,0,0,0,36,0,25,0 +0,0,0,0,0,0,33,0,0,35 +0,0,0,0,0,0,0,0,34,0 +0,0,33,0,0,0,0,0,25,37 +40,0,38,0,0,0,44,0,36,0 +0,0,39,0,0,0,37,0,37,0 +255,255,255,255,255,255,255,255,255,255 +0,0,0,0,37,0,0,0,41,42 +0,0,0,0,0,0,0,0,0,43 +0,0,0,0,0,0,0,0,40,43 +0,0,0,0,0,0,0,0,42,0 +255,0,37,0,255,0,45,0,255,255 +0,44,46,0,0,0,47,52,0,0 +0,0,0,0,0,0,45,0,0,0 +0,0,45,0,0,0,48,0,0,49 +0,0,47,0,0,0,51,0,0,50 +0,0,0,0,0,0,0,0,47,0 +0,0,0,0,0,0,0,0,48,0 +44,0,0,0,48,0,0,0,79,0 +56,0,0,45,0,0,53,0,0,0 +55,0,0,52,0,54,0,0,0,0 +0,0,0,0,0,0,0,0,60,53 +0,0,0,0,53,0,0,0,0,0 +0,0,0,0,52,0,57,0,0,0 +0,0,58,0,0,0,0,56,0,0 +0,59,0,0,0,0,57,0,0,0 +0,0,0,0,58,0,0,0,0,0 +0,61,0,0,0,54,0,0,0,54 +0,62,0,0,0,60,0,0,0,0 +0,0,63,0,0,0,61,0,0,0 +0,65,0,64,0,0,62,0,0,0 +63,0,0,0,68,0,0,0,0,0 +67,0,66,0,63,0,0,0,0,0 +0,0,0,0,0,0,65,0,0,0 +0,0,0,0,65,0,0,0,0,0 +0,0,69,0,0,0,64,0,0,0 +0,0,0,0,0,0,68,0,0,0 +0,0,50,0,0,0,71,0,0,50 +73,0,70,0,72,0,0,0,0,0 +0,71,0,0,0,0,0,0,0,0 +74,0,0,0,71,0,0,0,0,0 +0,0,0,0,73,0,75,0,0,0 +74,0,0,0,0,0,0,0,0,53 +78,0,0,0,77,0,0,0,0,45 +76,0,0,0,0,0,0,0,0,93 +27,0,0,44,76,0,0,0,0,0 +80,0,0,0,82,0,0,0,0,51 +81,0,0,0,79,0,0,0,0,0 +0,0,0,0,80,0,0,0,0,0 +79,0,83,0,0,0,0,0,0,0 +0,0,22,0,0,0,82,0,0,84 +86,0,0,0,85,0,0,0,0,0 +84,0,0,0,0,0,0,0,0,0 +45,0,84,0,0,0,87,0,0,0 +0,0,0,86,0,0,0,0,0,0 +89,82,91,95,90,83,92,91,95,94 +95,96,91,90,89,88,91,95,93,97 +92,92,91,93,88,97,88,96,99,87 +91,89,94,90,93,95,89,92,0,0 +95,97,93,96,94,95,21,92,0,0 +90,96,92,94,88,30,92,90,0,0 +98,89,90,91,89,93,95,97,0,0 +93,94,96,92,97,95,94,92,0,0 +92,99,97,96,89,94,90,90,91,92 +98,89,92,91,90,93,94,95,96,98 +95,90,93,91,94,92,90,99,98,15 +88,89,93,97,100,89,96,95,0,0 +0,0,0,0,99,0,0,0,0,0 diff --git a/BITEMS b/BITEMS new file mode 100644 index 0000000..00f0731 --- /dev/null +++ b/BITEMS @@ -0,0 +1,137 @@ +100, N +101, NE +102, E +103, SE +104, S +105, SW +106, W +107, NW +108, U +109, D +110, PLUGH +111, XYZZY +112, PLOVER +113, CROSS +114, CLIMB +115, JUMP +116, FILL +117, EMPTY +117, POUR +118, LOOK +118, L +119, LIGHT +119, ON +120, EXTINGUISH +120, OFF +121, IN +121, ENTER +122, LEAVE +122, OUT +123, INVENTORY +123, I +124, GET +124, CATCH +124, TAKE +125, DROP +125, DUMP +47, ALL +47, EVERYTHING +126, THROW +127, ATTACK +127, KILL +128, FEED +129, WATER +130, LOCK +131, UNLOCK +132, FREE +132, RELEASE +133, WAVE +134, OPEN +135, CLOSE +136, OIL +137, EAT +138, DRINK +139, FEE FIE FOE FOO +140, SHORT +141, LONG +142, BRIEF +143, QUIT +143, STOP +143, END +144, SCORE +145, SAVE +146, LOAD +147, READ +147, EXAMINE +148, YES +148, Y +149, BUG +1, GOLD +1, NUGGET +2, BARS +2, SILVER +3, JEWELRY +4, COINS +5, DIAMONDS +6, MING +6, VASE +7, PEARL +8, EGGS +8, NEST +9, TRIDENT +10, EMERALD +11, PLATINUM +11, PYRAMID +12, CHAIN +13, SPICES +14, PERSIAN +14, RUG +15, TREASURE +15, CHEST +16, WATER +17, OIL +18, LAMP +18, LANTERN +19, KEYS +20, FOOD +21, BOTTLE +22, CAGE +23, ROD +23, WAND +24, CLAM +25, MAGAZINE +26, BEAR +27, AXE +28, VELVET +28, PILLOW +29, SHARDS +30, OYSTER +31, BIRD +32, TROLL +33, DRAGON +34, SNAKE +35, DWARF +36, ROCK +36, BOULDER +37, STAIRS +38, STEPS +39, HOUSE +39, BUILDING +40, GRATE +41, STREAM +42, ROOM +43, BRIDGE +44, PIT +45, VOLCANO +46, ROAD +100, NORTH +101, NORTHEAST +102, EAST +103, SOUTHEAST +104, SOUTH +105, SOUTHWEST +106, WEST +107, NORTHWEST +108, UP +109, DOWN +150,* From 5ea3c4a0cb3bf431f2dfb2073ec8fd9c4632a8f3 Mon Sep 17 00:00:00 2001 From: brickbots Date: Sat, 11 Sep 2021 16:02:53 -0700 Subject: [PATCH 075/183] Fix instr selection issue --- rock_scissors_paper.bas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rock_scissors_paper.bas b/rock_scissors_paper.bas index 39e7482..4f30402 100644 --- a/rock_scissors_paper.bas +++ b/rock_scissors_paper.bas @@ -12,7 +12,7 @@ 110 REM ON YOURGUESS$ = "R" GOSUB 300 120 REM ON YOURGUESS$ = "S" GOSUB 500 130 REM ON YOURGUESS$ = "P" GOSUB 700 -135 ON INSTR("RSP",YOURGUESS$)+1 GOSUB 300,500,700 +135 ON INSTR("RSP",YOURGUESS$) GOSUB 300,500,700 150 GOTO 70 160 PRINT "My score: "; MYSCORE; " Your score: "; YOURSCORE 170 STOP From 2c6db9983acb76e911323866fe01e3ecd9f065bb Mon Sep 17 00:00:00 2001 From: brickbots Date: Sun, 12 Sep 2021 09:12:53 -0700 Subject: [PATCH 076/183] Restore on-goto statements --- PyBStartrek.bas | 79 +++++-------------------------------------------- 1 file changed, 7 insertions(+), 72 deletions(-) diff --git a/PyBStartrek.bas b/PyBStartrek.bas index 2582256..c1670f6 100644 --- a/PyBStartrek.bas +++ b/PyBStartrek.bas @@ -132,16 +132,7 @@ 1580 INPUT"command ";A$ 1590 FOR I=1 TO 9 1591 IF MID$(A$,1,3)<> MID$(A1$,3*I-2,3) THEN 1610 -1600 rem ON I GOTO 1720,1510,2440,2530,2750,3090,3180,3980,3510 -1601 if i = 1 THEN 1720 -1602 if i = 2 THEN 1510 -1603 if i = 3 THEN 2440 -1604 if i = 4 THEN 2530 -1605 if i = 5 THEN 2750 -1606 if i = 6 THEN 3090 -1607 if i = 7 THEN 3180 -1608 if i = 8 THEN 3980 -1609 if i = 9 THEN 3510 +1600 ON I GOTO 1720,1510,2440,2530,2750,3090,3180,3980,3510 1610 NEXT I 1611 PRINT"Enter one of the following:" 1620 PRINT" NAV (to set course)" @@ -450,16 +441,6 @@ 3540 PRINT"The Federation is in need of a new starship commander" 3550 PRINT"for a similar mission -- if there is a volunteer," 3560 INPUT"let him or her step forward and enter 'AYE' ";X$:IF X$="AYE" THEN 520 -3570 rem KEY 1,"LIST " -3580 rem KEY 2,"RUN"+CHR$(13) -3590 rem KEY 3,"LOAD"+CHR$(34) -3600 rem KEY 4, "SAVE"+CHR$(34) -3610 rem KEY 5, "CONT"+CHR$(13) -3620 rem KEY 6,","+CHR$(34)+"LPT1:"+CHR$(34)+CHR$(13) -3630 rem KEY 7, "TRON"+CHR$(13) -3640 rem KEY 8, "TROFF"+CHR$(13) -3650 rem KEY 9, "KEY " -3660 rem KEY 10,"SCREEN 0,0,0"+CHR$(13) 3670 END 3680 PRINT"Congratulations, Captain! the last Klingon battle cruiser" 3690 PRINT"menacing the Federation has been destroyed.":PRINT @@ -491,15 +472,7 @@ 3851 PRINT " . ";:GOTO 3861 3860 PRINT " ";MID$(Q$,J+1,3); 3861 NEXT J -3870 rem ON I GOTO 3880,3900,3910,3920,3930,3940,3950,3960 -3871 if i=1 then 3880 -3872 if i=2 then 3900 -3873 if i=3 then 3910 -3874 if i=4 then 3920 -3875 if i=5 then 3930 -3876 if i=6 then 3940 -3877 if i=7 then 3950 -3878 if i=8 then 3960 +3870 ON I GOTO 3880,3900,3910,3920,3930,3940,3950,3960 3880 PRINT" Stardate "; 3890 TT= T*10 : TT=INT(TT)*0.1:PRINT TT :GOTO 3970 3900 PRINT" Condition ";C$:GOTO 3970 @@ -527,13 +500,7 @@ 4080 INPUT"Computer active and awaiting command ";CM$:H8=1 4090 FOR K1= 1 TO 6 4100 IF MID$(CM$,1,3)<>MID$(CM1$,3*K1-2,3) THEN 4120 -4110 rem ON K1 GOTO 4230,4400,4490,4750,4550,4210 -4111 if k1=1 then 4230 -4112 if k1=2 then 4400 -4113 if k1=3 then 4490 -4114 if k1=4 then 4750 -4115 if k1=5 then 4550 -4116 if k1=6 then 4210 +4110 ON K1 GOTO 4230,4400,4490,4750,4550,4210 4120 NEXT K1 4121 gosub 4130 4122 goto 4080 @@ -643,15 +610,7 @@ 4861 Q$=MID$(Q$,1,189)+A$:RETURN 4870 Q$=MID$(Q$,1,S8-1)+A$+MID$(Q$,s8+3,192-s8-2):RETURN 4880 REM prints device name -4890 rem ON R1 GOTO 4900,4910,4920,4930,4940,4950,4960,4970 -4891 if r1 = 1 then 4900 -4892 if r1 = 2 then 4910 -4893 if r1 = 3 then 4920 -4894 if r1 = 4 then 4930 -4895 if r1 = 5 then 4940 -4896 if r1 = 6 then 4950 -4897 if r1 = 7 then 4960 -4898 if r1 = 8 then 4970 +4890 ON R1 GOTO 4900,4910,4920,4930,4940,4950,4960,4970 4900 G2$="Warp Engines":RETURN 4910 G2$="Short Range Sensors":RETURN 4920 G2$="Long Range Sensors":RETURN @@ -668,15 +627,7 @@ 5020 REM quadrant name in g2$ from z4,z5 (=q1,q2) 5030 REM call with g5=1 to get region name only 5040 IF Z5<=4 THEN 5140 -5041 rem ON Z4 GOTO 5060,5070,5080,5090,5100,5110,5120,5130 -5042 if z4 = 1 then 5060 -5043 if z4 = 2 then 5070 -5044 if z4 = 3 then 5080 -5045 if z4 = 4 then 5090 -5046 if z4 = 5 then 5100 -5047 if z4 = 6 then 5110 -5048 if z4 = 7 then 5120 -5049 if z4 = 8 then 5130 +5041 ON Z4 GOTO 5060,5070,5080,5090,5100,5110,5120,5130 5050 GOTO 5140 5060 G2$="Antares":GOTO 5230 5070 G2$="Rigel":GOTO 5230 @@ -686,15 +637,7 @@ 5110 G2$="Altair":GOTO 5230 5120 G2$="Sagittarius":GOTO 5230 5130 G2$="Pollux":GOTO 5230 -5140 rem ON Z4 GOTO 5150,5160,5170,5180,5180,5200,5210,5220 -5142 if z4 = 1 then 5150 -5143 if z4 = 2 then 5160 -5144 if z4 = 3 then 5170 -5145 if z4 = 4 then 5180 -5146 if z4 = 5 then 5190 -5147 if z4 = 6 then 5200 -5148 if z4 = 7 then 5210 -5149 if z4 = 8 then 5220 +5140 ON Z4 GOTO 5150,5160,5170,5180,5180,5200,5210,5220 5150 G2$="Sirius":GOTO 5230 5160 G2$="Deneb":GOTO 5230 5170 G2$="Capella":GOTO 5230 @@ -704,15 +647,7 @@ 5210 G2$="Arcturus":GOTO 5230 5220 G2$="Spica" 5230 IF G5=1 THEN 5240 -5231 rem ON Z5 GOTO 5250,5260,5270,5280,5250,5260,5270,5280 -5232 if z4 = 1 then 5250 -5233 if z4 = 2 then 5260 -5234 if z4 = 3 then 5270 -5235 if z4 = 4 then 5280 -5236 if z4 = 5 then 5250 -5237 if z4 = 6 then 5260 -5238 if z4 = 7 then 5270 -5239 if z4 = 8 then 5280 +5231 ON Z5 GOTO 5250,5260,5270,5280,5250,5260,5270,5280 5240 RETURN 5250 G2$=G2$+" i":RETURN 5260 G2$=G2$+" ii":RETURN From c83acf4cd293df2d7fe3847639d505a66d9b7052 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:45:30 +0100 Subject: [PATCH 077/183] Moved to new folder --- examples/factorial.bas | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/factorial.bas diff --git a/examples/factorial.bas b/examples/factorial.bas new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/factorial.bas @@ -0,0 +1 @@ + From 2eecb9f51fb30542f0b9d5b2c9461c1a64052821 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:46:39 +0100 Subject: [PATCH 078/183] Reorganising folder structure --- examples/ADESCRIP | 100 +++ examples/AITEMS | 35 + examples/AMESSAGE | 573 +++++++++++++++ examples/AMOVING | 100 +++ examples/BITEMS | 137 ++++ examples/PyBStartrek.bas | 688 ++++++++++++++++++ examples/adventure-fast.bas | 1112 ++++++++++++++++++++++++++++++ examples/bagels.bas | 83 +++ examples/factorial.bas | 10 +- examples/regression.bas | 105 +++ examples/rock_scissors_paper.bas | 63 ++ 11 files changed, 3005 insertions(+), 1 deletion(-) create mode 100644 examples/ADESCRIP create mode 100644 examples/AITEMS create mode 100644 examples/AMESSAGE create mode 100644 examples/AMOVING create mode 100644 examples/BITEMS create mode 100644 examples/PyBStartrek.bas create mode 100644 examples/adventure-fast.bas create mode 100644 examples/bagels.bas create mode 100644 examples/regression.bas create mode 100644 examples/rock_scissors_paper.bas diff --git a/examples/ADESCRIP b/examples/ADESCRIP new file mode 100644 index 0000000..7924165 --- /dev/null +++ b/examples/ADESCRIP @@ -0,0 +1,100 @@ +You're in the forest. +You're in the forest. +You're in the forest. +You're in the forest. +You're at the hill in the road. +You're at the end of the road again. +You're inside the Building. +You're in the valley. +You're at slit in the streambed. +You're directly above a grate in a dry streambed. +You're below the grate. +You're in the cobble crawl. +You're in the debris room. +You are in an awkward sloping east/west canyon. +You're in the bird chamber. +You're at the top of the small pit. +You're in the Hall of Mists. +You're in the nugget of gold room. +You're on the east bank of the fissure. +You are on the west side of the fissure in the Hall of Mists. +You're at west end of the Hall of Mists. +You're in the Hall of the Mountain King. +You are in the south side chamber. +You are in the west side chamber of the Hall of the Mountain King. +You are at a N/S passage at a hole in the floor. +You're at Y2 . +You're at the window near the pit. +The passage here is blocked by a recent cave-in. +You're at the east end of the Long Hall. +You're at west end of the Long Hall. +You are at a crossover of a high N/S passage and a low E/W one. +Dead end +You are in a dirty broken passage. +You're at the top of the small pit. +You are in a little pit which has a stream flowing through it. +You're in the dusty rock room. +You're at a complex junction. +You're in the anteroom. +You are at Witt's end. Passages lead off in *all* directions. +You're in the shell room. +You're in the arched hall. +You're in a long sloping corridor with ragged sharp walls. +You are in a cul-de-sac about eight feet across. How's your french? +You are in bedquilt, a long east/west passage with holes everywhere. +You're in the swiss cheese room. +You're in the soft room. +You're at the east end of the Twopit room. +You're at the west end of the Twopit room. +You're in the east pit. +You're in the west pit. +You're in the slab room. +You're in the Oriental room. +You are in a large low room. +You're in a sloping corridor. +Dead end crawl. +You're in the misty cavern. +You're in the alcove. +You're in the Plover room. +You're in the dark-room. +You're on the SW side of the chasm. +You're on the NE side of the chasm. +You're in a corridor. +You're at the fork in the path. +You're in the limestone passage. +You're at a junction with warm walls. +You're in the chamber of boulders. +You're at a breath-taking view. +You're in front of the barren room. +You're in the barren room. +You're in a narrow corridor. +You're in the Giant room. +The passage here is blocked by a recent cave-in. +You are at one end of an immense north/south passage. +You're in the cavern with a waterfall +You're at a steep incline above a large room. +You are in a secret N/S canyon above a sizable passage. +You're at the top of the stalactite. +You're at a junction of three secret canyons. +You are in a secret N/S canyon above a large room. +You're in mirror canyon. +You're at a reservoir. +You are in a secret canyon which exits to the north and east. +You're in the secret E/W canyon above a tight canyon. +You are at a wide place in a very tight N/S canyon. +The canyon here becomes too tight to go on. +You are in a tall E/W canyon. +The canyon runs into a mass of boulders -- dead end. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are in a maze of twisty little passages, all alike. +You are at a huge orange stalactite in the maze. +You are in a maze of twisty little passages, all alike. +You are at a dead end. \ No newline at end of file diff --git a/examples/AITEMS b/examples/AITEMS new file mode 100644 index 0000000..906760a --- /dev/null +++ b/examples/AITEMS @@ -0,0 +1,35 @@ +There is a large sparking nugget of gold here! +There are bars of silver here! +There is precious jewelry here! +There are many coins here! +There are several diamonds here! +There is a delicate, precious ming vase here! +To one side lies a glistening Pearl! +There is a nest here, full of golden eggs! +There is a jewel-encrusted trident here! +There is an emerald the size of a plover's egg here! +There is a platinum pyramid here, eight inches on a side! +There is a golden chain here! +There are rare spices here! +There is a valuable persian rug here! +There is a chest here, full of various treasures! +There is a bottle of water here. +There is a small pool of oil here. +There is a brass lantern here. +There is a set of keys here. +There is food here. +There is a glass bottle here. +There is a small wicker cage here. +There is a 3-foot black rod here. +There is an enormous clam here with its shell tightly shut. +There is a recent issue of 'Spelunker Today' here. +There is a bear nearby. +There is a little axe here. +There is a purple velvet pillow here. +There are shards of pottery scattered about. +There is an enormous oyster here with its shell tightly shut. +There is a little bird here. +A burly troll stands in front of you. +A huge fierce green dragon bars the way! +A huge fierce green snake bars the way! +There is a threatening little dwarf in the room with you! \ No newline at end of file diff --git a/examples/AMESSAGE b/examples/AMESSAGE new file mode 100644 index 0000000..2a1c352 --- /dev/null +++ b/examples/AMESSAGE @@ -0,0 +1,573 @@ +#1 +You can't move that way. +#2.1 +Nothing happens. +#2.2 +Nothing seems to happen. +#2.3 +I don't think that had any affect on anything. +#2.4 +I don't know how to apply that word here. +#2.5 +That had little effect, if any at all. +#3 +The mist is quite thick here, and the fissure is too wide to jump. +#4 +You are at the bottom of the pit with a broken neck. +#5 +The bottle is already filled! +#6 +The bear eagerly wolfs down your food, after which he seems to calm +down considerably and even becomes rather friendly. burp +#7 +The plant surges upwards, reaching halfway to the hole above you +#8 +The plant grows explosively, almost filling the bottom of the pit. +#9 +You overwatered the plant, you fool! +#10 +The grate is locked. +#11 +The grate is unlocked. +#12 +The bear is quite ferocious, and will no doubt tear you to +shreds if you approach it! +#13 +The chain is unlocked and the bear is free. +#14 +A crystal bridge now spans the fissure. +#15 +The bridge vanishes. +#16 +It is beyond your power to do that. +#17 +The clam opens momenntarily, and a glistening pearl falls out +and rolls away. Goodness, this must really have been an oyster. +(I never was any good at identifying bivalves, anyway.) +#18 +I can't close that! +#19 +The oil has freed up the hinges so that the door will now move, +although it requires some effort. +#20 +I can't eat. +#21 +How can I drink that? +#22 +"Glug, glug, glug, Belch!" +#23 +It doesn't want to eat anything (except maybe you!) +#24 +You fool! Dwarves only eat coal! Now you've made him +*** REALLY MAD ****! +#25 +Trolls are close relatives with the rocks and have skin as tough as +that of a rhinoceros. The troll fends off your blows effortlessly. +#26 +The troll deftly catches the axe, examines it carefully, and tosses it +back, declaring, "Good workmanship, but it's not valuable enough." +#27 +The troll catches your treasure and scurries away out of sight. +#28 +The bear lumbers toward the troll, who lets out a startled shriek and +scurries away. The bear soon gives up the pursuit and wanders back. +#29 +You attack a little dwarf, but he dodges out of the way. +#30 +You killed a little dwarf. The body vanishes in a cloud of greasy +black smoke. +#31 +There is a threatening little dwarf in the room with you! +#32 +One sharp nasty knife is thrown at you! +#33 +Out from the shadows behind you pounces a bearded pirate! "Har, har," +he chortles, "I'll just take all this booty and hide it away with me +chest deep in the maze!" he snatches your treasure and vanishes into +the gloom. +#34 +There are faint rustling noises from the darkness behind you. +#35 +The grate is open. +#36 +The grate is locked. +#37 +The bird was unafraid when you entered, but as you approach it becomes +disturbed and you cannot catch it. +#38 +The dome is unclimbable. +#39 +A crystal bridge now spans the fissure. +#40 +A huge green fierce snake bars the way! Hisssssss +#41 +A hollow voice says "Plugh". +#42 +The vase is now resting, delicately, on a velvet pillow. +#43 +Crash! Tinkle! The ming vase shatters into worthless crockery. +#44 +You fell into a pit and broke every bone in your body! +#45 +It is now pitch dark. If you proceed you will likely fall into a pit. +#46 +The nest of golden eggs has vanished! +#47 +There is a tiny little plant in the pit, murmuring "water, water, ..." +#48 +There is a 12-foot-tall beanstalk stretching up out of the pit, +bellowing "WATER!! WATER!!" +#49 +There is a gigantic beanstalk stretching all the way up to the hole. +#50 +You can't get by the snake. He hates your guts. +#51 +The dragon has a rather "volatile" personality, and he will +probably incinerate you if you get any closer. +#52 +You have crawled around in some little holes and wound up back in the +main passage. +#53 +Something you're carrying won't fit through the tunnel with you. +#54 +Your load is too heavy. You'd best take inventory and drop +something first. +#55 +A burly troll stands by the bridge and insists you throw him a +treasure before you may cross. +#56 +The troll pops from under the bridge and blocks your way. +#57 +The way north is barred by a massive, rusty, iron door. +#58 +The bear will bite off your hand. Besides, the chain is locked to the wall. +#59 +It's rather difficult to move a twenty-ton dragon, considering +he is lying on the rug. +#60 +The vase is now resting, delicately, on a velvet pillow. +#61.1 +A valliant attempt! +#61.2 +Nice try! +#61.3 +A valliant attempt, but you're not that strong! +#61.4 +Don't be silly! +#61.5 +Do you have any idea on how to do that? +#62 +A huge green fierce dragon blocks the way. The dragon is lying +on a persian rug! +#63 +A burly troll stands beside the bridge, blocking your way. +#64 +There is a ferocious cave bear eyeing you from the far end of the room! +#65 +The bear is locked to the wall with a golden chain! +#66 +There is a calm, friendly bear locked to the wall. +#67 +You are being followed by a very large, tame bear. +#68 +With what? Your bare hands? +#69 +Congratulations! You have just vanquished a dragon with your bare hands +(hard to believe ain't it?). +#70 +Attacking it is both dangerous and doesn't work. +#71 +There is nothing here to attack. +#72 +How can you do that to more than one thing at a time? +#73 +Thank you, it was delicious! +#74 +I see nothing special about that. +#75 +Oh dear, you seem to have gotten yourself killed. I might be able to +help you out, but I've never really done this before. Do you want me +to try to reincarnate you? +#76 +All right. But don't blame me if something goes wr...... + --- POOF!! --- +You are engulfed in a cloud of orange smoke. Coughing and gasping, +you emerge from the smoke and find.... +#77 +You clumsy oaf, you've done it again! I don't know how long I can +keep this up. Do you want me to try reincarnating you again? +#78 +Now you've really done it! I'm out of orange smoke! You don't expect +me to do a decent reincarnation without any orange smoke, do you? +Better luck next time! +#79 +The nest of golden eggs has vanished! +#80 +A little dwarf just walked around a corner, saw you, threw a little +axe at you which missed, cursed and ran away. +#81 +The nest of golden eggs has re-appeared! +#200 +You are in a forest, with trees all around you. +#205 +You have walked up a hill, still in the forest. The road slopes back +down the other side of the hill. There is a building in the distance. +#206 +You are standing at the end of a road before a small brick building. +Around you is a forest. A small stream flows out of the building and +down a gully. +#207 +You are inside a building, a well house for a large spring. +#208 +You are in a valley in the forest beside a stream tumbling along a +rocky bed. +#209 +At your feet all the water of the stream splashes into a 2-inch slit +in the rock. Downstream the streambed is bare rock. +#210 +You are in a 20-foot depression floored with bare dirt. Set into the +dirt is a strong steel grate mounted in concrete. A dry streambed +leads into the depression. +#211 +You are in a small chamber beneath a 3x3 steel grate to the surface. +A low crawl over cobbles leads inward to the west. +#212 +You are crawling over cobbles in a low passage. There is a dim light +at the east end of the passage. +#213 +You are in a debris room filled with stuff washed in from the surface. +A low wide passage with cobbles becomes plugged with mud and debris +here, but an awkward canyon leads upward and west. A note on the wall +says "magic word XYZZY." +#214 +You are in an awkward sloping east/west canyon. +#215 +You are in a splendid chamber thirty feet high. The walls are frozen +rivers of orange stone. An awkward canyon and a good passage exit +from east and west sides of the chamber. +#216 +At your feet is a small pit breathing traces of white mist. An east +passage ends here except for a small crack leading on. + +Rough stone steps lead down the pit. +#217 +You are at one end of a vast hall stretching forward out of sight to +the west. There are openings to either side. Nearby, a wide stone +staircase leads downward. The hall is filled with wisps of white mist +swaying to and fro almost as if alive. A cold wind blows up the +staircase. There is a passage at the top of a dome behind you. + +Rough stone steps lead to the top of the dome. +#218 +This is a low room with a crude note on the wall. The note says, +"you can't get it up the steps." +#219 +You are on the east bank of a fissure slicing clear across the hall. +The mist is quite thick here, and the fissure is too wide to jump. +#220 +You are on the west side of the fissure in the Hall of Mists. +#221 +You are at the west end of Hall of Mists. A low wide crawl continues +west and another goes north. To the south is a little passage 6 feet +off the floor. +#222 +You are in the Hall of the Mountain King, with passages off in all +directions. +#223 +You are in the south side chamber. +#224 +You are in the west side chamber of the Hall of the Mountain King. +A passage continues west and up here. +#225 +You are in a low N/S passage at a hole in the floor. The hole goes +down to an E/W passage. +#226 +You are in a large room, with a passage to the south, a passage to the +west, and a wall of broken rock to the east. There is a large "Y2" on +a rock in the room's center. +#227 +You're at a low window overlooking a huge pit, which extends up out of +sight. A floor is indistinctly visible over 50 feet below. Traces of +white mist cover the floor of the pit, becoming thicker to the right. +Marks in the dust around the window would seem to indicate that +someone has been here recently. Directly across the pit from you and +25 feet away there is a similar window looking into a lighted room. A +shadowy figure can be seen there peering back at you. + +The shadowy figure seems to be trying to attract your attention. +#228 +The passage here is blocked by a recent cave-in. +#229 +You are at the east end of a very long hall apparently without side +chambers. To the east a low wide crawl slants up. To the north a +round two foot hole slants down. +#230 +You are at the west end of a very long featureless hall. The hall +joins up with a narrow north/south passage. +#231 +You are at a crossover of a high N/S passage and a low E/W one. +#232 +Dead end +#233 +You are in a dirty broken passage. To the east is a crawl. To the +west is a large passage. Above you is a hole to another passage. +#234 +You are on the brink of a small clean climbable pit. A crawl leads +west. +#235 +You are in the bottom of a small pit with a little stream, which +enters and exits through tiny slits. +#236 +You are in a large room full of dusty rocks. There is a big hole in +the floor. There are cracks everywhere, and a passage leading east. +#237 +You are at a complex junction. A low hands and knees passage from the +north joins a higher crawl from the east to make a walking passage +going west. There is also a large room above. The air is damp here. +#238 +You are in an anteroom leading to a large passage to the east. Small +passages go west and up. The remnants of recent digging are evident. +a sign in midair here says "Cave under construction beyond this point. +proceed at own risk. "[Witt Construction Company]" +#239 +You are at Witt's end. Passages lead off in *all* directions. +#240 +You're in a large room carved out of sedimentary rock. The floor and +walls are littered with bits of shells embedded in the stone. A +shallow passage proceeds downward, and a somewhat steeper one leads +up. A low hands and knees passage enters from the south. +#241 +You are in an arched hall. A coral passage once continued up and east +from here, but is now blocked by debris. The air smells of sea water. +#242 +You are in a long sloping corridor with ragged sharp walls. +#243 +You are in a cul-de-sac about eight feet across. How's your french? +#244 +You are in bedquilt, a long east/west passage with holes everywhere. +To explore at random, select north, south, up or down. +#245 +You are in a room whose walls resemble swiss cheese. Obvious passages +go west, east, NE, and NW. Part of the room is occupied by a large +bedrock block. +#246 +You are in the soft room. The walls are covered with heavy curtains, +the floor with a thick pile carpet. Moss covers the ceiling. +#247 +You are at the east end of the Twopit room. The floor here is +littered with thin rock slabs, which make it easy to descend the pits. +There is a path here bypassing the pits to connect passages from east +and west. There are holes all over, but the only big one is on the +wall directly over the west pit where you can't get to it. +#248 +You are at the west end of the Twopit room. There is a large hole in +the wall above the pit at this end of the room. +#249 +You are at the bottom of the eastern pit in the Twopit room. There is +a small pool of oil in one corner of the pit. +#250 +You are at the bottom of the western pit in the Twopit room. There is +a large hole in the wall about 25 feet above you. +#251 +You are in a large low circular chamber whose floor is an immense slab +fallen from the ceiling (slab room). East and west there once were +large passages, but they are now filled with boulders. Low small +passages go north and south, and the south one quickly bends west +around the boulders. +#252 +This is the oriental room. Ancient oriental cave drawings cover the +walls. A gently sloping passage leads upward to the north, another +passage leads SE, and a hands and knees crawl leads west. +#253 +You are in a large low room. Crawls lead north, SE, and SW. +#254 +You are in a long winding corridor sloping out of sight in both +directions. +#255 +Dead end crawl. +#256 +You are following a wide path around the outer edge of a large cavern. +Far below, through a heavy white mist, strange splashing noises can be +heard. The mist rises up through a fissure in the ceiling. The path +exits to the south and west. +#257 +You are in an alcove. A small NW path seems to widen after a short +distance. An extremely tight tunnel leads east. It looks like a very +tight squeeze. An eerie light can be seen at the other end. +#258 +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads NE. +#259 +You're in the dark-room. A corridor leading south is the only exit. +A massive stone tablet embedded in the wall reads: +"Congratulations on bringing light into the dark-room!" +#260 +You are on one side of a large, deep chasm. A heavy white mist rising +up from below obscures all view of the far side. A SW path leads away. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A sign posted on the bridge reads, "STOP! Pay Troll!" +#261 +You are on the far side of the chasm. A NE path leads away from the +chasm on this side. + +A rickety wooden bridge extends across the chasm, vanishing into the +gloom. A sign posted on the bridge reads: "Stop! Pay troll!" +#262 +You're in a long east/west corridor. A faint rumbling noise can be +heard in the distance. +#263 +The path forks here. The left fork leads northeast. A dull rumbling +seems to get louder in that direction. The right fork leads southeast +down a gentle slope. The main corridor enters from the west. +#264 +You are walking along a gently sloping north/south passage lined with +oddly shaped limestone formations. +#265 +The walls are quite warm here. From the north can be heard a steady +roar, so loud that the entire cave seems to be trembling. Another +passage leads south, and a low crawl goes east. +#266 +You are in a small chamber filled with large boulders. The walls are +very warm, causing the air in the room to be almost stifling from the +heat. The only exit is a crawl heading west, through which is coming +a low rumbling. +#267 +You are on the edge of a breath-taking view. Far below you is an +active volcano, from which great gouts of molten lava come surging +out, cascading back down into the depths. The glowing rock fills the +farthest reaches of the cavern with a blood-red glare, giving every- +thing an eerie, macabre appearance. The air is filled with flickering +sparks of ash and a heavy smell of brimstone. The walls are hot to +the touch, and the thundering of the volcano drowns out all other +sounds. Embedded in the jagged roof far overhead are myriad twisted +formations composed of pure white alabaster, which scatter the murky +light into sinister apparitions upon the walls. To one side is a deep +gorge, filled with a bizarre chaos of tortured rock which seems to +have been crafted by the devil himself. An immense river of fire +crashes out from the depths of the volcano, burns its way through the +gorge, and plummets into a bottomless pit far off to your left. To +the right, an immense geyser of blistering steam erupts continuously +from a barren island in the center of a sulfurous lake, which bubbles +ominously. The far right wall is aflame with an incandescence of its +own, which lends an additional infernal splendor to the already +hellish scene. A dark, foreboding passage exits to the south. +#268 +You are standing at the entrance to a large, barren room. A sign +posted above the entrance reads: "Caution! Bear in room!" +#269 +You are inside a barren room. The center of the room is completely +empty except for some dust. Marks in the dust lead away toward the +far end of the room. The only exit is the way you came in. +#270 +You are in a long, narrow corridor stretching out of sight to the +west. At the eastern end is a hole through which you can see a +profusion of leaves. +#271 +You are in the giant room. The ceiling here is too high up for your +lamp to show it. Cavernous passages lead east, north, and south. On +the west wall is scrawled the inscription, "FEE FIE FOE FOO" [sic]. +#272 +The passage here is blocked by a recent cavein. +#273 +You are at one end of an immense north/south passage. +#274 +You are in a magnificent cavern with a rushing stream, which cascades +over a sparkling waterfall into a roaring whirlpool which disappears +through a hole in the floor. Passages exit to the south and west. +#275 +You are at the top of a steep incline above a large room. You could +climb down here, but you would not be able to climb up. There is a +passage leading back to the north. +#276 +You are in a secret N/S canyon above a sizable passage. +#277 +A large stalactite extends from the roof and almost reaches the floor +below. You could climb down it, and jump from it to the floor, but +having done so you would be unable to reach it to climb back up. + +The maze continues at this level. +#278 +You are in a secret canyon at a junction of three canyons, bearing +north, south, and SE. The north one is as tall as the other two +combined. +#279 +You are in a secret N/S canyon above a large room. +#280 +You are in a north/south canyon about 25 feet across. The floor is +covered by white mist seeping in from the north. The walls extend +upward for well over 100 feet. Suspended from some unseen point far +above you, an enormous two-sided mirror is hanging parallel to and +midway between the canyon walls. (The mirror is obviously provided +for the use of the dwarves, who as you know, are extremely vain.) A +small window can be seen in either wall, some fifty feet up. +#281 +You are at the edge of a large underground reservoir. An opaque cloud +of white mist fills the room and rises rapidly upward. The lake is +fed by a stream, which tumbles out of a hole in the wall about 10 feet +overhead and splashes noisily into the water somewhere within the +mist. The only passage goes back toward the south. +#282 +You are in a secret canyon which exits to the north and east. +#283 +You're in the secret E/W canyon above a tight canyon. +#284 +You are at a wide place in a very tight N/S canyon. +#285 +The canyon here becomes too tight to go on. +#286 +You are in a tall E/W canyon. A low tight crawl goes 3 feet north and +seems to open up. +#287 +The canyon runs into a maze of boulders -- dead end. +#288 +You are in a maze of twisty little passages, all alike. +#298 +You are near a large pit beside a large stalactite in the maze. +The stalactite is about 30 feet long, allowing you to descend +it. However, due to the smoothness of the stalactite you will +probably be unable to climb back up again. +#300 +Dead end. +#301 +Welcome to adventure.! Would you like instructions? +#302 +Somewhere nearby is Colossal Cave, where others have found fortunes in +treasure and gold, though it is rumored that some who enter are never +seen again. Magic is said to work in the cave. I will be your eyes +and hands. Direct me with commands such as get, take, or look. Since you +need to move around, you must usually enter compass directions +(N,NE,E,SE,S,SW,W,NW or up or down), although you may sometimes use +more vague verbs with with to move. I know of several objects in this +game, such as a lamp and a bottle. I also know of special objects in the +cave. Some of these objects have side effects, ie there is a rod in the +cave that scares a little bird. +----------------------------------------------------------------- +The object of this game is to gather as many treasures as you can and +put them back in the house. You also run the risk of getting robbed or +killed by some rather unfriendly inhabitants of the cave. +---------------------------------------------------------------- +There are some useful commands that you should know about: +--- Brief, Long, and Short - these commands control the amount of +--- detail you get in descriptions. +--- Look (or L) - gives a detailled descriptio nof your surroundings. +--- Inventory (or I) tells you what you are carrying. +--- Quit, Stop or End - these are self-explanatory. +--- Save - by typing this, you may save your game and continue it at +--- a later time. +--- Continue - lets you continue an old game. +--- Score - tells you hw well you're doing. + +Other helpful functions include: +* multiple commands on one input line, ie: +"Go south, get car, keys, Start car." (example only) +** oops, the PyBasic version doesn't support multiple commands :( +-------------------------------------------------------------- +"Adventure" is a version of the game created by Willy Crowther and +Dan Woods at M.I.T. +-------------------------------------------------------------- +#303 +I'm sorry, but the magazine is written in Dwarvish. +# +# +# \ No newline at end of file diff --git a/examples/AMOVING b/examples/AMOVING new file mode 100644 index 0000000..408591f --- /dev/null +++ b/examples/AMOVING @@ -0,0 +1,100 @@ +3,0,6,0,4,0,2,0,0,0 +1,0,8,0,2,0,4,0,0,0 +2,0,1,0,4,0,7,0,0,0 +1,0,3,0,5,0,2,0,0,0 +2,0,6,0,4,0,1,0,0,0 +2,0,7,0,8,0,5,0,0,0 +0,0,0,0,0,0,6,0,0,0 +6,0,3,0,9,0,4,0,0,0 +8,0,3,0,10,0,4,0,0,0 +9,0,0,0,11,0,0,0,0,11 +0,0,10,0,0,0,12,0,10,0 +0,0,11,0,0,0,13,0,0,0 +0,0,12,0,0,0,14,0,14,0 +0,0,13,0,0,0,15,0,15,13 +0,0,14,0,0,0,16,0,0,0 +0,0,15,0,0,0,0,0,0,17 +22,0,0,0,18,0,19,0,16,22 +17,0,0,0,0,0,0,0,0,0 +0,0,17,0,0,0,20,0,0,0 +0,0,19,0,0,0,21,0,0,0 +20,0,20,0,88,0,29,0,0,0 +25,0,17,0,23,83,24,0,17,0 +22,0,0,0,0,0,0,0,0,0 +0,0,22,0,0,0,31,0,31,0 +26,0,0,0,22,0,0,0,0,33 +0,0,28,0,25,0,27,0,0,0 +0,0,26,0,0,0,0,0,0,0 +0,0,0,0,0,26,0,0,0,0 +31,0,21,0,0,0,30,0,21,31 +31,0,29,0,94,0,0,0,0,0 +32,0,24,0,30,0,29,0,0,0 +31,0,0,0,0,0,0,0,0,0 +0,0,34,0,0,0,36,0,25,0 +0,0,0,0,0,0,33,0,0,35 +0,0,0,0,0,0,0,0,34,0 +0,0,33,0,0,0,0,0,25,37 +40,0,38,0,0,0,44,0,36,0 +0,0,39,0,0,0,37,0,37,0 +255,255,255,255,255,255,255,255,255,255 +0,0,0,0,37,0,0,0,41,42 +0,0,0,0,0,0,0,0,0,43 +0,0,0,0,0,0,0,0,40,43 +0,0,0,0,0,0,0,0,42,0 +255,0,37,0,255,0,45,0,255,255 +0,44,46,0,0,0,47,52,0,0 +0,0,0,0,0,0,45,0,0,0 +0,0,45,0,0,0,48,0,0,49 +0,0,47,0,0,0,51,0,0,50 +0,0,0,0,0,0,0,0,47,0 +0,0,0,0,0,0,0,0,48,0 +44,0,0,0,48,0,0,0,79,0 +56,0,0,45,0,0,53,0,0,0 +55,0,0,52,0,54,0,0,0,0 +0,0,0,0,0,0,0,0,60,53 +0,0,0,0,53,0,0,0,0,0 +0,0,0,0,52,0,57,0,0,0 +0,0,58,0,0,0,0,56,0,0 +0,59,0,0,0,0,57,0,0,0 +0,0,0,0,58,0,0,0,0,0 +0,61,0,0,0,54,0,0,0,54 +0,62,0,0,0,60,0,0,0,0 +0,0,63,0,0,0,61,0,0,0 +0,65,0,64,0,0,62,0,0,0 +63,0,0,0,68,0,0,0,0,0 +67,0,66,0,63,0,0,0,0,0 +0,0,0,0,0,0,65,0,0,0 +0,0,0,0,65,0,0,0,0,0 +0,0,69,0,0,0,64,0,0,0 +0,0,0,0,0,0,68,0,0,0 +0,0,50,0,0,0,71,0,0,50 +73,0,70,0,72,0,0,0,0,0 +0,71,0,0,0,0,0,0,0,0 +74,0,0,0,71,0,0,0,0,0 +0,0,0,0,73,0,75,0,0,0 +74,0,0,0,0,0,0,0,0,53 +78,0,0,0,77,0,0,0,0,45 +76,0,0,0,0,0,0,0,0,93 +27,0,0,44,76,0,0,0,0,0 +80,0,0,0,82,0,0,0,0,51 +81,0,0,0,79,0,0,0,0,0 +0,0,0,0,80,0,0,0,0,0 +79,0,83,0,0,0,0,0,0,0 +0,0,22,0,0,0,82,0,0,84 +86,0,0,0,85,0,0,0,0,0 +84,0,0,0,0,0,0,0,0,0 +45,0,84,0,0,0,87,0,0,0 +0,0,0,86,0,0,0,0,0,0 +89,82,91,95,90,83,92,91,95,94 +95,96,91,90,89,88,91,95,93,97 +92,92,91,93,88,97,88,96,99,87 +91,89,94,90,93,95,89,92,0,0 +95,97,93,96,94,95,21,92,0,0 +90,96,92,94,88,30,92,90,0,0 +98,89,90,91,89,93,95,97,0,0 +93,94,96,92,97,95,94,92,0,0 +92,99,97,96,89,94,90,90,91,92 +98,89,92,91,90,93,94,95,96,98 +95,90,93,91,94,92,90,99,98,15 +88,89,93,97,100,89,96,95,0,0 +0,0,0,0,99,0,0,0,0,0 diff --git a/examples/BITEMS b/examples/BITEMS new file mode 100644 index 0000000..00f0731 --- /dev/null +++ b/examples/BITEMS @@ -0,0 +1,137 @@ +100, N +101, NE +102, E +103, SE +104, S +105, SW +106, W +107, NW +108, U +109, D +110, PLUGH +111, XYZZY +112, PLOVER +113, CROSS +114, CLIMB +115, JUMP +116, FILL +117, EMPTY +117, POUR +118, LOOK +118, L +119, LIGHT +119, ON +120, EXTINGUISH +120, OFF +121, IN +121, ENTER +122, LEAVE +122, OUT +123, INVENTORY +123, I +124, GET +124, CATCH +124, TAKE +125, DROP +125, DUMP +47, ALL +47, EVERYTHING +126, THROW +127, ATTACK +127, KILL +128, FEED +129, WATER +130, LOCK +131, UNLOCK +132, FREE +132, RELEASE +133, WAVE +134, OPEN +135, CLOSE +136, OIL +137, EAT +138, DRINK +139, FEE FIE FOE FOO +140, SHORT +141, LONG +142, BRIEF +143, QUIT +143, STOP +143, END +144, SCORE +145, SAVE +146, LOAD +147, READ +147, EXAMINE +148, YES +148, Y +149, BUG +1, GOLD +1, NUGGET +2, BARS +2, SILVER +3, JEWELRY +4, COINS +5, DIAMONDS +6, MING +6, VASE +7, PEARL +8, EGGS +8, NEST +9, TRIDENT +10, EMERALD +11, PLATINUM +11, PYRAMID +12, CHAIN +13, SPICES +14, PERSIAN +14, RUG +15, TREASURE +15, CHEST +16, WATER +17, OIL +18, LAMP +18, LANTERN +19, KEYS +20, FOOD +21, BOTTLE +22, CAGE +23, ROD +23, WAND +24, CLAM +25, MAGAZINE +26, BEAR +27, AXE +28, VELVET +28, PILLOW +29, SHARDS +30, OYSTER +31, BIRD +32, TROLL +33, DRAGON +34, SNAKE +35, DWARF +36, ROCK +36, BOULDER +37, STAIRS +38, STEPS +39, HOUSE +39, BUILDING +40, GRATE +41, STREAM +42, ROOM +43, BRIDGE +44, PIT +45, VOLCANO +46, ROAD +100, NORTH +101, NORTHEAST +102, EAST +103, SOUTHEAST +104, SOUTH +105, SOUTHWEST +106, WEST +107, NORTHWEST +108, UP +109, DOWN +150,* diff --git a/examples/PyBStartrek.bas b/examples/PyBStartrek.bas new file mode 100644 index 0000000..c1670f6 --- /dev/null +++ b/examples/PyBStartrek.bas @@ -0,0 +1,688 @@ +520 REM +530 REM +540 REM **** **** STAR TREK **** **** +550 REM **** Simulation of a mission of the starship ENTERPRISE +560 REM **** as seen on the Star Trek tv show +570 REM **** Original program in Creative Computing +580 REM **** Basic Computer Games by Dave Ahl +590 REM **** Modifications by Bob Fritz and Sharon Fritz +600 REM **** for the IBM Personal Computer, October-November 1981 +610 REM **** Bob Fritz, 9915 Caninito Cuadro, San Diego, Ca., 92129 +620 REM **** (714) 484-2955 +630 REM **** +640 PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT +650 E1$= " ,-----------------, _" +660 E2$= " `---------- ----',-----/ \----," +670 E3$= " | | '- --------'" +680 E4$= " ,--' '------/ /--," +690 E5$= " '-----------------'" +691 PRINT +700 E6$= " THE USS ENTERPRISE --- NCC-1701" +720 PRINT E1$ +730 PRINT E2$ +740 PRINT E3$ +750 PRINT E4$ +760 PRINT E5$ +770 PRINT +780 PRINT E6$ +790 PRINT:PRINT: PRINT:PRINT:PRINT:PRINT:PRINT +811 RANDOMIZE +960 dim D(8) +961 dim C(9,2) +962 dim K(3,3) +963 dim N(3) +965 DIM G(8,8) +966 DIM Z(8,8) +970 T=INT(RND(1)*20+20)*100:T0=T:T9=25+INT(RND(1)*10):D0=0:E=3000:E0=E +980 P=10:P0=P:S9=200:S=0:B9=0:K9=0:X$="":X0$=" is " +990 rem DEF FND(D)=SQR((K(I,1)-S1)^2+(K(I,2)-S2)^2) +1000 rem DEF FNR(R)=INT(RND(1)(R)*7.98+1.01) +1010 REM initialize enterprise's position +1020 Q1=INT(RND(1)*7.98+1.01):Q2=INT(RND(1)*7.98+1.01):S1=INT(RND(1)*7.98+1.01):S2=INT(RND(1)*7.98+1.01) +1030 FOR I=1 TO 9 +1031 C(I,1)=0:C(I,2)=0 +1032 NEXT I +1040 C(3,1)=-1:C(2,1)=-1:C(4,1)=-1:C(4,2)=-1:C(5,2)=-1:C(6,2)=-1 +1050 C(1,2)=1:C(2,2)=1:C(6,1)=1:C(7,1)=1:C(8,1)=1:C(8,2)=1:C(9,2)=1 +1060 FOR I=1 TO 8 +1061 D(I)=0 +1062 NEXT I +1070 A1$="NAVSRSLRSPHATORSHIDAMCOMRES" +1080 REM set up what exists in galaxy +1090 REM k3=#klingons b3=#starbases s3=#stars +1100 FOR I=1 TO 8 +1101 FOR J=1 TO 8 +1102 K3=0:Z(I,J)=0:R1=RND(1) +1110 IF R1<=0.9799999 THEN goto 1120 +1111 K3=3:K9=K9+3: GOTO 1140 +1120 IF R1<=0.95 THEN goto 1130 +1121 K3=2:K9=K9+2: GOTO 1140 +1130 IF R1<=0.8 THEN goto 1140 +1131 K3=1:K9=K9+1 +1140 B3=0:IF RND(1)<=0.96 THEN goto 1150 +1141 B3=1:B9=B9+1 +1150 G(I,J)=K3*100+B3*10+INT(RND(1)*7.98+1.01) +1151 NEXT J +1152 NEXT I +1153 IF K9<=T9 THEN goto 1160 +1154 T9=K9+1 +1160 IF B9<>0 THEN 1190 +1170 IF G(Q1,Q2)>=200 THEN 1180 +1171 G(Q1,Q2)=G(Q1,Q2)+100:K9=K9+1 +1180 B9=1:G(Q1,Q2)=G(Q1,Q2)+10:Q1=INT(RND(1)*7.98+1.01):Q2=INT(RND(1)*7.98+1.01) +1190 K7=K9:IF B9=1 THEN 1200 +1191 X$="s":X0$=" are " +1200 PRINT" Your orders are as follows: " +1210 PRINT" Destroy the ";K9;" Klingon warships which have invaded" +1220 PRINT" the galaxy before they can attack Federation headquarters" +1230 PRINT" on stardate ";T0+T9;". This gives you ";T9;" days. there";X0$ +1240 PRINT" ";B9;" starbase";X$;" in the galaxy for resupplying your ship" +1261 PRINT:INPUT"hit return when ready to accept command";i$ +1270 REM here any time new quadrant entered +1280 Z4=Q1:Z5=Q2:K3=0:B3=0:S3=0:G5=0:D4=0.5*RND(1):Z(Q1,Q2)=G(Q1,Q2) +1290 IF Q1<1 OR Q1>8 OR Q2<1 OR Q2>8 THEN 1410 +1300 GOSUB 5040 +1301 PRINT:IF T0 <>T THEN 1330 +1310 PRINT"Your mission begins with your starship located" +1320 PRINT"in the galactic quadrant, '";G2$;"'.":GOTO 1340 +1330 PRINT"Now entering ";G2$;" quadrant. . ." +1340 PRINT:K3=INT(G(Q1,Q2)*0.01):B3=INT(G(Q1,Q2)*0.1)-10*K3 +1350 S3=G(Q1,Q2)-100*K3-10*B3:IF K3=0 THEN 1400 +1360 PRINT "COMBAT AREA!! Condition"; +1370 PRINT " RED "; : PRINT +1380 GOSUB 5290 +1381 IF S>200 THEN 1400 +1390 PRINT" SHIELDS DANGEROUSLY LOW"; : PRINT +1400 FOR I=1 TO 3 +1401 K(I,1)=0:K(I,2)=0 +1402 NEXT I +1410 FOR I=1 TO 3 +1411 K(I,3)=0 +1412 NEXT I +1413 Q$=" "*192 +1420 REM position enterprise in quadrant, then place "k3" klingons,& +1430 REM "b3" starbases & "s3" stars elsewhere. +1440 A$="\e/":Z1=S1:Z2=S2 +1441 GOSUB 4830 +1442 IF K3<1 THEN 1470 +1450 FOR I=1 TO K3 +1451 GOSUB 4800 +1452 A$=chr$(187)+"K"+chr$(171):Z1=R1:Z2=R2 +1460 GOSUB 4830 +1461 K(I,1)=R1:K(I,2)=R2:K(I,3)=S9*(0.5+RND(1)) +1462 NEXT I +1470 IF B3<1 THEN 1500 +1480 GOSUB 4800 +1481 A$="("+chr$(174)+")":Z1=R1:B4=R1:Z2=R2:B5=R2 +1490 GOSUB 4830 +1500 FOR I=1 TO S3 +1501 GOSUB 4800 +1502 A$=" * ":Z1=R1:Z2=R2 +1503 GOSUB 4830 +1504 NEXT I +1510 GOSUB 3720 +1520 IF S+E<=10 THEN 1530 +1521 IF E>10 OR D(7)>=0 THEN 1580 +1530 PRINT"*** FATAL ERROR ***"; +1531 GOSUB 5290 +1540 PRINT"You've just stranded your ship in " +1550 PRINT"space":PRINT"You have insufficient maneuvering energy,"; +1560 PRINT" and shield control":PRINT"is presently incapable of cross"; +1570 PRINT"-circuiting to engine room!!":GOTO 3480 +1580 INPUT"command ";A$ +1590 FOR I=1 TO 9 +1591 IF MID$(A$,1,3)<> MID$(A1$,3*I-2,3) THEN 1610 +1600 ON I GOTO 1720,1510,2440,2530,2750,3090,3180,3980,3510 +1610 NEXT I +1611 PRINT"Enter one of the following:" +1620 PRINT" NAV (to set course)" +1630 PRINT" SRS (for short range sensor scan)" +1640 PRINT" LRS (for long range sensor scan)" +1650 PRINT" PHA (to fire phasers)" +1660 PRINT" TOR (to fire photon torpedoes)" +1670 PRINT" SHI (to raise or lower shields)" +1680 PRINT" DAM (for damage control reports)" +1690 PRINT" COM (to call on library-computer)" +1700 PRINT" RES (to resign your command)":PRINT:GOTO 1520 +1710 REM course control begins here +1720 INPUT"Course (1-9) ";C2$:C1=VAL(C2$):IF C1<>9 THEN 1730 +1721 C1=1 +1730 IF C1>=1 AND C1<9 THEN 1750 +1740 PRINT" Lt. Sulu reports, 'Incorrect course data, sir!'":GOTO 1520 +1750 X$="8":IF D(1)>=0 THEN 1760 +1751 X$="0.2" +1760 PRINT"Warp factor(0-";X$;") ";:INPUT C2$:W1=VAL(C2$):IF D(1)<0 AND W1>0.2 THEN 1810 +1770 IF W1>0 AND W1<8 THEN 1820 +1780 IF W1=0 THEN 1520 +1790 PRINT" Chief Engineer Scott reports 'The engines won't take"; +1800 PRINT" warp ";W1;"!":GOTO 1520 +1810 PRINT"Warp engines are damaged. Maximum speed = warp 0.2":GOTO 1520 +1820 N1=INT(W1*8+0.5):IF E-N1>=0 THEN 1900 +1830 PRINT"Engineering reports 'Insufficient energy available" +1840 PRINT" for maneuvering at warp ";W1;"!'" +1850 IF S=0 THEN 1990 +1950 D(I)=min(0,D(I)+D6):IF D(I)<=-0.1 or D(I)>=0 THEN 1960 +1951 D(I)=-0.1 +1952 GOTO 1990 +1960 IF D(I)<0 THEN 1990 +1970 IF D1=1 THEN 1980 +1971 D1=1:PRINT"DAMAGE CONTROL REPORT: "; +1980 PRINT TAB(8);:R1=I +1981 GOSUB 4890 +1982 PRINT G2$;" Repair completed." +1990 NEXT I +1991 IF RND(1)>0.2 THEN 2070 +2000 R1=INT(RND(1)*7.98+1.01):IF RND(1)>=0.6 THEN 2040 +2010 IF K3=0 THEN 2070 +2020 D(R1)=D(R1)-(RND(1)*5+1):PRINT"DAMAGE CONTROL REPORT: "; +2030 GOSUB 4890 +2031 PRINT G2$;" damaged":PRINT:GOTO 2070 +2040 D(R1)=min(0,D(R1)+RND(1)*3+1):PRINT"DAMAGE CONTROL REPORT: "; +2050 GOSUB 4890 +2051 PRINT G2$;" State of repair improved":PRINT +2060 REM begin moving starship +2070 A$=" " :Z1=INT(S1):Z2=INT(S2) +2071 GOSUB 4830 +2079 Z1=INT(C1):C1=C1-Z1 +2080 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1:X=S1:Y=S2 +2090 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1:Q4=Q1:Q5=Q2 +2100 FOR I=1 TO N1 +2101 S1=S1+X1:S2=S2+X2:IF S1<1 OR S1>=9 OR S2<1 OR S2>=9 THEN 2220 +2110 S8=INT(S1)*24+INT(S2)*3-26:IF MID$(Q$,S8+1,2)=" " THEN 2140 +2120 S1=INT(S1-X1):S2=INT(S2-X2):PRINT"Warp engines shut down at "; +2130 PRINT "sector ";S1;",";S2;" due to bad navigation.":GOTO 2150 +2140 NEXT I +2141 S1=INT(S1):S2=INT(S2) +2150 A$="\e/" +2160 Z1=INT(S1):Z2=INT(S2) +2161 GOSUB 4830 +2162 GOSUB 2390 +2163 T8=1 +2170 IF W1>=1 THEN 2180 +2171 T8=0.1*INT(10*W1) +2180 T=T+T8:IF T>T0+T9 THEN 3480 +2190 REM see if docked then get command +2200 GOTO 1510 +2210 REM exceeded quadrant limits +2220 X=8*Q1+X+N1*X1:Y=8*Q2+Y+N1*X2:Q1=INT(X/8):Q2=INT(Y/8):S1=INT(X-Q1*8) +2230 S2=INT(Y-Q2*8):IF S1<>0 THEN 2240 +2231 Q1=Q1-1:S1=8 +2240 IF S2<>0 THEN 2250 +2241 Q2=Q2-1:S2=8 +2250 X5=0:IF Q1>=1 THEN 2260 +2251 X5=1:Q1=1:S1=1 +2260 IF Q1<=8 THEN 2270 +2261 X5=1:Q1=8:S1=8 +2270 IF Q2>=1 THEN 2280 +2271 X5=1:Q2=1:S2=1 +2280 IF Q2<=8 THEN 2290 +2281 X5=1:Q2=8:S2=8 +2290 IF X5=0 THEN 2360 +2300 PRINT"Lt. Uhura reports message from Starfleet Command:" +2310 PRINT" 'Permission to attempt crossing of galactic perimeter" +2320 PRINT" is hereby *DENIED*. Shut down your engines.'" +2330 PRINT"Chief Engineer Scott reports 'Warp engines shut down" +2340 PRINT" at sector ";S1;",";S2;" of quadrant ";Q1;",";Q2".'" +2350 IF T>T0 THEN 3480 +2360 IF 8*Q1+Q2=8*Q4+Q5 THEN 2150 +2370 T=T+1 +2371 GOSUB 2390 +2372 GOTO 1280 +2380 REM maneuver energy s/r ** +2390 E=E-N1-10:IF E<=0 THEN 2400 +2391 RETURN +2400 PRINT"Shield control supplies energy to complete the maneuver." +2410 S=S+E:E=0:IF S<=0 THEN 2420 +2411 S=0 +2420 RETURN +2430 REM long range sensor scan code +2440 IF D(3)>=0 THEN 2450 +2441 PRINT"Long Range Sensors are inoperable":GOTO 1520 +2450 PRINT"Long Range Scan for quadrant ";Q1;",";Q2 +2460 PRINT "-"*19 +2470 FOR I=Q1-1 TO Q1+1 +2471 N(1)=-1:N(2)=-2:N(3)=-3 +2472 FOR J=Q2-1 TO Q2+1 +2480 IF I<=0 or I>=9 or J<=0 or J>=9 THEN 2490 +2481 N(J-Q2+2)=G(I,J) +2482 REM added so long range sensor scans are added to computer database +2483 z(i,j)=g(i,j) +2490 NEXT J +2491 FOR L=1 TO 3 +2492 PRINT"| "; +2493 IF N(L)>=0 THEN 2500 +2494 PRINT"*** ";:GOTO 2510 +2500 PRINT MID$(STR$(N(L)+1000),2,3);" "; +2510 NEXT L +2511 PRINT"|" +2512 PRINT "-"*19 +2513 NEXT I +2514 GOTO 1520 +2520 REM phaser control code begins here +2530 IF D(4)>=0 THEN 2540 +2531 PRINT"Phasers Inoperative":GOTO 1520 +2540 IF K3>0 THEN 2570 +2550 PRINT"Science Officer Spock reports 'Sensors show no enemy ships" +2560 PRINT" in this quadrant'":GOTO 1520 +2570 IF D(8)>=0 THEN 2580 +2571 PRINT"Computer failure hampers accuracy" +2580 PRINT"Phasers locked on target "; +2590 PRINT"Energy available = ";E;" units" +2600 INPUT"Numbers of units to fire ";X:IF X<=0 THEN 1520 +2610 IF E-X<0 THEN 2590 +2620 E=E-X +2621 GOSUB 5420 +2622 IF D(7)<0 THEN 2630 +2623 X=X*RND(1) +2630 H1=INT(X/K3) +2631 FOR I=1 TO 3 +2632 IF K(I,3)<=0 THEN 2730 +2640 KSQ1 = (K(I,1)-S1)*(K(I,1)-S1) +2641 KSQ2 = (K(I,2)-S2)*(K(I,2)-S2) +2642 H= SQR( KSQ1 + KSQ2 ) +2646 H = H1 / H +2647 H = INT(H * (RND(1)+2)) +2648 IF H>0.15*K(I,3) THEN 2660 +2650 PRINT"Sensors show no damage to enemy at ";K(I,1);",";K(I,2):GOTO 2730 +2660 K(I,3)=K(I,3)-H:PRINT H;"Unit hit on Klingon at sector ";K(I,1);","; +2670 PRINT K(I,2):IF K(I,3)> 0 THEN GOTO 2700 +2680 PRINT "**** KLINGON DESTROYED ****" +2690 GOTO 2710 +2700 PRINT" (Sensors show ";K(I,3);" units remaining)":GOTO 2730 +2710 K3=K3-1:K9=K9-1:Z1=K(I,1):Z2=K(I,2):A$=" " +2711 GOSUB 4830 +2720 K(I,3)=0:G(Q1,Q2)=G(Q1,Q2)-100:Z(Q1,Q2)=G(Q1,Q2):IF K9<=0 THEN 3680 +2730 NEXT I +2731 GOSUB 3350 +2732 GOTO 1520 +2740 REM photon torpedo code begins here +2750 IF P>0 THEN 2760 +2751 PRINT"All photon torpedoes expended":GOTO 1520 +2760 IF D(5)>=0 THEN 2770 +2761 PRINT"Photon tubes are not operational":GOTO 1520 +2770 INPUT"Photon torpedo course (1-9) ";C2$:C1=VAL(C2$):IF C1<>9 THEN 2780 +2771 C1=1 +2780 IF C1>=1 AND C1<9 THEN 2810 +2790 PRINT"Ensign Chekov reports, 'Incorrect course data, sir!'" +2800 GOTO 1520 +2810 Z1=INT(C1):C1=C1-Z1 +2811 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1:E=E-2:P=P-1 +2820 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1:X=S1:Y=S2 +2821 GOSUB 5360 +2830 PRINT"Torpedo track:" +2840 X=X+X1:Y=Y+X2:X3=INT(X+0.5):Y3=INT(Y+0.5) +2850 IF X3<1 OR X3>8 OR Y3<1 OR Y3>8 THEN 3070 +2860 PRINT" ";X3;",";Y3:A$=" ":Z1=X:Z2=Y +2861 GOSUB 4990 +2870 IF Z3<>0 THEN 2840 +2880 A$=chr$(187)+"K"+chr$(171):Z1=X:Z2=Y +2881 GOSUB 4990 +2882 IF Z3=0 THEN 2940 +2890 PRINT"**** KLINGON DESTROYED ****" +2900 K3=K3-1:K9=K9-1:IF K9<=0 THEN 3680 +2910 FOR I=1 TO 3 +2911 IF X3=K(I,1) AND Y3=K(I,2) THEN 2930 +2920 NEXT I +2921 I=3 +2930 K(I,3)=0:GOTO 3050 +2940 A$=" * ":Z1=X:Z2=Y +2941 GOSUB 4990 +2942 IF Z3=0 THEN 2960 +2950 PRINT"Star at ";X3;",";Y3;" absorbed torpedo energy." +2951 GOSUB 3350 +2952 GOTO 1520 +2960 A$="("+chr$(174)+")":Z1=X:Z2=Y +2961 GOSUB 4990 +2962 IF Z3<>0 THEN 2970 +2963 PRINT "Torpedo absorbed by unknown object at ";x3;",";y3 +2964 goto 1520 +2970 PRINT"*** STARBASE DESTROYED ***" +2980 B3=B3-1 : B9=B9-1 +2990 IF B9>0 OR K9>T-T0-T9 THEN 3030 +3000 PRINT"THAT DOES IT, CAPTAIN!! You are hereby relieved of command" +3010 PRINT"and sentenced to 99 stardates of hard labor on CYGNUS 12!!" +3020 GOTO 3510 +3030 PRINT"Starfleet reviewing your record to consider" +3040 PRINT"court martial!":D0=0 +3050 Z1=X:Z2=Y:A$=" " +3051 GOSUB 4830 +3060 G(Q1,Q2)=K3*100+B3*10+S3:Z(Q1,Q2)=G(Q1,Q2) +3061 GOSUB 3350 +3062 GOTO 1520 +3070 PRINT"Torpedo missed" +3071 GOSUB 3350 +3072 GOTO 1520 +3080 REM shield control +3090 IF D(7)>=0 THEN 3100 +3091 PRINT"Shield control inoperable":GOTO 1520 +3100 PRINT"Energy available = ";E+S :INPUT "Number of units to shields? ";X +3110 IF X>=0 and S<>X THEN 3120 +3111 PRINT"":GOTO 1520 +3120 IF X":goto 1990 +3150 E=E+S-X:S=X:PRINT"Deflector Control Room report:" +3160 PRINT" 'Shields now at ";INT(S);" units per your command.'":GOTO 1520 +3170 REM damage control +3180 IF D(6)>=0 THEN 3290 +3190 PRINT"Damage control report not available":IF D0=0 THEN 1520 +3200 D3=0:FOR I=1 TO 8 +3201 IF D(I)>=0 THEN 3210 +3202 D3=D3+1 +3210 NEXT I +3211 IF D3=0 THEN 1520 +3220 PRINT:D3=D3+D4:IF D3<1 THEN 3230 +3221 D3=0.9 +3230 PRINT"Technicians standing by to effect repairs to your ship;" +3240 PRINT"estimated time to repair: ";0.01*INT(100*D3);" stardates" +3250 INPUT"Will you authorize the repair order (Y/N)? ";A$ +3260 IF A$<>"y" AND A$<> "Y" THEN 1520 +3270 FOR I=1 TO 8 +3271 IF D(I)>=0 THEN 3280 +3272 D(I)=0 +3280 NEXT I +3281 T=T+D3+0.1 +3290 PRINT:PRINT"Device state of repair" +3291 FOR R1=1 TO 8 +3300 GOSUB 4890 +3301 PRINT G2$;tab(25); +3310 GG2=INT(D(R1)*100)*0.01:PRINT GG2 +3320 NEXT R1 +3321 PRINT:IF D0<>0 THEN 3200 +3330 GOTO 1520 +3340 REM klingons shooting +3350 IF K3>0 THEN 3360 +3351 RETURN +3360 IF D0=0 THEN 3370 +3361 PRINT"Starbase shields protect the ENTERPRISE" +3362 RETURN +3370 FOR I=1 TO 3 +3371 IF K(I,3)<=0 THEN 3460 +3380 ksq1 = (K(I,1)-S1)*(K(I,1)-S1) +3381 ksq2 = (K(I,2)-S2)*(K(I,2)-S2) +3382 H=( K(I,3) / SQR( ksq1 + ksq2 ) )*(2+RND(1)) +3383 h = int(h) +3384 S=S-H:K(I,3)=K(I,3)/(3+RND(1)) +3390 PRINT "ENTERPRISE HIT!" +3400 GOSUB 5480 +3401 PRINT H;" Unit hit on ENTERPRISE from sector ";K(I,1);",";K(I,2) +3410 IF S<=0 THEN 3490 +3420 PRINT" ":IF H<20 THEN 3460 +3430 IF RND(1)>0.6 OR H/S<=0.02 THEN 3460 +3440 R1=INT(RND(1)*7.98+1.01):D(R1)=D(R1)-H/S-0.5*RND(1) +3441 GOSUB 4890 +3450 PRINT"Damage control reports '";G2$;" damaged by the hit'" +3460 NEXT I +3461 RETURN +3470 REM end of game +3480 PRINT"It is stardate";T:GOTO 3510 +3490 PRINT:PRINT"the ENTERPRISE has been destroyed. The Federation "; +3500 PRINT"will be conquered":GOTO 3480 +3510 PRINT"There were ";K9;" Klingon battle cruisers left at" +3520 PRINT"the end of your mission" +3530 PRINT:PRINT:IF B9=0 THEN 3670 +3540 PRINT"The Federation is in need of a new starship commander" +3550 PRINT"for a similar mission -- if there is a volunteer," +3560 INPUT"let him or her step forward and enter 'AYE' ";X$:IF X$="AYE" THEN 520 +3670 END +3680 PRINT"Congratulations, Captain! the last Klingon battle cruiser" +3690 PRINT"menacing the Federation has been destroyed.":PRINT +3700 PRINT"Your efficiency rating is ";:cc1 = k7/(t-t0):PRINT 1000*cc1*cc1:GOTO 3530 +3710 REM short range sensor scan & startup subroutine +3720 A$="("+chr$(174)+")":Z3=0 +3721 FOR I=S1-1 TO S1+1 +3722 FOR J=S2-1 TO S2+1 +3730 IF INT(I+0.5)<1 OR INT(I+0.5)>8 OR INT(J+0.5)<1 OR INT(J+0.5)>8 or Z3=1 THEN 3760 +3750 Z1=I:Z2=J +3751 GOSUB 4990 +3760 NEXT J +3761 NEXT I +3762 IF Z3=1 THEN 3770 +3763 D0=0:GOTO 3790 +3770 D0=1:CC$="docked":E=E0:P=P0 +3780 PRINT"Shields dropped for docking purposes":S=0:GOTO 3810 +3790 IF K3<=0 THEN 3800 +3791 C$="*red*":GOTO 3810 +3800 C$="GREEN":IF E>=E0*0.1 THEN 3810 +3801 C$="YELLOW" +3810 IF D(2)>=0 THEN 3830 +3820 PRINT:PRINT"*** Short Range Sensors are out ***":PRINT +3821 RETURN +3830 PRINT "-"*33 +3832 FOR I=1 TO 8 +3840 FOR J=(I-1)*24 TO (I-1)*24+21 STEP 3 +3850 IF MID$(Q$,J+1,3)<>" " THEN 3860 +3851 PRINT " . ";:GOTO 3861 +3860 PRINT " ";MID$(Q$,J+1,3); +3861 NEXT J +3870 ON I GOTO 3880,3900,3910,3920,3930,3940,3950,3960 +3880 PRINT" Stardate "; +3890 TT= T*10 : TT=INT(TT)*0.1:PRINT TT :GOTO 3970 +3900 PRINT" Condition ";C$:GOTO 3970 +3910 PRINT" Quadrant ";Q1;",";Q2:GOTO 3970 +3920 PRINT" Sector ";S1;",";S2:GOTO 3970 +3930 PRINT" Photon torpedoes ";INT(P):GOTO 3970 +3940 PRINT" Total energy ";INT(E+S):GOTO 3970 +3950 PRINT" Shields ";INT(S):GOTO 3970 +3960 PRINT" Klingons remaining ";INT(K9) +3970 NEXT I +3971 PRINT "-"*33 +3972 RETURN +3980 REM library computer code +3990 CM1$="GALSTATORBASDIRREG" +4000 IF D(8)>=0 THEN 4010 +4001 PRINT"Computer Disabled":GOTO 1520 +4010 rem KEY 1, "GAL RCD"+CHR$(13) +4020 rem KEY 2, "STATUS"+CHR$(13) +4030 rem KEY 3, "TOR DATA"+CHR$(13) +4040 rem KEY 4, "BASE NAV"+CHR$(13) +4050 rem KEY 5, "DIR/DIST"+CHR$(13) +4060 rem KEY 6, "REG MAP"+CHR$(13) +4070 rem KEY 7,CHR$(13):KEY 8,CHR$(13):KEY 9,CHR$(13):KEY 10,CHR$(13) +4071 gosub 4130 +4080 INPUT"Computer active and awaiting command ";CM$:H8=1 +4090 FOR K1= 1 TO 6 +4100 IF MID$(CM$,1,3)<>MID$(CM1$,3*K1-2,3) THEN 4120 +4110 ON K1 GOTO 4230,4400,4490,4750,4550,4210 +4120 NEXT K1 +4121 gosub 4130 +4122 goto 4080 +4130 PRINT"Functions available from library-computer:" +4140 PRINT" GAL = Cumulative galactic record" +4150 PRINT" STA = Status report" +4160 PRINT" TOR = Photon torpedo data" +4170 PRINT" BAS = Starbase nav data" +4180 PRINT" DIR = Direction/distance calculator" +4190 PRINT" REG = Galaxy 'region name' map":PRINT +4191 return +4200 REM setup to change cum gal record to galaxy map +4210 GOSUB 840 +4211 H8=0:G5=1:PRINT" the galaxy":GOTO 4290 +4250 GOSUB 840 +4260 PRINT:PRINT" "; +4270 PRINT "Computer record of galaxy for quadrant ";Q1;",";Q2 +4280 PRINT +4290 PRINT" 1 2 3 4 5 6 7 8" +4300 O1$=" ----- ----- ----- ----- ----- ----- ----- -----" +4310 PRINT O1$ +4311 FOR I=1 TO 8 +4312 PRINT I," ";:IF H8=0 THEN 4350 +4320 FOR J=1 TO 8 +4321 PRINT" ";:IF Z(I,J)<>0 THEN 4330 +4322 PRINT"***";:GOTO 4340 +4330 ZLEN = len(str$(z(i,j)+1000) +4331 PRINT MID$(STR$(Z(I,J)+1000),zlen-2,3); +4340 NEXT J +4341 GOTO 4370 +4350 Z4=I:Z5=1 +4351 GOSUB 5040 +4352 J0=INT(15-0.5*LEN(G2$)):PRINT TAB(J0);G2$; +4360 Z5=5 +4361 GOSUB 5040 +4362 J0=INT(40-0.5*LEN(G2$)):PRINT TAB(J0);G2$; +4370 PRINT:PRINT O1$ +4371 NEXT I +4372 PRINT +4373 rem 'POKE 1229,0 POKE 1237,1 +4380 GOTO 1520 +4390 REM status report +4400 GOSUB 840 +4401 PRINT" Status Report":X$="":IF K9<=1 THEN 4410 +4402 X$="s" +4410 PRINT"Klingon";X$;" left: ";K9 +4420 PRINT"Mission must be completed in ";0.1*INT((T0+T9-T)*10);" stardates" +4430 X$="s":IF B9>=2 THEN 4440 +4431 X$="":IF B9<1 THEN 4460 +4440 PRINT"The federation is maintaining ";B9;" starbase";X$;" in the galaxy" +4450 GOTO 3180 +4460 PRINT"Your stupidity has left you on your own in" +4470 PRINT" the galaxy -- you have no starbases left!":GOTO 3180 +4480 REM torpedo, base nav, d/d calculator +4490 GOSUB 840 +4491 IF K3<=0 THEN 2550 +4500 X$="":IF K3<=1 THEN 4510 +4501 X$="s" +4510 PRINT"From ENTERPRISE to Klingon battle cruiser";X$ +4520 H8=0:FOR I=1 TO 3 +4521 IF K(I,3)<=0 THEN 4740 +4530 W1=K(I,1):X=K(I,2) +4540 C1=S1:A=S2:GOTO 4590 +4550 GOSUB 840 +4551 PRINT"Direction/Distance Calculator:" +4560 PRINT"You are at quadrant ";Q1;",";Q2;" sector ";S1;",";S2 +4570 PRINT"Please enter ":INPUT" initial coordinates (x,y) ";C1,A +4580 INPUT" Final coordinates (x,y) ";W1,X +4590 X=X-A:A=C1-W1:aa=abs(a):ax=abs(x):IF X<0 THEN 4670 +4600 IF A<0 THEN 4690 +4610 IF X>0 THEN 4630 +4620 IF A<>0 THEN 4630 +4621 C1=5:GOTO 4640 +4630 C1=1 +4640 IF AA<=AX THEN 4660 +4650 PRINT"Direction1 = ";:cc1=(AA-AX+AA)/AA:PRINT c1+cc1:GOTO 4730 +4660 PRINT"Direction2 = ";:cc1=C1+(AA/AX):PRINT cc1:GOTO 4730 +4670 IF A<=0 THEN 4680 +4671 C1=3:GOTO 4700 +4680 IF X=0 THEN 4690 +4681 C1=5:GOTO 4640 +4690 C1=7 +4700 IF AA>=AX THEN 4720 +4710 PRINT"Direction3 = ";:cc1=(AX-AA+AX)/AX:PRINT c1+cc1:GOTO 4730 +4720 PRINT"Direction4 = ";:CC1=C1+(AX/AA):PRINT CC1 +4730 PRINT"Distance = ";:cc1=SQR(x*X+A*A):PRINT cc1:IF H8=1 THEN 1520 +4740 NEXT I +4741 GOTO 1520 +4750 GOSUB 840 +4751 IF B3=0 THEN 4770 +4752 PRINT"From ENTERPRISE to Starbase:":W1=B4:X=B5 +4760 GOTO 4540 +4770 PRINT"Mr. Spock reports, 'Sensors show no starbases in this"; +4780 PRINT"quadrant.'":GOTO 1520 +4790 REM find empty place in quadrant (for things) +4800 R1=INT(RND(1)*7.98+1.01):R2=INT(RND(1)*7.98+1.01):A$=" ":Z1=R1:Z2=R2 +4801 GOSUB 4990 +4802 IF Z3=0 THEN 4800 +4810 RETURN +4820 REM insert in string array for quadrant +4830 S8=INT(Z2-0.5)*3+INT(Z1-0.5)*24+1 +4840 IF LEN(A$)=3 THEN 4850 +4841 PRINT"ERROR":STOP +4850 IF S8<>1 THEN 4860 +4851 Q$=A$+MID$(Q$,4,189):RETURN +4860 IF S8<>190 THEN 4870 +4861 Q$=MID$(Q$,1,189)+A$:RETURN +4870 Q$=MID$(Q$,1,S8-1)+A$+MID$(Q$,s8+3,192-s8-2):RETURN +4880 REM prints device name +4890 ON R1 GOTO 4900,4910,4920,4930,4940,4950,4960,4970 +4900 G2$="Warp Engines":RETURN +4910 G2$="Short Range Sensors":RETURN +4920 G2$="Long Range Sensors":RETURN +4930 G2$="Phaser Control":RETURN +4940 G2$="Photon Tubes":RETURN +4950 G2$="Damage Control":RETURN +4960 G2$="Shield Control":RETURN +4970 G2$="Library-Computer":RETURN +4980 REM string comparison in quadrant array +4990 Z1=INT(Z1+0.5):Z2=INT(Z2+0.5):S8=(Z2-1)*3+(Z1-1)*24:Z3=0 +5000 IF MID$(Q$,S8+1,3)=A$ THEN 5010 +5001 RETURN +5010 Z3=1:RETURN +5020 REM quadrant name in g2$ from z4,z5 (=q1,q2) +5030 REM call with g5=1 to get region name only +5040 IF Z5<=4 THEN 5140 +5041 ON Z4 GOTO 5060,5070,5080,5090,5100,5110,5120,5130 +5050 GOTO 5140 +5060 G2$="Antares":GOTO 5230 +5070 G2$="Rigel":GOTO 5230 +5080 G2$="Procyon":GOTO 5230 +5090 G2$="Vega":GOTO 5230 +5100 G2$="Canopus":GOTO 5230 +5110 G2$="Altair":GOTO 5230 +5120 G2$="Sagittarius":GOTO 5230 +5130 G2$="Pollux":GOTO 5230 +5140 ON Z4 GOTO 5150,5160,5170,5180,5180,5200,5210,5220 +5150 G2$="Sirius":GOTO 5230 +5160 G2$="Deneb":GOTO 5230 +5170 G2$="Capella":GOTO 5230 +5180 G2$="Betelgeuse":GOTO 5230 +5190 G2$="Aldebaran":GOTO 5230 +5200 G2$="Regulus":GOTO 5230 +5210 G2$="Arcturus":GOTO 5230 +5220 G2$="Spica" +5230 IF G5=1 THEN 5240 +5231 ON Z5 GOTO 5250,5260,5270,5280,5250,5260,5270,5280 +5240 RETURN +5250 G2$=G2$+" i":RETURN +5260 G2$=G2$+" ii":RETURN +5270 G2$=G2$+" iii":RETURN +5280 G2$=G2$+" iv":RETURN +5290 rem red alert sound +5291 return +5300 FOR J= 1 TO 4 +5310 FOR K=1000 TO 2000 STEP 20 +5320 rem SOUND K,0.01*18.2 +5330 NEXT K +5340 NEXT J +5350 RETURN +5360 rem torpedo sound +5361 return +5370 FOR J = 1500 TO 100 STEP -20 +5380 rem SOUND J,0.01*18.2 +5390 rem SOUND 3600-J,.01*18.2 +5400 NEXT J +5410 RETURN +5420 rem phaser sound +5421 return +5430 FOR J= 1 TO 40 +5440 rem SOUND 800,.01*18.2 +5450 rem SOUND 2500,.008*18.2 +5460 NEXT J +5470 RETURN +5480 rem alarm sound +5481 return +5490 FOR SI = 1 TO 3 +5500 FOR J= 800 TO 1500 STEP 20 +5510 rem SOUND J,.01 *18.2 +5520 NEXT J +5530 FOR K = 1500 TO 800 STEP -20 +5540 rem SOUND K, .01 *18.2 +5550 NEXT K +5560 NEXT SI +5570 RETURN diff --git a/examples/adventure-fast.bas b/examples/adventure-fast.bas new file mode 100644 index 0000000..c9d06d5 --- /dev/null +++ b/examples/adventure-fast.bas @@ -0,0 +1,1112 @@ +10 rem ADVENTURE/3000 VERSION 3.2 27 FEB 1979 AT 5:30 PM +11 rem THIS PROGRAM IS RELATIVELY BUG-FREE, BUT ONE STILL MUST +12 rem realize that murphy 'S LAW STILL PREVAILS! +13 rem +14 rem ADVENTURE: PROGRAMMED IN HP/3000 BASIC BY BENJAMIN MOSER +15 rem JAMES MADISON HIGH SCHOOL, VIENNA, VIRGINIA. THE BASIC LAYOUT OF THE +16 rem GAME WAS CONCEIVED BY DON WOODS & WILLIE CROWTHER, BOTH OF M.I.T. +17 rem +18 rem ADVENTURE WAS PORTED TO THE MACINTOSH PLUS BY THE ELIZABETH AND DAVID HUNTER +19 rem IN MARCH 1998 AND THEN TO PYBASIC FOR THE RASPBERRY RP2040 IN JUNE 2021 +20 rem PRINT "Adventure 3.2 on ";date$;" at ";time$ +21 PRINT "Adventure 3.2" +22 open "AMESSAGE" for INPUT as #3:open "AMOVING" for INPUT as #4 +23 open "ADESCRIP" for INPUT as #1:open "AITEMS" for INPUT as #2 +44 rem dirs is an array of possible room directions, it replaces file AMOVING +45 dim dirs(100,10) +46 dim indx(303) +47 dim fraindx(10) +50 dim s(99) +51 dim v(100) +52 dim k(2) +53 dim o(15) +54 string$ = "100 N 101 NE 102 E 103 SE 104 S 105 SW 106 W 107 NW 108 U 109 D 110 PLUGH 111 XYZZY 112 PLOVER 113 CROSS 114 CLIMB 115 JUMP 116 FILL 117 EMPTY 117 POUR 118 LOOK 118 L 119 LIGHT 119 ON 120 EXTINGUISH 120 OFF 121 IN 121 ENTER 122 LEAVE 122 OUT 123 INVENTORY 123 I 124 GET 124 CATCH 124 TAKE 125 DROP 125 DUMP 047 ALL 047 EVERYTHING 126 THROW 127 ATTACK 127 KILL 128 FEED 129 WATER 130 LOCK 131 UNLOCK 132 FREE 132 RELEASE 133 WAVE 134 OPEN 135 CLOSE 136 OIL 137 EAT 138 DRINK 139 FEE FIE FOE FOO 140 SHORT 141 LONG 142 BRIEF 143 QUIT 143 STOP 143 END 144 SCORE 145 SAVE 146 LOAD 147 READ 147 EXAMINE 148 YES 148 Y 149 BUG 001 GOLD 001 NUGGET 002 BARS 002 SILVER 003 JEWELRY 004 COINS 005 DIAMONDS 006 MING 006 VASE 007 PEARL 008 EGGS 008 NEST 009 TRIDENT 010 EMERALD 011 PLATINUM 011 PYRAMID 012 CHAIN 013 SPICES 014 PERSIAN 014 RUG 015 TREASURE 015 CHEST 016 WATER 017 OIL 018 LAMP 018 LANTERN 019 KEYS 020 FOOD 021 BOTTLE 022 CAGE 023 ROD 023 WAND 024 CLAM 025 MAGAZINE 026 BEAR 027 AXE 028 VELVET 028 PILLOW 029 SHARDS 030 OYSTER 031 BIRD 032 TROLL 033 DRAGON 034 SNAKE 035 DWARF 036 ROCK 036 BOULDER 037 STAIRS 038 STEPS 039 HOUSE 039 BUILDING 040 GRATE 041 STREAM 042 ROOM 043 BRIDGE 044 PIT 045 VOLCANO 046 ROAD 100 NORTH 101 NORTHEAST 102 EAST 103 SOUTHEAST 104 SOUTH 105 SOUTHWEST 106 WEST 107 NORTHWEST 108 UP 109 DOWN " +70 PRINT:PRINT "Initializing."; +110 rem initialize +130 rem total rooms, items,and keywords +150 l1 = int(RND(1)*4)+1 : l2 = l1 +160 g = 0 : b0 = 1 : sn = 1 : d1 = 1 : d2 = 0 : t = 1 : b1 = 0 : b2 = 0 : p1 = 0: dead = 0: lastc$="" +161 l = 0 : c = 0 : d3 = 0 : b3 = 0 : d0 = 2 : t1 = 100 : t2 = 35 : t3 = 149 : r0 = 0 : c0 = 0: c$="" +162 KC = 1.03 +170 for ii = 1 to 99 +171 s(ii) = 0 : v(ii) = 0 +172 next ii +175 PRINT ".";:v(100) = 0 +182 indx(1) = -1:indx(2)=-1 +190 rem read in possible movement direction array +192 for z2 = 1 to 100 +193 INPUT #4,dx1,dx2,dx3,dx4,dx5,dx6,dx7,dx8,dx9,dx0 +194 dirs(z2,1)=dx1:dirs(z2,2)=dx2:dirs(z2,3)=dx3:dirs(z2,4)=dx4:dirs(z2,5)=dx5 +195 dirs(z2,6)=dx6:dirs(z2,7)=dx7:dirs(z2,8)=dx8:dirs(z2,9)=dx9:dirs(z2,10)=dx0 +196 PRINT "."; +197 next z2 +198 close #4 +200 restore 230 +210 rem read in locations of items +220 for z2 = 1 to T2 step 5 +221 read dx1,dx2,dx3,dx4,dx5:s(z2)=dx1:s(z2+1)=dx2:s(z2+2)=dx3:s(z2+3)=dx4:s(z2+4)=dx5 +222 PRINT "."; +228 next z2 +230 data 18,25,23,24,21 +231 data 52,0,71,74,58 +232 data 59,69,66,82,100 +233 data 7,49,7,7,7 +234 data 7,12,13,40,38 +235 data 69,0,46,0,0 +236 data 15,60,82,22,250 +240 for ii = 1 to 15 step 5 +241 read dx1,dx2,dx3,dx4,dx5:o(ii)=dx1:o(ii+1)=dx2:o(ii+2)=dx3:o(ii+3)=dx4:o(ii+4)=dx5 +242 PRINT "."; +243 next ii +245 data 1,2,2,2,2 +246 data 3,4,3,3,2 +247 data 5,3,2,3,3 +260 rem ASK IF HE WANTS DIRECTIONS +263 z59 = 301:gosub 7620 +264 gosub 9860 +266 if z0 then 267 else 320 +267 z59 = 302:gosub 7620 +280 rem command INPUT routine +300 rem PRINT room, items +310 rem If it's dark don't let him see anything +320 if l1 < 13 or l1 = 58 then 350 +321 if l = 1 and (s(18) = l1 or s(18) = -1) then 350 +330 z59 = 45:gosub 7620 +340 goto 400 +350 on d0+1 gosub 6780,7990,8180 +360 v(l1) = 1 +370 gosub 6680 +375 if dead = 1 then 9540 +380 gosub 7800 +390 rem INPUT LOOP --- MULTIPLE COMMAANDS, REMOVE JUNK CHARACTERS +400 rem if len(c$) > 0 then 500 +410 INPUT ">";c$:c$=upper$(c$) +420 if c$ = "" then 410 +425 PRINT:PRINT:NKEY=0 +431 if c$ <> lastc$ then 433 +432 c$ = "":goto 900 +433 numkeys=1:lastc$ = c$:a$="" +449 rem 65-90 = A-Z 48-57 = 0-9 46 = . +450 for x = 1 to len(c$) +460 z5 = asc(mid$(c$,x,1)) +470 if (z5 > 64 and z5 < 91) or (z5 > 47 and z5 < 58) or z5 = 46 then 480 else 471 +471 c$ = mid$(c$,1,x-1)+" "+mid$(c$,x+1) +480 next x +490 if instr(c$," ")=0 then 520 +500 numkeys=2:a$ = mid$(c$,instr(c$," "),len(c$)-instr(c$," ")+1)+" " +510 c$ = mid$(c$,1,instr(c$," ")-1) +520 c$ = " "+c$+" " +800 k(1)=0:k(2)=0:z3 = 0 +870 a1 = instr(string$,c$):if a1=0 then 872 +871 c$ = mid$(string$,a1-3,3):k(1)=int(val(c$)) +872 if numkeys=1 then 900 +873 a1 = instr(string$,a$):if a1=0 then 900 +874 b$ = mid$(string$,a1-3,3):k(2)=int(val(b$)) +890 rem EXOTIC WORDS +900 z0 = 36 +910 if k(1) >= z0 and k(1) <=46 then 930 +920 if k(2) >= z0 and k(2) <=46 then 930 else 980 +930 gosub 8390 +940 PRINT "What do you want to do with the ";d$;"?" +950 goto 410 +980 if k(1) >= 110 and k(1) <= T3 then 1950 +990 if k(2) >= 110 and k(2) <= T3 then 1950 +1000 rem THEN IS IT A DIRECTION +1010 for d = 1 to 10 +1020 if k(1) = d+99 or k(2) = d+99 then 1070 +1030 next d +1040 rem COMMAND NOT A DIRECTION +1050 goto 1950 +1060 rem CAN HE MOVE THAT WAY? +1070 z2 = dirs(L1,d) +1130 if z2 = 255 then 1470 +1140 if z2 < 1 or z2 > 254 then 1220 +1150 rem NORMAL MOVING +1160 rem CHECK FOR SPECIAL MOVE CONDITIONS +1170 goto 1260 +1180 l2 = l1 : l1 = z2 +1190 if s(35) = l2 then 1191 else 1200 +1191 s(35) = l1 +1200 goto 9780 +1220 z59 = 1 +1221 gosub 7620 +1230 goto 400 +1240 rem SPECIAL ROOM DIRECTIONS +1250 rem GRATE +1260 if L1 = 10 and (d = 10 or d = 5) THEN 1280 +1261 IF L1 = 11 and (d = 9 or d = 3) then 1280 else 1320 +1270 rem IF GRATE IS OPEN (G=0) MOVE HIM +1280 if g = 1 then 1180 +1290 z59 = 10:gosub 7620 +1300 goto 400 +1310 rem CAN'T TAKE NUGGET UPPSTAIRS +1320 if not (l1 = 17 and d = 9 and s(1) = -1) then 1360 +1330 z59 = 38:gosub 7620 +1340 goto 400 +1350 rem CRYSTAL BRIDGE AND FISSURE +1360 if not (l1 = 19 and d = 7 or l1 = 20 and d = 3) then 1410 +1370 if b2 then 1180 +1380 z59 = 3:gosub 7620 +1390 goto 400 +1400 rem MT. KING & SNAKE +1410 if not (l1 = 22 and d <> 3 and d <> 9) then 1690 +1420 if sn = 0 then 1180 +1430 z59 = 50:gosub 7620 +1440 goto 400 +1460 rem bedquilt and random directiosn +1470 if l1 <> 44 then 1590 +1480 if RND(1) > 0.5 then 1510 +1490 z59 = 52:gosub 7620 +1500 goto 400 +1510 restore 1530 +1520 rem ROOMS TO JU +1530 data 33,37,45,92,76 +1540 for z3 = 1 to int(RND(1)*5)+1 +1550 read z2 +1560 next z3 +1570 goto 1180 +1580 rem WITT's END +1590 if l1 <> 39 then 1220 +1600 rem SHOULD WE LET HIM OUT? +1610 if RND(1) < 0.15 then 1650 +1620 rem NO +1630 z59 = 52:gosub 7620 +1640 goto 400 +1650 rem YES +1660 z2 = 38 +1670 goto 1180 +1680 rem Narrow Tunnel +1690 if not (l1 = 57 or l1 = 58) then 1780 +1691 if k(1) <> 102 and k(2) <> 102 and k(1) <> 106 and k(2) <> 106 then 1780 +1700 for z3 = 1 to t2 +1710 if z3 = 10 then 1750 +1720 if s(z3) <> -1 then 1750 +1730 z59 = 53:gosub 7620 +1740 goto 400 +1750 next z3 +1760 goto 1180 +1770 rem TROLL +1780 IF L1=60 AND D=2 THEN 1790 +1781 IF L1=61 AND D=6 THEN 1790 ELSE 1860 +1790 on t+1 goto 1180,1800,1820,1840 +1800 z59 = 55:gosub 7620 +1810 goto 400 +1820 z59 = 56:gosub 7620 +1821 z59 = 55:gosub 7620 +1830 T=1:goto 400 +1840 t = 2 +1850 goto 1180 +1860 if not (l1 = 73 and d = 1 and d2 = 0) then 1890 +1870 z59 = 57:gosub 7620 +1880 goto 400 +1890 if not (l1 = 82 and s(33) = l1 and d = 1) then 1180 +1900 rem DRAGON +1910 z59 = 51:gosub 7620 +1920 goto 400 +1940 rem OTHER COMMANDS +1950 z1=k(1):if k(1) >= 110 and k(1) <= T3 then 2090 +1960 z1=k(2):if k(2) >= 110 and k(2) <= T3 then 2090 +1980 rem ITEM BO NO VERB? +1990 if k(1) >= 1 and k(1) <= 35 then 2000 +1991 if k(2) >= 1 and k(2) <= 35 then 2000 else 2040 +2000 for x = 1 to 35 +2020 if k(1) <> x and k(2) <> x then 2030 +2021 restore 9960+x +2022 read d$ +2023 goto 940 +2030 next x +2040 restore 2070 +2050 for x = 1 to int(RND(1)*4)+1 +2051 read b$ +2052 next x +2060 PRINT b$ +2070 data "What?","I don't understand.","I can't understand that.","I don't know that word." +2080 goto 400 +2090 z1 = z1-109 +2095 on z1 goto 2120,2220,2300,2390,2570,2640,2680,2860,2930,3000,3080,3130,3290,3450,3590,3920,4160,4430,4630,4800,4950,5060,5190,5410,5560,5750,5810,5920,6000,6090,6230,6270,6310,6350,6410,8970,9080,9220,4490,9330 +2110 goto 2040 +2120 REM *** PLUGH *** +2130 IF L1<>7 THEN 2170 +2140 IF S(35)=L1 THEN S(35)=0 +2150 Z2=26 +2160 GOTO 1180 +2170 IF L1<>26 THEN 2200 +2180 Z2=7 +2190 GOTO 1180 +2200 z59 = 2:gosub 7620 +2210 GOTO 400 +2220 REM *** XYZZY *** +2230 IF L1<>7 THEN 2270 +2240 IF S(35)=L1 THEN S(35)=0 +2250 Z2=13 +2260 GOTO 1180 +2270 IF L1<>13 THEN 2200 +2280 Z2=7 +2290 GOTO 1180 +2300 REM *** PLOVER *** (CAN'T BRING EMERALD WITH HIM) +2310 IF L1>26 THEN 2360 +2320 IF S(35)<>L1 THEN 2330 +2325 S(35) = 0 +2330 IF S(10)<>-1 THEN 2340 +2335 S(10) = L1 +2340 Z2 = 58 +2350 GOTO 1180 +2360 IF L1<>58 THEN 2200 +2370 Z2=26 +2380 GOTO 1180 +2390 REM *** CROSS *** +2400 IF L1<>19 THEN 2470 +2410 IF B2<>0 THEN 2440 +2420 z59 = 3:gosub 7620 +2430 GOTO 400 +2440 D=7 +2450 REM JUST GIVE NEW DIRECTION, USE MOVE ROUTINE +2460 GOTO 1070 +2470 IF L1<>20 THEN 2510 +2480 IF B2=0 THEN 2420 +2490 D=3 +2500 GOTO 1070 +2510 IF L1<>60 THEN 2540 +2520 D=2 +2530 GOTO 1070 +2540 IF L1<>61 THEN 2200 +2550 D=6 +2560 GOTO 1070 +2570 REM *** CLIMB *** +2580 IF L1<>50 THEN 2200 +2590 REM CAN HE CLIMB BEANSTALK? +2600 IF P1<2 THEN 2200 +2610 REM YES +2620 Z2=70 +2630 GOTO 1180 +2640 REM *** JUMP *** STRICTLY SUICIDAL +2650 IF L1<>16 AND L1<>19 AND L1<>20 AND L1<>27 THEN 2200 +2660 z59 = 4:gosub 7620 +2670 GOTO 9550 +2680 REM FILL +2690 IF S(21)=-1 THEN 2730 +2700 B$="bottle" +2710 PRINT "You don't have the ";b$ +2720 goto 410 +2730 IF B0=0 THEN 2760 +2740 z59 = 5:gosub 7620 +2750 GOTO 410 +2760 IF L1<>7 AND L1<>8 AND L1<>9 AND L1<>35 AND L1<>74 AND L1<>81 THEN 2790 +2770 B0=1:S(16)=-1 +2780 GOTO 2840 +2790 IF L1=49 THEN 2830 +2800 B$="oil" +2810 PRINT "I see no ";B$;" here." +2820 GOTO 400 +2830 B0=2:S(17)=-1 +2840 PRINT "The bottle is now filled." +2850 GOTO 400 +2860 REM *** EMPTY *** +2870 IF S(21)=-1 THEN 2890 +2880 GOTO 2700 +2890 REM EMPTY BOTTLE (ASSUMED FULL) +2900 S(B0+15)=0:B0=0 +2910 PRINT "Emptied" +2920 GOTO 400 +2930 rem *** LOOK *** +2940 if l1 < 13 or l1 = 58 then 2970 +2941 if l = 1 and (s(18) = l1 or s(18) = -1) then 2970 +2950 z59 = 45:gosub 7620 +2960 goto 400 +2970 gosub 8050 +2980 gosub 6680 +2990 goto 400 +3000 rem *** LIGHT *** +3010 if s(18) = -1 then 3040 +3020 b$ = "lamp" +3030 goto 2710 +3040 l = 1 +3050 b$ = "on" +3060 PRINT "The lamp is now ";b$ +3070 goto 2940 +3080 REM *** OFF (EXTINGUSIH) *** +3090 IF S(18)=-1 THEN 3110 +3100 GOTO 3020 +3110 L=0:B$="off" +3120 GOTO 3060 +3130 REM *** ENTER *** +3140 IF L1<>6 THEN 3180 +3150 REM TO HOUSE +3160 D=3 +3170 GOTO 1070 +3180 IF L1<>68 THEN 3240 +3190 REM TO BARREN ROOM +3200 D=3 +3210 GOTO 1070 +3240 FOR D=10 TO 1 step -1 +3250 Z2 = DIRS(L1,D) +3260 IF Z2>0 AND Z2<101 THEN 1150 +3270 NEXT D +3280 GOTO 2200 +3290 REM ** LEAVE *** +3300 IF L1<>7 THEN 3340 +3310 REM LEAVE HOUSE +3320 D=7 +3330 GOTO 1070 +3340 IF L1<>69 THEN 3400 +3350 REM LEAVE BARREN ROOM +3360 D = 7 +3370 GOTO 1070 +3400 FOR D = 1 TO 10 +3410 Z2 = DIRS(L1,D) +3420 IF Z2>0 AND Z2<101 THEN 1150 +3430 NEXT D +3440 GOTO 2200 +3450 REM *** INVENTORY *** +3470 Z0=0 +3480 PRINT "You are carrying:"; +3490 FOR X=1 TO T2 +3510 IF S(X)<>-1 THEN 3540 +3511 restore 9960+x:read b$ +3520 PRINT B$ +3530 Z0 = Z0 + 1 +3540 NEXT X +3550 IF Z0=0 THEN 3551 else 3560 +3551 PRINT "nothing." +3560 PRINT +3570 GOTO 400 +3590 rem *** GET *** +3630 if k(1) = 47 or k(2) = 47 then 3680 +3640 gosub 8280 +3650 if z8 > 0 then 3680 +3660 PRINT "Get what?" +3670 goto 2040 +3680 for z3 = 1 to t2 +3710 if k(1) = 47 or k(2) = 47 then 3730 +3720 if k(1) <> z3 and k(2) <> z3 then 3900 +3730 if s(z3) <> l1 then 3750 +3740 if s(z3) = l1 then 3790 +3750 if k(1) = 47 or k(2) = 47 then 3900 +3760 restore 9960+z3:read a$:PRINT a$;" not here." +3770 goto 3900 +3780 rem MUST CHECK NOW FOR LEGALITY OF TAKING ITEM +3790 z8 = 0 +3800 for x = 1 to t2 +3810 if s(x) <> -1 then 3820 +3811 z8 = z8+1 +3820 next x +3830 if z8 < 7 then 3870 +3840 rem CARRYING TOO MUCH +3850 z59 = 54:gosub 7620 +3860 goto 410 +3870 goto 6880 +3880 s(z3) = -1 +3890 restore 9960+z3:read a$:PRINT a$;":taken." +3900 next z3 +3910 goto 400 +3920 REM *** DROP *** +3940 if k(1) = 47 or k(2) = 47 then 4000 +3950 gosub 8280 +3960 IF Z8>0 THEN 4000 +3970 PRINT "Drop what?" +3980 GOTO 2040 +4000 FOR Z3=1 TO T2 +4030 IF K(1) = 47 or k(2) = 47 THEN 4060 +4040 IF K(1) <> Z3 and k(2) <> z3 THEN 4140 +4050 IF S(Z3)=0 THEN 4140 +4060 IF S(Z3)=-1 THEN 4100 +4070 IF K(1)=47 or k(2)=47 THEN 4140 +4080 restore 9960+z3:read b$:PRINT "You don't have the ";B$ +4090 GOTO 4140 +4100 REM STILL NEED TO ELABORATE ON DROP (BIRD IN CAGE, BOTTLE) +4110 GOTO 7380 +4120 restore 9960+z3:read b$:PRINT B$;":dropped." +4130 S(Z3)=L1 +4140 NEXT Z3 +4150 GOTO 400 +4160 REM *** THROW *** +4170 GOSUB 8280 +4180 IF Z8>0 THEN 4210 +4190 PRINT "Throw what?" +4200 GOTO 2040 +4210 IF S(Z3)<>-1 THEN 2710 +4220 IF NOT (Z3<16 AND S(32)=L1) THEN 4260 +4230 REM THROW TREASURE TO TROLL +4240 z59 = 27:gosub 7620 +4241 S(Z3)=0:T=3:GOTO 400 +4260 IF NOT (Z3=27 AND S(32)=L1) THEN 4300 +4270 REM TRYING TO BUTCHER TROLL? +4280 z59 = 26:gosub 7620 +4281 S(27)=L1:GOTO 400 +4300 IF NOT (Z3=27 AND S(35)=L1) THEN 4380 +4310 REM TRYING TO KILL DWARF +4320 IF RND(1)>0.5 THEN 4360 +4330 z59 = 29:gosub 7620 +4340 GOSUB 8650 +4350 GOTO 4410 +4360 z59 = 30:gosub 7620 +4361 S(35)=0:GOTO 4410 +4380 REM NOTHING SPECIAL, JUST DROP ITEM +4390 IF S(35)<>L1 THEN 4400 +4391 GOSUB 8550 +4400 PRINT "Thrown." +4410 S(Z3) = L1 +4415 if dead = 1 then 9540 +4420 GOTO 400 +4430 REM *** ATTACK *** +4440 GOSUB 8280 +4450 IF NOT (Z3=33 AND S(Z3)=L1 AND L1=82) THEN 4520 +4460 REM HE CAN KILL DRAGON +4470 z59 = 68:gosub 7620 +4480 GOTO 410 +4490 IF L1<>82 THEN 2040 +4500 z59 = 69:gosub 7620 +4501 S(33)=0:D1=0:GOTO 400 +4520 IF S(32)<>L1 THEN 4560 +4530 REM TRYING TO MUNGE TROLL +4540 Z9=FNA(25):GOTO 400 +4560 IF NOT (Z3=26 OR Z3>30) THEN 4600 +4570 REM DANGEROUS TO ATTACK THESE +4580 z59 = 70:gosub 7620 +4590 GOTO 400 +4600 REM NOTHING TO ATTACK +4610 z59 = 71:gosub 7620 +4620 GOTO 400 +4630 REM *** FEED *** +4640 GOSUB 8280 +4650 IF Z3<>35 THEN 4690 +4660 REM CAN'T FEED DWARF! +4670 z59 = 24:gosub 7620 +4680 GOTO 400 +4690 IF S(20) = -1 THEN 4720 +4700 B$ = "FOOD":GOTO 2710 +4720 IF L1=69 THEN 4760 +4730 PRINT "I can't feed it." +4740 z59 = 23:gosub 7620 +4750 GOTO 400 +4760 IF S(20)=L1 THEN 7600 +4770 B1=1:S(20)=0:z59 = 6:gosub 7620 +4790 GOTO 400 +4800 REM *** WATER *** +4810 IF S(16) = -1 THEN 4840 +4820 B$ = "water":GOTO 2710 +4840 IF L1<>50 THEN 2200 +4850 REM GOTO P1+1 OF 4860,4890,4920 +4851 IF P1 = 0 THEN 4860 +4852 IF P1 = 1 THEN 4890 +4853 IF P1 = 2 THEN 4920 +4860 z59 = 7:gosub 7620 +4870 P1=1:S(16)=0:B0=0:GOTO 400 +4890 z59 = 8:gosub 7620 +4900 P1=2:S(16)=0:B0=0:GOTO 400 +4920 z59 = 9:gosub 7620 +4930 P1=0:S(16)=0:B0=0:GOTO 400 +4950 REM *** LOCK *** +4960 IF L1=10 OR L1=11 THEN 4990 +4970 REM NOTHING LOCKABLE +4980 GOTO 2200 +4990 IF S(19)=-1 THEN 5020 +5000 B$="keys":goto 2710 +5020 G=0:z59 = 10:gosub 7620 +5040 GOTO 400 +5060 REM *** UNLOCK *** +5070 IF S(19)<>-1 THEN 5000 +5080 IF L1<>10 AND L1<>11 THEN 5120 +5090 G=1:z59 = 11:gosub 7620 +5110 GOTO 400 +5120 IF L1<>69 THEN 2200 +5130 IF B1>0 THEN 5160 +5140 z59 = 12:gosub 7620 +5150 goto 400 +5160 IF C<>0 THEN 5170 +5165 C=1:B1=2 +5170 z59 = 13:gosub 7620 +5180 GOTO 400 +5190 REM *** FREE *** +5200 IF K(1) = 31 or k(2) = 31 THEN 5240 +5210 REM CAN'T FREE ANYTHING BUT BIRD +5220 z59 = 2:gosub 7620 +5230 GOTO 410 +5240 IF S(31)<>-1 THEN 5220 +5250 S(31) = L1:B3=0 +5260 PRINT "Freed." +5270 IF L1<>22 THEN 5350 +5280 IF SN<>1 THEN 400 +5290 B$="snake" +5300 PRINT "The little bird attacks the green ";B$;" and" +5310 IF L1=82 THEN 5380 +5320 PRINT "drives it off" +5330 SN=0:S(34)=0:GOTO 400 +5350 IF L1<>82 THEN 400 +5360 B$="dragon":GOTO 5300 +5380 PRINT "gets burned to a crisp" +5390 S(31)=0 +5400 GOTO 400 +5410 REM *** WAVE *** +5420 IF K(1) <> 23 and k(2) <> 23 THEN 2200 +5430 IF S(23)=-1 THEN 5460 +5440 B$="rod":GOTO 2710 +5460 REM IS HERE NEAR FISSURE +5470 IF L1<>19 AND L1<>20 THEN 2200 +5480 REM yes +5490 REM GOTO B2+1 OF 5500,5530 +5491 IF B2=0 THEN 5500 +5492 IF B2=1 THEN 5530 +5500 z59 = 14:gosub 7620 +5510 B2=1:GOTO 400 +5530 z59 = 15:gosub 7620 +5540 B2=0:GOTO 400 +5560 REM *** OPEN *** +5570 GOSUB 8280 +5580 IF Z3>0 THEN 5610 +5590 PRINT "Open ";:goto 2040 +5610 IF Z3=40 THEN 5070 +5620 IF S(Z3)=L1 THEN 5650 +5630 PRINT "I see no ";b$;" here.":goto 400 +5650 if z3=24 THEN 5680 +5660 PRINT "I don't know how to open a ";B$:GOTO 400 +5680 IF S(9)=-1 THEN 5710 +5690 z59 = 16:gosub 7620 +5700 GOTO 400 +5710 IF S(Z3) = 0 THEN 2200 +5720 REM HE'S OPENED CLAM, SO PRINT DESCRIPTION OF THIS +5730 REM PUT PEARL IN CUL-DE-SAC +5740 S(7)=43:S(24)=0:S(30)=L1:z59 = 17:gosub 7620 +5750 GOTO 400 +5760 REM *** CLOSE *** +5770 GOSUB 8280 +5780 IF Z3=40 THEN 4960 +5790 z59 = 18:gosub 7620 +5800 GOTO 400 +5810 REM OIL +5820 IF K(1) <> 17 and k(2) <> 17 THEN 2200 +5830 IF S(17)=-1 THEN 5860 +5840 B$="oil":GOTO 5630 +5860 IF L1<>73 THEN 2200 +5870 REM IS DOOR STILL RUSTED +5880 IF D2=1 THEN 2200 +5890 D2=1:S(17)=0:B0=0:z59 = 19:gosub 7620 +5900 GOTO 400 +5910 REM *** EAT *** +5920 IF K(1) = 20 or k(2) = 20 THEN 5950 +5930 z59 = 20:gosub 7620 +5940 GOTO 410 +5950 Z3=20:GOSUB 8490 +5970 IF Z5=0 THEN 410 +5980 z59 = 73:gosub 7620 +5381 S(20)=0:B0=0:GOTO 400 +6000 REM *** DRINK *** +6010 IF K(1) = 16 or k(2) =16 THEN 6040 +6020 z59 = 21:gosub 7620 +6030 GOTO 410 +6040 Z3=16:GOSUB 8490 +6060 IF Z5=0 THEN 410 +6070 z59 = 22:gosub 7620 +6071 S(17)=0:B0=0:GOTO 400 +6090 REM *** FEE FIE FOE FOO *** +6100 IF L1=71 THEN 6130 +6110 z59 = 2:gosub 7620 +6120 GOTO 410 +6130 IF S(8)<>L1 THEN 6180 +6140 REM MAKE NEST VANISH +6150 z59 = 79:gosub 7620 +6160 S(8)=0:GOTO 400 +6180 REM IF S(8)=0 THEN 6110 +6190 S(8)=L1 +6200 rem MAKE NEST RE-APPEAR +6210 z59 = 81:gosub 7620 +6220 GOTO 400 +6230 REM *** SHORT *** +6240 PRINT "Short descriptions" +6250 D0=0:GOTO 400 +6270 REM *** LONG *** +6280 PRINT "Long descriptions" +6290 D0=1:GOTO 400 +6310 REM *** BRIEF *** +6320 PRINT "OK, I'll only describe the room in detail the first time." +6330 D0=2:GOTO 400 +6350 REM *** QUIT *** +6360 PRINT "Save game"; +6370 GOSUB 9860 +6380 IF Z0=1 THEN 8970 +6390 GOTO 9750 +6400 REM SCORE *** +6410 GOSUB 6430 +6420 GOTO 400 +6430 REM PRINT OUT SCORE DATA +6440 GOSUB 6510 +6450 PRINT "Your score is now ";S0 +6451 PRINT "You have explored ";(Z9/T1)*T1;"% of the cave." +6460 RESTORE 6470 +6470 DATA "beginner","novice","experienced","advanced","expert" +6480 Z9 = INT((S0-1)/100) +6481 IF Z9 <= 4 THEN 6483 +6482 Z9=4 +6483 FOR Z0=0 TO Z9 +6484 READ D$ +6485 NEXT Z0 +6490 PRINT "That makes you a ";D$;" adventurer." +6500 RETURN +6510 REM COMPUTE CURRENT SCORE +6520 RESTORE 230 +6530 Z9=0:S0=0 +6540 FOR Z0=1 TO 15 +6550 READ Z1 +6560 IF Z1=0 THEN 6590 +6570 IF V(Z1)<>1 THEN 6580 +6575 S0=S0+4*O(Z0) +6580 IF S(Z0)<>7 THEN 6590 +6585 S0=S0+4*O(Z0) +6590 NEXT Z0 +6600 S0=(G=1)*10 + S0:S0=(SN=0)*20 + S0:S0=(D1=0)*30 + S0:S0=(T=0)*30 + S0:S0=(B1=2)*20 + S0 +6605 S0=(B2=1)*20 + S0:S0=(P1=2)*20 + S0:S0=(D2=1)*20 + S0:S0=(C=1)*20 + S0 +6610 FOR Z0 = 1 TO T1 +6620 IF V(Z0)<>1 THEN 6630 +6621 S0=S0+1:Z9=Z9+1 +6630 NEXT Z0 +6640 RETURN +6660 rem list items at location l1 +6680 fseek #2,0 +6690 for z1 = 1 to t2 +6700 INPUT #2,a$ +6710 if s(z1) <> l1 then 6720 +6711 PRINT a$ +6720 next z1 +6721 IF S(26)<>-1 THEN 6730 +6722 z59 = 67:gosub 7620 +6730 rem CHECK FOR DWARF,PIRATE +6740 gosub 8560 +6745 if dead = 1 then 6770 +6750 gosub 8800 +6760 PRINT +6770 return +6775 rem Print Short room description +6780 fseek #1,0 +6820 for z1 = 1 to l1 +6830 INPUT #1,a$ +6840 next z1 +6850 v(l1) = 1 +6860 PRINT a$ +6870 return +6880 rem SPECIAL GETS +6890 if not (z3 = 24 or z3 = 30 or z3 > 31) then 6930 +6900 rem CAN'T GET THESE FOR SOME REASON +6910 z59 = 61:gosub 7620 +6920 goto 400 +6930 if not (z3 = 12 and c = 0) then 6970 +6940 rem CHAIN +6950 z59 = 58:gosub 7620 +6960 goto 3900 +6970 rem BEAR IS HE FED? UNLOCKED? +6980 if not (z3 = 26 and b1 <> 2) then 7010 +6990 z59 = 61:gosub 7620 +7000 goto 3900 +7010 if not (z3 = 14 and d1 = 1) then 7050 +7020 rem DRAGON AND RUG +7030 z59 = 59:gosub 7620 +7040 goto 3900 +7050 if not (z3 = 16 or z3 = 17) then 7090 +7060 rem OIL AND WATER DO SAME AS FILL +7070 PRINT "Why not say 'fill'?" +7080 goto 3900 +7090 if not (z3 = 22 and b3) then 7140 +7100 rem TAKE BIRD SINCE IT'S IN CAGE +7110 s(31) = -1:PRINT "Bird and ";:goto 3880 +7140 if z3 <> 31 then 7310 +7150 rem GETTING BIRD +7160 if b3 <> 1 then 7210 +7170 rem TAKE CAGE, SINCE BIRD IS IN IT +7180 PRINT "Cage and ";:s(22) = -1:goto 3880 +7210 if s(22) = -1 then 7240 +7220 b$ = "cage":goto 2810 +7240 if s(23) = -1 then 7280 +7250 rem OK TO TAKE BIRD +7260 s(31) = -1 : b3 = 1:goto 3890 +7280 rem ROD SCARES BIRD +7290 z59 = 37:gosub 7620 +7300 goto 3900 +7310 rem BOTTLE FULL? IF SO, GET CONTENTS +7320 if not (z3 = 21 and b0) then 7360 +7330 PRINT "Contents and the "; +7340 s(b0+15) = -1 +7360 goto 3880 +7370 rem SPECIAL "DROP" +7380 IF Z3<>31 THEN 7440 +7390 REM BIRD IN CAGE +7400 S(31)=L1:S(22)=L1:B3=1 +7410 IF Z3<>31 THEN 7420 +7415 PRINT "Cage and "; +7420 IF Z3=22 THEN 7430 +7425 PRINT "Bird and "; +7430 goto 4120 +7440 if z3=22 and b3=1 then 7400 +7450 IF Z3<>21 THEN 7520 +7460 REM BOTTLE +7470 IF B0=0 THEN 4120 +7480 REM BOTTLE IS FULL, DO DROP CONTENTS TOO +7490 PRINT "Contents and "; +7500 S(15+B0)=L1:GOTO 4120 +7520 IF NOT (Z3=16 OR Z3=17) THEN 7541 +7530 PRINT "Try saying 'empty'":goto 4140 +7541 IF Z3<>26 OR T<>1 OR (L1<>60 AND L1<>61) THEN 7550 +7542 z59 = 28:gosub 7620 +7543 T=0:S(26)=L1:S(32)=0:GOTO 400 +7550 IF Z3<>6 THEN 4120 +7560 IF S(28)=L1 THEN 7600 +7570 REM GOODBYE, FRAGILE VASE! +7580 z59 = 43:gosub 7620 +7581 S(6)=0:S(29)=L1:GOTO 400 +7600 z59 = 60:gosub 7620 +7610 GOTO 4120 +7619 rem PRINT MESSAGE +7620 if indx(1) >= 0 then 7640 +7630 gosub 12500 +7640 z59 = int(z59):xtmp = indx(z59) +7645 if z59 <> 2 and z59 <> 61 then 7660 +7650 xtmp = fraindx(xtmp+min(int(RND(1)*5),5)) +7660 fseek #3,xtmp +7670 INPUT #3,b1$ +7672 if len(b1$) = 0 then 7673 else 7690 +7673 b1$ = " " +7690 if instr(b1$,"#") = 0 then 7670 +7700 z4 = val(mid$(b1$,2)) +7705 if int(z4) = z59 then 7720 +7710 if int(z4) < z59 then 7670 +7712 if int(z4) > z59 then 7760 +7720 INPUT #3,b1$ +7730 if mid$(b1$,1,1) = "#" then 7770 +7740 PRINT b1$ +7750 goto 7720 +7760 PRINT "NO DESC. # ";z59;" IN FILE AMESSAGE" +7770 return +7800 rem +7810 rem SITUATION DESCRIPTIONS +7820 rem +7830 rem GRATE +7840 if l1 = 10 or l1 = 11 then 7842 else 7860 +7842 z59 = (g+10):gosub 7620 +7850 rem CRYSTAL BRIDGE +7860 if (l1 = 19 or l1 = 20) and b2 = 1 then 7861 else 7880 +7861 z59 = 14:gosub 7620 +7870 rem PLUGH NOISE +7880 if l1 = 26 and RND(1) > 0.3 then 7881 else 7900 +7881 z59 = 41:gosub 7620 +7890 rem IRON DOOR +7900 if l1 = 73 and d2 = 0 then 7901 else 7920 +7901 z59 = 57:gosub 7620 +7910 rem TROLL +7920 if (l1 = 60 or l1 = 61) and t = 1 then 7921 else 7940 +7921 z59 = 63:gosub 7620 +7930 rem BEAR +7940 if l1 = 69 and b1 = 0 then 7941 else 7950 +7941 z59 = 64:gosub 7620 +7950 if l1 = 69 and b1 = 1 then 7951 else 7970 +7951 z59 = 66:gosub 7620 +7960 rem PLANT IN PIT +7970 if l1 = 48 or l1 = 50 then 7971 else 7980 +7971 z59 = 47+p1:gosub 7620 +7980 return +7990 rem +8000 rem PRINT long room description from "amessage" file +8010 rem description is noormally l1+200 except for +8020 rem maze or forest +8030 rem set v(l1)=1 so as not to repeat long desc(brief mode +8040 v(l1) = 1 +8050 if l1 > 4 then 8080 +8060 z59 = 200:gosub 7620 +8070 goto 8130 +8080 if not (l1 > 88 and l1 < 98 or l1 = 99) then 8110 +8090 z59 = 288:gosub 7620 +8100 goto 8130 +8110 rem normal description +8120 z59 = 200+l1:gosub 7620 +8130 return +8180 rem always give long descriptio nfor forest and maze +8190 if l1 < 5 or (l1 > 88 and l1 < 98) or l1 = 99 then 8220 +8200 if v(l1)=1 then 8201 else 8220 +8201 gosub 6780 +8202 goto 8235 +8210 rem he hassn't seen this room, so give a long desc. +8220 v(l1) = 1 +8230 gosub 7990 +8235 return +8240 rem +8250 rem fetch first item code in k(1 to t2) +8260 rem z8=total # of items found in list +8270 rem z3=item code first found +8280 z8 = 0 : z3 = 0: d$ = "" +8300 for z5 = 1 to 45 +8320 if k(1) <> z5 and k(2) <> z5 then 8360 +8330 z8 = z8+1 +8340 restore 9960+z5:read b$:d$ = b$ +8350 if k(1) = z5 and z8 = 1 then 8352 +8351 if k(2) = z5 and z8 = 1 then 8352 else 8360 +8352 z3 = z5 +8360 next z5 +8370 b$ = d$ +8380 return +8390 rem FIND FIRST ITEM AT ROOM +8400 x1 = 0 +8410 FOR Z1=1 TO 47 +8415 if x1=1 then 8440 +8430 IF K(1) = Z1 OR K(2) = Z1 THEN 8431 else 8440 +8431 restore 9960+z1:read d$:x1=1 +8440 NEXT Z1 +8450 RETURN +8460 REM +8470 REM MAKE SURE HE'S CARRYING ITEM * Z3 +8480 REM +8490 IF S(Z3)=-1 THEN 8530 +8500 PRINT "You don't have the ";A$ +8510 Z5=0 +8520 RETURN +8530 Z5=1 +8540 RETURN +8550 rem *** DWARF *** +8560 if d3 <> 0 then 8640 +8570 rem SHOULD DWARF GIVE AWAY AXE? +8580 if l1 < 13 then 8790 +8590 if RND(1) > 0.05 then 8790 +8600 rem GIVE AWAY AXE +8610 z59 = 80:gosub 7620 +8620 s(27) = l1 : d3 = 1 +8630 goto 8790 +8640 rem SHOULD DWARF ATTACK? +8650 if L1 >= 13 then 8660 +8652 s(35) = 0:goto 8790 +8660 if s(35) <> L1 then 8770 +8670 if RND(1) > 0.5 then 8790 +8680 rem YES! +8690 z59 = 32:gosub 7620 +8700 rem DOES THE KNIFE KILL THE PLAYER? +8705 KC = KC - 0.02 +8706 IF KC >= 0.5 THEN 8710 +8707 KC = 0.5 +8710 if RND(1) <= KC then 8750 +8720 rem YES +8730 PRINT "It gets you!" +8740 dead = 1 : goto 8790 +8750 PRINT "It misses!" +8760 goto 8790 +8770 rem SHOULD WE PUT A DWARF HERE? +8780 if RND(1) >= 0.1 then 8790 +8785 s(35) = l1 +8786 z59=31:gosub 7620 +8790 return +8800 rem *** PIRATE *** +8810 rem FIRST, DOES HE HAVE ANYTHING WORTH STEALING? +8820 z3 = 0 +8830 if l1 < 13 then 8960 +8840 for x = 1 to 15 +8850 if s(x) <> -1 then 8860 +8855 z3 = z3+1 +8860 next x +8870 if z3 < int(RND(1)*4)+1 then 8960 +8880 rem SHOULD WE RIP OFF HIS VALUABLES? +8890 if RND(1) < 0.05 then 8920 +8900 z59 = 34:gosub 7620 +8910 goto 8960 +8920 z59 = 33:gosub 7620 +8930 for x = 1 to 15 +8940 if s(x) <> -1 then 8950 +8945 s(x) = 100 +8950 next x +8960 return +8970 REM *** SAVE GAME *** +8980 INPUT "What do you want to call the save file? ";A$ +8990 OPEN A$ FOR OUTPUT AS #5 ELSE 9010 +9000 GOTO 9030 +9010 PRINT "File ";a$;" not created" +9020 GOTO 410 +9030 PRINT #5,T1;",";T2;",";T3;",";L1;",";L2;",";G;",";B0;",";SN;",";D1;",";D2;",";D0;",";T;",";B1;",";B2;",";P1;",";L;",";C;",";D3;",";B3;",";R0;",";KC +9040 FOR X=1 TO 99 +9041 PRINT #5,S(X);",";V(X) +9042 NEXT X +9043 PRINT #5,V(100) +9044 CLOSE #5 +9050 PRINT "Game saved" +9051 C0 = 0 +9060 if k(1) = 143 OR K(2) = 143 then 9750 +9070 GOTO 410 +9080 REM *** LOAD OLD GAME *** +9090 IF C0=0 THEN 9120 +9100 PRINT "You already have a loaded game!" +9110 GOTO 410 +9120 INPUT "Save file name? ";A$ +9130 OPEN A$ FOR INPUT AS #5 ELSE 9150 +9140 GOTO 9170 +9150 PRINT "Unable to use file ";A$ +9160 GOTO 410 +9170 INPUT #5,T1,T2,T3,L1,L2,G,B0,SN,D1,D2,D0,T,B1,B2,P1,L,C,D3,B3,R0,KCX$ +9171 kc = val(kcx$) +9180 FOR X=1 TO 99 +9191 INPUT #5,SX,VX:s(x)=sx:v(x)=vx +9192 NEXT X +9193 INPUT #5,vx:v(100)=vx +9194 CLOSE #5 +9200 C0=1 +9210 GOTO 300 +9220 rem *** READ THE MAGAZINE *** +9230 GOSUB 8280 +9240 IF Z3=25 THEN 9270 +9250 z59 = 74:gosub 7620 +9260 GOTO 410 +9270 IF S(25)=-1 THEN 9300 +9280 B$="magazine" +9290 GOTO 2710 +9300 REM OK, LET HIM READ IT +9310 z59 = 303:gosub 7620 +9320 GOTO 400 +9330 REM *** BUG *** +9340 A$ = "ADVBUGS.TXT" +9341 OPEN A$ FOR APPEND AS #5 ELSE 9150 +9390 INPUT "Your name: ";A$ +9400 A$=A$+" "+DATE$ +9410 PRINT #5,A$ +9420 PRINT "Enter your gripe in up to five lines (hit return to quit):" +9430 FOR Z0=1 TO 5 +9440 PRINT Z0; +9450 INPUT A$ +9460 IF A$="" THEN 9490 +9470 PRINT #5,A$ +9480 NEXT Z0 +9490 PRINT "Message recorded. Thank you!" +9500 CLOSE #5 +9510 GOTO 410 +9540 REM REINCARNATE HIM +9550 R0=R0+1 +9560 IF R0=1 THEN 9580 +9561 IF R0=2 THEN 9610 +9562 IF R0=3 THEN 9740 +9570 REM ASK HIM IF HE WANTS TO BE REINCARNATED +9580 z59 = 75:gosub 7620 +9590 GOSUB 9860 +9600 GOTO 9630 +9610 z59 = 77:gosub 7620 +9620 GOTO 9580 +9630 IF Z0=0 THEN 9750 +9640 z59 = 76:gosub 7620 +9650 REM PUT HIM BACK IN HOUSE, REARRANGE HIS STUFF +9660 S(18)=7:L=0:DEAD=0:KC=1.03 +9670 FOR X=1 TO T2 +9680 IF S(X)<>-1 THEN 9690 +9685 S(X)=L1 +9690 NEXT X +9700 REM WE'VE PUT THE LAMP IN HOUSE AND OTHER ITEMS WHERE HE DIED +9710 L1=INT(RND(1)*4)+1:L2=L1 +9720 GOTO 320 +9730 REM THIRD DEATH--END OF GAME +9740 z59 = 78:gosub 7620 +9750 PRINT "Oh well..." +9760 GOSUB 6430 +9762 close #1:close #2:close #3 +9770 STOP +9780 rem *** PITS *** +9790 if l1 < 13 THEN 300 +9791 IF l = 1 and (s(18) = -1 or s(18) = l1) then 300 +9800 rem IS HE GOING TO FALL INTO A PIT? +9810 if l1 = 16 or l1 = 17 or l1 = 19 or l1 = 20 or l1 = 25 or l1 = 47 or l1 = 48 or l1 = 59 or l1 = 60 or l1 = 61 or l1 = 75 or l1 = 76 or l1 = 98 then 9840 +9820 goto 300 +9830 rem he fell into a pit +9840 z59 = 44:gosub 7620 +9850 goto 9540 +9860 rem *** SEEK A "YES" OR "NO" +9870 INPUT a$ +9875 if len(a$) <> 0 then 9880 +9877 a$ = " " +9880 a$ = LOWER$(mid$(a$,1,1)) +9890 if a$ <> "y" and a$ <> "n" then 9930 +9900 if a$ = "y" then 9901 else 9910 +9901 z0 = 1 +9910 if a$ = "n" then 9911 else 9920 +9911 z0 = 0 +9920 goto 9945 +9930 PRINT "Yes or No-"; +9940 goto 9870 +9945 return +9950 rem ---- SHORT NAMES FOR STUFF ---- +9961 data "large gold nugget" +9962 data "bars of silver" +9963 data "precious jewelry" +9964 data "many coins" +9965 data "several diamonds" +9966 data "fragile ming vase" +9967 data "glistening pearl" +9968 data "nest of golden eggs" +9969 data "jewel-encrusted trident" +9970 data "egg-sized emerald" +9971 data "platinum pyramid" +9972 data "golden chain" +9973 data "rare spices" +9974 data "persian rug" +9975 data "treasure chest" +9976 data "water" +9977 data "oil" +9978 data "brass lamp" +9979 data "keys" +9980 data "food" +9981 data "bottle" +9982 data "wicker cage" +9983 data "3-foot black rod" +9984 data "clam" +9985 data "magazine" +9986 data "bear" +9987 data "axe" +9988 data "velvet pillow" +9989 data "shards of pottery" +9990 data "oyster" +9991 data "bird" +9992 data "troll" +9993 data "dragon" +9994 data "snake" +9995 data "dwarf" +9996 data "rock" +9997 data "stairs" +9998 data "steps" +9999 data "house" +10000 data "grate" +10001 data "stream" +10002 data "room" +10003 data "bridge" +10004 data "pit" +10005 data "volcano" +10006 data "road" +10007 data "everything" +12500 rem INITIALIZE MESSAGE INDEX +12501 open "AMESSAGE.IDX" for INPUT as #6 else 12505 +12502 goto 12610 +12504 rem Message indx file doesn't exist, create it +12505 OPEN "AMESSAGE.IDX" FOR OUTPUT AS #6 +12506 fpos = -2:b$ = "":fracnt= 0:lastfra=-1:fseek #3,0 +12520 fpos = fpos+len(b$)+2 +12522 INPUT #3,b$ +12530 if len(b$) = 0 then 12531 else 12540 +12531 b$ = " " : fpos = fpos-1 +12540 if b$="#" then 12591 +12550 if instr(b$,"#") = 0 then 12520 +12560 z4 = val(mid$(b$,2)) +12570 if int(z4) = z4 then 12580 +12571 fracnt = fracnt + 1 +12572 if lastfra = int(z4) then 12574 +12573 indx(int(z4)) = fracnt:lastfra = int(z4):PRINT #6,int(z4);",";FRACNT +12574 fraindx(fracnt) = fpos +12576 goto 12585 +12580 indx(int(z4)) = fpos +12581 PRINT #6,int(z4);",";FPOS +12585 PRINT "*"; +12590 goto 12520 +12591 PRINT #6,-999;",";-999 +12592 FOR I = 1 TO FRACNT +12593 PRINT #6,FRAINDX(I) +12594 NEXT I +12595 PRINT #6,-999 +12596 close #3 +12597 open "AMESSAGE" for INPUT as #3 +12600 GOTO 12670 +12604 REM READ AMESSAGE.IDX FILE INTO ARRAYS +12610 INPUT #6,II,I +12612 PRINT "*"; +12615 IF I=-999 THEN 12630 +12620 INDX(II)=I:GOTO 12610 +12630 II = 0 +12640 INPUT #6,I +12641 PRINT "*"; +12650 IF I=-999 THEN 12670 +12660 II = II +1:FRAINDX(II)=I:GOTO 12640 +12670 CLOSE #6:PRINT:PRINT +12680 RETURN diff --git a/examples/bagels.bas b/examples/bagels.bas new file mode 100644 index 0000000..7168dc7 --- /dev/null +++ b/examples/bagels.bas @@ -0,0 +1,83 @@ +5 PRINT TAB(33);"BAGELS" +10 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY":PRINT:PRINT +15 REM *** BAGLES NUMBER GUESSING GAME +20 REM *** ORIGINAL SOURCE UNKNOWN BUT SUSPECTED TO BE +25 REM *** LAWRENCE HALL OF SCIENCE UC BERKELY +30 DIM A1(6),A(3),B(3) +40 Y=0:T=255 +50 PRINT:PRINT:PRINT +70 INPUT "WOULD YOU LIKE THE RULES (YES OR NO)";A$ +90 IF LEFT$(A$,1)="N" THEN 150 +100 PRINT:PRINT "I AM THINKING OF A THREE-DIGIT NUMBER. TRY TO GUESS" +110 PRINT "MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:" +120 PRINT " PICO - ONE DIGIT CORRECT BUT IN THE WRONG POSITION" +130 PRINT " FERMI - ONE DIGIT CORRECT AND IN THE RIGHT POSITION" +140 PRINT " BAGELS - NO DIGITS CORRECT" +150 FOR I=1 TO 3 +160 A(I)=INT(10*RND(1)) +165 IF I-1=0 THEN 200 +170 FOR J=1 TO I-1 +180 IF A(I)=A(J) THEN 160 +190 NEXT J +200 NEXT I +210 PRINT:PRINT "O.K. I HAVE A NUMBER IN MIND." +220 FOR I=1 TO 20 +230 PRINT "GUESS #";I +240 INPUT A$ +245 IF LEN(A$)<>3 THEN 630 +250 FOR Z=1 TO 3 +251 A1(Z)=ASC(MID$(A$,Z,1)) +252 NEXT Z +260 FOR J=1 TO 3 +270 IF A1(J)<48 THEN 300 +280 IF A1(J)>57 THEN 300 +285 B(J)=A1(J)-48 +290 NEXT J +295 GOTO 320 +300 PRINT "WHAT?" +310 GOTO 230 +320 IF B(1)=B(2) THEN 650 +330 IF B(2)=B(3) THEN 650 +340 IF B(3)=B(1) THEN 650 +350 C=0:D=0 +360 FOR J=1 TO 2 +370 IF A(J)<>B(J+1) THEN 390 +380 C=C+1 +390 IF A(J+1)<>B(J) THEN 410 +400 C=C+1 +410 NEXT J +420 IF A(1)<>B(3) THEN 440 +430 C=C+1 +440 IF A(3)<>B(1) THEN 460 +450 C=C+1 +460 FOR J=1 TO 3 +470 IF A(J)<>B(J) THEN 490 +480 D=D+1 +490 NEXT J +500 IF D=3 THEN 680 +505 IF C=0 THEN 545 +520 FOR J=1 TO C +530 PRINT "PICO "; +540 NEXT J +545 IF D=0 THEN 580 +550 FOR J=1 TO D +560 PRINT "FERMI "; +570 NEXT J +580 IF C+D<>0 THEN 600 +590 PRINT "BAGELS"; +600 PRINT +605 NEXT I +610 PRINT "OH WELL." +615 PRINT "THAT'S TWNETY GUESSES. MY NUMBER WAS";100*A(1)+10*A(2)+A(3) +620 GOTO 700 +630 PRINT "TRY GUESSING A THREE-DIGIT NUMBER.":GOTO 230 +650 PRINT "OH, I FORGOT TO TELL YOU THAT THE NUMBER I HAVE IN MIND" +660 PRINT "HAS NO TWO DIGITS THE SAME.":GOTO 230 +680 PRINT "YOU GOT IT!!!":PRINT +690 Y=Y+1 +700 INPUT "PLAY AGAIN (YES OR NO)";A$ +720 IF LEFT$(A$,1)="YES" THEN 150 +730 IF Y=0 THEN 750 +740 PRINT:PRINT "A";Y;"POINT BAGELS BUFF!!" +750 PRINT "HOPE YOU HAD FUN. BYE." +999 END diff --git a/examples/factorial.bas b/examples/factorial.bas index 8b13789..1f443fd 100644 --- a/examples/factorial.bas +++ b/examples/factorial.bas @@ -1 +1,9 @@ - +10 REM A SHORT PROGRAM TO CALCULATE FACTORIAL OF +20 REM SUPPLIED NUMBER N +30 INPUT "Please provide N: "; N +35 REM OUTPUT IS NOW A RESERVED KEYWORD +40 OUTPUTVAL = 1 +50 FOR I = 1 TO N +60 OUTPUTVAL = OUTPUTVAL * I +70 NEXT I +80 PRINT "Factorial of N is "; OUTPUTVAL diff --git a/examples/regression.bas b/examples/regression.bas new file mode 100644 index 0000000..3ce9284 --- /dev/null +++ b/examples/regression.bas @@ -0,0 +1,105 @@ +10 REM A BASIC PROGRAM THAT CAN BE USED FOR REGRESSION TESTING +20 REM OF ALL INTERPRETER FUNCTIONALITY +30 PRINT "*** Testing basic arithmetic functions and multiple statements ***" +40 LET I = 100: LET J = 200 +60 PRINT "Expecting the sum to be 300:" +70 PRINT I + J +80 PRINT "Expecting the product to be 20000:" +90 PRINT I * J +100 PRINT "Expecting the sum to be 20100:" +110 PRINT 100 + I * J +120 PRINT "Expecting sum to be 40000" +130 PRINT (100 + I) * J +140 IF I > J THEN 150 ELSE 180 +150 PRINT "Should not print the smaller value of I which is "; I +160 PRINT I +170 GOTO 180 +180 PRINT "Should print the larger value of J which is "; J +190 PRINT J +200 GOTO 220 +210 PRINT "Should not print this line" +220 PRINT "*** Testing subroutine behaviour ***" +230 PRINT "Calling subroutine" +240 GOSUB 1630 +250 PRINT "Exited subroutine" +260 PRINT "Now testing nested subroutines" +270 GOSUB 1660 +280 PRINT "*** Testing loops ***" +290 PRINT "This loop should count to 5 in increments of 1:" +300 FOR I = 1 TO 5 +310 PRINT I +320 NEXT I +330 PRINT "This loop should count back from 10 to 1 in decrements of 2:" +340 FOR I = 10 TO 1 STEP -2 +350 PRINT I +360 NEXT I +370 PRINT "These nested loops should print 11, 12, 13, 21, 22, 23:" +380 FOR I = 1 TO 2 +390 FOR J = 1 TO 3 +400 PRINT I; J +410 NEXT J +420 NEXT I +430 PRINT "*** Testing arrays ***" +440 DIM A(3, 3) +450 FOR I = 0 TO 2 +460 FOR J = 0 TO 2 +470 LET A(I, J) = 5 +480 NEXT J +490 NEXT I +500 PRINT "This should print 555" +510 PRINT A(0, 0); A(1, 1); A(2, 2) +520 PRINT "*** Testing file i/o ***" +530 OPEN "REGRESSION.TXT" FOR OUTPUT AS #1 +540 PRINT #1,"0123456789Hello World!" +545 PRINT #1,"This is second line for testing" +550 CLOSE #1 +560 OPEN "REGRESSION.TXT" FOR INPUT AS #2 +570 PRINT "The next line should say 'Hello World!'" +580 FSEEK #2,10 +590 INPUT #2,A$ +600 PRINT A$ +620 N = 0 +630 I = 7 +640 PRINT "This loop should count to 5 in increments of 1 twice:" +650 FOR I = 1 TO 10 +660 PRINT I +670 IF I = 5 THEN GOTO 690 +680 NEXT I +690 N = N + 1 +700 IF N < 2 THEN GOTO 650 +810 PRINT "The loop variable I should be equal to 5, I=";I +815 DATA "DATA Statement tests..." +820 READ A$ +825 PRINT "The next line should read: DATA Statement tests..." +830 PRINT A$ +840 DATA 1 , 2 , 3 +850 DATA 4 , 5 , 6 +855 DATA 1.5 , 2 , "test" +858 PRINT "The next three lines should be: 12 34 56" +860 FOR I = 1 TO 3 +870 READ J , K +880 PRINT J ; K +890 NEXT I +900 RESTORE 840 +910 READ I , J , K +920 PRINT "the next line should print 123" +930 PRINT I ; J ; K +970 RESTORE 855 +980 READ A1 , B , C$ +990 PRINT "Float: " ; A1 ; " Int: " ; B ; " String: " ; C$ +1000 RESTORE 850 +1010 READ I , J , K , L +1020 PRINT "the next line should print 4561.5:" +1030 PRINT I; J ; K ; L +1610 PRINT "*** Finished ***" +1620 STOP +1630 REM A SUBROUTINE TEST +1640 PRINT "Executing the subroutine" +1650 RETURN +1660 REM AN OUTER SUBROUTINE +1670 GOSUB 1700 +1680 PRINT "This should be printed second" +1690 RETURN +1700 REM A NESTED SUBROUTINE +1710 PRINT "This should be printed first" +1720 RETURN diff --git a/examples/rock_scissors_paper.bas b/examples/rock_scissors_paper.bas new file mode 100644 index 0000000..4f30402 --- /dev/null +++ b/examples/rock_scissors_paper.bas @@ -0,0 +1,63 @@ +10 REM ROCK, SCISSORS, PAPER AGAINST +20 REM THE COMPUTER +30 RANDOMIZE +40 MYSCORE = 0 +50 YOURSCORE = 0 +60 PRINT "Let's play rock-scissors-paper!" +70 INPUT "(R)ock, (S)cissors, (P)aper or e(X)it: "; YOURGUESS$ +75 IF YOURGUESS$ = "X" THEN 160 +80 RANDOM = RND(1) +90 GOSUB 190 +100 PRINT "Your guess: "; YOURGUESS$; ", My guess: "; MYGUESS$ +110 REM ON YOURGUESS$ = "R" GOSUB 300 +120 REM ON YOURGUESS$ = "S" GOSUB 500 +130 REM ON YOURGUESS$ = "P" GOSUB 700 +135 ON INSTR("RSP",YOURGUESS$) GOSUB 300,500,700 +150 GOTO 70 +160 PRINT "My score: "; MYSCORE; " Your score: "; YOURSCORE +170 STOP +190 REM RANDOMLY ASSIGN MYGUESS$ +200 IF RANDOM < 0.3 THEN 210 ELSE 230 +210 MYGUESS$ = "R" +220 RETURN +230 IF RANDOM < 0.6 THEN 240 ELSE 260 +240 MYGUESS$ = "S" +250 RETURN +260 MYGUESS$ = "P" +270 RETURN +300 REM ROCK +310 IF MYGUESS$ = "R" THEN 320 ELSE 340 +320 PRINT "A draw!" +330 RETURN +340 IF MYGUESS$ = "S" THEN 350 ELSE 380 +350 PRINT "I lost!" +360 YOURSCORE = YOURSCORE + 1 +370 RETURN +380 IF MYGUESS$ = "P" THEN 390 ELSE 410 +390 PRINT "I won!" +400 MYSCORE = MYSCORE + 1 +410 RETURN +500 REM SCISSORS +510 IF MYGUESS$ = "S" THEN 520 ELSE 540 +520 PRINT "A draw!" +530 RETURN +540 IF MYGUESS$ = "P" THEN 550 ELSE 580 +550 PRINT "I lost!" +560 YOURSCORE = YOURSCORE + 1 +570 RETURN +580 IF MYGUESS$ = "R" THEN 390 ELSE 410 +590 PRINT "I won!" +600 MYSCORE = MYSCORE + 1 +610 RETURN +700 REM PAPER +710 IF MYGUESS$ = "P" THEN 720 ELSE 740 +720 PRINT "A draw!" +730 RETURN +740 IF MYGUESS$ = "R" THEN 750 ELSE 780 +750 PRINT "I lost!" +760 YOURSCORE = YOURSCORE + 1 +770 RETURN +780 IF MYGUESS$ = "S" THEN 790 ELSE 810 +790 PRINT "I won!" +800 MYSCORE = MYSCORE + 1 +810 RETURN From f799d84f8cb77ae4457544ef34e16aefb66b254d Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:48:24 +0100 Subject: [PATCH 079/183] Moved to examples --- ADESCRIP | 100 ------------------------------------------------------- 1 file changed, 100 deletions(-) delete mode 100644 ADESCRIP diff --git a/ADESCRIP b/ADESCRIP deleted file mode 100644 index 7924165..0000000 --- a/ADESCRIP +++ /dev/null @@ -1,100 +0,0 @@ -You're in the forest. -You're in the forest. -You're in the forest. -You're in the forest. -You're at the hill in the road. -You're at the end of the road again. -You're inside the Building. -You're in the valley. -You're at slit in the streambed. -You're directly above a grate in a dry streambed. -You're below the grate. -You're in the cobble crawl. -You're in the debris room. -You are in an awkward sloping east/west canyon. -You're in the bird chamber. -You're at the top of the small pit. -You're in the Hall of Mists. -You're in the nugget of gold room. -You're on the east bank of the fissure. -You are on the west side of the fissure in the Hall of Mists. -You're at west end of the Hall of Mists. -You're in the Hall of the Mountain King. -You are in the south side chamber. -You are in the west side chamber of the Hall of the Mountain King. -You are at a N/S passage at a hole in the floor. -You're at Y2 . -You're at the window near the pit. -The passage here is blocked by a recent cave-in. -You're at the east end of the Long Hall. -You're at west end of the Long Hall. -You are at a crossover of a high N/S passage and a low E/W one. -Dead end -You are in a dirty broken passage. -You're at the top of the small pit. -You are in a little pit which has a stream flowing through it. -You're in the dusty rock room. -You're at a complex junction. -You're in the anteroom. -You are at Witt's end. Passages lead off in *all* directions. -You're in the shell room. -You're in the arched hall. -You're in a long sloping corridor with ragged sharp walls. -You are in a cul-de-sac about eight feet across. How's your french? -You are in bedquilt, a long east/west passage with holes everywhere. -You're in the swiss cheese room. -You're in the soft room. -You're at the east end of the Twopit room. -You're at the west end of the Twopit room. -You're in the east pit. -You're in the west pit. -You're in the slab room. -You're in the Oriental room. -You are in a large low room. -You're in a sloping corridor. -Dead end crawl. -You're in the misty cavern. -You're in the alcove. -You're in the Plover room. -You're in the dark-room. -You're on the SW side of the chasm. -You're on the NE side of the chasm. -You're in a corridor. -You're at the fork in the path. -You're in the limestone passage. -You're at a junction with warm walls. -You're in the chamber of boulders. -You're at a breath-taking view. -You're in front of the barren room. -You're in the barren room. -You're in a narrow corridor. -You're in the Giant room. -The passage here is blocked by a recent cave-in. -You are at one end of an immense north/south passage. -You're in the cavern with a waterfall -You're at a steep incline above a large room. -You are in a secret N/S canyon above a sizable passage. -You're at the top of the stalactite. -You're at a junction of three secret canyons. -You are in a secret N/S canyon above a large room. -You're in mirror canyon. -You're at a reservoir. -You are in a secret canyon which exits to the north and east. -You're in the secret E/W canyon above a tight canyon. -You are at a wide place in a very tight N/S canyon. -The canyon here becomes too tight to go on. -You are in a tall E/W canyon. -The canyon runs into a mass of boulders -- dead end. -You are in a maze of twisty little passages, all alike. -You are in a maze of twisty little passages, all alike. -You are in a maze of twisty little passages, all alike. -You are in a maze of twisty little passages, all alike. -You are in a maze of twisty little passages, all alike. -You are in a maze of twisty little passages, all alike. -You are in a maze of twisty little passages, all alike. -You are in a maze of twisty little passages, all alike. -You are in a maze of twisty little passages, all alike. -You are in a maze of twisty little passages, all alike. -You are at a huge orange stalactite in the maze. -You are in a maze of twisty little passages, all alike. -You are at a dead end. \ No newline at end of file From 630890599b18b72c28996fe27da20e89459d9012 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:48:48 +0100 Subject: [PATCH 080/183] Moved to examples --- AITEMS | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 AITEMS diff --git a/AITEMS b/AITEMS deleted file mode 100644 index 906760a..0000000 --- a/AITEMS +++ /dev/null @@ -1,35 +0,0 @@ -There is a large sparking nugget of gold here! -There are bars of silver here! -There is precious jewelry here! -There are many coins here! -There are several diamonds here! -There is a delicate, precious ming vase here! -To one side lies a glistening Pearl! -There is a nest here, full of golden eggs! -There is a jewel-encrusted trident here! -There is an emerald the size of a plover's egg here! -There is a platinum pyramid here, eight inches on a side! -There is a golden chain here! -There are rare spices here! -There is a valuable persian rug here! -There is a chest here, full of various treasures! -There is a bottle of water here. -There is a small pool of oil here. -There is a brass lantern here. -There is a set of keys here. -There is food here. -There is a glass bottle here. -There is a small wicker cage here. -There is a 3-foot black rod here. -There is an enormous clam here with its shell tightly shut. -There is a recent issue of 'Spelunker Today' here. -There is a bear nearby. -There is a little axe here. -There is a purple velvet pillow here. -There are shards of pottery scattered about. -There is an enormous oyster here with its shell tightly shut. -There is a little bird here. -A burly troll stands in front of you. -A huge fierce green dragon bars the way! -A huge fierce green snake bars the way! -There is a threatening little dwarf in the room with you! \ No newline at end of file From fdced44158b79c32106c62e6651071de87979181 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:49:00 +0100 Subject: [PATCH 081/183] Moved to examples --- AMESSAGE | 573 ------------------------------------------------------- 1 file changed, 573 deletions(-) delete mode 100644 AMESSAGE diff --git a/AMESSAGE b/AMESSAGE deleted file mode 100644 index 2a1c352..0000000 --- a/AMESSAGE +++ /dev/null @@ -1,573 +0,0 @@ -#1 -You can't move that way. -#2.1 -Nothing happens. -#2.2 -Nothing seems to happen. -#2.3 -I don't think that had any affect on anything. -#2.4 -I don't know how to apply that word here. -#2.5 -That had little effect, if any at all. -#3 -The mist is quite thick here, and the fissure is too wide to jump. -#4 -You are at the bottom of the pit with a broken neck. -#5 -The bottle is already filled! -#6 -The bear eagerly wolfs down your food, after which he seems to calm -down considerably and even becomes rather friendly. burp -#7 -The plant surges upwards, reaching halfway to the hole above you -#8 -The plant grows explosively, almost filling the bottom of the pit. -#9 -You overwatered the plant, you fool! -#10 -The grate is locked. -#11 -The grate is unlocked. -#12 -The bear is quite ferocious, and will no doubt tear you to -shreds if you approach it! -#13 -The chain is unlocked and the bear is free. -#14 -A crystal bridge now spans the fissure. -#15 -The bridge vanishes. -#16 -It is beyond your power to do that. -#17 -The clam opens momenntarily, and a glistening pearl falls out -and rolls away. Goodness, this must really have been an oyster. -(I never was any good at identifying bivalves, anyway.) -#18 -I can't close that! -#19 -The oil has freed up the hinges so that the door will now move, -although it requires some effort. -#20 -I can't eat. -#21 -How can I drink that? -#22 -"Glug, glug, glug, Belch!" -#23 -It doesn't want to eat anything (except maybe you!) -#24 -You fool! Dwarves only eat coal! Now you've made him -*** REALLY MAD ****! -#25 -Trolls are close relatives with the rocks and have skin as tough as -that of a rhinoceros. The troll fends off your blows effortlessly. -#26 -The troll deftly catches the axe, examines it carefully, and tosses it -back, declaring, "Good workmanship, but it's not valuable enough." -#27 -The troll catches your treasure and scurries away out of sight. -#28 -The bear lumbers toward the troll, who lets out a startled shriek and -scurries away. The bear soon gives up the pursuit and wanders back. -#29 -You attack a little dwarf, but he dodges out of the way. -#30 -You killed a little dwarf. The body vanishes in a cloud of greasy -black smoke. -#31 -There is a threatening little dwarf in the room with you! -#32 -One sharp nasty knife is thrown at you! -#33 -Out from the shadows behind you pounces a bearded pirate! "Har, har," -he chortles, "I'll just take all this booty and hide it away with me -chest deep in the maze!" he snatches your treasure and vanishes into -the gloom. -#34 -There are faint rustling noises from the darkness behind you. -#35 -The grate is open. -#36 -The grate is locked. -#37 -The bird was unafraid when you entered, but as you approach it becomes -disturbed and you cannot catch it. -#38 -The dome is unclimbable. -#39 -A crystal bridge now spans the fissure. -#40 -A huge green fierce snake bars the way! Hisssssss -#41 -A hollow voice says "Plugh". -#42 -The vase is now resting, delicately, on a velvet pillow. -#43 -Crash! Tinkle! The ming vase shatters into worthless crockery. -#44 -You fell into a pit and broke every bone in your body! -#45 -It is now pitch dark. If you proceed you will likely fall into a pit. -#46 -The nest of golden eggs has vanished! -#47 -There is a tiny little plant in the pit, murmuring "water, water, ..." -#48 -There is a 12-foot-tall beanstalk stretching up out of the pit, -bellowing "WATER!! WATER!!" -#49 -There is a gigantic beanstalk stretching all the way up to the hole. -#50 -You can't get by the snake. He hates your guts. -#51 -The dragon has a rather "volatile" personality, and he will -probably incinerate you if you get any closer. -#52 -You have crawled around in some little holes and wound up back in the -main passage. -#53 -Something you're carrying won't fit through the tunnel with you. -#54 -Your load is too heavy. You'd best take inventory and drop -something first. -#55 -A burly troll stands by the bridge and insists you throw him a -treasure before you may cross. -#56 -The troll pops from under the bridge and blocks your way. -#57 -The way north is barred by a massive, rusty, iron door. -#58 -The bear will bite off your hand. Besides, the chain is locked to the wall. -#59 -It's rather difficult to move a twenty-ton dragon, considering -he is lying on the rug. -#60 -The vase is now resting, delicately, on a velvet pillow. -#61.1 -A valliant attempt! -#61.2 -Nice try! -#61.3 -A valliant attempt, but you're not that strong! -#61.4 -Don't be silly! -#61.5 -Do you have any idea on how to do that? -#62 -A huge green fierce dragon blocks the way. The dragon is lying -on a persian rug! -#63 -A burly troll stands beside the bridge, blocking your way. -#64 -There is a ferocious cave bear eyeing you from the far end of the room! -#65 -The bear is locked to the wall with a golden chain! -#66 -There is a calm, friendly bear locked to the wall. -#67 -You are being followed by a very large, tame bear. -#68 -With what? Your bare hands? -#69 -Congratulations! You have just vanquished a dragon with your bare hands -(hard to believe ain't it?). -#70 -Attacking it is both dangerous and doesn't work. -#71 -There is nothing here to attack. -#72 -How can you do that to more than one thing at a time? -#73 -Thank you, it was delicious! -#74 -I see nothing special about that. -#75 -Oh dear, you seem to have gotten yourself killed. I might be able to -help you out, but I've never really done this before. Do you want me -to try to reincarnate you? -#76 -All right. But don't blame me if something goes wr...... - --- POOF!! --- -You are engulfed in a cloud of orange smoke. Coughing and gasping, -you emerge from the smoke and find.... -#77 -You clumsy oaf, you've done it again! I don't know how long I can -keep this up. Do you want me to try reincarnating you again? -#78 -Now you've really done it! I'm out of orange smoke! You don't expect -me to do a decent reincarnation without any orange smoke, do you? -Better luck next time! -#79 -The nest of golden eggs has vanished! -#80 -A little dwarf just walked around a corner, saw you, threw a little -axe at you which missed, cursed and ran away. -#81 -The nest of golden eggs has re-appeared! -#200 -You are in a forest, with trees all around you. -#205 -You have walked up a hill, still in the forest. The road slopes back -down the other side of the hill. There is a building in the distance. -#206 -You are standing at the end of a road before a small brick building. -Around you is a forest. A small stream flows out of the building and -down a gully. -#207 -You are inside a building, a well house for a large spring. -#208 -You are in a valley in the forest beside a stream tumbling along a -rocky bed. -#209 -At your feet all the water of the stream splashes into a 2-inch slit -in the rock. Downstream the streambed is bare rock. -#210 -You are in a 20-foot depression floored with bare dirt. Set into the -dirt is a strong steel grate mounted in concrete. A dry streambed -leads into the depression. -#211 -You are in a small chamber beneath a 3x3 steel grate to the surface. -A low crawl over cobbles leads inward to the west. -#212 -You are crawling over cobbles in a low passage. There is a dim light -at the east end of the passage. -#213 -You are in a debris room filled with stuff washed in from the surface. -A low wide passage with cobbles becomes plugged with mud and debris -here, but an awkward canyon leads upward and west. A note on the wall -says "magic word XYZZY." -#214 -You are in an awkward sloping east/west canyon. -#215 -You are in a splendid chamber thirty feet high. The walls are frozen -rivers of orange stone. An awkward canyon and a good passage exit -from east and west sides of the chamber. -#216 -At your feet is a small pit breathing traces of white mist. An east -passage ends here except for a small crack leading on. - -Rough stone steps lead down the pit. -#217 -You are at one end of a vast hall stretching forward out of sight to -the west. There are openings to either side. Nearby, a wide stone -staircase leads downward. The hall is filled with wisps of white mist -swaying to and fro almost as if alive. A cold wind blows up the -staircase. There is a passage at the top of a dome behind you. - -Rough stone steps lead to the top of the dome. -#218 -This is a low room with a crude note on the wall. The note says, -"you can't get it up the steps." -#219 -You are on the east bank of a fissure slicing clear across the hall. -The mist is quite thick here, and the fissure is too wide to jump. -#220 -You are on the west side of the fissure in the Hall of Mists. -#221 -You are at the west end of Hall of Mists. A low wide crawl continues -west and another goes north. To the south is a little passage 6 feet -off the floor. -#222 -You are in the Hall of the Mountain King, with passages off in all -directions. -#223 -You are in the south side chamber. -#224 -You are in the west side chamber of the Hall of the Mountain King. -A passage continues west and up here. -#225 -You are in a low N/S passage at a hole in the floor. The hole goes -down to an E/W passage. -#226 -You are in a large room, with a passage to the south, a passage to the -west, and a wall of broken rock to the east. There is a large "Y2" on -a rock in the room's center. -#227 -You're at a low window overlooking a huge pit, which extends up out of -sight. A floor is indistinctly visible over 50 feet below. Traces of -white mist cover the floor of the pit, becoming thicker to the right. -Marks in the dust around the window would seem to indicate that -someone has been here recently. Directly across the pit from you and -25 feet away there is a similar window looking into a lighted room. A -shadowy figure can be seen there peering back at you. - -The shadowy figure seems to be trying to attract your attention. -#228 -The passage here is blocked by a recent cave-in. -#229 -You are at the east end of a very long hall apparently without side -chambers. To the east a low wide crawl slants up. To the north a -round two foot hole slants down. -#230 -You are at the west end of a very long featureless hall. The hall -joins up with a narrow north/south passage. -#231 -You are at a crossover of a high N/S passage and a low E/W one. -#232 -Dead end -#233 -You are in a dirty broken passage. To the east is a crawl. To the -west is a large passage. Above you is a hole to another passage. -#234 -You are on the brink of a small clean climbable pit. A crawl leads -west. -#235 -You are in the bottom of a small pit with a little stream, which -enters and exits through tiny slits. -#236 -You are in a large room full of dusty rocks. There is a big hole in -the floor. There are cracks everywhere, and a passage leading east. -#237 -You are at a complex junction. A low hands and knees passage from the -north joins a higher crawl from the east to make a walking passage -going west. There is also a large room above. The air is damp here. -#238 -You are in an anteroom leading to a large passage to the east. Small -passages go west and up. The remnants of recent digging are evident. -a sign in midair here says "Cave under construction beyond this point. -proceed at own risk. "[Witt Construction Company]" -#239 -You are at Witt's end. Passages lead off in *all* directions. -#240 -You're in a large room carved out of sedimentary rock. The floor and -walls are littered with bits of shells embedded in the stone. A -shallow passage proceeds downward, and a somewhat steeper one leads -up. A low hands and knees passage enters from the south. -#241 -You are in an arched hall. A coral passage once continued up and east -from here, but is now blocked by debris. The air smells of sea water. -#242 -You are in a long sloping corridor with ragged sharp walls. -#243 -You are in a cul-de-sac about eight feet across. How's your french? -#244 -You are in bedquilt, a long east/west passage with holes everywhere. -To explore at random, select north, south, up or down. -#245 -You are in a room whose walls resemble swiss cheese. Obvious passages -go west, east, NE, and NW. Part of the room is occupied by a large -bedrock block. -#246 -You are in the soft room. The walls are covered with heavy curtains, -the floor with a thick pile carpet. Moss covers the ceiling. -#247 -You are at the east end of the Twopit room. The floor here is -littered with thin rock slabs, which make it easy to descend the pits. -There is a path here bypassing the pits to connect passages from east -and west. There are holes all over, but the only big one is on the -wall directly over the west pit where you can't get to it. -#248 -You are at the west end of the Twopit room. There is a large hole in -the wall above the pit at this end of the room. -#249 -You are at the bottom of the eastern pit in the Twopit room. There is -a small pool of oil in one corner of the pit. -#250 -You are at the bottom of the western pit in the Twopit room. There is -a large hole in the wall about 25 feet above you. -#251 -You are in a large low circular chamber whose floor is an immense slab -fallen from the ceiling (slab room). East and west there once were -large passages, but they are now filled with boulders. Low small -passages go north and south, and the south one quickly bends west -around the boulders. -#252 -This is the oriental room. Ancient oriental cave drawings cover the -walls. A gently sloping passage leads upward to the north, another -passage leads SE, and a hands and knees crawl leads west. -#253 -You are in a large low room. Crawls lead north, SE, and SW. -#254 -You are in a long winding corridor sloping out of sight in both -directions. -#255 -Dead end crawl. -#256 -You are following a wide path around the outer edge of a large cavern. -Far below, through a heavy white mist, strange splashing noises can be -heard. The mist rises up through a fissure in the ceiling. The path -exits to the south and west. -#257 -You are in an alcove. A small NW path seems to widen after a short -distance. An extremely tight tunnel leads east. It looks like a very -tight squeeze. An eerie light can be seen at the other end. -#258 -You're in a small chamber lit by an eerie green light. An extremely -narrow tunnel exits to the west. A dark corridor leads NE. -#259 -You're in the dark-room. A corridor leading south is the only exit. -A massive stone tablet embedded in the wall reads: -"Congratulations on bringing light into the dark-room!" -#260 -You are on one side of a large, deep chasm. A heavy white mist rising -up from below obscures all view of the far side. A SW path leads away. - -A rickety wooden bridge extends across the chasm, vanishing into the -mist. A sign posted on the bridge reads, "STOP! Pay Troll!" -#261 -You are on the far side of the chasm. A NE path leads away from the -chasm on this side. - -A rickety wooden bridge extends across the chasm, vanishing into the -gloom. A sign posted on the bridge reads: "Stop! Pay troll!" -#262 -You're in a long east/west corridor. A faint rumbling noise can be -heard in the distance. -#263 -The path forks here. The left fork leads northeast. A dull rumbling -seems to get louder in that direction. The right fork leads southeast -down a gentle slope. The main corridor enters from the west. -#264 -You are walking along a gently sloping north/south passage lined with -oddly shaped limestone formations. -#265 -The walls are quite warm here. From the north can be heard a steady -roar, so loud that the entire cave seems to be trembling. Another -passage leads south, and a low crawl goes east. -#266 -You are in a small chamber filled with large boulders. The walls are -very warm, causing the air in the room to be almost stifling from the -heat. The only exit is a crawl heading west, through which is coming -a low rumbling. -#267 -You are on the edge of a breath-taking view. Far below you is an -active volcano, from which great gouts of molten lava come surging -out, cascading back down into the depths. The glowing rock fills the -farthest reaches of the cavern with a blood-red glare, giving every- -thing an eerie, macabre appearance. The air is filled with flickering -sparks of ash and a heavy smell of brimstone. The walls are hot to -the touch, and the thundering of the volcano drowns out all other -sounds. Embedded in the jagged roof far overhead are myriad twisted -formations composed of pure white alabaster, which scatter the murky -light into sinister apparitions upon the walls. To one side is a deep -gorge, filled with a bizarre chaos of tortured rock which seems to -have been crafted by the devil himself. An immense river of fire -crashes out from the depths of the volcano, burns its way through the -gorge, and plummets into a bottomless pit far off to your left. To -the right, an immense geyser of blistering steam erupts continuously -from a barren island in the center of a sulfurous lake, which bubbles -ominously. The far right wall is aflame with an incandescence of its -own, which lends an additional infernal splendor to the already -hellish scene. A dark, foreboding passage exits to the south. -#268 -You are standing at the entrance to a large, barren room. A sign -posted above the entrance reads: "Caution! Bear in room!" -#269 -You are inside a barren room. The center of the room is completely -empty except for some dust. Marks in the dust lead away toward the -far end of the room. The only exit is the way you came in. -#270 -You are in a long, narrow corridor stretching out of sight to the -west. At the eastern end is a hole through which you can see a -profusion of leaves. -#271 -You are in the giant room. The ceiling here is too high up for your -lamp to show it. Cavernous passages lead east, north, and south. On -the west wall is scrawled the inscription, "FEE FIE FOE FOO" [sic]. -#272 -The passage here is blocked by a recent cavein. -#273 -You are at one end of an immense north/south passage. -#274 -You are in a magnificent cavern with a rushing stream, which cascades -over a sparkling waterfall into a roaring whirlpool which disappears -through a hole in the floor. Passages exit to the south and west. -#275 -You are at the top of a steep incline above a large room. You could -climb down here, but you would not be able to climb up. There is a -passage leading back to the north. -#276 -You are in a secret N/S canyon above a sizable passage. -#277 -A large stalactite extends from the roof and almost reaches the floor -below. You could climb down it, and jump from it to the floor, but -having done so you would be unable to reach it to climb back up. - -The maze continues at this level. -#278 -You are in a secret canyon at a junction of three canyons, bearing -north, south, and SE. The north one is as tall as the other two -combined. -#279 -You are in a secret N/S canyon above a large room. -#280 -You are in a north/south canyon about 25 feet across. The floor is -covered by white mist seeping in from the north. The walls extend -upward for well over 100 feet. Suspended from some unseen point far -above you, an enormous two-sided mirror is hanging parallel to and -midway between the canyon walls. (The mirror is obviously provided -for the use of the dwarves, who as you know, are extremely vain.) A -small window can be seen in either wall, some fifty feet up. -#281 -You are at the edge of a large underground reservoir. An opaque cloud -of white mist fills the room and rises rapidly upward. The lake is -fed by a stream, which tumbles out of a hole in the wall about 10 feet -overhead and splashes noisily into the water somewhere within the -mist. The only passage goes back toward the south. -#282 -You are in a secret canyon which exits to the north and east. -#283 -You're in the secret E/W canyon above a tight canyon. -#284 -You are at a wide place in a very tight N/S canyon. -#285 -The canyon here becomes too tight to go on. -#286 -You are in a tall E/W canyon. A low tight crawl goes 3 feet north and -seems to open up. -#287 -The canyon runs into a maze of boulders -- dead end. -#288 -You are in a maze of twisty little passages, all alike. -#298 -You are near a large pit beside a large stalactite in the maze. -The stalactite is about 30 feet long, allowing you to descend -it. However, due to the smoothness of the stalactite you will -probably be unable to climb back up again. -#300 -Dead end. -#301 -Welcome to adventure.! Would you like instructions? -#302 -Somewhere nearby is Colossal Cave, where others have found fortunes in -treasure and gold, though it is rumored that some who enter are never -seen again. Magic is said to work in the cave. I will be your eyes -and hands. Direct me with commands such as get, take, or look. Since you -need to move around, you must usually enter compass directions -(N,NE,E,SE,S,SW,W,NW or up or down), although you may sometimes use -more vague verbs with with to move. I know of several objects in this -game, such as a lamp and a bottle. I also know of special objects in the -cave. Some of these objects have side effects, ie there is a rod in the -cave that scares a little bird. ------------------------------------------------------------------ -The object of this game is to gather as many treasures as you can and -put them back in the house. You also run the risk of getting robbed or -killed by some rather unfriendly inhabitants of the cave. ----------------------------------------------------------------- -There are some useful commands that you should know about: ---- Brief, Long, and Short - these commands control the amount of ---- detail you get in descriptions. ---- Look (or L) - gives a detailled descriptio nof your surroundings. ---- Inventory (or I) tells you what you are carrying. ---- Quit, Stop or End - these are self-explanatory. ---- Save - by typing this, you may save your game and continue it at ---- a later time. ---- Continue - lets you continue an old game. ---- Score - tells you hw well you're doing. - -Other helpful functions include: -* multiple commands on one input line, ie: -"Go south, get car, keys, Start car." (example only) -** oops, the PyBasic version doesn't support multiple commands :( --------------------------------------------------------------- -"Adventure" is a version of the game created by Willy Crowther and -Dan Woods at M.I.T. --------------------------------------------------------------- -#303 -I'm sorry, but the magazine is written in Dwarvish. -# -# -# \ No newline at end of file From 695d4493bf6d849d5b414a6050b4e5873b196e16 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:49:12 +0100 Subject: [PATCH 082/183] Moved to examples --- AMOVING | 100 -------------------------------------------------------- 1 file changed, 100 deletions(-) delete mode 100644 AMOVING diff --git a/AMOVING b/AMOVING deleted file mode 100644 index 408591f..0000000 --- a/AMOVING +++ /dev/null @@ -1,100 +0,0 @@ -3,0,6,0,4,0,2,0,0,0 -1,0,8,0,2,0,4,0,0,0 -2,0,1,0,4,0,7,0,0,0 -1,0,3,0,5,0,2,0,0,0 -2,0,6,0,4,0,1,0,0,0 -2,0,7,0,8,0,5,0,0,0 -0,0,0,0,0,0,6,0,0,0 -6,0,3,0,9,0,4,0,0,0 -8,0,3,0,10,0,4,0,0,0 -9,0,0,0,11,0,0,0,0,11 -0,0,10,0,0,0,12,0,10,0 -0,0,11,0,0,0,13,0,0,0 -0,0,12,0,0,0,14,0,14,0 -0,0,13,0,0,0,15,0,15,13 -0,0,14,0,0,0,16,0,0,0 -0,0,15,0,0,0,0,0,0,17 -22,0,0,0,18,0,19,0,16,22 -17,0,0,0,0,0,0,0,0,0 -0,0,17,0,0,0,20,0,0,0 -0,0,19,0,0,0,21,0,0,0 -20,0,20,0,88,0,29,0,0,0 -25,0,17,0,23,83,24,0,17,0 -22,0,0,0,0,0,0,0,0,0 -0,0,22,0,0,0,31,0,31,0 -26,0,0,0,22,0,0,0,0,33 -0,0,28,0,25,0,27,0,0,0 -0,0,26,0,0,0,0,0,0,0 -0,0,0,0,0,26,0,0,0,0 -31,0,21,0,0,0,30,0,21,31 -31,0,29,0,94,0,0,0,0,0 -32,0,24,0,30,0,29,0,0,0 -31,0,0,0,0,0,0,0,0,0 -0,0,34,0,0,0,36,0,25,0 -0,0,0,0,0,0,33,0,0,35 -0,0,0,0,0,0,0,0,34,0 -0,0,33,0,0,0,0,0,25,37 -40,0,38,0,0,0,44,0,36,0 -0,0,39,0,0,0,37,0,37,0 -255,255,255,255,255,255,255,255,255,255 -0,0,0,0,37,0,0,0,41,42 -0,0,0,0,0,0,0,0,0,43 -0,0,0,0,0,0,0,0,40,43 -0,0,0,0,0,0,0,0,42,0 -255,0,37,0,255,0,45,0,255,255 -0,44,46,0,0,0,47,52,0,0 -0,0,0,0,0,0,45,0,0,0 -0,0,45,0,0,0,48,0,0,49 -0,0,47,0,0,0,51,0,0,50 -0,0,0,0,0,0,0,0,47,0 -0,0,0,0,0,0,0,0,48,0 -44,0,0,0,48,0,0,0,79,0 -56,0,0,45,0,0,53,0,0,0 -55,0,0,52,0,54,0,0,0,0 -0,0,0,0,0,0,0,0,60,53 -0,0,0,0,53,0,0,0,0,0 -0,0,0,0,52,0,57,0,0,0 -0,0,58,0,0,0,0,56,0,0 -0,59,0,0,0,0,57,0,0,0 -0,0,0,0,58,0,0,0,0,0 -0,61,0,0,0,54,0,0,0,54 -0,62,0,0,0,60,0,0,0,0 -0,0,63,0,0,0,61,0,0,0 -0,65,0,64,0,0,62,0,0,0 -63,0,0,0,68,0,0,0,0,0 -67,0,66,0,63,0,0,0,0,0 -0,0,0,0,0,0,65,0,0,0 -0,0,0,0,65,0,0,0,0,0 -0,0,69,0,0,0,64,0,0,0 -0,0,0,0,0,0,68,0,0,0 -0,0,50,0,0,0,71,0,0,50 -73,0,70,0,72,0,0,0,0,0 -0,71,0,0,0,0,0,0,0,0 -74,0,0,0,71,0,0,0,0,0 -0,0,0,0,73,0,75,0,0,0 -74,0,0,0,0,0,0,0,0,53 -78,0,0,0,77,0,0,0,0,45 -76,0,0,0,0,0,0,0,0,93 -27,0,0,44,76,0,0,0,0,0 -80,0,0,0,82,0,0,0,0,51 -81,0,0,0,79,0,0,0,0,0 -0,0,0,0,80,0,0,0,0,0 -79,0,83,0,0,0,0,0,0,0 -0,0,22,0,0,0,82,0,0,84 -86,0,0,0,85,0,0,0,0,0 -84,0,0,0,0,0,0,0,0,0 -45,0,84,0,0,0,87,0,0,0 -0,0,0,86,0,0,0,0,0,0 -89,82,91,95,90,83,92,91,95,94 -95,96,91,90,89,88,91,95,93,97 -92,92,91,93,88,97,88,96,99,87 -91,89,94,90,93,95,89,92,0,0 -95,97,93,96,94,95,21,92,0,0 -90,96,92,94,88,30,92,90,0,0 -98,89,90,91,89,93,95,97,0,0 -93,94,96,92,97,95,94,92,0,0 -92,99,97,96,89,94,90,90,91,92 -98,89,92,91,90,93,94,95,96,98 -95,90,93,91,94,92,90,99,98,15 -88,89,93,97,100,89,96,95,0,0 -0,0,0,0,99,0,0,0,0,0 From 961411183878cabc364bd300fd91531e7b4a8131 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:49:26 +0100 Subject: [PATCH 083/183] Moved to examples --- BITEMS | 137 --------------------------------------------------------- 1 file changed, 137 deletions(-) delete mode 100644 BITEMS diff --git a/BITEMS b/BITEMS deleted file mode 100644 index 00f0731..0000000 --- a/BITEMS +++ /dev/null @@ -1,137 +0,0 @@ -100, N -101, NE -102, E -103, SE -104, S -105, SW -106, W -107, NW -108, U -109, D -110, PLUGH -111, XYZZY -112, PLOVER -113, CROSS -114, CLIMB -115, JUMP -116, FILL -117, EMPTY -117, POUR -118, LOOK -118, L -119, LIGHT -119, ON -120, EXTINGUISH -120, OFF -121, IN -121, ENTER -122, LEAVE -122, OUT -123, INVENTORY -123, I -124, GET -124, CATCH -124, TAKE -125, DROP -125, DUMP -47, ALL -47, EVERYTHING -126, THROW -127, ATTACK -127, KILL -128, FEED -129, WATER -130, LOCK -131, UNLOCK -132, FREE -132, RELEASE -133, WAVE -134, OPEN -135, CLOSE -136, OIL -137, EAT -138, DRINK -139, FEE FIE FOE FOO -140, SHORT -141, LONG -142, BRIEF -143, QUIT -143, STOP -143, END -144, SCORE -145, SAVE -146, LOAD -147, READ -147, EXAMINE -148, YES -148, Y -149, BUG -1, GOLD -1, NUGGET -2, BARS -2, SILVER -3, JEWELRY -4, COINS -5, DIAMONDS -6, MING -6, VASE -7, PEARL -8, EGGS -8, NEST -9, TRIDENT -10, EMERALD -11, PLATINUM -11, PYRAMID -12, CHAIN -13, SPICES -14, PERSIAN -14, RUG -15, TREASURE -15, CHEST -16, WATER -17, OIL -18, LAMP -18, LANTERN -19, KEYS -20, FOOD -21, BOTTLE -22, CAGE -23, ROD -23, WAND -24, CLAM -25, MAGAZINE -26, BEAR -27, AXE -28, VELVET -28, PILLOW -29, SHARDS -30, OYSTER -31, BIRD -32, TROLL -33, DRAGON -34, SNAKE -35, DWARF -36, ROCK -36, BOULDER -37, STAIRS -38, STEPS -39, HOUSE -39, BUILDING -40, GRATE -41, STREAM -42, ROOM -43, BRIDGE -44, PIT -45, VOLCANO -46, ROAD -100, NORTH -101, NORTHEAST -102, EAST -103, SOUTHEAST -104, SOUTH -105, SOUTHWEST -106, WEST -107, NORTHWEST -108, UP -109, DOWN -150,* From dd5043d18a8262fff9337fbe9796433dc1c48691 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:49:43 +0100 Subject: [PATCH 084/183] Moved to examples --- PyBStartrek.bas | 688 ------------------------------------------------ 1 file changed, 688 deletions(-) delete mode 100644 PyBStartrek.bas diff --git a/PyBStartrek.bas b/PyBStartrek.bas deleted file mode 100644 index c1670f6..0000000 --- a/PyBStartrek.bas +++ /dev/null @@ -1,688 +0,0 @@ -520 REM -530 REM -540 REM **** **** STAR TREK **** **** -550 REM **** Simulation of a mission of the starship ENTERPRISE -560 REM **** as seen on the Star Trek tv show -570 REM **** Original program in Creative Computing -580 REM **** Basic Computer Games by Dave Ahl -590 REM **** Modifications by Bob Fritz and Sharon Fritz -600 REM **** for the IBM Personal Computer, October-November 1981 -610 REM **** Bob Fritz, 9915 Caninito Cuadro, San Diego, Ca., 92129 -620 REM **** (714) 484-2955 -630 REM **** -640 PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT -650 E1$= " ,-----------------, _" -660 E2$= " `---------- ----',-----/ \----," -670 E3$= " | | '- --------'" -680 E4$= " ,--' '------/ /--," -690 E5$= " '-----------------'" -691 PRINT -700 E6$= " THE USS ENTERPRISE --- NCC-1701" -720 PRINT E1$ -730 PRINT E2$ -740 PRINT E3$ -750 PRINT E4$ -760 PRINT E5$ -770 PRINT -780 PRINT E6$ -790 PRINT:PRINT: PRINT:PRINT:PRINT:PRINT:PRINT -811 RANDOMIZE -960 dim D(8) -961 dim C(9,2) -962 dim K(3,3) -963 dim N(3) -965 DIM G(8,8) -966 DIM Z(8,8) -970 T=INT(RND(1)*20+20)*100:T0=T:T9=25+INT(RND(1)*10):D0=0:E=3000:E0=E -980 P=10:P0=P:S9=200:S=0:B9=0:K9=0:X$="":X0$=" is " -990 rem DEF FND(D)=SQR((K(I,1)-S1)^2+(K(I,2)-S2)^2) -1000 rem DEF FNR(R)=INT(RND(1)(R)*7.98+1.01) -1010 REM initialize enterprise's position -1020 Q1=INT(RND(1)*7.98+1.01):Q2=INT(RND(1)*7.98+1.01):S1=INT(RND(1)*7.98+1.01):S2=INT(RND(1)*7.98+1.01) -1030 FOR I=1 TO 9 -1031 C(I,1)=0:C(I,2)=0 -1032 NEXT I -1040 C(3,1)=-1:C(2,1)=-1:C(4,1)=-1:C(4,2)=-1:C(5,2)=-1:C(6,2)=-1 -1050 C(1,2)=1:C(2,2)=1:C(6,1)=1:C(7,1)=1:C(8,1)=1:C(8,2)=1:C(9,2)=1 -1060 FOR I=1 TO 8 -1061 D(I)=0 -1062 NEXT I -1070 A1$="NAVSRSLRSPHATORSHIDAMCOMRES" -1080 REM set up what exists in galaxy -1090 REM k3=#klingons b3=#starbases s3=#stars -1100 FOR I=1 TO 8 -1101 FOR J=1 TO 8 -1102 K3=0:Z(I,J)=0:R1=RND(1) -1110 IF R1<=0.9799999 THEN goto 1120 -1111 K3=3:K9=K9+3: GOTO 1140 -1120 IF R1<=0.95 THEN goto 1130 -1121 K3=2:K9=K9+2: GOTO 1140 -1130 IF R1<=0.8 THEN goto 1140 -1131 K3=1:K9=K9+1 -1140 B3=0:IF RND(1)<=0.96 THEN goto 1150 -1141 B3=1:B9=B9+1 -1150 G(I,J)=K3*100+B3*10+INT(RND(1)*7.98+1.01) -1151 NEXT J -1152 NEXT I -1153 IF K9<=T9 THEN goto 1160 -1154 T9=K9+1 -1160 IF B9<>0 THEN 1190 -1170 IF G(Q1,Q2)>=200 THEN 1180 -1171 G(Q1,Q2)=G(Q1,Q2)+100:K9=K9+1 -1180 B9=1:G(Q1,Q2)=G(Q1,Q2)+10:Q1=INT(RND(1)*7.98+1.01):Q2=INT(RND(1)*7.98+1.01) -1190 K7=K9:IF B9=1 THEN 1200 -1191 X$="s":X0$=" are " -1200 PRINT" Your orders are as follows: " -1210 PRINT" Destroy the ";K9;" Klingon warships which have invaded" -1220 PRINT" the galaxy before they can attack Federation headquarters" -1230 PRINT" on stardate ";T0+T9;". This gives you ";T9;" days. there";X0$ -1240 PRINT" ";B9;" starbase";X$;" in the galaxy for resupplying your ship" -1261 PRINT:INPUT"hit return when ready to accept command";i$ -1270 REM here any time new quadrant entered -1280 Z4=Q1:Z5=Q2:K3=0:B3=0:S3=0:G5=0:D4=0.5*RND(1):Z(Q1,Q2)=G(Q1,Q2) -1290 IF Q1<1 OR Q1>8 OR Q2<1 OR Q2>8 THEN 1410 -1300 GOSUB 5040 -1301 PRINT:IF T0 <>T THEN 1330 -1310 PRINT"Your mission begins with your starship located" -1320 PRINT"in the galactic quadrant, '";G2$;"'.":GOTO 1340 -1330 PRINT"Now entering ";G2$;" quadrant. . ." -1340 PRINT:K3=INT(G(Q1,Q2)*0.01):B3=INT(G(Q1,Q2)*0.1)-10*K3 -1350 S3=G(Q1,Q2)-100*K3-10*B3:IF K3=0 THEN 1400 -1360 PRINT "COMBAT AREA!! Condition"; -1370 PRINT " RED "; : PRINT -1380 GOSUB 5290 -1381 IF S>200 THEN 1400 -1390 PRINT" SHIELDS DANGEROUSLY LOW"; : PRINT -1400 FOR I=1 TO 3 -1401 K(I,1)=0:K(I,2)=0 -1402 NEXT I -1410 FOR I=1 TO 3 -1411 K(I,3)=0 -1412 NEXT I -1413 Q$=" "*192 -1420 REM position enterprise in quadrant, then place "k3" klingons,& -1430 REM "b3" starbases & "s3" stars elsewhere. -1440 A$="\e/":Z1=S1:Z2=S2 -1441 GOSUB 4830 -1442 IF K3<1 THEN 1470 -1450 FOR I=1 TO K3 -1451 GOSUB 4800 -1452 A$=chr$(187)+"K"+chr$(171):Z1=R1:Z2=R2 -1460 GOSUB 4830 -1461 K(I,1)=R1:K(I,2)=R2:K(I,3)=S9*(0.5+RND(1)) -1462 NEXT I -1470 IF B3<1 THEN 1500 -1480 GOSUB 4800 -1481 A$="("+chr$(174)+")":Z1=R1:B4=R1:Z2=R2:B5=R2 -1490 GOSUB 4830 -1500 FOR I=1 TO S3 -1501 GOSUB 4800 -1502 A$=" * ":Z1=R1:Z2=R2 -1503 GOSUB 4830 -1504 NEXT I -1510 GOSUB 3720 -1520 IF S+E<=10 THEN 1530 -1521 IF E>10 OR D(7)>=0 THEN 1580 -1530 PRINT"*** FATAL ERROR ***"; -1531 GOSUB 5290 -1540 PRINT"You've just stranded your ship in " -1550 PRINT"space":PRINT"You have insufficient maneuvering energy,"; -1560 PRINT" and shield control":PRINT"is presently incapable of cross"; -1570 PRINT"-circuiting to engine room!!":GOTO 3480 -1580 INPUT"command ";A$ -1590 FOR I=1 TO 9 -1591 IF MID$(A$,1,3)<> MID$(A1$,3*I-2,3) THEN 1610 -1600 ON I GOTO 1720,1510,2440,2530,2750,3090,3180,3980,3510 -1610 NEXT I -1611 PRINT"Enter one of the following:" -1620 PRINT" NAV (to set course)" -1630 PRINT" SRS (for short range sensor scan)" -1640 PRINT" LRS (for long range sensor scan)" -1650 PRINT" PHA (to fire phasers)" -1660 PRINT" TOR (to fire photon torpedoes)" -1670 PRINT" SHI (to raise or lower shields)" -1680 PRINT" DAM (for damage control reports)" -1690 PRINT" COM (to call on library-computer)" -1700 PRINT" RES (to resign your command)":PRINT:GOTO 1520 -1710 REM course control begins here -1720 INPUT"Course (1-9) ";C2$:C1=VAL(C2$):IF C1<>9 THEN 1730 -1721 C1=1 -1730 IF C1>=1 AND C1<9 THEN 1750 -1740 PRINT" Lt. Sulu reports, 'Incorrect course data, sir!'":GOTO 1520 -1750 X$="8":IF D(1)>=0 THEN 1760 -1751 X$="0.2" -1760 PRINT"Warp factor(0-";X$;") ";:INPUT C2$:W1=VAL(C2$):IF D(1)<0 AND W1>0.2 THEN 1810 -1770 IF W1>0 AND W1<8 THEN 1820 -1780 IF W1=0 THEN 1520 -1790 PRINT" Chief Engineer Scott reports 'The engines won't take"; -1800 PRINT" warp ";W1;"!":GOTO 1520 -1810 PRINT"Warp engines are damaged. Maximum speed = warp 0.2":GOTO 1520 -1820 N1=INT(W1*8+0.5):IF E-N1>=0 THEN 1900 -1830 PRINT"Engineering reports 'Insufficient energy available" -1840 PRINT" for maneuvering at warp ";W1;"!'" -1850 IF S=0 THEN 1990 -1950 D(I)=min(0,D(I)+D6):IF D(I)<=-0.1 or D(I)>=0 THEN 1960 -1951 D(I)=-0.1 -1952 GOTO 1990 -1960 IF D(I)<0 THEN 1990 -1970 IF D1=1 THEN 1980 -1971 D1=1:PRINT"DAMAGE CONTROL REPORT: "; -1980 PRINT TAB(8);:R1=I -1981 GOSUB 4890 -1982 PRINT G2$;" Repair completed." -1990 NEXT I -1991 IF RND(1)>0.2 THEN 2070 -2000 R1=INT(RND(1)*7.98+1.01):IF RND(1)>=0.6 THEN 2040 -2010 IF K3=0 THEN 2070 -2020 D(R1)=D(R1)-(RND(1)*5+1):PRINT"DAMAGE CONTROL REPORT: "; -2030 GOSUB 4890 -2031 PRINT G2$;" damaged":PRINT:GOTO 2070 -2040 D(R1)=min(0,D(R1)+RND(1)*3+1):PRINT"DAMAGE CONTROL REPORT: "; -2050 GOSUB 4890 -2051 PRINT G2$;" State of repair improved":PRINT -2060 REM begin moving starship -2070 A$=" " :Z1=INT(S1):Z2=INT(S2) -2071 GOSUB 4830 -2079 Z1=INT(C1):C1=C1-Z1 -2080 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1:X=S1:Y=S2 -2090 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1:Q4=Q1:Q5=Q2 -2100 FOR I=1 TO N1 -2101 S1=S1+X1:S2=S2+X2:IF S1<1 OR S1>=9 OR S2<1 OR S2>=9 THEN 2220 -2110 S8=INT(S1)*24+INT(S2)*3-26:IF MID$(Q$,S8+1,2)=" " THEN 2140 -2120 S1=INT(S1-X1):S2=INT(S2-X2):PRINT"Warp engines shut down at "; -2130 PRINT "sector ";S1;",";S2;" due to bad navigation.":GOTO 2150 -2140 NEXT I -2141 S1=INT(S1):S2=INT(S2) -2150 A$="\e/" -2160 Z1=INT(S1):Z2=INT(S2) -2161 GOSUB 4830 -2162 GOSUB 2390 -2163 T8=1 -2170 IF W1>=1 THEN 2180 -2171 T8=0.1*INT(10*W1) -2180 T=T+T8:IF T>T0+T9 THEN 3480 -2190 REM see if docked then get command -2200 GOTO 1510 -2210 REM exceeded quadrant limits -2220 X=8*Q1+X+N1*X1:Y=8*Q2+Y+N1*X2:Q1=INT(X/8):Q2=INT(Y/8):S1=INT(X-Q1*8) -2230 S2=INT(Y-Q2*8):IF S1<>0 THEN 2240 -2231 Q1=Q1-1:S1=8 -2240 IF S2<>0 THEN 2250 -2241 Q2=Q2-1:S2=8 -2250 X5=0:IF Q1>=1 THEN 2260 -2251 X5=1:Q1=1:S1=1 -2260 IF Q1<=8 THEN 2270 -2261 X5=1:Q1=8:S1=8 -2270 IF Q2>=1 THEN 2280 -2271 X5=1:Q2=1:S2=1 -2280 IF Q2<=8 THEN 2290 -2281 X5=1:Q2=8:S2=8 -2290 IF X5=0 THEN 2360 -2300 PRINT"Lt. Uhura reports message from Starfleet Command:" -2310 PRINT" 'Permission to attempt crossing of galactic perimeter" -2320 PRINT" is hereby *DENIED*. Shut down your engines.'" -2330 PRINT"Chief Engineer Scott reports 'Warp engines shut down" -2340 PRINT" at sector ";S1;",";S2;" of quadrant ";Q1;",";Q2".'" -2350 IF T>T0 THEN 3480 -2360 IF 8*Q1+Q2=8*Q4+Q5 THEN 2150 -2370 T=T+1 -2371 GOSUB 2390 -2372 GOTO 1280 -2380 REM maneuver energy s/r ** -2390 E=E-N1-10:IF E<=0 THEN 2400 -2391 RETURN -2400 PRINT"Shield control supplies energy to complete the maneuver." -2410 S=S+E:E=0:IF S<=0 THEN 2420 -2411 S=0 -2420 RETURN -2430 REM long range sensor scan code -2440 IF D(3)>=0 THEN 2450 -2441 PRINT"Long Range Sensors are inoperable":GOTO 1520 -2450 PRINT"Long Range Scan for quadrant ";Q1;",";Q2 -2460 PRINT "-"*19 -2470 FOR I=Q1-1 TO Q1+1 -2471 N(1)=-1:N(2)=-2:N(3)=-3 -2472 FOR J=Q2-1 TO Q2+1 -2480 IF I<=0 or I>=9 or J<=0 or J>=9 THEN 2490 -2481 N(J-Q2+2)=G(I,J) -2482 REM added so long range sensor scans are added to computer database -2483 z(i,j)=g(i,j) -2490 NEXT J -2491 FOR L=1 TO 3 -2492 PRINT"| "; -2493 IF N(L)>=0 THEN 2500 -2494 PRINT"*** ";:GOTO 2510 -2500 PRINT MID$(STR$(N(L)+1000),2,3);" "; -2510 NEXT L -2511 PRINT"|" -2512 PRINT "-"*19 -2513 NEXT I -2514 GOTO 1520 -2520 REM phaser control code begins here -2530 IF D(4)>=0 THEN 2540 -2531 PRINT"Phasers Inoperative":GOTO 1520 -2540 IF K3>0 THEN 2570 -2550 PRINT"Science Officer Spock reports 'Sensors show no enemy ships" -2560 PRINT" in this quadrant'":GOTO 1520 -2570 IF D(8)>=0 THEN 2580 -2571 PRINT"Computer failure hampers accuracy" -2580 PRINT"Phasers locked on target "; -2590 PRINT"Energy available = ";E;" units" -2600 INPUT"Numbers of units to fire ";X:IF X<=0 THEN 1520 -2610 IF E-X<0 THEN 2590 -2620 E=E-X -2621 GOSUB 5420 -2622 IF D(7)<0 THEN 2630 -2623 X=X*RND(1) -2630 H1=INT(X/K3) -2631 FOR I=1 TO 3 -2632 IF K(I,3)<=0 THEN 2730 -2640 KSQ1 = (K(I,1)-S1)*(K(I,1)-S1) -2641 KSQ2 = (K(I,2)-S2)*(K(I,2)-S2) -2642 H= SQR( KSQ1 + KSQ2 ) -2646 H = H1 / H -2647 H = INT(H * (RND(1)+2)) -2648 IF H>0.15*K(I,3) THEN 2660 -2650 PRINT"Sensors show no damage to enemy at ";K(I,1);",";K(I,2):GOTO 2730 -2660 K(I,3)=K(I,3)-H:PRINT H;"Unit hit on Klingon at sector ";K(I,1);","; -2670 PRINT K(I,2):IF K(I,3)> 0 THEN GOTO 2700 -2680 PRINT "**** KLINGON DESTROYED ****" -2690 GOTO 2710 -2700 PRINT" (Sensors show ";K(I,3);" units remaining)":GOTO 2730 -2710 K3=K3-1:K9=K9-1:Z1=K(I,1):Z2=K(I,2):A$=" " -2711 GOSUB 4830 -2720 K(I,3)=0:G(Q1,Q2)=G(Q1,Q2)-100:Z(Q1,Q2)=G(Q1,Q2):IF K9<=0 THEN 3680 -2730 NEXT I -2731 GOSUB 3350 -2732 GOTO 1520 -2740 REM photon torpedo code begins here -2750 IF P>0 THEN 2760 -2751 PRINT"All photon torpedoes expended":GOTO 1520 -2760 IF D(5)>=0 THEN 2770 -2761 PRINT"Photon tubes are not operational":GOTO 1520 -2770 INPUT"Photon torpedo course (1-9) ";C2$:C1=VAL(C2$):IF C1<>9 THEN 2780 -2771 C1=1 -2780 IF C1>=1 AND C1<9 THEN 2810 -2790 PRINT"Ensign Chekov reports, 'Incorrect course data, sir!'" -2800 GOTO 1520 -2810 Z1=INT(C1):C1=C1-Z1 -2811 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1:E=E-2:P=P-1 -2820 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1:X=S1:Y=S2 -2821 GOSUB 5360 -2830 PRINT"Torpedo track:" -2840 X=X+X1:Y=Y+X2:X3=INT(X+0.5):Y3=INT(Y+0.5) -2850 IF X3<1 OR X3>8 OR Y3<1 OR Y3>8 THEN 3070 -2860 PRINT" ";X3;",";Y3:A$=" ":Z1=X:Z2=Y -2861 GOSUB 4990 -2870 IF Z3<>0 THEN 2840 -2880 A$=chr$(187)+"K"+chr$(171):Z1=X:Z2=Y -2881 GOSUB 4990 -2882 IF Z3=0 THEN 2940 -2890 PRINT"**** KLINGON DESTROYED ****" -2900 K3=K3-1:K9=K9-1:IF K9<=0 THEN 3680 -2910 FOR I=1 TO 3 -2911 IF X3=K(I,1) AND Y3=K(I,2) THEN 2930 -2920 NEXT I -2921 I=3 -2930 K(I,3)=0:GOTO 3050 -2940 A$=" * ":Z1=X:Z2=Y -2941 GOSUB 4990 -2942 IF Z3=0 THEN 2960 -2950 PRINT"Star at ";X3;",";Y3;" absorbed torpedo energy." -2951 GOSUB 3350 -2952 GOTO 1520 -2960 A$="("+chr$(174)+")":Z1=X:Z2=Y -2961 GOSUB 4990 -2962 IF Z3<>0 THEN 2970 -2963 PRINT "Torpedo absorbed by unknown object at ";x3;",";y3 -2964 goto 1520 -2970 PRINT"*** STARBASE DESTROYED ***" -2980 B3=B3-1 : B9=B9-1 -2990 IF B9>0 OR K9>T-T0-T9 THEN 3030 -3000 PRINT"THAT DOES IT, CAPTAIN!! You are hereby relieved of command" -3010 PRINT"and sentenced to 99 stardates of hard labor on CYGNUS 12!!" -3020 GOTO 3510 -3030 PRINT"Starfleet reviewing your record to consider" -3040 PRINT"court martial!":D0=0 -3050 Z1=X:Z2=Y:A$=" " -3051 GOSUB 4830 -3060 G(Q1,Q2)=K3*100+B3*10+S3:Z(Q1,Q2)=G(Q1,Q2) -3061 GOSUB 3350 -3062 GOTO 1520 -3070 PRINT"Torpedo missed" -3071 GOSUB 3350 -3072 GOTO 1520 -3080 REM shield control -3090 IF D(7)>=0 THEN 3100 -3091 PRINT"Shield control inoperable":GOTO 1520 -3100 PRINT"Energy available = ";E+S :INPUT "Number of units to shields? ";X -3110 IF X>=0 and S<>X THEN 3120 -3111 PRINT"":GOTO 1520 -3120 IF X":goto 1990 -3150 E=E+S-X:S=X:PRINT"Deflector Control Room report:" -3160 PRINT" 'Shields now at ";INT(S);" units per your command.'":GOTO 1520 -3170 REM damage control -3180 IF D(6)>=0 THEN 3290 -3190 PRINT"Damage control report not available":IF D0=0 THEN 1520 -3200 D3=0:FOR I=1 TO 8 -3201 IF D(I)>=0 THEN 3210 -3202 D3=D3+1 -3210 NEXT I -3211 IF D3=0 THEN 1520 -3220 PRINT:D3=D3+D4:IF D3<1 THEN 3230 -3221 D3=0.9 -3230 PRINT"Technicians standing by to effect repairs to your ship;" -3240 PRINT"estimated time to repair: ";0.01*INT(100*D3);" stardates" -3250 INPUT"Will you authorize the repair order (Y/N)? ";A$ -3260 IF A$<>"y" AND A$<> "Y" THEN 1520 -3270 FOR I=1 TO 8 -3271 IF D(I)>=0 THEN 3280 -3272 D(I)=0 -3280 NEXT I -3281 T=T+D3+0.1 -3290 PRINT:PRINT"Device state of repair" -3291 FOR R1=1 TO 8 -3300 GOSUB 4890 -3301 PRINT G2$;tab(25); -3310 GG2=INT(D(R1)*100)*0.01:PRINT GG2 -3320 NEXT R1 -3321 PRINT:IF D0<>0 THEN 3200 -3330 GOTO 1520 -3340 REM klingons shooting -3350 IF K3>0 THEN 3360 -3351 RETURN -3360 IF D0=0 THEN 3370 -3361 PRINT"Starbase shields protect the ENTERPRISE" -3362 RETURN -3370 FOR I=1 TO 3 -3371 IF K(I,3)<=0 THEN 3460 -3380 ksq1 = (K(I,1)-S1)*(K(I,1)-S1) -3381 ksq2 = (K(I,2)-S2)*(K(I,2)-S2) -3382 H=( K(I,3) / SQR( ksq1 + ksq2 ) )*(2+RND(1)) -3383 h = int(h) -3384 S=S-H:K(I,3)=K(I,3)/(3+RND(1)) -3390 PRINT "ENTERPRISE HIT!" -3400 GOSUB 5480 -3401 PRINT H;" Unit hit on ENTERPRISE from sector ";K(I,1);",";K(I,2) -3410 IF S<=0 THEN 3490 -3420 PRINT" ":IF H<20 THEN 3460 -3430 IF RND(1)>0.6 OR H/S<=0.02 THEN 3460 -3440 R1=INT(RND(1)*7.98+1.01):D(R1)=D(R1)-H/S-0.5*RND(1) -3441 GOSUB 4890 -3450 PRINT"Damage control reports '";G2$;" damaged by the hit'" -3460 NEXT I -3461 RETURN -3470 REM end of game -3480 PRINT"It is stardate";T:GOTO 3510 -3490 PRINT:PRINT"the ENTERPRISE has been destroyed. The Federation "; -3500 PRINT"will be conquered":GOTO 3480 -3510 PRINT"There were ";K9;" Klingon battle cruisers left at" -3520 PRINT"the end of your mission" -3530 PRINT:PRINT:IF B9=0 THEN 3670 -3540 PRINT"The Federation is in need of a new starship commander" -3550 PRINT"for a similar mission -- if there is a volunteer," -3560 INPUT"let him or her step forward and enter 'AYE' ";X$:IF X$="AYE" THEN 520 -3670 END -3680 PRINT"Congratulations, Captain! the last Klingon battle cruiser" -3690 PRINT"menacing the Federation has been destroyed.":PRINT -3700 PRINT"Your efficiency rating is ";:cc1 = k7/(t-t0):PRINT 1000*cc1*cc1:GOTO 3530 -3710 REM short range sensor scan & startup subroutine -3720 A$="("+chr$(174)+")":Z3=0 -3721 FOR I=S1-1 TO S1+1 -3722 FOR J=S2-1 TO S2+1 -3730 IF INT(I+0.5)<1 OR INT(I+0.5)>8 OR INT(J+0.5)<1 OR INT(J+0.5)>8 or Z3=1 THEN 3760 -3750 Z1=I:Z2=J -3751 GOSUB 4990 -3760 NEXT J -3761 NEXT I -3762 IF Z3=1 THEN 3770 -3763 D0=0:GOTO 3790 -3770 D0=1:CC$="docked":E=E0:P=P0 -3780 PRINT"Shields dropped for docking purposes":S=0:GOTO 3810 -3790 IF K3<=0 THEN 3800 -3791 C$="*red*":GOTO 3810 -3800 C$="GREEN":IF E>=E0*0.1 THEN 3810 -3801 C$="YELLOW" -3810 IF D(2)>=0 THEN 3830 -3820 PRINT:PRINT"*** Short Range Sensors are out ***":PRINT -3821 RETURN -3830 PRINT "-"*33 -3832 FOR I=1 TO 8 -3840 FOR J=(I-1)*24 TO (I-1)*24+21 STEP 3 -3850 IF MID$(Q$,J+1,3)<>" " THEN 3860 -3851 PRINT " . ";:GOTO 3861 -3860 PRINT " ";MID$(Q$,J+1,3); -3861 NEXT J -3870 ON I GOTO 3880,3900,3910,3920,3930,3940,3950,3960 -3880 PRINT" Stardate "; -3890 TT= T*10 : TT=INT(TT)*0.1:PRINT TT :GOTO 3970 -3900 PRINT" Condition ";C$:GOTO 3970 -3910 PRINT" Quadrant ";Q1;",";Q2:GOTO 3970 -3920 PRINT" Sector ";S1;",";S2:GOTO 3970 -3930 PRINT" Photon torpedoes ";INT(P):GOTO 3970 -3940 PRINT" Total energy ";INT(E+S):GOTO 3970 -3950 PRINT" Shields ";INT(S):GOTO 3970 -3960 PRINT" Klingons remaining ";INT(K9) -3970 NEXT I -3971 PRINT "-"*33 -3972 RETURN -3980 REM library computer code -3990 CM1$="GALSTATORBASDIRREG" -4000 IF D(8)>=0 THEN 4010 -4001 PRINT"Computer Disabled":GOTO 1520 -4010 rem KEY 1, "GAL RCD"+CHR$(13) -4020 rem KEY 2, "STATUS"+CHR$(13) -4030 rem KEY 3, "TOR DATA"+CHR$(13) -4040 rem KEY 4, "BASE NAV"+CHR$(13) -4050 rem KEY 5, "DIR/DIST"+CHR$(13) -4060 rem KEY 6, "REG MAP"+CHR$(13) -4070 rem KEY 7,CHR$(13):KEY 8,CHR$(13):KEY 9,CHR$(13):KEY 10,CHR$(13) -4071 gosub 4130 -4080 INPUT"Computer active and awaiting command ";CM$:H8=1 -4090 FOR K1= 1 TO 6 -4100 IF MID$(CM$,1,3)<>MID$(CM1$,3*K1-2,3) THEN 4120 -4110 ON K1 GOTO 4230,4400,4490,4750,4550,4210 -4120 NEXT K1 -4121 gosub 4130 -4122 goto 4080 -4130 PRINT"Functions available from library-computer:" -4140 PRINT" GAL = Cumulative galactic record" -4150 PRINT" STA = Status report" -4160 PRINT" TOR = Photon torpedo data" -4170 PRINT" BAS = Starbase nav data" -4180 PRINT" DIR = Direction/distance calculator" -4190 PRINT" REG = Galaxy 'region name' map":PRINT -4191 return -4200 REM setup to change cum gal record to galaxy map -4210 GOSUB 840 -4211 H8=0:G5=1:PRINT" the galaxy":GOTO 4290 -4250 GOSUB 840 -4260 PRINT:PRINT" "; -4270 PRINT "Computer record of galaxy for quadrant ";Q1;",";Q2 -4280 PRINT -4290 PRINT" 1 2 3 4 5 6 7 8" -4300 O1$=" ----- ----- ----- ----- ----- ----- ----- -----" -4310 PRINT O1$ -4311 FOR I=1 TO 8 -4312 PRINT I," ";:IF H8=0 THEN 4350 -4320 FOR J=1 TO 8 -4321 PRINT" ";:IF Z(I,J)<>0 THEN 4330 -4322 PRINT"***";:GOTO 4340 -4330 ZLEN = len(str$(z(i,j)+1000) -4331 PRINT MID$(STR$(Z(I,J)+1000),zlen-2,3); -4340 NEXT J -4341 GOTO 4370 -4350 Z4=I:Z5=1 -4351 GOSUB 5040 -4352 J0=INT(15-0.5*LEN(G2$)):PRINT TAB(J0);G2$; -4360 Z5=5 -4361 GOSUB 5040 -4362 J0=INT(40-0.5*LEN(G2$)):PRINT TAB(J0);G2$; -4370 PRINT:PRINT O1$ -4371 NEXT I -4372 PRINT -4373 rem 'POKE 1229,0 POKE 1237,1 -4380 GOTO 1520 -4390 REM status report -4400 GOSUB 840 -4401 PRINT" Status Report":X$="":IF K9<=1 THEN 4410 -4402 X$="s" -4410 PRINT"Klingon";X$;" left: ";K9 -4420 PRINT"Mission must be completed in ";0.1*INT((T0+T9-T)*10);" stardates" -4430 X$="s":IF B9>=2 THEN 4440 -4431 X$="":IF B9<1 THEN 4460 -4440 PRINT"The federation is maintaining ";B9;" starbase";X$;" in the galaxy" -4450 GOTO 3180 -4460 PRINT"Your stupidity has left you on your own in" -4470 PRINT" the galaxy -- you have no starbases left!":GOTO 3180 -4480 REM torpedo, base nav, d/d calculator -4490 GOSUB 840 -4491 IF K3<=0 THEN 2550 -4500 X$="":IF K3<=1 THEN 4510 -4501 X$="s" -4510 PRINT"From ENTERPRISE to Klingon battle cruiser";X$ -4520 H8=0:FOR I=1 TO 3 -4521 IF K(I,3)<=0 THEN 4740 -4530 W1=K(I,1):X=K(I,2) -4540 C1=S1:A=S2:GOTO 4590 -4550 GOSUB 840 -4551 PRINT"Direction/Distance Calculator:" -4560 PRINT"You are at quadrant ";Q1;",";Q2;" sector ";S1;",";S2 -4570 PRINT"Please enter ":INPUT" initial coordinates (x,y) ";C1,A -4580 INPUT" Final coordinates (x,y) ";W1,X -4590 X=X-A:A=C1-W1:aa=abs(a):ax=abs(x):IF X<0 THEN 4670 -4600 IF A<0 THEN 4690 -4610 IF X>0 THEN 4630 -4620 IF A<>0 THEN 4630 -4621 C1=5:GOTO 4640 -4630 C1=1 -4640 IF AA<=AX THEN 4660 -4650 PRINT"Direction1 = ";:cc1=(AA-AX+AA)/AA:PRINT c1+cc1:GOTO 4730 -4660 PRINT"Direction2 = ";:cc1=C1+(AA/AX):PRINT cc1:GOTO 4730 -4670 IF A<=0 THEN 4680 -4671 C1=3:GOTO 4700 -4680 IF X=0 THEN 4690 -4681 C1=5:GOTO 4640 -4690 C1=7 -4700 IF AA>=AX THEN 4720 -4710 PRINT"Direction3 = ";:cc1=(AX-AA+AX)/AX:PRINT c1+cc1:GOTO 4730 -4720 PRINT"Direction4 = ";:CC1=C1+(AX/AA):PRINT CC1 -4730 PRINT"Distance = ";:cc1=SQR(x*X+A*A):PRINT cc1:IF H8=1 THEN 1520 -4740 NEXT I -4741 GOTO 1520 -4750 GOSUB 840 -4751 IF B3=0 THEN 4770 -4752 PRINT"From ENTERPRISE to Starbase:":W1=B4:X=B5 -4760 GOTO 4540 -4770 PRINT"Mr. Spock reports, 'Sensors show no starbases in this"; -4780 PRINT"quadrant.'":GOTO 1520 -4790 REM find empty place in quadrant (for things) -4800 R1=INT(RND(1)*7.98+1.01):R2=INT(RND(1)*7.98+1.01):A$=" ":Z1=R1:Z2=R2 -4801 GOSUB 4990 -4802 IF Z3=0 THEN 4800 -4810 RETURN -4820 REM insert in string array for quadrant -4830 S8=INT(Z2-0.5)*3+INT(Z1-0.5)*24+1 -4840 IF LEN(A$)=3 THEN 4850 -4841 PRINT"ERROR":STOP -4850 IF S8<>1 THEN 4860 -4851 Q$=A$+MID$(Q$,4,189):RETURN -4860 IF S8<>190 THEN 4870 -4861 Q$=MID$(Q$,1,189)+A$:RETURN -4870 Q$=MID$(Q$,1,S8-1)+A$+MID$(Q$,s8+3,192-s8-2):RETURN -4880 REM prints device name -4890 ON R1 GOTO 4900,4910,4920,4930,4940,4950,4960,4970 -4900 G2$="Warp Engines":RETURN -4910 G2$="Short Range Sensors":RETURN -4920 G2$="Long Range Sensors":RETURN -4930 G2$="Phaser Control":RETURN -4940 G2$="Photon Tubes":RETURN -4950 G2$="Damage Control":RETURN -4960 G2$="Shield Control":RETURN -4970 G2$="Library-Computer":RETURN -4980 REM string comparison in quadrant array -4990 Z1=INT(Z1+0.5):Z2=INT(Z2+0.5):S8=(Z2-1)*3+(Z1-1)*24:Z3=0 -5000 IF MID$(Q$,S8+1,3)=A$ THEN 5010 -5001 RETURN -5010 Z3=1:RETURN -5020 REM quadrant name in g2$ from z4,z5 (=q1,q2) -5030 REM call with g5=1 to get region name only -5040 IF Z5<=4 THEN 5140 -5041 ON Z4 GOTO 5060,5070,5080,5090,5100,5110,5120,5130 -5050 GOTO 5140 -5060 G2$="Antares":GOTO 5230 -5070 G2$="Rigel":GOTO 5230 -5080 G2$="Procyon":GOTO 5230 -5090 G2$="Vega":GOTO 5230 -5100 G2$="Canopus":GOTO 5230 -5110 G2$="Altair":GOTO 5230 -5120 G2$="Sagittarius":GOTO 5230 -5130 G2$="Pollux":GOTO 5230 -5140 ON Z4 GOTO 5150,5160,5170,5180,5180,5200,5210,5220 -5150 G2$="Sirius":GOTO 5230 -5160 G2$="Deneb":GOTO 5230 -5170 G2$="Capella":GOTO 5230 -5180 G2$="Betelgeuse":GOTO 5230 -5190 G2$="Aldebaran":GOTO 5230 -5200 G2$="Regulus":GOTO 5230 -5210 G2$="Arcturus":GOTO 5230 -5220 G2$="Spica" -5230 IF G5=1 THEN 5240 -5231 ON Z5 GOTO 5250,5260,5270,5280,5250,5260,5270,5280 -5240 RETURN -5250 G2$=G2$+" i":RETURN -5260 G2$=G2$+" ii":RETURN -5270 G2$=G2$+" iii":RETURN -5280 G2$=G2$+" iv":RETURN -5290 rem red alert sound -5291 return -5300 FOR J= 1 TO 4 -5310 FOR K=1000 TO 2000 STEP 20 -5320 rem SOUND K,0.01*18.2 -5330 NEXT K -5340 NEXT J -5350 RETURN -5360 rem torpedo sound -5361 return -5370 FOR J = 1500 TO 100 STEP -20 -5380 rem SOUND J,0.01*18.2 -5390 rem SOUND 3600-J,.01*18.2 -5400 NEXT J -5410 RETURN -5420 rem phaser sound -5421 return -5430 FOR J= 1 TO 40 -5440 rem SOUND 800,.01*18.2 -5450 rem SOUND 2500,.008*18.2 -5460 NEXT J -5470 RETURN -5480 rem alarm sound -5481 return -5490 FOR SI = 1 TO 3 -5500 FOR J= 800 TO 1500 STEP 20 -5510 rem SOUND J,.01 *18.2 -5520 NEXT J -5530 FOR K = 1500 TO 800 STEP -20 -5540 rem SOUND K, .01 *18.2 -5550 NEXT K -5560 NEXT SI -5570 RETURN From a71a2dc8729d38989a81a5cdbcb5a2ebf218ef71 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:49:58 +0100 Subject: [PATCH 085/183] Moved to examples --- adventure-fast.bas | 1112 -------------------------------------------- 1 file changed, 1112 deletions(-) delete mode 100644 adventure-fast.bas diff --git a/adventure-fast.bas b/adventure-fast.bas deleted file mode 100644 index c9d06d5..0000000 --- a/adventure-fast.bas +++ /dev/null @@ -1,1112 +0,0 @@ -10 rem ADVENTURE/3000 VERSION 3.2 27 FEB 1979 AT 5:30 PM -11 rem THIS PROGRAM IS RELATIVELY BUG-FREE, BUT ONE STILL MUST -12 rem realize that murphy 'S LAW STILL PREVAILS! -13 rem -14 rem ADVENTURE: PROGRAMMED IN HP/3000 BASIC BY BENJAMIN MOSER -15 rem JAMES MADISON HIGH SCHOOL, VIENNA, VIRGINIA. THE BASIC LAYOUT OF THE -16 rem GAME WAS CONCEIVED BY DON WOODS & WILLIE CROWTHER, BOTH OF M.I.T. -17 rem -18 rem ADVENTURE WAS PORTED TO THE MACINTOSH PLUS BY THE ELIZABETH AND DAVID HUNTER -19 rem IN MARCH 1998 AND THEN TO PYBASIC FOR THE RASPBERRY RP2040 IN JUNE 2021 -20 rem PRINT "Adventure 3.2 on ";date$;" at ";time$ -21 PRINT "Adventure 3.2" -22 open "AMESSAGE" for INPUT as #3:open "AMOVING" for INPUT as #4 -23 open "ADESCRIP" for INPUT as #1:open "AITEMS" for INPUT as #2 -44 rem dirs is an array of possible room directions, it replaces file AMOVING -45 dim dirs(100,10) -46 dim indx(303) -47 dim fraindx(10) -50 dim s(99) -51 dim v(100) -52 dim k(2) -53 dim o(15) -54 string$ = "100 N 101 NE 102 E 103 SE 104 S 105 SW 106 W 107 NW 108 U 109 D 110 PLUGH 111 XYZZY 112 PLOVER 113 CROSS 114 CLIMB 115 JUMP 116 FILL 117 EMPTY 117 POUR 118 LOOK 118 L 119 LIGHT 119 ON 120 EXTINGUISH 120 OFF 121 IN 121 ENTER 122 LEAVE 122 OUT 123 INVENTORY 123 I 124 GET 124 CATCH 124 TAKE 125 DROP 125 DUMP 047 ALL 047 EVERYTHING 126 THROW 127 ATTACK 127 KILL 128 FEED 129 WATER 130 LOCK 131 UNLOCK 132 FREE 132 RELEASE 133 WAVE 134 OPEN 135 CLOSE 136 OIL 137 EAT 138 DRINK 139 FEE FIE FOE FOO 140 SHORT 141 LONG 142 BRIEF 143 QUIT 143 STOP 143 END 144 SCORE 145 SAVE 146 LOAD 147 READ 147 EXAMINE 148 YES 148 Y 149 BUG 001 GOLD 001 NUGGET 002 BARS 002 SILVER 003 JEWELRY 004 COINS 005 DIAMONDS 006 MING 006 VASE 007 PEARL 008 EGGS 008 NEST 009 TRIDENT 010 EMERALD 011 PLATINUM 011 PYRAMID 012 CHAIN 013 SPICES 014 PERSIAN 014 RUG 015 TREASURE 015 CHEST 016 WATER 017 OIL 018 LAMP 018 LANTERN 019 KEYS 020 FOOD 021 BOTTLE 022 CAGE 023 ROD 023 WAND 024 CLAM 025 MAGAZINE 026 BEAR 027 AXE 028 VELVET 028 PILLOW 029 SHARDS 030 OYSTER 031 BIRD 032 TROLL 033 DRAGON 034 SNAKE 035 DWARF 036 ROCK 036 BOULDER 037 STAIRS 038 STEPS 039 HOUSE 039 BUILDING 040 GRATE 041 STREAM 042 ROOM 043 BRIDGE 044 PIT 045 VOLCANO 046 ROAD 100 NORTH 101 NORTHEAST 102 EAST 103 SOUTHEAST 104 SOUTH 105 SOUTHWEST 106 WEST 107 NORTHWEST 108 UP 109 DOWN " -70 PRINT:PRINT "Initializing."; -110 rem initialize -130 rem total rooms, items,and keywords -150 l1 = int(RND(1)*4)+1 : l2 = l1 -160 g = 0 : b0 = 1 : sn = 1 : d1 = 1 : d2 = 0 : t = 1 : b1 = 0 : b2 = 0 : p1 = 0: dead = 0: lastc$="" -161 l = 0 : c = 0 : d3 = 0 : b3 = 0 : d0 = 2 : t1 = 100 : t2 = 35 : t3 = 149 : r0 = 0 : c0 = 0: c$="" -162 KC = 1.03 -170 for ii = 1 to 99 -171 s(ii) = 0 : v(ii) = 0 -172 next ii -175 PRINT ".";:v(100) = 0 -182 indx(1) = -1:indx(2)=-1 -190 rem read in possible movement direction array -192 for z2 = 1 to 100 -193 INPUT #4,dx1,dx2,dx3,dx4,dx5,dx6,dx7,dx8,dx9,dx0 -194 dirs(z2,1)=dx1:dirs(z2,2)=dx2:dirs(z2,3)=dx3:dirs(z2,4)=dx4:dirs(z2,5)=dx5 -195 dirs(z2,6)=dx6:dirs(z2,7)=dx7:dirs(z2,8)=dx8:dirs(z2,9)=dx9:dirs(z2,10)=dx0 -196 PRINT "."; -197 next z2 -198 close #4 -200 restore 230 -210 rem read in locations of items -220 for z2 = 1 to T2 step 5 -221 read dx1,dx2,dx3,dx4,dx5:s(z2)=dx1:s(z2+1)=dx2:s(z2+2)=dx3:s(z2+3)=dx4:s(z2+4)=dx5 -222 PRINT "."; -228 next z2 -230 data 18,25,23,24,21 -231 data 52,0,71,74,58 -232 data 59,69,66,82,100 -233 data 7,49,7,7,7 -234 data 7,12,13,40,38 -235 data 69,0,46,0,0 -236 data 15,60,82,22,250 -240 for ii = 1 to 15 step 5 -241 read dx1,dx2,dx3,dx4,dx5:o(ii)=dx1:o(ii+1)=dx2:o(ii+2)=dx3:o(ii+3)=dx4:o(ii+4)=dx5 -242 PRINT "."; -243 next ii -245 data 1,2,2,2,2 -246 data 3,4,3,3,2 -247 data 5,3,2,3,3 -260 rem ASK IF HE WANTS DIRECTIONS -263 z59 = 301:gosub 7620 -264 gosub 9860 -266 if z0 then 267 else 320 -267 z59 = 302:gosub 7620 -280 rem command INPUT routine -300 rem PRINT room, items -310 rem If it's dark don't let him see anything -320 if l1 < 13 or l1 = 58 then 350 -321 if l = 1 and (s(18) = l1 or s(18) = -1) then 350 -330 z59 = 45:gosub 7620 -340 goto 400 -350 on d0+1 gosub 6780,7990,8180 -360 v(l1) = 1 -370 gosub 6680 -375 if dead = 1 then 9540 -380 gosub 7800 -390 rem INPUT LOOP --- MULTIPLE COMMAANDS, REMOVE JUNK CHARACTERS -400 rem if len(c$) > 0 then 500 -410 INPUT ">";c$:c$=upper$(c$) -420 if c$ = "" then 410 -425 PRINT:PRINT:NKEY=0 -431 if c$ <> lastc$ then 433 -432 c$ = "":goto 900 -433 numkeys=1:lastc$ = c$:a$="" -449 rem 65-90 = A-Z 48-57 = 0-9 46 = . -450 for x = 1 to len(c$) -460 z5 = asc(mid$(c$,x,1)) -470 if (z5 > 64 and z5 < 91) or (z5 > 47 and z5 < 58) or z5 = 46 then 480 else 471 -471 c$ = mid$(c$,1,x-1)+" "+mid$(c$,x+1) -480 next x -490 if instr(c$," ")=0 then 520 -500 numkeys=2:a$ = mid$(c$,instr(c$," "),len(c$)-instr(c$," ")+1)+" " -510 c$ = mid$(c$,1,instr(c$," ")-1) -520 c$ = " "+c$+" " -800 k(1)=0:k(2)=0:z3 = 0 -870 a1 = instr(string$,c$):if a1=0 then 872 -871 c$ = mid$(string$,a1-3,3):k(1)=int(val(c$)) -872 if numkeys=1 then 900 -873 a1 = instr(string$,a$):if a1=0 then 900 -874 b$ = mid$(string$,a1-3,3):k(2)=int(val(b$)) -890 rem EXOTIC WORDS -900 z0 = 36 -910 if k(1) >= z0 and k(1) <=46 then 930 -920 if k(2) >= z0 and k(2) <=46 then 930 else 980 -930 gosub 8390 -940 PRINT "What do you want to do with the ";d$;"?" -950 goto 410 -980 if k(1) >= 110 and k(1) <= T3 then 1950 -990 if k(2) >= 110 and k(2) <= T3 then 1950 -1000 rem THEN IS IT A DIRECTION -1010 for d = 1 to 10 -1020 if k(1) = d+99 or k(2) = d+99 then 1070 -1030 next d -1040 rem COMMAND NOT A DIRECTION -1050 goto 1950 -1060 rem CAN HE MOVE THAT WAY? -1070 z2 = dirs(L1,d) -1130 if z2 = 255 then 1470 -1140 if z2 < 1 or z2 > 254 then 1220 -1150 rem NORMAL MOVING -1160 rem CHECK FOR SPECIAL MOVE CONDITIONS -1170 goto 1260 -1180 l2 = l1 : l1 = z2 -1190 if s(35) = l2 then 1191 else 1200 -1191 s(35) = l1 -1200 goto 9780 -1220 z59 = 1 -1221 gosub 7620 -1230 goto 400 -1240 rem SPECIAL ROOM DIRECTIONS -1250 rem GRATE -1260 if L1 = 10 and (d = 10 or d = 5) THEN 1280 -1261 IF L1 = 11 and (d = 9 or d = 3) then 1280 else 1320 -1270 rem IF GRATE IS OPEN (G=0) MOVE HIM -1280 if g = 1 then 1180 -1290 z59 = 10:gosub 7620 -1300 goto 400 -1310 rem CAN'T TAKE NUGGET UPPSTAIRS -1320 if not (l1 = 17 and d = 9 and s(1) = -1) then 1360 -1330 z59 = 38:gosub 7620 -1340 goto 400 -1350 rem CRYSTAL BRIDGE AND FISSURE -1360 if not (l1 = 19 and d = 7 or l1 = 20 and d = 3) then 1410 -1370 if b2 then 1180 -1380 z59 = 3:gosub 7620 -1390 goto 400 -1400 rem MT. KING & SNAKE -1410 if not (l1 = 22 and d <> 3 and d <> 9) then 1690 -1420 if sn = 0 then 1180 -1430 z59 = 50:gosub 7620 -1440 goto 400 -1460 rem bedquilt and random directiosn -1470 if l1 <> 44 then 1590 -1480 if RND(1) > 0.5 then 1510 -1490 z59 = 52:gosub 7620 -1500 goto 400 -1510 restore 1530 -1520 rem ROOMS TO JU -1530 data 33,37,45,92,76 -1540 for z3 = 1 to int(RND(1)*5)+1 -1550 read z2 -1560 next z3 -1570 goto 1180 -1580 rem WITT's END -1590 if l1 <> 39 then 1220 -1600 rem SHOULD WE LET HIM OUT? -1610 if RND(1) < 0.15 then 1650 -1620 rem NO -1630 z59 = 52:gosub 7620 -1640 goto 400 -1650 rem YES -1660 z2 = 38 -1670 goto 1180 -1680 rem Narrow Tunnel -1690 if not (l1 = 57 or l1 = 58) then 1780 -1691 if k(1) <> 102 and k(2) <> 102 and k(1) <> 106 and k(2) <> 106 then 1780 -1700 for z3 = 1 to t2 -1710 if z3 = 10 then 1750 -1720 if s(z3) <> -1 then 1750 -1730 z59 = 53:gosub 7620 -1740 goto 400 -1750 next z3 -1760 goto 1180 -1770 rem TROLL -1780 IF L1=60 AND D=2 THEN 1790 -1781 IF L1=61 AND D=6 THEN 1790 ELSE 1860 -1790 on t+1 goto 1180,1800,1820,1840 -1800 z59 = 55:gosub 7620 -1810 goto 400 -1820 z59 = 56:gosub 7620 -1821 z59 = 55:gosub 7620 -1830 T=1:goto 400 -1840 t = 2 -1850 goto 1180 -1860 if not (l1 = 73 and d = 1 and d2 = 0) then 1890 -1870 z59 = 57:gosub 7620 -1880 goto 400 -1890 if not (l1 = 82 and s(33) = l1 and d = 1) then 1180 -1900 rem DRAGON -1910 z59 = 51:gosub 7620 -1920 goto 400 -1940 rem OTHER COMMANDS -1950 z1=k(1):if k(1) >= 110 and k(1) <= T3 then 2090 -1960 z1=k(2):if k(2) >= 110 and k(2) <= T3 then 2090 -1980 rem ITEM BO NO VERB? -1990 if k(1) >= 1 and k(1) <= 35 then 2000 -1991 if k(2) >= 1 and k(2) <= 35 then 2000 else 2040 -2000 for x = 1 to 35 -2020 if k(1) <> x and k(2) <> x then 2030 -2021 restore 9960+x -2022 read d$ -2023 goto 940 -2030 next x -2040 restore 2070 -2050 for x = 1 to int(RND(1)*4)+1 -2051 read b$ -2052 next x -2060 PRINT b$ -2070 data "What?","I don't understand.","I can't understand that.","I don't know that word." -2080 goto 400 -2090 z1 = z1-109 -2095 on z1 goto 2120,2220,2300,2390,2570,2640,2680,2860,2930,3000,3080,3130,3290,3450,3590,3920,4160,4430,4630,4800,4950,5060,5190,5410,5560,5750,5810,5920,6000,6090,6230,6270,6310,6350,6410,8970,9080,9220,4490,9330 -2110 goto 2040 -2120 REM *** PLUGH *** -2130 IF L1<>7 THEN 2170 -2140 IF S(35)=L1 THEN S(35)=0 -2150 Z2=26 -2160 GOTO 1180 -2170 IF L1<>26 THEN 2200 -2180 Z2=7 -2190 GOTO 1180 -2200 z59 = 2:gosub 7620 -2210 GOTO 400 -2220 REM *** XYZZY *** -2230 IF L1<>7 THEN 2270 -2240 IF S(35)=L1 THEN S(35)=0 -2250 Z2=13 -2260 GOTO 1180 -2270 IF L1<>13 THEN 2200 -2280 Z2=7 -2290 GOTO 1180 -2300 REM *** PLOVER *** (CAN'T BRING EMERALD WITH HIM) -2310 IF L1>26 THEN 2360 -2320 IF S(35)<>L1 THEN 2330 -2325 S(35) = 0 -2330 IF S(10)<>-1 THEN 2340 -2335 S(10) = L1 -2340 Z2 = 58 -2350 GOTO 1180 -2360 IF L1<>58 THEN 2200 -2370 Z2=26 -2380 GOTO 1180 -2390 REM *** CROSS *** -2400 IF L1<>19 THEN 2470 -2410 IF B2<>0 THEN 2440 -2420 z59 = 3:gosub 7620 -2430 GOTO 400 -2440 D=7 -2450 REM JUST GIVE NEW DIRECTION, USE MOVE ROUTINE -2460 GOTO 1070 -2470 IF L1<>20 THEN 2510 -2480 IF B2=0 THEN 2420 -2490 D=3 -2500 GOTO 1070 -2510 IF L1<>60 THEN 2540 -2520 D=2 -2530 GOTO 1070 -2540 IF L1<>61 THEN 2200 -2550 D=6 -2560 GOTO 1070 -2570 REM *** CLIMB *** -2580 IF L1<>50 THEN 2200 -2590 REM CAN HE CLIMB BEANSTALK? -2600 IF P1<2 THEN 2200 -2610 REM YES -2620 Z2=70 -2630 GOTO 1180 -2640 REM *** JUMP *** STRICTLY SUICIDAL -2650 IF L1<>16 AND L1<>19 AND L1<>20 AND L1<>27 THEN 2200 -2660 z59 = 4:gosub 7620 -2670 GOTO 9550 -2680 REM FILL -2690 IF S(21)=-1 THEN 2730 -2700 B$="bottle" -2710 PRINT "You don't have the ";b$ -2720 goto 410 -2730 IF B0=0 THEN 2760 -2740 z59 = 5:gosub 7620 -2750 GOTO 410 -2760 IF L1<>7 AND L1<>8 AND L1<>9 AND L1<>35 AND L1<>74 AND L1<>81 THEN 2790 -2770 B0=1:S(16)=-1 -2780 GOTO 2840 -2790 IF L1=49 THEN 2830 -2800 B$="oil" -2810 PRINT "I see no ";B$;" here." -2820 GOTO 400 -2830 B0=2:S(17)=-1 -2840 PRINT "The bottle is now filled." -2850 GOTO 400 -2860 REM *** EMPTY *** -2870 IF S(21)=-1 THEN 2890 -2880 GOTO 2700 -2890 REM EMPTY BOTTLE (ASSUMED FULL) -2900 S(B0+15)=0:B0=0 -2910 PRINT "Emptied" -2920 GOTO 400 -2930 rem *** LOOK *** -2940 if l1 < 13 or l1 = 58 then 2970 -2941 if l = 1 and (s(18) = l1 or s(18) = -1) then 2970 -2950 z59 = 45:gosub 7620 -2960 goto 400 -2970 gosub 8050 -2980 gosub 6680 -2990 goto 400 -3000 rem *** LIGHT *** -3010 if s(18) = -1 then 3040 -3020 b$ = "lamp" -3030 goto 2710 -3040 l = 1 -3050 b$ = "on" -3060 PRINT "The lamp is now ";b$ -3070 goto 2940 -3080 REM *** OFF (EXTINGUSIH) *** -3090 IF S(18)=-1 THEN 3110 -3100 GOTO 3020 -3110 L=0:B$="off" -3120 GOTO 3060 -3130 REM *** ENTER *** -3140 IF L1<>6 THEN 3180 -3150 REM TO HOUSE -3160 D=3 -3170 GOTO 1070 -3180 IF L1<>68 THEN 3240 -3190 REM TO BARREN ROOM -3200 D=3 -3210 GOTO 1070 -3240 FOR D=10 TO 1 step -1 -3250 Z2 = DIRS(L1,D) -3260 IF Z2>0 AND Z2<101 THEN 1150 -3270 NEXT D -3280 GOTO 2200 -3290 REM ** LEAVE *** -3300 IF L1<>7 THEN 3340 -3310 REM LEAVE HOUSE -3320 D=7 -3330 GOTO 1070 -3340 IF L1<>69 THEN 3400 -3350 REM LEAVE BARREN ROOM -3360 D = 7 -3370 GOTO 1070 -3400 FOR D = 1 TO 10 -3410 Z2 = DIRS(L1,D) -3420 IF Z2>0 AND Z2<101 THEN 1150 -3430 NEXT D -3440 GOTO 2200 -3450 REM *** INVENTORY *** -3470 Z0=0 -3480 PRINT "You are carrying:"; -3490 FOR X=1 TO T2 -3510 IF S(X)<>-1 THEN 3540 -3511 restore 9960+x:read b$ -3520 PRINT B$ -3530 Z0 = Z0 + 1 -3540 NEXT X -3550 IF Z0=0 THEN 3551 else 3560 -3551 PRINT "nothing." -3560 PRINT -3570 GOTO 400 -3590 rem *** GET *** -3630 if k(1) = 47 or k(2) = 47 then 3680 -3640 gosub 8280 -3650 if z8 > 0 then 3680 -3660 PRINT "Get what?" -3670 goto 2040 -3680 for z3 = 1 to t2 -3710 if k(1) = 47 or k(2) = 47 then 3730 -3720 if k(1) <> z3 and k(2) <> z3 then 3900 -3730 if s(z3) <> l1 then 3750 -3740 if s(z3) = l1 then 3790 -3750 if k(1) = 47 or k(2) = 47 then 3900 -3760 restore 9960+z3:read a$:PRINT a$;" not here." -3770 goto 3900 -3780 rem MUST CHECK NOW FOR LEGALITY OF TAKING ITEM -3790 z8 = 0 -3800 for x = 1 to t2 -3810 if s(x) <> -1 then 3820 -3811 z8 = z8+1 -3820 next x -3830 if z8 < 7 then 3870 -3840 rem CARRYING TOO MUCH -3850 z59 = 54:gosub 7620 -3860 goto 410 -3870 goto 6880 -3880 s(z3) = -1 -3890 restore 9960+z3:read a$:PRINT a$;":taken." -3900 next z3 -3910 goto 400 -3920 REM *** DROP *** -3940 if k(1) = 47 or k(2) = 47 then 4000 -3950 gosub 8280 -3960 IF Z8>0 THEN 4000 -3970 PRINT "Drop what?" -3980 GOTO 2040 -4000 FOR Z3=1 TO T2 -4030 IF K(1) = 47 or k(2) = 47 THEN 4060 -4040 IF K(1) <> Z3 and k(2) <> z3 THEN 4140 -4050 IF S(Z3)=0 THEN 4140 -4060 IF S(Z3)=-1 THEN 4100 -4070 IF K(1)=47 or k(2)=47 THEN 4140 -4080 restore 9960+z3:read b$:PRINT "You don't have the ";B$ -4090 GOTO 4140 -4100 REM STILL NEED TO ELABORATE ON DROP (BIRD IN CAGE, BOTTLE) -4110 GOTO 7380 -4120 restore 9960+z3:read b$:PRINT B$;":dropped." -4130 S(Z3)=L1 -4140 NEXT Z3 -4150 GOTO 400 -4160 REM *** THROW *** -4170 GOSUB 8280 -4180 IF Z8>0 THEN 4210 -4190 PRINT "Throw what?" -4200 GOTO 2040 -4210 IF S(Z3)<>-1 THEN 2710 -4220 IF NOT (Z3<16 AND S(32)=L1) THEN 4260 -4230 REM THROW TREASURE TO TROLL -4240 z59 = 27:gosub 7620 -4241 S(Z3)=0:T=3:GOTO 400 -4260 IF NOT (Z3=27 AND S(32)=L1) THEN 4300 -4270 REM TRYING TO BUTCHER TROLL? -4280 z59 = 26:gosub 7620 -4281 S(27)=L1:GOTO 400 -4300 IF NOT (Z3=27 AND S(35)=L1) THEN 4380 -4310 REM TRYING TO KILL DWARF -4320 IF RND(1)>0.5 THEN 4360 -4330 z59 = 29:gosub 7620 -4340 GOSUB 8650 -4350 GOTO 4410 -4360 z59 = 30:gosub 7620 -4361 S(35)=0:GOTO 4410 -4380 REM NOTHING SPECIAL, JUST DROP ITEM -4390 IF S(35)<>L1 THEN 4400 -4391 GOSUB 8550 -4400 PRINT "Thrown." -4410 S(Z3) = L1 -4415 if dead = 1 then 9540 -4420 GOTO 400 -4430 REM *** ATTACK *** -4440 GOSUB 8280 -4450 IF NOT (Z3=33 AND S(Z3)=L1 AND L1=82) THEN 4520 -4460 REM HE CAN KILL DRAGON -4470 z59 = 68:gosub 7620 -4480 GOTO 410 -4490 IF L1<>82 THEN 2040 -4500 z59 = 69:gosub 7620 -4501 S(33)=0:D1=0:GOTO 400 -4520 IF S(32)<>L1 THEN 4560 -4530 REM TRYING TO MUNGE TROLL -4540 Z9=FNA(25):GOTO 400 -4560 IF NOT (Z3=26 OR Z3>30) THEN 4600 -4570 REM DANGEROUS TO ATTACK THESE -4580 z59 = 70:gosub 7620 -4590 GOTO 400 -4600 REM NOTHING TO ATTACK -4610 z59 = 71:gosub 7620 -4620 GOTO 400 -4630 REM *** FEED *** -4640 GOSUB 8280 -4650 IF Z3<>35 THEN 4690 -4660 REM CAN'T FEED DWARF! -4670 z59 = 24:gosub 7620 -4680 GOTO 400 -4690 IF S(20) = -1 THEN 4720 -4700 B$ = "FOOD":GOTO 2710 -4720 IF L1=69 THEN 4760 -4730 PRINT "I can't feed it." -4740 z59 = 23:gosub 7620 -4750 GOTO 400 -4760 IF S(20)=L1 THEN 7600 -4770 B1=1:S(20)=0:z59 = 6:gosub 7620 -4790 GOTO 400 -4800 REM *** WATER *** -4810 IF S(16) = -1 THEN 4840 -4820 B$ = "water":GOTO 2710 -4840 IF L1<>50 THEN 2200 -4850 REM GOTO P1+1 OF 4860,4890,4920 -4851 IF P1 = 0 THEN 4860 -4852 IF P1 = 1 THEN 4890 -4853 IF P1 = 2 THEN 4920 -4860 z59 = 7:gosub 7620 -4870 P1=1:S(16)=0:B0=0:GOTO 400 -4890 z59 = 8:gosub 7620 -4900 P1=2:S(16)=0:B0=0:GOTO 400 -4920 z59 = 9:gosub 7620 -4930 P1=0:S(16)=0:B0=0:GOTO 400 -4950 REM *** LOCK *** -4960 IF L1=10 OR L1=11 THEN 4990 -4970 REM NOTHING LOCKABLE -4980 GOTO 2200 -4990 IF S(19)=-1 THEN 5020 -5000 B$="keys":goto 2710 -5020 G=0:z59 = 10:gosub 7620 -5040 GOTO 400 -5060 REM *** UNLOCK *** -5070 IF S(19)<>-1 THEN 5000 -5080 IF L1<>10 AND L1<>11 THEN 5120 -5090 G=1:z59 = 11:gosub 7620 -5110 GOTO 400 -5120 IF L1<>69 THEN 2200 -5130 IF B1>0 THEN 5160 -5140 z59 = 12:gosub 7620 -5150 goto 400 -5160 IF C<>0 THEN 5170 -5165 C=1:B1=2 -5170 z59 = 13:gosub 7620 -5180 GOTO 400 -5190 REM *** FREE *** -5200 IF K(1) = 31 or k(2) = 31 THEN 5240 -5210 REM CAN'T FREE ANYTHING BUT BIRD -5220 z59 = 2:gosub 7620 -5230 GOTO 410 -5240 IF S(31)<>-1 THEN 5220 -5250 S(31) = L1:B3=0 -5260 PRINT "Freed." -5270 IF L1<>22 THEN 5350 -5280 IF SN<>1 THEN 400 -5290 B$="snake" -5300 PRINT "The little bird attacks the green ";B$;" and" -5310 IF L1=82 THEN 5380 -5320 PRINT "drives it off" -5330 SN=0:S(34)=0:GOTO 400 -5350 IF L1<>82 THEN 400 -5360 B$="dragon":GOTO 5300 -5380 PRINT "gets burned to a crisp" -5390 S(31)=0 -5400 GOTO 400 -5410 REM *** WAVE *** -5420 IF K(1) <> 23 and k(2) <> 23 THEN 2200 -5430 IF S(23)=-1 THEN 5460 -5440 B$="rod":GOTO 2710 -5460 REM IS HERE NEAR FISSURE -5470 IF L1<>19 AND L1<>20 THEN 2200 -5480 REM yes -5490 REM GOTO B2+1 OF 5500,5530 -5491 IF B2=0 THEN 5500 -5492 IF B2=1 THEN 5530 -5500 z59 = 14:gosub 7620 -5510 B2=1:GOTO 400 -5530 z59 = 15:gosub 7620 -5540 B2=0:GOTO 400 -5560 REM *** OPEN *** -5570 GOSUB 8280 -5580 IF Z3>0 THEN 5610 -5590 PRINT "Open ";:goto 2040 -5610 IF Z3=40 THEN 5070 -5620 IF S(Z3)=L1 THEN 5650 -5630 PRINT "I see no ";b$;" here.":goto 400 -5650 if z3=24 THEN 5680 -5660 PRINT "I don't know how to open a ";B$:GOTO 400 -5680 IF S(9)=-1 THEN 5710 -5690 z59 = 16:gosub 7620 -5700 GOTO 400 -5710 IF S(Z3) = 0 THEN 2200 -5720 REM HE'S OPENED CLAM, SO PRINT DESCRIPTION OF THIS -5730 REM PUT PEARL IN CUL-DE-SAC -5740 S(7)=43:S(24)=0:S(30)=L1:z59 = 17:gosub 7620 -5750 GOTO 400 -5760 REM *** CLOSE *** -5770 GOSUB 8280 -5780 IF Z3=40 THEN 4960 -5790 z59 = 18:gosub 7620 -5800 GOTO 400 -5810 REM OIL -5820 IF K(1) <> 17 and k(2) <> 17 THEN 2200 -5830 IF S(17)=-1 THEN 5860 -5840 B$="oil":GOTO 5630 -5860 IF L1<>73 THEN 2200 -5870 REM IS DOOR STILL RUSTED -5880 IF D2=1 THEN 2200 -5890 D2=1:S(17)=0:B0=0:z59 = 19:gosub 7620 -5900 GOTO 400 -5910 REM *** EAT *** -5920 IF K(1) = 20 or k(2) = 20 THEN 5950 -5930 z59 = 20:gosub 7620 -5940 GOTO 410 -5950 Z3=20:GOSUB 8490 -5970 IF Z5=0 THEN 410 -5980 z59 = 73:gosub 7620 -5381 S(20)=0:B0=0:GOTO 400 -6000 REM *** DRINK *** -6010 IF K(1) = 16 or k(2) =16 THEN 6040 -6020 z59 = 21:gosub 7620 -6030 GOTO 410 -6040 Z3=16:GOSUB 8490 -6060 IF Z5=0 THEN 410 -6070 z59 = 22:gosub 7620 -6071 S(17)=0:B0=0:GOTO 400 -6090 REM *** FEE FIE FOE FOO *** -6100 IF L1=71 THEN 6130 -6110 z59 = 2:gosub 7620 -6120 GOTO 410 -6130 IF S(8)<>L1 THEN 6180 -6140 REM MAKE NEST VANISH -6150 z59 = 79:gosub 7620 -6160 S(8)=0:GOTO 400 -6180 REM IF S(8)=0 THEN 6110 -6190 S(8)=L1 -6200 rem MAKE NEST RE-APPEAR -6210 z59 = 81:gosub 7620 -6220 GOTO 400 -6230 REM *** SHORT *** -6240 PRINT "Short descriptions" -6250 D0=0:GOTO 400 -6270 REM *** LONG *** -6280 PRINT "Long descriptions" -6290 D0=1:GOTO 400 -6310 REM *** BRIEF *** -6320 PRINT "OK, I'll only describe the room in detail the first time." -6330 D0=2:GOTO 400 -6350 REM *** QUIT *** -6360 PRINT "Save game"; -6370 GOSUB 9860 -6380 IF Z0=1 THEN 8970 -6390 GOTO 9750 -6400 REM SCORE *** -6410 GOSUB 6430 -6420 GOTO 400 -6430 REM PRINT OUT SCORE DATA -6440 GOSUB 6510 -6450 PRINT "Your score is now ";S0 -6451 PRINT "You have explored ";(Z9/T1)*T1;"% of the cave." -6460 RESTORE 6470 -6470 DATA "beginner","novice","experienced","advanced","expert" -6480 Z9 = INT((S0-1)/100) -6481 IF Z9 <= 4 THEN 6483 -6482 Z9=4 -6483 FOR Z0=0 TO Z9 -6484 READ D$ -6485 NEXT Z0 -6490 PRINT "That makes you a ";D$;" adventurer." -6500 RETURN -6510 REM COMPUTE CURRENT SCORE -6520 RESTORE 230 -6530 Z9=0:S0=0 -6540 FOR Z0=1 TO 15 -6550 READ Z1 -6560 IF Z1=0 THEN 6590 -6570 IF V(Z1)<>1 THEN 6580 -6575 S0=S0+4*O(Z0) -6580 IF S(Z0)<>7 THEN 6590 -6585 S0=S0+4*O(Z0) -6590 NEXT Z0 -6600 S0=(G=1)*10 + S0:S0=(SN=0)*20 + S0:S0=(D1=0)*30 + S0:S0=(T=0)*30 + S0:S0=(B1=2)*20 + S0 -6605 S0=(B2=1)*20 + S0:S0=(P1=2)*20 + S0:S0=(D2=1)*20 + S0:S0=(C=1)*20 + S0 -6610 FOR Z0 = 1 TO T1 -6620 IF V(Z0)<>1 THEN 6630 -6621 S0=S0+1:Z9=Z9+1 -6630 NEXT Z0 -6640 RETURN -6660 rem list items at location l1 -6680 fseek #2,0 -6690 for z1 = 1 to t2 -6700 INPUT #2,a$ -6710 if s(z1) <> l1 then 6720 -6711 PRINT a$ -6720 next z1 -6721 IF S(26)<>-1 THEN 6730 -6722 z59 = 67:gosub 7620 -6730 rem CHECK FOR DWARF,PIRATE -6740 gosub 8560 -6745 if dead = 1 then 6770 -6750 gosub 8800 -6760 PRINT -6770 return -6775 rem Print Short room description -6780 fseek #1,0 -6820 for z1 = 1 to l1 -6830 INPUT #1,a$ -6840 next z1 -6850 v(l1) = 1 -6860 PRINT a$ -6870 return -6880 rem SPECIAL GETS -6890 if not (z3 = 24 or z3 = 30 or z3 > 31) then 6930 -6900 rem CAN'T GET THESE FOR SOME REASON -6910 z59 = 61:gosub 7620 -6920 goto 400 -6930 if not (z3 = 12 and c = 0) then 6970 -6940 rem CHAIN -6950 z59 = 58:gosub 7620 -6960 goto 3900 -6970 rem BEAR IS HE FED? UNLOCKED? -6980 if not (z3 = 26 and b1 <> 2) then 7010 -6990 z59 = 61:gosub 7620 -7000 goto 3900 -7010 if not (z3 = 14 and d1 = 1) then 7050 -7020 rem DRAGON AND RUG -7030 z59 = 59:gosub 7620 -7040 goto 3900 -7050 if not (z3 = 16 or z3 = 17) then 7090 -7060 rem OIL AND WATER DO SAME AS FILL -7070 PRINT "Why not say 'fill'?" -7080 goto 3900 -7090 if not (z3 = 22 and b3) then 7140 -7100 rem TAKE BIRD SINCE IT'S IN CAGE -7110 s(31) = -1:PRINT "Bird and ";:goto 3880 -7140 if z3 <> 31 then 7310 -7150 rem GETTING BIRD -7160 if b3 <> 1 then 7210 -7170 rem TAKE CAGE, SINCE BIRD IS IN IT -7180 PRINT "Cage and ";:s(22) = -1:goto 3880 -7210 if s(22) = -1 then 7240 -7220 b$ = "cage":goto 2810 -7240 if s(23) = -1 then 7280 -7250 rem OK TO TAKE BIRD -7260 s(31) = -1 : b3 = 1:goto 3890 -7280 rem ROD SCARES BIRD -7290 z59 = 37:gosub 7620 -7300 goto 3900 -7310 rem BOTTLE FULL? IF SO, GET CONTENTS -7320 if not (z3 = 21 and b0) then 7360 -7330 PRINT "Contents and the "; -7340 s(b0+15) = -1 -7360 goto 3880 -7370 rem SPECIAL "DROP" -7380 IF Z3<>31 THEN 7440 -7390 REM BIRD IN CAGE -7400 S(31)=L1:S(22)=L1:B3=1 -7410 IF Z3<>31 THEN 7420 -7415 PRINT "Cage and "; -7420 IF Z3=22 THEN 7430 -7425 PRINT "Bird and "; -7430 goto 4120 -7440 if z3=22 and b3=1 then 7400 -7450 IF Z3<>21 THEN 7520 -7460 REM BOTTLE -7470 IF B0=0 THEN 4120 -7480 REM BOTTLE IS FULL, DO DROP CONTENTS TOO -7490 PRINT "Contents and "; -7500 S(15+B0)=L1:GOTO 4120 -7520 IF NOT (Z3=16 OR Z3=17) THEN 7541 -7530 PRINT "Try saying 'empty'":goto 4140 -7541 IF Z3<>26 OR T<>1 OR (L1<>60 AND L1<>61) THEN 7550 -7542 z59 = 28:gosub 7620 -7543 T=0:S(26)=L1:S(32)=0:GOTO 400 -7550 IF Z3<>6 THEN 4120 -7560 IF S(28)=L1 THEN 7600 -7570 REM GOODBYE, FRAGILE VASE! -7580 z59 = 43:gosub 7620 -7581 S(6)=0:S(29)=L1:GOTO 400 -7600 z59 = 60:gosub 7620 -7610 GOTO 4120 -7619 rem PRINT MESSAGE -7620 if indx(1) >= 0 then 7640 -7630 gosub 12500 -7640 z59 = int(z59):xtmp = indx(z59) -7645 if z59 <> 2 and z59 <> 61 then 7660 -7650 xtmp = fraindx(xtmp+min(int(RND(1)*5),5)) -7660 fseek #3,xtmp -7670 INPUT #3,b1$ -7672 if len(b1$) = 0 then 7673 else 7690 -7673 b1$ = " " -7690 if instr(b1$,"#") = 0 then 7670 -7700 z4 = val(mid$(b1$,2)) -7705 if int(z4) = z59 then 7720 -7710 if int(z4) < z59 then 7670 -7712 if int(z4) > z59 then 7760 -7720 INPUT #3,b1$ -7730 if mid$(b1$,1,1) = "#" then 7770 -7740 PRINT b1$ -7750 goto 7720 -7760 PRINT "NO DESC. # ";z59;" IN FILE AMESSAGE" -7770 return -7800 rem -7810 rem SITUATION DESCRIPTIONS -7820 rem -7830 rem GRATE -7840 if l1 = 10 or l1 = 11 then 7842 else 7860 -7842 z59 = (g+10):gosub 7620 -7850 rem CRYSTAL BRIDGE -7860 if (l1 = 19 or l1 = 20) and b2 = 1 then 7861 else 7880 -7861 z59 = 14:gosub 7620 -7870 rem PLUGH NOISE -7880 if l1 = 26 and RND(1) > 0.3 then 7881 else 7900 -7881 z59 = 41:gosub 7620 -7890 rem IRON DOOR -7900 if l1 = 73 and d2 = 0 then 7901 else 7920 -7901 z59 = 57:gosub 7620 -7910 rem TROLL -7920 if (l1 = 60 or l1 = 61) and t = 1 then 7921 else 7940 -7921 z59 = 63:gosub 7620 -7930 rem BEAR -7940 if l1 = 69 and b1 = 0 then 7941 else 7950 -7941 z59 = 64:gosub 7620 -7950 if l1 = 69 and b1 = 1 then 7951 else 7970 -7951 z59 = 66:gosub 7620 -7960 rem PLANT IN PIT -7970 if l1 = 48 or l1 = 50 then 7971 else 7980 -7971 z59 = 47+p1:gosub 7620 -7980 return -7990 rem -8000 rem PRINT long room description from "amessage" file -8010 rem description is noormally l1+200 except for -8020 rem maze or forest -8030 rem set v(l1)=1 so as not to repeat long desc(brief mode -8040 v(l1) = 1 -8050 if l1 > 4 then 8080 -8060 z59 = 200:gosub 7620 -8070 goto 8130 -8080 if not (l1 > 88 and l1 < 98 or l1 = 99) then 8110 -8090 z59 = 288:gosub 7620 -8100 goto 8130 -8110 rem normal description -8120 z59 = 200+l1:gosub 7620 -8130 return -8180 rem always give long descriptio nfor forest and maze -8190 if l1 < 5 or (l1 > 88 and l1 < 98) or l1 = 99 then 8220 -8200 if v(l1)=1 then 8201 else 8220 -8201 gosub 6780 -8202 goto 8235 -8210 rem he hassn't seen this room, so give a long desc. -8220 v(l1) = 1 -8230 gosub 7990 -8235 return -8240 rem -8250 rem fetch first item code in k(1 to t2) -8260 rem z8=total # of items found in list -8270 rem z3=item code first found -8280 z8 = 0 : z3 = 0: d$ = "" -8300 for z5 = 1 to 45 -8320 if k(1) <> z5 and k(2) <> z5 then 8360 -8330 z8 = z8+1 -8340 restore 9960+z5:read b$:d$ = b$ -8350 if k(1) = z5 and z8 = 1 then 8352 -8351 if k(2) = z5 and z8 = 1 then 8352 else 8360 -8352 z3 = z5 -8360 next z5 -8370 b$ = d$ -8380 return -8390 rem FIND FIRST ITEM AT ROOM -8400 x1 = 0 -8410 FOR Z1=1 TO 47 -8415 if x1=1 then 8440 -8430 IF K(1) = Z1 OR K(2) = Z1 THEN 8431 else 8440 -8431 restore 9960+z1:read d$:x1=1 -8440 NEXT Z1 -8450 RETURN -8460 REM -8470 REM MAKE SURE HE'S CARRYING ITEM * Z3 -8480 REM -8490 IF S(Z3)=-1 THEN 8530 -8500 PRINT "You don't have the ";A$ -8510 Z5=0 -8520 RETURN -8530 Z5=1 -8540 RETURN -8550 rem *** DWARF *** -8560 if d3 <> 0 then 8640 -8570 rem SHOULD DWARF GIVE AWAY AXE? -8580 if l1 < 13 then 8790 -8590 if RND(1) > 0.05 then 8790 -8600 rem GIVE AWAY AXE -8610 z59 = 80:gosub 7620 -8620 s(27) = l1 : d3 = 1 -8630 goto 8790 -8640 rem SHOULD DWARF ATTACK? -8650 if L1 >= 13 then 8660 -8652 s(35) = 0:goto 8790 -8660 if s(35) <> L1 then 8770 -8670 if RND(1) > 0.5 then 8790 -8680 rem YES! -8690 z59 = 32:gosub 7620 -8700 rem DOES THE KNIFE KILL THE PLAYER? -8705 KC = KC - 0.02 -8706 IF KC >= 0.5 THEN 8710 -8707 KC = 0.5 -8710 if RND(1) <= KC then 8750 -8720 rem YES -8730 PRINT "It gets you!" -8740 dead = 1 : goto 8790 -8750 PRINT "It misses!" -8760 goto 8790 -8770 rem SHOULD WE PUT A DWARF HERE? -8780 if RND(1) >= 0.1 then 8790 -8785 s(35) = l1 -8786 z59=31:gosub 7620 -8790 return -8800 rem *** PIRATE *** -8810 rem FIRST, DOES HE HAVE ANYTHING WORTH STEALING? -8820 z3 = 0 -8830 if l1 < 13 then 8960 -8840 for x = 1 to 15 -8850 if s(x) <> -1 then 8860 -8855 z3 = z3+1 -8860 next x -8870 if z3 < int(RND(1)*4)+1 then 8960 -8880 rem SHOULD WE RIP OFF HIS VALUABLES? -8890 if RND(1) < 0.05 then 8920 -8900 z59 = 34:gosub 7620 -8910 goto 8960 -8920 z59 = 33:gosub 7620 -8930 for x = 1 to 15 -8940 if s(x) <> -1 then 8950 -8945 s(x) = 100 -8950 next x -8960 return -8970 REM *** SAVE GAME *** -8980 INPUT "What do you want to call the save file? ";A$ -8990 OPEN A$ FOR OUTPUT AS #5 ELSE 9010 -9000 GOTO 9030 -9010 PRINT "File ";a$;" not created" -9020 GOTO 410 -9030 PRINT #5,T1;",";T2;",";T3;",";L1;",";L2;",";G;",";B0;",";SN;",";D1;",";D2;",";D0;",";T;",";B1;",";B2;",";P1;",";L;",";C;",";D3;",";B3;",";R0;",";KC -9040 FOR X=1 TO 99 -9041 PRINT #5,S(X);",";V(X) -9042 NEXT X -9043 PRINT #5,V(100) -9044 CLOSE #5 -9050 PRINT "Game saved" -9051 C0 = 0 -9060 if k(1) = 143 OR K(2) = 143 then 9750 -9070 GOTO 410 -9080 REM *** LOAD OLD GAME *** -9090 IF C0=0 THEN 9120 -9100 PRINT "You already have a loaded game!" -9110 GOTO 410 -9120 INPUT "Save file name? ";A$ -9130 OPEN A$ FOR INPUT AS #5 ELSE 9150 -9140 GOTO 9170 -9150 PRINT "Unable to use file ";A$ -9160 GOTO 410 -9170 INPUT #5,T1,T2,T3,L1,L2,G,B0,SN,D1,D2,D0,T,B1,B2,P1,L,C,D3,B3,R0,KCX$ -9171 kc = val(kcx$) -9180 FOR X=1 TO 99 -9191 INPUT #5,SX,VX:s(x)=sx:v(x)=vx -9192 NEXT X -9193 INPUT #5,vx:v(100)=vx -9194 CLOSE #5 -9200 C0=1 -9210 GOTO 300 -9220 rem *** READ THE MAGAZINE *** -9230 GOSUB 8280 -9240 IF Z3=25 THEN 9270 -9250 z59 = 74:gosub 7620 -9260 GOTO 410 -9270 IF S(25)=-1 THEN 9300 -9280 B$="magazine" -9290 GOTO 2710 -9300 REM OK, LET HIM READ IT -9310 z59 = 303:gosub 7620 -9320 GOTO 400 -9330 REM *** BUG *** -9340 A$ = "ADVBUGS.TXT" -9341 OPEN A$ FOR APPEND AS #5 ELSE 9150 -9390 INPUT "Your name: ";A$ -9400 A$=A$+" "+DATE$ -9410 PRINT #5,A$ -9420 PRINT "Enter your gripe in up to five lines (hit return to quit):" -9430 FOR Z0=1 TO 5 -9440 PRINT Z0; -9450 INPUT A$ -9460 IF A$="" THEN 9490 -9470 PRINT #5,A$ -9480 NEXT Z0 -9490 PRINT "Message recorded. Thank you!" -9500 CLOSE #5 -9510 GOTO 410 -9540 REM REINCARNATE HIM -9550 R0=R0+1 -9560 IF R0=1 THEN 9580 -9561 IF R0=2 THEN 9610 -9562 IF R0=3 THEN 9740 -9570 REM ASK HIM IF HE WANTS TO BE REINCARNATED -9580 z59 = 75:gosub 7620 -9590 GOSUB 9860 -9600 GOTO 9630 -9610 z59 = 77:gosub 7620 -9620 GOTO 9580 -9630 IF Z0=0 THEN 9750 -9640 z59 = 76:gosub 7620 -9650 REM PUT HIM BACK IN HOUSE, REARRANGE HIS STUFF -9660 S(18)=7:L=0:DEAD=0:KC=1.03 -9670 FOR X=1 TO T2 -9680 IF S(X)<>-1 THEN 9690 -9685 S(X)=L1 -9690 NEXT X -9700 REM WE'VE PUT THE LAMP IN HOUSE AND OTHER ITEMS WHERE HE DIED -9710 L1=INT(RND(1)*4)+1:L2=L1 -9720 GOTO 320 -9730 REM THIRD DEATH--END OF GAME -9740 z59 = 78:gosub 7620 -9750 PRINT "Oh well..." -9760 GOSUB 6430 -9762 close #1:close #2:close #3 -9770 STOP -9780 rem *** PITS *** -9790 if l1 < 13 THEN 300 -9791 IF l = 1 and (s(18) = -1 or s(18) = l1) then 300 -9800 rem IS HE GOING TO FALL INTO A PIT? -9810 if l1 = 16 or l1 = 17 or l1 = 19 or l1 = 20 or l1 = 25 or l1 = 47 or l1 = 48 or l1 = 59 or l1 = 60 or l1 = 61 or l1 = 75 or l1 = 76 or l1 = 98 then 9840 -9820 goto 300 -9830 rem he fell into a pit -9840 z59 = 44:gosub 7620 -9850 goto 9540 -9860 rem *** SEEK A "YES" OR "NO" -9870 INPUT a$ -9875 if len(a$) <> 0 then 9880 -9877 a$ = " " -9880 a$ = LOWER$(mid$(a$,1,1)) -9890 if a$ <> "y" and a$ <> "n" then 9930 -9900 if a$ = "y" then 9901 else 9910 -9901 z0 = 1 -9910 if a$ = "n" then 9911 else 9920 -9911 z0 = 0 -9920 goto 9945 -9930 PRINT "Yes or No-"; -9940 goto 9870 -9945 return -9950 rem ---- SHORT NAMES FOR STUFF ---- -9961 data "large gold nugget" -9962 data "bars of silver" -9963 data "precious jewelry" -9964 data "many coins" -9965 data "several diamonds" -9966 data "fragile ming vase" -9967 data "glistening pearl" -9968 data "nest of golden eggs" -9969 data "jewel-encrusted trident" -9970 data "egg-sized emerald" -9971 data "platinum pyramid" -9972 data "golden chain" -9973 data "rare spices" -9974 data "persian rug" -9975 data "treasure chest" -9976 data "water" -9977 data "oil" -9978 data "brass lamp" -9979 data "keys" -9980 data "food" -9981 data "bottle" -9982 data "wicker cage" -9983 data "3-foot black rod" -9984 data "clam" -9985 data "magazine" -9986 data "bear" -9987 data "axe" -9988 data "velvet pillow" -9989 data "shards of pottery" -9990 data "oyster" -9991 data "bird" -9992 data "troll" -9993 data "dragon" -9994 data "snake" -9995 data "dwarf" -9996 data "rock" -9997 data "stairs" -9998 data "steps" -9999 data "house" -10000 data "grate" -10001 data "stream" -10002 data "room" -10003 data "bridge" -10004 data "pit" -10005 data "volcano" -10006 data "road" -10007 data "everything" -12500 rem INITIALIZE MESSAGE INDEX -12501 open "AMESSAGE.IDX" for INPUT as #6 else 12505 -12502 goto 12610 -12504 rem Message indx file doesn't exist, create it -12505 OPEN "AMESSAGE.IDX" FOR OUTPUT AS #6 -12506 fpos = -2:b$ = "":fracnt= 0:lastfra=-1:fseek #3,0 -12520 fpos = fpos+len(b$)+2 -12522 INPUT #3,b$ -12530 if len(b$) = 0 then 12531 else 12540 -12531 b$ = " " : fpos = fpos-1 -12540 if b$="#" then 12591 -12550 if instr(b$,"#") = 0 then 12520 -12560 z4 = val(mid$(b$,2)) -12570 if int(z4) = z4 then 12580 -12571 fracnt = fracnt + 1 -12572 if lastfra = int(z4) then 12574 -12573 indx(int(z4)) = fracnt:lastfra = int(z4):PRINT #6,int(z4);",";FRACNT -12574 fraindx(fracnt) = fpos -12576 goto 12585 -12580 indx(int(z4)) = fpos -12581 PRINT #6,int(z4);",";FPOS -12585 PRINT "*"; -12590 goto 12520 -12591 PRINT #6,-999;",";-999 -12592 FOR I = 1 TO FRACNT -12593 PRINT #6,FRAINDX(I) -12594 NEXT I -12595 PRINT #6,-999 -12596 close #3 -12597 open "AMESSAGE" for INPUT as #3 -12600 GOTO 12670 -12604 REM READ AMESSAGE.IDX FILE INTO ARRAYS -12610 INPUT #6,II,I -12612 PRINT "*"; -12615 IF I=-999 THEN 12630 -12620 INDX(II)=I:GOTO 12610 -12630 II = 0 -12640 INPUT #6,I -12641 PRINT "*"; -12650 IF I=-999 THEN 12670 -12660 II = II +1:FRAINDX(II)=I:GOTO 12640 -12670 CLOSE #6:PRINT:PRINT -12680 RETURN From 69eae57d1d6395172d8c987d9949e641daf9ba93 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:50:16 +0100 Subject: [PATCH 086/183] Moved to examples --- bagels.bas | 83 ------------------------------------------------------ 1 file changed, 83 deletions(-) delete mode 100644 bagels.bas diff --git a/bagels.bas b/bagels.bas deleted file mode 100644 index 7168dc7..0000000 --- a/bagels.bas +++ /dev/null @@ -1,83 +0,0 @@ -5 PRINT TAB(33);"BAGELS" -10 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY":PRINT:PRINT -15 REM *** BAGLES NUMBER GUESSING GAME -20 REM *** ORIGINAL SOURCE UNKNOWN BUT SUSPECTED TO BE -25 REM *** LAWRENCE HALL OF SCIENCE UC BERKELY -30 DIM A1(6),A(3),B(3) -40 Y=0:T=255 -50 PRINT:PRINT:PRINT -70 INPUT "WOULD YOU LIKE THE RULES (YES OR NO)";A$ -90 IF LEFT$(A$,1)="N" THEN 150 -100 PRINT:PRINT "I AM THINKING OF A THREE-DIGIT NUMBER. TRY TO GUESS" -110 PRINT "MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:" -120 PRINT " PICO - ONE DIGIT CORRECT BUT IN THE WRONG POSITION" -130 PRINT " FERMI - ONE DIGIT CORRECT AND IN THE RIGHT POSITION" -140 PRINT " BAGELS - NO DIGITS CORRECT" -150 FOR I=1 TO 3 -160 A(I)=INT(10*RND(1)) -165 IF I-1=0 THEN 200 -170 FOR J=1 TO I-1 -180 IF A(I)=A(J) THEN 160 -190 NEXT J -200 NEXT I -210 PRINT:PRINT "O.K. I HAVE A NUMBER IN MIND." -220 FOR I=1 TO 20 -230 PRINT "GUESS #";I -240 INPUT A$ -245 IF LEN(A$)<>3 THEN 630 -250 FOR Z=1 TO 3 -251 A1(Z)=ASC(MID$(A$,Z,1)) -252 NEXT Z -260 FOR J=1 TO 3 -270 IF A1(J)<48 THEN 300 -280 IF A1(J)>57 THEN 300 -285 B(J)=A1(J)-48 -290 NEXT J -295 GOTO 320 -300 PRINT "WHAT?" -310 GOTO 230 -320 IF B(1)=B(2) THEN 650 -330 IF B(2)=B(3) THEN 650 -340 IF B(3)=B(1) THEN 650 -350 C=0:D=0 -360 FOR J=1 TO 2 -370 IF A(J)<>B(J+1) THEN 390 -380 C=C+1 -390 IF A(J+1)<>B(J) THEN 410 -400 C=C+1 -410 NEXT J -420 IF A(1)<>B(3) THEN 440 -430 C=C+1 -440 IF A(3)<>B(1) THEN 460 -450 C=C+1 -460 FOR J=1 TO 3 -470 IF A(J)<>B(J) THEN 490 -480 D=D+1 -490 NEXT J -500 IF D=3 THEN 680 -505 IF C=0 THEN 545 -520 FOR J=1 TO C -530 PRINT "PICO "; -540 NEXT J -545 IF D=0 THEN 580 -550 FOR J=1 TO D -560 PRINT "FERMI "; -570 NEXT J -580 IF C+D<>0 THEN 600 -590 PRINT "BAGELS"; -600 PRINT -605 NEXT I -610 PRINT "OH WELL." -615 PRINT "THAT'S TWNETY GUESSES. MY NUMBER WAS";100*A(1)+10*A(2)+A(3) -620 GOTO 700 -630 PRINT "TRY GUESSING A THREE-DIGIT NUMBER.":GOTO 230 -650 PRINT "OH, I FORGOT TO TELL YOU THAT THE NUMBER I HAVE IN MIND" -660 PRINT "HAS NO TWO DIGITS THE SAME.":GOTO 230 -680 PRINT "YOU GOT IT!!!":PRINT -690 Y=Y+1 -700 INPUT "PLAY AGAIN (YES OR NO)";A$ -720 IF LEFT$(A$,1)="YES" THEN 150 -730 IF Y=0 THEN 750 -740 PRINT:PRINT "A";Y;"POINT BAGELS BUFF!!" -750 PRINT "HOPE YOU HAD FUN. BYE." -999 END From b252cad24f2573f4b2f69363e15cd77335315fc3 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:50:29 +0100 Subject: [PATCH 087/183] Moved to examples --- factorial.bas | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 factorial.bas diff --git a/factorial.bas b/factorial.bas deleted file mode 100644 index 1f443fd..0000000 --- a/factorial.bas +++ /dev/null @@ -1,9 +0,0 @@ -10 REM A SHORT PROGRAM TO CALCULATE FACTORIAL OF -20 REM SUPPLIED NUMBER N -30 INPUT "Please provide N: "; N -35 REM OUTPUT IS NOW A RESERVED KEYWORD -40 OUTPUTVAL = 1 -50 FOR I = 1 TO N -60 OUTPUTVAL = OUTPUTVAL * I -70 NEXT I -80 PRINT "Factorial of N is "; OUTPUTVAL From cfd0783b64e7c7a99edec92581f4bf75a5b945e2 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:50:43 +0100 Subject: [PATCH 088/183] Moved to examples --- regression.bas | 105 ------------------------------------------------- 1 file changed, 105 deletions(-) delete mode 100644 regression.bas diff --git a/regression.bas b/regression.bas deleted file mode 100644 index 3ce9284..0000000 --- a/regression.bas +++ /dev/null @@ -1,105 +0,0 @@ -10 REM A BASIC PROGRAM THAT CAN BE USED FOR REGRESSION TESTING -20 REM OF ALL INTERPRETER FUNCTIONALITY -30 PRINT "*** Testing basic arithmetic functions and multiple statements ***" -40 LET I = 100: LET J = 200 -60 PRINT "Expecting the sum to be 300:" -70 PRINT I + J -80 PRINT "Expecting the product to be 20000:" -90 PRINT I * J -100 PRINT "Expecting the sum to be 20100:" -110 PRINT 100 + I * J -120 PRINT "Expecting sum to be 40000" -130 PRINT (100 + I) * J -140 IF I > J THEN 150 ELSE 180 -150 PRINT "Should not print the smaller value of I which is "; I -160 PRINT I -170 GOTO 180 -180 PRINT "Should print the larger value of J which is "; J -190 PRINT J -200 GOTO 220 -210 PRINT "Should not print this line" -220 PRINT "*** Testing subroutine behaviour ***" -230 PRINT "Calling subroutine" -240 GOSUB 1630 -250 PRINT "Exited subroutine" -260 PRINT "Now testing nested subroutines" -270 GOSUB 1660 -280 PRINT "*** Testing loops ***" -290 PRINT "This loop should count to 5 in increments of 1:" -300 FOR I = 1 TO 5 -310 PRINT I -320 NEXT I -330 PRINT "This loop should count back from 10 to 1 in decrements of 2:" -340 FOR I = 10 TO 1 STEP -2 -350 PRINT I -360 NEXT I -370 PRINT "These nested loops should print 11, 12, 13, 21, 22, 23:" -380 FOR I = 1 TO 2 -390 FOR J = 1 TO 3 -400 PRINT I; J -410 NEXT J -420 NEXT I -430 PRINT "*** Testing arrays ***" -440 DIM A(3, 3) -450 FOR I = 0 TO 2 -460 FOR J = 0 TO 2 -470 LET A(I, J) = 5 -480 NEXT J -490 NEXT I -500 PRINT "This should print 555" -510 PRINT A(0, 0); A(1, 1); A(2, 2) -520 PRINT "*** Testing file i/o ***" -530 OPEN "REGRESSION.TXT" FOR OUTPUT AS #1 -540 PRINT #1,"0123456789Hello World!" -545 PRINT #1,"This is second line for testing" -550 CLOSE #1 -560 OPEN "REGRESSION.TXT" FOR INPUT AS #2 -570 PRINT "The next line should say 'Hello World!'" -580 FSEEK #2,10 -590 INPUT #2,A$ -600 PRINT A$ -620 N = 0 -630 I = 7 -640 PRINT "This loop should count to 5 in increments of 1 twice:" -650 FOR I = 1 TO 10 -660 PRINT I -670 IF I = 5 THEN GOTO 690 -680 NEXT I -690 N = N + 1 -700 IF N < 2 THEN GOTO 650 -810 PRINT "The loop variable I should be equal to 5, I=";I -815 DATA "DATA Statement tests..." -820 READ A$ -825 PRINT "The next line should read: DATA Statement tests..." -830 PRINT A$ -840 DATA 1 , 2 , 3 -850 DATA 4 , 5 , 6 -855 DATA 1.5 , 2 , "test" -858 PRINT "The next three lines should be: 12 34 56" -860 FOR I = 1 TO 3 -870 READ J , K -880 PRINT J ; K -890 NEXT I -900 RESTORE 840 -910 READ I , J , K -920 PRINT "the next line should print 123" -930 PRINT I ; J ; K -970 RESTORE 855 -980 READ A1 , B , C$ -990 PRINT "Float: " ; A1 ; " Int: " ; B ; " String: " ; C$ -1000 RESTORE 850 -1010 READ I , J , K , L -1020 PRINT "the next line should print 4561.5:" -1030 PRINT I; J ; K ; L -1610 PRINT "*** Finished ***" -1620 STOP -1630 REM A SUBROUTINE TEST -1640 PRINT "Executing the subroutine" -1650 RETURN -1660 REM AN OUTER SUBROUTINE -1670 GOSUB 1700 -1680 PRINT "This should be printed second" -1690 RETURN -1700 REM A NESTED SUBROUTINE -1710 PRINT "This should be printed first" -1720 RETURN From 4ecc595d571b35981f2dc20f1d52c02b0dcaa129 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:50:55 +0100 Subject: [PATCH 089/183] Moved to examples --- rock_scissors_paper.bas | 63 ----------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 rock_scissors_paper.bas diff --git a/rock_scissors_paper.bas b/rock_scissors_paper.bas deleted file mode 100644 index 4f30402..0000000 --- a/rock_scissors_paper.bas +++ /dev/null @@ -1,63 +0,0 @@ -10 REM ROCK, SCISSORS, PAPER AGAINST -20 REM THE COMPUTER -30 RANDOMIZE -40 MYSCORE = 0 -50 YOURSCORE = 0 -60 PRINT "Let's play rock-scissors-paper!" -70 INPUT "(R)ock, (S)cissors, (P)aper or e(X)it: "; YOURGUESS$ -75 IF YOURGUESS$ = "X" THEN 160 -80 RANDOM = RND(1) -90 GOSUB 190 -100 PRINT "Your guess: "; YOURGUESS$; ", My guess: "; MYGUESS$ -110 REM ON YOURGUESS$ = "R" GOSUB 300 -120 REM ON YOURGUESS$ = "S" GOSUB 500 -130 REM ON YOURGUESS$ = "P" GOSUB 700 -135 ON INSTR("RSP",YOURGUESS$) GOSUB 300,500,700 -150 GOTO 70 -160 PRINT "My score: "; MYSCORE; " Your score: "; YOURSCORE -170 STOP -190 REM RANDOMLY ASSIGN MYGUESS$ -200 IF RANDOM < 0.3 THEN 210 ELSE 230 -210 MYGUESS$ = "R" -220 RETURN -230 IF RANDOM < 0.6 THEN 240 ELSE 260 -240 MYGUESS$ = "S" -250 RETURN -260 MYGUESS$ = "P" -270 RETURN -300 REM ROCK -310 IF MYGUESS$ = "R" THEN 320 ELSE 340 -320 PRINT "A draw!" -330 RETURN -340 IF MYGUESS$ = "S" THEN 350 ELSE 380 -350 PRINT "I lost!" -360 YOURSCORE = YOURSCORE + 1 -370 RETURN -380 IF MYGUESS$ = "P" THEN 390 ELSE 410 -390 PRINT "I won!" -400 MYSCORE = MYSCORE + 1 -410 RETURN -500 REM SCISSORS -510 IF MYGUESS$ = "S" THEN 520 ELSE 540 -520 PRINT "A draw!" -530 RETURN -540 IF MYGUESS$ = "P" THEN 550 ELSE 580 -550 PRINT "I lost!" -560 YOURSCORE = YOURSCORE + 1 -570 RETURN -580 IF MYGUESS$ = "R" THEN 390 ELSE 410 -590 PRINT "I won!" -600 MYSCORE = MYSCORE + 1 -610 RETURN -700 REM PAPER -710 IF MYGUESS$ = "P" THEN 720 ELSE 740 -720 PRINT "A draw!" -730 RETURN -740 IF MYGUESS$ = "R" THEN 750 ELSE 780 -750 PRINT "I lost!" -760 YOURSCORE = YOURSCORE + 1 -770 RETURN -780 IF MYGUESS$ = "S" THEN 790 ELSE 810 -790 PRINT "I won!" -800 MYSCORE = MYSCORE + 1 -810 RETURN From ea5b24ed6af668edf1fb83be83c9a1a891b38d28 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 12 Sep 2021 17:57:07 +0100 Subject: [PATCH 090/183] Minor formatting fixes, list all BASIC examples --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b1cc45..4719937 100644 --- a/README.md +++ b/README.md @@ -790,17 +790,28 @@ The functions are: * **ASC**(x$) - Returns the character code for *x$*. *x$* is expected to be a single character. Note that despite the name, this function can return codes outside the ASCII range. + * **CHR$**(x) - Returns the character specified by character code *x*. + * **INSTR**(x$, y$[, start[, end]]) - Returns position of *y$* inside *x$*, optionally start searching at position *start* and end at *end*. Returns 0 if no match found. + * **LEN**(x$) - Returns the length of *x$*. + * **LOWER$**(x$) - Returns a lower-case version of *x$*. + * **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y*. If z is provided, that number of characters is returned, if omitted the entire rest of the string is returned + * **LEFT$**(x$, y) - Returns the left most r characters from string x$. If y * exceeds the length of x$, the entire string will be returned. + * **RIGHT$**(x$, y) - Returns the right most r characters from string x$. If y * exceeds the length of x$, the entire string will be returned. + * **STR$**(x) - Returns a string representation of numeric value *x*. + * **UPPER$**(x$) - Returns an upper-case version of *x$* + * **VAL**(x$) - Attempts to convert *x$* to a numeric value. If *x$* is not numeric, returns 0. + * **TAB**(x) - Generates a string containing x spaces with no CR/LF **NOTE** For compatibility with older basic dialetcs, all string indexes are 1 based. @@ -818,7 +829,7 @@ A - 65 ## Example programs -A number of example BASIC programs have been supplied in the repository: +A number of example BASIC programs have been supplied in the repository, in the examples directory: * *regression.bas* - A program to exercise the key programming language constructs in such a way as to allow verification that the interpreter is functioning correctly. @@ -830,6 +841,10 @@ calculate the corresponding factorial *N!*. * *PyBStartrek.bas* - A port of the 1971 Star Trek text based strategy game. +* *adventure-fast.bas* - A port of a 1979 text based adventure game. + +* *bagels.bas* - A guessing game. + ## Informal grammar definition **ABS**(*numerical-expression*) - Calculates the absolute value of the result of *numerical-expression* From 9b4ff6d1f02e104bd937918783bc061d6fea3d8c Mon Sep 17 00:00:00 2001 From: brickbots Date: Sun, 12 Sep 2021 11:59:20 -0700 Subject: [PATCH 091/183] Clarify string indexing, add missing LEFT/RIGHT to grammer section, fix syntax discrepencies in grammer section for input/print --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4719937..7bf31f3 100644 --- a/README.md +++ b/README.md @@ -784,7 +784,7 @@ Seeds may not produce the same result on another platform. Some functions are provided to help you manipulate strings. Functions that return a string have a '$' suffix like string variables. -Note that unlike some other BASIC variants, string positions start at *0*. +**NOTE** For compatibility with older basic dialetcs, all string indexes are 1 based. The functions are: @@ -802,9 +802,9 @@ at position *start* and end at *end*. Returns 0 if no match found. * **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y*. If z is provided, that number of characters is returned, if omitted the entire rest of the string is returned -* **LEFT$**(x$, y) - Returns the left most r characters from string x$. If y * exceeds the length of x$, the entire string will be returned. +* **LEFT$**(x$, y) - Returns the left most y characters from string x$. If y * exceeds the length of x$, the entire string will be returned. -* **RIGHT$**(x$, y) - Returns the right most r characters from string x$. If y * exceeds the length of x$, the entire string will be returned. +* **RIGHT$**(x$, y) - Returns the right most y characters from string x$. If y * exceeds the length of x$, the entire string will be returned. * **STR$**(x) - Returns a string representation of numeric value *x*. @@ -814,7 +814,6 @@ at position *start* and end at *end*. Returns 0 if no match found. * **TAB**(x) - Generates a string containing x spaces with no CR/LF -**NOTE** For compatibility with older basic dialetcs, all string indexes are 1 based. Examples for **ASC**, **CHR$** and **STR$** ``` @@ -882,10 +881,12 @@ will read starting at file position *filepos* **IF$**(*expression*, *string-expression*, *string-expression*) - Evaluates *expression* and returns the value of the result of the first *string-expression* if true, or the second if false. -**INPUT** [*#filenum*,|*input-prompt*:] *simple-variable-list* - Processes user or file input presented as a comma separated list +**INPUT** [*#filenum*,|*input-prompt*;] *simple-variable-list* - Processes user or file input presented as a comma separated list **INSTR**(*hackstack-string-expression*, *needle-string-expression*[, *start-numeric-expression*[, *end-numeric-expression*]]) - Returns position of first *needle-string-expression* inside first *hackstack-string-expression*, optionally start searching at position given by *start-numeric-expression* and optionally ending at position given by *end-numeric-expression*. Returns -1 if no match found. +**LEFT$**(*string-expression*, *char-count*) - Takes the result of *string-expression* and returns the left-most *char-count* characters. If *char-count* exceeds string length the entire string is returned. + **LEN**(*string-expression*) - Returns the length of the result of *string-expression* [**LET**] *variable* = *numeric-expression* | *string-expression* - Assigns a value to a simple variable or array variable @@ -918,7 +919,7 @@ on the next line. **POW**(*base*, *exponent*) - Calculates the result of raising the base to the power of the exponent -**PRINT** [*#filenum*,]*print-list* - Prints a comma separated list of literals or variables to the screen or to a file +**PRINT** [*#filenum*,]*print-list* - Prints a semicolon separated list of literals or variables to the screen or to a file. Included CR/LF by default, but this can be suppressed by ending the statement with a semicolon. **RANDOMIZE** [*numeric-expression*] - Resets random number generator to an unpredictable sequence. With optional seed (*numeric expression*), the sequence is predictable. @@ -931,7 +932,9 @@ optional seed (*numeric expression*), the sequence is predictable. **RESTORE** *line-number* - sets the line number that the next **READ** will start loading constants from. *line-number* must refer to a **DATA** statement -**RND** - Generates a pseudo random number N, where 0 <= N < 1 +**RIGHT$**(*string-expression*, *char-count*) - Takes the result of *string-expression* and returns the right-most *char-count* characters. If *char-count* exceeds string length, the entire string is returned. + +**RND**(*mode*) - For mode values >= 0 generates a pseudo random number N, where 0 <= N < 1. For values < 0 reseeds the PRNG **RNDINT**(*lo-numerical-expression*, *hi-numerical-expression*) - Generates a pseudo random integer N, where *lo-numerical-expression* <= N <= *hi-numerical-expression* From 67b4b3962c123ad4c459da263a9ff5a18606cd3d Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 12 Sep 2021 21:24:21 -0400 Subject: [PATCH 092/183] Delete BITEMS --- examples/BITEMS | 137 ------------------------------------------------ 1 file changed, 137 deletions(-) delete mode 100644 examples/BITEMS diff --git a/examples/BITEMS b/examples/BITEMS deleted file mode 100644 index 00f0731..0000000 --- a/examples/BITEMS +++ /dev/null @@ -1,137 +0,0 @@ -100, N -101, NE -102, E -103, SE -104, S -105, SW -106, W -107, NW -108, U -109, D -110, PLUGH -111, XYZZY -112, PLOVER -113, CROSS -114, CLIMB -115, JUMP -116, FILL -117, EMPTY -117, POUR -118, LOOK -118, L -119, LIGHT -119, ON -120, EXTINGUISH -120, OFF -121, IN -121, ENTER -122, LEAVE -122, OUT -123, INVENTORY -123, I -124, GET -124, CATCH -124, TAKE -125, DROP -125, DUMP -47, ALL -47, EVERYTHING -126, THROW -127, ATTACK -127, KILL -128, FEED -129, WATER -130, LOCK -131, UNLOCK -132, FREE -132, RELEASE -133, WAVE -134, OPEN -135, CLOSE -136, OIL -137, EAT -138, DRINK -139, FEE FIE FOE FOO -140, SHORT -141, LONG -142, BRIEF -143, QUIT -143, STOP -143, END -144, SCORE -145, SAVE -146, LOAD -147, READ -147, EXAMINE -148, YES -148, Y -149, BUG -1, GOLD -1, NUGGET -2, BARS -2, SILVER -3, JEWELRY -4, COINS -5, DIAMONDS -6, MING -6, VASE -7, PEARL -8, EGGS -8, NEST -9, TRIDENT -10, EMERALD -11, PLATINUM -11, PYRAMID -12, CHAIN -13, SPICES -14, PERSIAN -14, RUG -15, TREASURE -15, CHEST -16, WATER -17, OIL -18, LAMP -18, LANTERN -19, KEYS -20, FOOD -21, BOTTLE -22, CAGE -23, ROD -23, WAND -24, CLAM -25, MAGAZINE -26, BEAR -27, AXE -28, VELVET -28, PILLOW -29, SHARDS -30, OYSTER -31, BIRD -32, TROLL -33, DRAGON -34, SNAKE -35, DWARF -36, ROCK -36, BOULDER -37, STAIRS -38, STEPS -39, HOUSE -39, BUILDING -40, GRATE -41, STREAM -42, ROOM -43, BRIDGE -44, PIT -45, VOLCANO -46, ROAD -100, NORTH -101, NORTHEAST -102, EAST -103, SOUTHEAST -104, SOUTH -105, SOUTHWEST -106, WEST -107, NORTHWEST -108, UP -109, DOWN -150,* From c9707713ced71f34266b6f25c934a65a50b236de Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 12 Sep 2021 21:24:49 -0400 Subject: [PATCH 093/183] Delete adventure-fast.bas --- examples/adventure-fast.bas | 1112 ----------------------------------- 1 file changed, 1112 deletions(-) delete mode 100644 examples/adventure-fast.bas diff --git a/examples/adventure-fast.bas b/examples/adventure-fast.bas deleted file mode 100644 index c9d06d5..0000000 --- a/examples/adventure-fast.bas +++ /dev/null @@ -1,1112 +0,0 @@ -10 rem ADVENTURE/3000 VERSION 3.2 27 FEB 1979 AT 5:30 PM -11 rem THIS PROGRAM IS RELATIVELY BUG-FREE, BUT ONE STILL MUST -12 rem realize that murphy 'S LAW STILL PREVAILS! -13 rem -14 rem ADVENTURE: PROGRAMMED IN HP/3000 BASIC BY BENJAMIN MOSER -15 rem JAMES MADISON HIGH SCHOOL, VIENNA, VIRGINIA. THE BASIC LAYOUT OF THE -16 rem GAME WAS CONCEIVED BY DON WOODS & WILLIE CROWTHER, BOTH OF M.I.T. -17 rem -18 rem ADVENTURE WAS PORTED TO THE MACINTOSH PLUS BY THE ELIZABETH AND DAVID HUNTER -19 rem IN MARCH 1998 AND THEN TO PYBASIC FOR THE RASPBERRY RP2040 IN JUNE 2021 -20 rem PRINT "Adventure 3.2 on ";date$;" at ";time$ -21 PRINT "Adventure 3.2" -22 open "AMESSAGE" for INPUT as #3:open "AMOVING" for INPUT as #4 -23 open "ADESCRIP" for INPUT as #1:open "AITEMS" for INPUT as #2 -44 rem dirs is an array of possible room directions, it replaces file AMOVING -45 dim dirs(100,10) -46 dim indx(303) -47 dim fraindx(10) -50 dim s(99) -51 dim v(100) -52 dim k(2) -53 dim o(15) -54 string$ = "100 N 101 NE 102 E 103 SE 104 S 105 SW 106 W 107 NW 108 U 109 D 110 PLUGH 111 XYZZY 112 PLOVER 113 CROSS 114 CLIMB 115 JUMP 116 FILL 117 EMPTY 117 POUR 118 LOOK 118 L 119 LIGHT 119 ON 120 EXTINGUISH 120 OFF 121 IN 121 ENTER 122 LEAVE 122 OUT 123 INVENTORY 123 I 124 GET 124 CATCH 124 TAKE 125 DROP 125 DUMP 047 ALL 047 EVERYTHING 126 THROW 127 ATTACK 127 KILL 128 FEED 129 WATER 130 LOCK 131 UNLOCK 132 FREE 132 RELEASE 133 WAVE 134 OPEN 135 CLOSE 136 OIL 137 EAT 138 DRINK 139 FEE FIE FOE FOO 140 SHORT 141 LONG 142 BRIEF 143 QUIT 143 STOP 143 END 144 SCORE 145 SAVE 146 LOAD 147 READ 147 EXAMINE 148 YES 148 Y 149 BUG 001 GOLD 001 NUGGET 002 BARS 002 SILVER 003 JEWELRY 004 COINS 005 DIAMONDS 006 MING 006 VASE 007 PEARL 008 EGGS 008 NEST 009 TRIDENT 010 EMERALD 011 PLATINUM 011 PYRAMID 012 CHAIN 013 SPICES 014 PERSIAN 014 RUG 015 TREASURE 015 CHEST 016 WATER 017 OIL 018 LAMP 018 LANTERN 019 KEYS 020 FOOD 021 BOTTLE 022 CAGE 023 ROD 023 WAND 024 CLAM 025 MAGAZINE 026 BEAR 027 AXE 028 VELVET 028 PILLOW 029 SHARDS 030 OYSTER 031 BIRD 032 TROLL 033 DRAGON 034 SNAKE 035 DWARF 036 ROCK 036 BOULDER 037 STAIRS 038 STEPS 039 HOUSE 039 BUILDING 040 GRATE 041 STREAM 042 ROOM 043 BRIDGE 044 PIT 045 VOLCANO 046 ROAD 100 NORTH 101 NORTHEAST 102 EAST 103 SOUTHEAST 104 SOUTH 105 SOUTHWEST 106 WEST 107 NORTHWEST 108 UP 109 DOWN " -70 PRINT:PRINT "Initializing."; -110 rem initialize -130 rem total rooms, items,and keywords -150 l1 = int(RND(1)*4)+1 : l2 = l1 -160 g = 0 : b0 = 1 : sn = 1 : d1 = 1 : d2 = 0 : t = 1 : b1 = 0 : b2 = 0 : p1 = 0: dead = 0: lastc$="" -161 l = 0 : c = 0 : d3 = 0 : b3 = 0 : d0 = 2 : t1 = 100 : t2 = 35 : t3 = 149 : r0 = 0 : c0 = 0: c$="" -162 KC = 1.03 -170 for ii = 1 to 99 -171 s(ii) = 0 : v(ii) = 0 -172 next ii -175 PRINT ".";:v(100) = 0 -182 indx(1) = -1:indx(2)=-1 -190 rem read in possible movement direction array -192 for z2 = 1 to 100 -193 INPUT #4,dx1,dx2,dx3,dx4,dx5,dx6,dx7,dx8,dx9,dx0 -194 dirs(z2,1)=dx1:dirs(z2,2)=dx2:dirs(z2,3)=dx3:dirs(z2,4)=dx4:dirs(z2,5)=dx5 -195 dirs(z2,6)=dx6:dirs(z2,7)=dx7:dirs(z2,8)=dx8:dirs(z2,9)=dx9:dirs(z2,10)=dx0 -196 PRINT "."; -197 next z2 -198 close #4 -200 restore 230 -210 rem read in locations of items -220 for z2 = 1 to T2 step 5 -221 read dx1,dx2,dx3,dx4,dx5:s(z2)=dx1:s(z2+1)=dx2:s(z2+2)=dx3:s(z2+3)=dx4:s(z2+4)=dx5 -222 PRINT "."; -228 next z2 -230 data 18,25,23,24,21 -231 data 52,0,71,74,58 -232 data 59,69,66,82,100 -233 data 7,49,7,7,7 -234 data 7,12,13,40,38 -235 data 69,0,46,0,0 -236 data 15,60,82,22,250 -240 for ii = 1 to 15 step 5 -241 read dx1,dx2,dx3,dx4,dx5:o(ii)=dx1:o(ii+1)=dx2:o(ii+2)=dx3:o(ii+3)=dx4:o(ii+4)=dx5 -242 PRINT "."; -243 next ii -245 data 1,2,2,2,2 -246 data 3,4,3,3,2 -247 data 5,3,2,3,3 -260 rem ASK IF HE WANTS DIRECTIONS -263 z59 = 301:gosub 7620 -264 gosub 9860 -266 if z0 then 267 else 320 -267 z59 = 302:gosub 7620 -280 rem command INPUT routine -300 rem PRINT room, items -310 rem If it's dark don't let him see anything -320 if l1 < 13 or l1 = 58 then 350 -321 if l = 1 and (s(18) = l1 or s(18) = -1) then 350 -330 z59 = 45:gosub 7620 -340 goto 400 -350 on d0+1 gosub 6780,7990,8180 -360 v(l1) = 1 -370 gosub 6680 -375 if dead = 1 then 9540 -380 gosub 7800 -390 rem INPUT LOOP --- MULTIPLE COMMAANDS, REMOVE JUNK CHARACTERS -400 rem if len(c$) > 0 then 500 -410 INPUT ">";c$:c$=upper$(c$) -420 if c$ = "" then 410 -425 PRINT:PRINT:NKEY=0 -431 if c$ <> lastc$ then 433 -432 c$ = "":goto 900 -433 numkeys=1:lastc$ = c$:a$="" -449 rem 65-90 = A-Z 48-57 = 0-9 46 = . -450 for x = 1 to len(c$) -460 z5 = asc(mid$(c$,x,1)) -470 if (z5 > 64 and z5 < 91) or (z5 > 47 and z5 < 58) or z5 = 46 then 480 else 471 -471 c$ = mid$(c$,1,x-1)+" "+mid$(c$,x+1) -480 next x -490 if instr(c$," ")=0 then 520 -500 numkeys=2:a$ = mid$(c$,instr(c$," "),len(c$)-instr(c$," ")+1)+" " -510 c$ = mid$(c$,1,instr(c$," ")-1) -520 c$ = " "+c$+" " -800 k(1)=0:k(2)=0:z3 = 0 -870 a1 = instr(string$,c$):if a1=0 then 872 -871 c$ = mid$(string$,a1-3,3):k(1)=int(val(c$)) -872 if numkeys=1 then 900 -873 a1 = instr(string$,a$):if a1=0 then 900 -874 b$ = mid$(string$,a1-3,3):k(2)=int(val(b$)) -890 rem EXOTIC WORDS -900 z0 = 36 -910 if k(1) >= z0 and k(1) <=46 then 930 -920 if k(2) >= z0 and k(2) <=46 then 930 else 980 -930 gosub 8390 -940 PRINT "What do you want to do with the ";d$;"?" -950 goto 410 -980 if k(1) >= 110 and k(1) <= T3 then 1950 -990 if k(2) >= 110 and k(2) <= T3 then 1950 -1000 rem THEN IS IT A DIRECTION -1010 for d = 1 to 10 -1020 if k(1) = d+99 or k(2) = d+99 then 1070 -1030 next d -1040 rem COMMAND NOT A DIRECTION -1050 goto 1950 -1060 rem CAN HE MOVE THAT WAY? -1070 z2 = dirs(L1,d) -1130 if z2 = 255 then 1470 -1140 if z2 < 1 or z2 > 254 then 1220 -1150 rem NORMAL MOVING -1160 rem CHECK FOR SPECIAL MOVE CONDITIONS -1170 goto 1260 -1180 l2 = l1 : l1 = z2 -1190 if s(35) = l2 then 1191 else 1200 -1191 s(35) = l1 -1200 goto 9780 -1220 z59 = 1 -1221 gosub 7620 -1230 goto 400 -1240 rem SPECIAL ROOM DIRECTIONS -1250 rem GRATE -1260 if L1 = 10 and (d = 10 or d = 5) THEN 1280 -1261 IF L1 = 11 and (d = 9 or d = 3) then 1280 else 1320 -1270 rem IF GRATE IS OPEN (G=0) MOVE HIM -1280 if g = 1 then 1180 -1290 z59 = 10:gosub 7620 -1300 goto 400 -1310 rem CAN'T TAKE NUGGET UPPSTAIRS -1320 if not (l1 = 17 and d = 9 and s(1) = -1) then 1360 -1330 z59 = 38:gosub 7620 -1340 goto 400 -1350 rem CRYSTAL BRIDGE AND FISSURE -1360 if not (l1 = 19 and d = 7 or l1 = 20 and d = 3) then 1410 -1370 if b2 then 1180 -1380 z59 = 3:gosub 7620 -1390 goto 400 -1400 rem MT. KING & SNAKE -1410 if not (l1 = 22 and d <> 3 and d <> 9) then 1690 -1420 if sn = 0 then 1180 -1430 z59 = 50:gosub 7620 -1440 goto 400 -1460 rem bedquilt and random directiosn -1470 if l1 <> 44 then 1590 -1480 if RND(1) > 0.5 then 1510 -1490 z59 = 52:gosub 7620 -1500 goto 400 -1510 restore 1530 -1520 rem ROOMS TO JU -1530 data 33,37,45,92,76 -1540 for z3 = 1 to int(RND(1)*5)+1 -1550 read z2 -1560 next z3 -1570 goto 1180 -1580 rem WITT's END -1590 if l1 <> 39 then 1220 -1600 rem SHOULD WE LET HIM OUT? -1610 if RND(1) < 0.15 then 1650 -1620 rem NO -1630 z59 = 52:gosub 7620 -1640 goto 400 -1650 rem YES -1660 z2 = 38 -1670 goto 1180 -1680 rem Narrow Tunnel -1690 if not (l1 = 57 or l1 = 58) then 1780 -1691 if k(1) <> 102 and k(2) <> 102 and k(1) <> 106 and k(2) <> 106 then 1780 -1700 for z3 = 1 to t2 -1710 if z3 = 10 then 1750 -1720 if s(z3) <> -1 then 1750 -1730 z59 = 53:gosub 7620 -1740 goto 400 -1750 next z3 -1760 goto 1180 -1770 rem TROLL -1780 IF L1=60 AND D=2 THEN 1790 -1781 IF L1=61 AND D=6 THEN 1790 ELSE 1860 -1790 on t+1 goto 1180,1800,1820,1840 -1800 z59 = 55:gosub 7620 -1810 goto 400 -1820 z59 = 56:gosub 7620 -1821 z59 = 55:gosub 7620 -1830 T=1:goto 400 -1840 t = 2 -1850 goto 1180 -1860 if not (l1 = 73 and d = 1 and d2 = 0) then 1890 -1870 z59 = 57:gosub 7620 -1880 goto 400 -1890 if not (l1 = 82 and s(33) = l1 and d = 1) then 1180 -1900 rem DRAGON -1910 z59 = 51:gosub 7620 -1920 goto 400 -1940 rem OTHER COMMANDS -1950 z1=k(1):if k(1) >= 110 and k(1) <= T3 then 2090 -1960 z1=k(2):if k(2) >= 110 and k(2) <= T3 then 2090 -1980 rem ITEM BO NO VERB? -1990 if k(1) >= 1 and k(1) <= 35 then 2000 -1991 if k(2) >= 1 and k(2) <= 35 then 2000 else 2040 -2000 for x = 1 to 35 -2020 if k(1) <> x and k(2) <> x then 2030 -2021 restore 9960+x -2022 read d$ -2023 goto 940 -2030 next x -2040 restore 2070 -2050 for x = 1 to int(RND(1)*4)+1 -2051 read b$ -2052 next x -2060 PRINT b$ -2070 data "What?","I don't understand.","I can't understand that.","I don't know that word." -2080 goto 400 -2090 z1 = z1-109 -2095 on z1 goto 2120,2220,2300,2390,2570,2640,2680,2860,2930,3000,3080,3130,3290,3450,3590,3920,4160,4430,4630,4800,4950,5060,5190,5410,5560,5750,5810,5920,6000,6090,6230,6270,6310,6350,6410,8970,9080,9220,4490,9330 -2110 goto 2040 -2120 REM *** PLUGH *** -2130 IF L1<>7 THEN 2170 -2140 IF S(35)=L1 THEN S(35)=0 -2150 Z2=26 -2160 GOTO 1180 -2170 IF L1<>26 THEN 2200 -2180 Z2=7 -2190 GOTO 1180 -2200 z59 = 2:gosub 7620 -2210 GOTO 400 -2220 REM *** XYZZY *** -2230 IF L1<>7 THEN 2270 -2240 IF S(35)=L1 THEN S(35)=0 -2250 Z2=13 -2260 GOTO 1180 -2270 IF L1<>13 THEN 2200 -2280 Z2=7 -2290 GOTO 1180 -2300 REM *** PLOVER *** (CAN'T BRING EMERALD WITH HIM) -2310 IF L1>26 THEN 2360 -2320 IF S(35)<>L1 THEN 2330 -2325 S(35) = 0 -2330 IF S(10)<>-1 THEN 2340 -2335 S(10) = L1 -2340 Z2 = 58 -2350 GOTO 1180 -2360 IF L1<>58 THEN 2200 -2370 Z2=26 -2380 GOTO 1180 -2390 REM *** CROSS *** -2400 IF L1<>19 THEN 2470 -2410 IF B2<>0 THEN 2440 -2420 z59 = 3:gosub 7620 -2430 GOTO 400 -2440 D=7 -2450 REM JUST GIVE NEW DIRECTION, USE MOVE ROUTINE -2460 GOTO 1070 -2470 IF L1<>20 THEN 2510 -2480 IF B2=0 THEN 2420 -2490 D=3 -2500 GOTO 1070 -2510 IF L1<>60 THEN 2540 -2520 D=2 -2530 GOTO 1070 -2540 IF L1<>61 THEN 2200 -2550 D=6 -2560 GOTO 1070 -2570 REM *** CLIMB *** -2580 IF L1<>50 THEN 2200 -2590 REM CAN HE CLIMB BEANSTALK? -2600 IF P1<2 THEN 2200 -2610 REM YES -2620 Z2=70 -2630 GOTO 1180 -2640 REM *** JUMP *** STRICTLY SUICIDAL -2650 IF L1<>16 AND L1<>19 AND L1<>20 AND L1<>27 THEN 2200 -2660 z59 = 4:gosub 7620 -2670 GOTO 9550 -2680 REM FILL -2690 IF S(21)=-1 THEN 2730 -2700 B$="bottle" -2710 PRINT "You don't have the ";b$ -2720 goto 410 -2730 IF B0=0 THEN 2760 -2740 z59 = 5:gosub 7620 -2750 GOTO 410 -2760 IF L1<>7 AND L1<>8 AND L1<>9 AND L1<>35 AND L1<>74 AND L1<>81 THEN 2790 -2770 B0=1:S(16)=-1 -2780 GOTO 2840 -2790 IF L1=49 THEN 2830 -2800 B$="oil" -2810 PRINT "I see no ";B$;" here." -2820 GOTO 400 -2830 B0=2:S(17)=-1 -2840 PRINT "The bottle is now filled." -2850 GOTO 400 -2860 REM *** EMPTY *** -2870 IF S(21)=-1 THEN 2890 -2880 GOTO 2700 -2890 REM EMPTY BOTTLE (ASSUMED FULL) -2900 S(B0+15)=0:B0=0 -2910 PRINT "Emptied" -2920 GOTO 400 -2930 rem *** LOOK *** -2940 if l1 < 13 or l1 = 58 then 2970 -2941 if l = 1 and (s(18) = l1 or s(18) = -1) then 2970 -2950 z59 = 45:gosub 7620 -2960 goto 400 -2970 gosub 8050 -2980 gosub 6680 -2990 goto 400 -3000 rem *** LIGHT *** -3010 if s(18) = -1 then 3040 -3020 b$ = "lamp" -3030 goto 2710 -3040 l = 1 -3050 b$ = "on" -3060 PRINT "The lamp is now ";b$ -3070 goto 2940 -3080 REM *** OFF (EXTINGUSIH) *** -3090 IF S(18)=-1 THEN 3110 -3100 GOTO 3020 -3110 L=0:B$="off" -3120 GOTO 3060 -3130 REM *** ENTER *** -3140 IF L1<>6 THEN 3180 -3150 REM TO HOUSE -3160 D=3 -3170 GOTO 1070 -3180 IF L1<>68 THEN 3240 -3190 REM TO BARREN ROOM -3200 D=3 -3210 GOTO 1070 -3240 FOR D=10 TO 1 step -1 -3250 Z2 = DIRS(L1,D) -3260 IF Z2>0 AND Z2<101 THEN 1150 -3270 NEXT D -3280 GOTO 2200 -3290 REM ** LEAVE *** -3300 IF L1<>7 THEN 3340 -3310 REM LEAVE HOUSE -3320 D=7 -3330 GOTO 1070 -3340 IF L1<>69 THEN 3400 -3350 REM LEAVE BARREN ROOM -3360 D = 7 -3370 GOTO 1070 -3400 FOR D = 1 TO 10 -3410 Z2 = DIRS(L1,D) -3420 IF Z2>0 AND Z2<101 THEN 1150 -3430 NEXT D -3440 GOTO 2200 -3450 REM *** INVENTORY *** -3470 Z0=0 -3480 PRINT "You are carrying:"; -3490 FOR X=1 TO T2 -3510 IF S(X)<>-1 THEN 3540 -3511 restore 9960+x:read b$ -3520 PRINT B$ -3530 Z0 = Z0 + 1 -3540 NEXT X -3550 IF Z0=0 THEN 3551 else 3560 -3551 PRINT "nothing." -3560 PRINT -3570 GOTO 400 -3590 rem *** GET *** -3630 if k(1) = 47 or k(2) = 47 then 3680 -3640 gosub 8280 -3650 if z8 > 0 then 3680 -3660 PRINT "Get what?" -3670 goto 2040 -3680 for z3 = 1 to t2 -3710 if k(1) = 47 or k(2) = 47 then 3730 -3720 if k(1) <> z3 and k(2) <> z3 then 3900 -3730 if s(z3) <> l1 then 3750 -3740 if s(z3) = l1 then 3790 -3750 if k(1) = 47 or k(2) = 47 then 3900 -3760 restore 9960+z3:read a$:PRINT a$;" not here." -3770 goto 3900 -3780 rem MUST CHECK NOW FOR LEGALITY OF TAKING ITEM -3790 z8 = 0 -3800 for x = 1 to t2 -3810 if s(x) <> -1 then 3820 -3811 z8 = z8+1 -3820 next x -3830 if z8 < 7 then 3870 -3840 rem CARRYING TOO MUCH -3850 z59 = 54:gosub 7620 -3860 goto 410 -3870 goto 6880 -3880 s(z3) = -1 -3890 restore 9960+z3:read a$:PRINT a$;":taken." -3900 next z3 -3910 goto 400 -3920 REM *** DROP *** -3940 if k(1) = 47 or k(2) = 47 then 4000 -3950 gosub 8280 -3960 IF Z8>0 THEN 4000 -3970 PRINT "Drop what?" -3980 GOTO 2040 -4000 FOR Z3=1 TO T2 -4030 IF K(1) = 47 or k(2) = 47 THEN 4060 -4040 IF K(1) <> Z3 and k(2) <> z3 THEN 4140 -4050 IF S(Z3)=0 THEN 4140 -4060 IF S(Z3)=-1 THEN 4100 -4070 IF K(1)=47 or k(2)=47 THEN 4140 -4080 restore 9960+z3:read b$:PRINT "You don't have the ";B$ -4090 GOTO 4140 -4100 REM STILL NEED TO ELABORATE ON DROP (BIRD IN CAGE, BOTTLE) -4110 GOTO 7380 -4120 restore 9960+z3:read b$:PRINT B$;":dropped." -4130 S(Z3)=L1 -4140 NEXT Z3 -4150 GOTO 400 -4160 REM *** THROW *** -4170 GOSUB 8280 -4180 IF Z8>0 THEN 4210 -4190 PRINT "Throw what?" -4200 GOTO 2040 -4210 IF S(Z3)<>-1 THEN 2710 -4220 IF NOT (Z3<16 AND S(32)=L1) THEN 4260 -4230 REM THROW TREASURE TO TROLL -4240 z59 = 27:gosub 7620 -4241 S(Z3)=0:T=3:GOTO 400 -4260 IF NOT (Z3=27 AND S(32)=L1) THEN 4300 -4270 REM TRYING TO BUTCHER TROLL? -4280 z59 = 26:gosub 7620 -4281 S(27)=L1:GOTO 400 -4300 IF NOT (Z3=27 AND S(35)=L1) THEN 4380 -4310 REM TRYING TO KILL DWARF -4320 IF RND(1)>0.5 THEN 4360 -4330 z59 = 29:gosub 7620 -4340 GOSUB 8650 -4350 GOTO 4410 -4360 z59 = 30:gosub 7620 -4361 S(35)=0:GOTO 4410 -4380 REM NOTHING SPECIAL, JUST DROP ITEM -4390 IF S(35)<>L1 THEN 4400 -4391 GOSUB 8550 -4400 PRINT "Thrown." -4410 S(Z3) = L1 -4415 if dead = 1 then 9540 -4420 GOTO 400 -4430 REM *** ATTACK *** -4440 GOSUB 8280 -4450 IF NOT (Z3=33 AND S(Z3)=L1 AND L1=82) THEN 4520 -4460 REM HE CAN KILL DRAGON -4470 z59 = 68:gosub 7620 -4480 GOTO 410 -4490 IF L1<>82 THEN 2040 -4500 z59 = 69:gosub 7620 -4501 S(33)=0:D1=0:GOTO 400 -4520 IF S(32)<>L1 THEN 4560 -4530 REM TRYING TO MUNGE TROLL -4540 Z9=FNA(25):GOTO 400 -4560 IF NOT (Z3=26 OR Z3>30) THEN 4600 -4570 REM DANGEROUS TO ATTACK THESE -4580 z59 = 70:gosub 7620 -4590 GOTO 400 -4600 REM NOTHING TO ATTACK -4610 z59 = 71:gosub 7620 -4620 GOTO 400 -4630 REM *** FEED *** -4640 GOSUB 8280 -4650 IF Z3<>35 THEN 4690 -4660 REM CAN'T FEED DWARF! -4670 z59 = 24:gosub 7620 -4680 GOTO 400 -4690 IF S(20) = -1 THEN 4720 -4700 B$ = "FOOD":GOTO 2710 -4720 IF L1=69 THEN 4760 -4730 PRINT "I can't feed it." -4740 z59 = 23:gosub 7620 -4750 GOTO 400 -4760 IF S(20)=L1 THEN 7600 -4770 B1=1:S(20)=0:z59 = 6:gosub 7620 -4790 GOTO 400 -4800 REM *** WATER *** -4810 IF S(16) = -1 THEN 4840 -4820 B$ = "water":GOTO 2710 -4840 IF L1<>50 THEN 2200 -4850 REM GOTO P1+1 OF 4860,4890,4920 -4851 IF P1 = 0 THEN 4860 -4852 IF P1 = 1 THEN 4890 -4853 IF P1 = 2 THEN 4920 -4860 z59 = 7:gosub 7620 -4870 P1=1:S(16)=0:B0=0:GOTO 400 -4890 z59 = 8:gosub 7620 -4900 P1=2:S(16)=0:B0=0:GOTO 400 -4920 z59 = 9:gosub 7620 -4930 P1=0:S(16)=0:B0=0:GOTO 400 -4950 REM *** LOCK *** -4960 IF L1=10 OR L1=11 THEN 4990 -4970 REM NOTHING LOCKABLE -4980 GOTO 2200 -4990 IF S(19)=-1 THEN 5020 -5000 B$="keys":goto 2710 -5020 G=0:z59 = 10:gosub 7620 -5040 GOTO 400 -5060 REM *** UNLOCK *** -5070 IF S(19)<>-1 THEN 5000 -5080 IF L1<>10 AND L1<>11 THEN 5120 -5090 G=1:z59 = 11:gosub 7620 -5110 GOTO 400 -5120 IF L1<>69 THEN 2200 -5130 IF B1>0 THEN 5160 -5140 z59 = 12:gosub 7620 -5150 goto 400 -5160 IF C<>0 THEN 5170 -5165 C=1:B1=2 -5170 z59 = 13:gosub 7620 -5180 GOTO 400 -5190 REM *** FREE *** -5200 IF K(1) = 31 or k(2) = 31 THEN 5240 -5210 REM CAN'T FREE ANYTHING BUT BIRD -5220 z59 = 2:gosub 7620 -5230 GOTO 410 -5240 IF S(31)<>-1 THEN 5220 -5250 S(31) = L1:B3=0 -5260 PRINT "Freed." -5270 IF L1<>22 THEN 5350 -5280 IF SN<>1 THEN 400 -5290 B$="snake" -5300 PRINT "The little bird attacks the green ";B$;" and" -5310 IF L1=82 THEN 5380 -5320 PRINT "drives it off" -5330 SN=0:S(34)=0:GOTO 400 -5350 IF L1<>82 THEN 400 -5360 B$="dragon":GOTO 5300 -5380 PRINT "gets burned to a crisp" -5390 S(31)=0 -5400 GOTO 400 -5410 REM *** WAVE *** -5420 IF K(1) <> 23 and k(2) <> 23 THEN 2200 -5430 IF S(23)=-1 THEN 5460 -5440 B$="rod":GOTO 2710 -5460 REM IS HERE NEAR FISSURE -5470 IF L1<>19 AND L1<>20 THEN 2200 -5480 REM yes -5490 REM GOTO B2+1 OF 5500,5530 -5491 IF B2=0 THEN 5500 -5492 IF B2=1 THEN 5530 -5500 z59 = 14:gosub 7620 -5510 B2=1:GOTO 400 -5530 z59 = 15:gosub 7620 -5540 B2=0:GOTO 400 -5560 REM *** OPEN *** -5570 GOSUB 8280 -5580 IF Z3>0 THEN 5610 -5590 PRINT "Open ";:goto 2040 -5610 IF Z3=40 THEN 5070 -5620 IF S(Z3)=L1 THEN 5650 -5630 PRINT "I see no ";b$;" here.":goto 400 -5650 if z3=24 THEN 5680 -5660 PRINT "I don't know how to open a ";B$:GOTO 400 -5680 IF S(9)=-1 THEN 5710 -5690 z59 = 16:gosub 7620 -5700 GOTO 400 -5710 IF S(Z3) = 0 THEN 2200 -5720 REM HE'S OPENED CLAM, SO PRINT DESCRIPTION OF THIS -5730 REM PUT PEARL IN CUL-DE-SAC -5740 S(7)=43:S(24)=0:S(30)=L1:z59 = 17:gosub 7620 -5750 GOTO 400 -5760 REM *** CLOSE *** -5770 GOSUB 8280 -5780 IF Z3=40 THEN 4960 -5790 z59 = 18:gosub 7620 -5800 GOTO 400 -5810 REM OIL -5820 IF K(1) <> 17 and k(2) <> 17 THEN 2200 -5830 IF S(17)=-1 THEN 5860 -5840 B$="oil":GOTO 5630 -5860 IF L1<>73 THEN 2200 -5870 REM IS DOOR STILL RUSTED -5880 IF D2=1 THEN 2200 -5890 D2=1:S(17)=0:B0=0:z59 = 19:gosub 7620 -5900 GOTO 400 -5910 REM *** EAT *** -5920 IF K(1) = 20 or k(2) = 20 THEN 5950 -5930 z59 = 20:gosub 7620 -5940 GOTO 410 -5950 Z3=20:GOSUB 8490 -5970 IF Z5=0 THEN 410 -5980 z59 = 73:gosub 7620 -5381 S(20)=0:B0=0:GOTO 400 -6000 REM *** DRINK *** -6010 IF K(1) = 16 or k(2) =16 THEN 6040 -6020 z59 = 21:gosub 7620 -6030 GOTO 410 -6040 Z3=16:GOSUB 8490 -6060 IF Z5=0 THEN 410 -6070 z59 = 22:gosub 7620 -6071 S(17)=0:B0=0:GOTO 400 -6090 REM *** FEE FIE FOE FOO *** -6100 IF L1=71 THEN 6130 -6110 z59 = 2:gosub 7620 -6120 GOTO 410 -6130 IF S(8)<>L1 THEN 6180 -6140 REM MAKE NEST VANISH -6150 z59 = 79:gosub 7620 -6160 S(8)=0:GOTO 400 -6180 REM IF S(8)=0 THEN 6110 -6190 S(8)=L1 -6200 rem MAKE NEST RE-APPEAR -6210 z59 = 81:gosub 7620 -6220 GOTO 400 -6230 REM *** SHORT *** -6240 PRINT "Short descriptions" -6250 D0=0:GOTO 400 -6270 REM *** LONG *** -6280 PRINT "Long descriptions" -6290 D0=1:GOTO 400 -6310 REM *** BRIEF *** -6320 PRINT "OK, I'll only describe the room in detail the first time." -6330 D0=2:GOTO 400 -6350 REM *** QUIT *** -6360 PRINT "Save game"; -6370 GOSUB 9860 -6380 IF Z0=1 THEN 8970 -6390 GOTO 9750 -6400 REM SCORE *** -6410 GOSUB 6430 -6420 GOTO 400 -6430 REM PRINT OUT SCORE DATA -6440 GOSUB 6510 -6450 PRINT "Your score is now ";S0 -6451 PRINT "You have explored ";(Z9/T1)*T1;"% of the cave." -6460 RESTORE 6470 -6470 DATA "beginner","novice","experienced","advanced","expert" -6480 Z9 = INT((S0-1)/100) -6481 IF Z9 <= 4 THEN 6483 -6482 Z9=4 -6483 FOR Z0=0 TO Z9 -6484 READ D$ -6485 NEXT Z0 -6490 PRINT "That makes you a ";D$;" adventurer." -6500 RETURN -6510 REM COMPUTE CURRENT SCORE -6520 RESTORE 230 -6530 Z9=0:S0=0 -6540 FOR Z0=1 TO 15 -6550 READ Z1 -6560 IF Z1=0 THEN 6590 -6570 IF V(Z1)<>1 THEN 6580 -6575 S0=S0+4*O(Z0) -6580 IF S(Z0)<>7 THEN 6590 -6585 S0=S0+4*O(Z0) -6590 NEXT Z0 -6600 S0=(G=1)*10 + S0:S0=(SN=0)*20 + S0:S0=(D1=0)*30 + S0:S0=(T=0)*30 + S0:S0=(B1=2)*20 + S0 -6605 S0=(B2=1)*20 + S0:S0=(P1=2)*20 + S0:S0=(D2=1)*20 + S0:S0=(C=1)*20 + S0 -6610 FOR Z0 = 1 TO T1 -6620 IF V(Z0)<>1 THEN 6630 -6621 S0=S0+1:Z9=Z9+1 -6630 NEXT Z0 -6640 RETURN -6660 rem list items at location l1 -6680 fseek #2,0 -6690 for z1 = 1 to t2 -6700 INPUT #2,a$ -6710 if s(z1) <> l1 then 6720 -6711 PRINT a$ -6720 next z1 -6721 IF S(26)<>-1 THEN 6730 -6722 z59 = 67:gosub 7620 -6730 rem CHECK FOR DWARF,PIRATE -6740 gosub 8560 -6745 if dead = 1 then 6770 -6750 gosub 8800 -6760 PRINT -6770 return -6775 rem Print Short room description -6780 fseek #1,0 -6820 for z1 = 1 to l1 -6830 INPUT #1,a$ -6840 next z1 -6850 v(l1) = 1 -6860 PRINT a$ -6870 return -6880 rem SPECIAL GETS -6890 if not (z3 = 24 or z3 = 30 or z3 > 31) then 6930 -6900 rem CAN'T GET THESE FOR SOME REASON -6910 z59 = 61:gosub 7620 -6920 goto 400 -6930 if not (z3 = 12 and c = 0) then 6970 -6940 rem CHAIN -6950 z59 = 58:gosub 7620 -6960 goto 3900 -6970 rem BEAR IS HE FED? UNLOCKED? -6980 if not (z3 = 26 and b1 <> 2) then 7010 -6990 z59 = 61:gosub 7620 -7000 goto 3900 -7010 if not (z3 = 14 and d1 = 1) then 7050 -7020 rem DRAGON AND RUG -7030 z59 = 59:gosub 7620 -7040 goto 3900 -7050 if not (z3 = 16 or z3 = 17) then 7090 -7060 rem OIL AND WATER DO SAME AS FILL -7070 PRINT "Why not say 'fill'?" -7080 goto 3900 -7090 if not (z3 = 22 and b3) then 7140 -7100 rem TAKE BIRD SINCE IT'S IN CAGE -7110 s(31) = -1:PRINT "Bird and ";:goto 3880 -7140 if z3 <> 31 then 7310 -7150 rem GETTING BIRD -7160 if b3 <> 1 then 7210 -7170 rem TAKE CAGE, SINCE BIRD IS IN IT -7180 PRINT "Cage and ";:s(22) = -1:goto 3880 -7210 if s(22) = -1 then 7240 -7220 b$ = "cage":goto 2810 -7240 if s(23) = -1 then 7280 -7250 rem OK TO TAKE BIRD -7260 s(31) = -1 : b3 = 1:goto 3890 -7280 rem ROD SCARES BIRD -7290 z59 = 37:gosub 7620 -7300 goto 3900 -7310 rem BOTTLE FULL? IF SO, GET CONTENTS -7320 if not (z3 = 21 and b0) then 7360 -7330 PRINT "Contents and the "; -7340 s(b0+15) = -1 -7360 goto 3880 -7370 rem SPECIAL "DROP" -7380 IF Z3<>31 THEN 7440 -7390 REM BIRD IN CAGE -7400 S(31)=L1:S(22)=L1:B3=1 -7410 IF Z3<>31 THEN 7420 -7415 PRINT "Cage and "; -7420 IF Z3=22 THEN 7430 -7425 PRINT "Bird and "; -7430 goto 4120 -7440 if z3=22 and b3=1 then 7400 -7450 IF Z3<>21 THEN 7520 -7460 REM BOTTLE -7470 IF B0=0 THEN 4120 -7480 REM BOTTLE IS FULL, DO DROP CONTENTS TOO -7490 PRINT "Contents and "; -7500 S(15+B0)=L1:GOTO 4120 -7520 IF NOT (Z3=16 OR Z3=17) THEN 7541 -7530 PRINT "Try saying 'empty'":goto 4140 -7541 IF Z3<>26 OR T<>1 OR (L1<>60 AND L1<>61) THEN 7550 -7542 z59 = 28:gosub 7620 -7543 T=0:S(26)=L1:S(32)=0:GOTO 400 -7550 IF Z3<>6 THEN 4120 -7560 IF S(28)=L1 THEN 7600 -7570 REM GOODBYE, FRAGILE VASE! -7580 z59 = 43:gosub 7620 -7581 S(6)=0:S(29)=L1:GOTO 400 -7600 z59 = 60:gosub 7620 -7610 GOTO 4120 -7619 rem PRINT MESSAGE -7620 if indx(1) >= 0 then 7640 -7630 gosub 12500 -7640 z59 = int(z59):xtmp = indx(z59) -7645 if z59 <> 2 and z59 <> 61 then 7660 -7650 xtmp = fraindx(xtmp+min(int(RND(1)*5),5)) -7660 fseek #3,xtmp -7670 INPUT #3,b1$ -7672 if len(b1$) = 0 then 7673 else 7690 -7673 b1$ = " " -7690 if instr(b1$,"#") = 0 then 7670 -7700 z4 = val(mid$(b1$,2)) -7705 if int(z4) = z59 then 7720 -7710 if int(z4) < z59 then 7670 -7712 if int(z4) > z59 then 7760 -7720 INPUT #3,b1$ -7730 if mid$(b1$,1,1) = "#" then 7770 -7740 PRINT b1$ -7750 goto 7720 -7760 PRINT "NO DESC. # ";z59;" IN FILE AMESSAGE" -7770 return -7800 rem -7810 rem SITUATION DESCRIPTIONS -7820 rem -7830 rem GRATE -7840 if l1 = 10 or l1 = 11 then 7842 else 7860 -7842 z59 = (g+10):gosub 7620 -7850 rem CRYSTAL BRIDGE -7860 if (l1 = 19 or l1 = 20) and b2 = 1 then 7861 else 7880 -7861 z59 = 14:gosub 7620 -7870 rem PLUGH NOISE -7880 if l1 = 26 and RND(1) > 0.3 then 7881 else 7900 -7881 z59 = 41:gosub 7620 -7890 rem IRON DOOR -7900 if l1 = 73 and d2 = 0 then 7901 else 7920 -7901 z59 = 57:gosub 7620 -7910 rem TROLL -7920 if (l1 = 60 or l1 = 61) and t = 1 then 7921 else 7940 -7921 z59 = 63:gosub 7620 -7930 rem BEAR -7940 if l1 = 69 and b1 = 0 then 7941 else 7950 -7941 z59 = 64:gosub 7620 -7950 if l1 = 69 and b1 = 1 then 7951 else 7970 -7951 z59 = 66:gosub 7620 -7960 rem PLANT IN PIT -7970 if l1 = 48 or l1 = 50 then 7971 else 7980 -7971 z59 = 47+p1:gosub 7620 -7980 return -7990 rem -8000 rem PRINT long room description from "amessage" file -8010 rem description is noormally l1+200 except for -8020 rem maze or forest -8030 rem set v(l1)=1 so as not to repeat long desc(brief mode -8040 v(l1) = 1 -8050 if l1 > 4 then 8080 -8060 z59 = 200:gosub 7620 -8070 goto 8130 -8080 if not (l1 > 88 and l1 < 98 or l1 = 99) then 8110 -8090 z59 = 288:gosub 7620 -8100 goto 8130 -8110 rem normal description -8120 z59 = 200+l1:gosub 7620 -8130 return -8180 rem always give long descriptio nfor forest and maze -8190 if l1 < 5 or (l1 > 88 and l1 < 98) or l1 = 99 then 8220 -8200 if v(l1)=1 then 8201 else 8220 -8201 gosub 6780 -8202 goto 8235 -8210 rem he hassn't seen this room, so give a long desc. -8220 v(l1) = 1 -8230 gosub 7990 -8235 return -8240 rem -8250 rem fetch first item code in k(1 to t2) -8260 rem z8=total # of items found in list -8270 rem z3=item code first found -8280 z8 = 0 : z3 = 0: d$ = "" -8300 for z5 = 1 to 45 -8320 if k(1) <> z5 and k(2) <> z5 then 8360 -8330 z8 = z8+1 -8340 restore 9960+z5:read b$:d$ = b$ -8350 if k(1) = z5 and z8 = 1 then 8352 -8351 if k(2) = z5 and z8 = 1 then 8352 else 8360 -8352 z3 = z5 -8360 next z5 -8370 b$ = d$ -8380 return -8390 rem FIND FIRST ITEM AT ROOM -8400 x1 = 0 -8410 FOR Z1=1 TO 47 -8415 if x1=1 then 8440 -8430 IF K(1) = Z1 OR K(2) = Z1 THEN 8431 else 8440 -8431 restore 9960+z1:read d$:x1=1 -8440 NEXT Z1 -8450 RETURN -8460 REM -8470 REM MAKE SURE HE'S CARRYING ITEM * Z3 -8480 REM -8490 IF S(Z3)=-1 THEN 8530 -8500 PRINT "You don't have the ";A$ -8510 Z5=0 -8520 RETURN -8530 Z5=1 -8540 RETURN -8550 rem *** DWARF *** -8560 if d3 <> 0 then 8640 -8570 rem SHOULD DWARF GIVE AWAY AXE? -8580 if l1 < 13 then 8790 -8590 if RND(1) > 0.05 then 8790 -8600 rem GIVE AWAY AXE -8610 z59 = 80:gosub 7620 -8620 s(27) = l1 : d3 = 1 -8630 goto 8790 -8640 rem SHOULD DWARF ATTACK? -8650 if L1 >= 13 then 8660 -8652 s(35) = 0:goto 8790 -8660 if s(35) <> L1 then 8770 -8670 if RND(1) > 0.5 then 8790 -8680 rem YES! -8690 z59 = 32:gosub 7620 -8700 rem DOES THE KNIFE KILL THE PLAYER? -8705 KC = KC - 0.02 -8706 IF KC >= 0.5 THEN 8710 -8707 KC = 0.5 -8710 if RND(1) <= KC then 8750 -8720 rem YES -8730 PRINT "It gets you!" -8740 dead = 1 : goto 8790 -8750 PRINT "It misses!" -8760 goto 8790 -8770 rem SHOULD WE PUT A DWARF HERE? -8780 if RND(1) >= 0.1 then 8790 -8785 s(35) = l1 -8786 z59=31:gosub 7620 -8790 return -8800 rem *** PIRATE *** -8810 rem FIRST, DOES HE HAVE ANYTHING WORTH STEALING? -8820 z3 = 0 -8830 if l1 < 13 then 8960 -8840 for x = 1 to 15 -8850 if s(x) <> -1 then 8860 -8855 z3 = z3+1 -8860 next x -8870 if z3 < int(RND(1)*4)+1 then 8960 -8880 rem SHOULD WE RIP OFF HIS VALUABLES? -8890 if RND(1) < 0.05 then 8920 -8900 z59 = 34:gosub 7620 -8910 goto 8960 -8920 z59 = 33:gosub 7620 -8930 for x = 1 to 15 -8940 if s(x) <> -1 then 8950 -8945 s(x) = 100 -8950 next x -8960 return -8970 REM *** SAVE GAME *** -8980 INPUT "What do you want to call the save file? ";A$ -8990 OPEN A$ FOR OUTPUT AS #5 ELSE 9010 -9000 GOTO 9030 -9010 PRINT "File ";a$;" not created" -9020 GOTO 410 -9030 PRINT #5,T1;",";T2;",";T3;",";L1;",";L2;",";G;",";B0;",";SN;",";D1;",";D2;",";D0;",";T;",";B1;",";B2;",";P1;",";L;",";C;",";D3;",";B3;",";R0;",";KC -9040 FOR X=1 TO 99 -9041 PRINT #5,S(X);",";V(X) -9042 NEXT X -9043 PRINT #5,V(100) -9044 CLOSE #5 -9050 PRINT "Game saved" -9051 C0 = 0 -9060 if k(1) = 143 OR K(2) = 143 then 9750 -9070 GOTO 410 -9080 REM *** LOAD OLD GAME *** -9090 IF C0=0 THEN 9120 -9100 PRINT "You already have a loaded game!" -9110 GOTO 410 -9120 INPUT "Save file name? ";A$ -9130 OPEN A$ FOR INPUT AS #5 ELSE 9150 -9140 GOTO 9170 -9150 PRINT "Unable to use file ";A$ -9160 GOTO 410 -9170 INPUT #5,T1,T2,T3,L1,L2,G,B0,SN,D1,D2,D0,T,B1,B2,P1,L,C,D3,B3,R0,KCX$ -9171 kc = val(kcx$) -9180 FOR X=1 TO 99 -9191 INPUT #5,SX,VX:s(x)=sx:v(x)=vx -9192 NEXT X -9193 INPUT #5,vx:v(100)=vx -9194 CLOSE #5 -9200 C0=1 -9210 GOTO 300 -9220 rem *** READ THE MAGAZINE *** -9230 GOSUB 8280 -9240 IF Z3=25 THEN 9270 -9250 z59 = 74:gosub 7620 -9260 GOTO 410 -9270 IF S(25)=-1 THEN 9300 -9280 B$="magazine" -9290 GOTO 2710 -9300 REM OK, LET HIM READ IT -9310 z59 = 303:gosub 7620 -9320 GOTO 400 -9330 REM *** BUG *** -9340 A$ = "ADVBUGS.TXT" -9341 OPEN A$ FOR APPEND AS #5 ELSE 9150 -9390 INPUT "Your name: ";A$ -9400 A$=A$+" "+DATE$ -9410 PRINT #5,A$ -9420 PRINT "Enter your gripe in up to five lines (hit return to quit):" -9430 FOR Z0=1 TO 5 -9440 PRINT Z0; -9450 INPUT A$ -9460 IF A$="" THEN 9490 -9470 PRINT #5,A$ -9480 NEXT Z0 -9490 PRINT "Message recorded. Thank you!" -9500 CLOSE #5 -9510 GOTO 410 -9540 REM REINCARNATE HIM -9550 R0=R0+1 -9560 IF R0=1 THEN 9580 -9561 IF R0=2 THEN 9610 -9562 IF R0=3 THEN 9740 -9570 REM ASK HIM IF HE WANTS TO BE REINCARNATED -9580 z59 = 75:gosub 7620 -9590 GOSUB 9860 -9600 GOTO 9630 -9610 z59 = 77:gosub 7620 -9620 GOTO 9580 -9630 IF Z0=0 THEN 9750 -9640 z59 = 76:gosub 7620 -9650 REM PUT HIM BACK IN HOUSE, REARRANGE HIS STUFF -9660 S(18)=7:L=0:DEAD=0:KC=1.03 -9670 FOR X=1 TO T2 -9680 IF S(X)<>-1 THEN 9690 -9685 S(X)=L1 -9690 NEXT X -9700 REM WE'VE PUT THE LAMP IN HOUSE AND OTHER ITEMS WHERE HE DIED -9710 L1=INT(RND(1)*4)+1:L2=L1 -9720 GOTO 320 -9730 REM THIRD DEATH--END OF GAME -9740 z59 = 78:gosub 7620 -9750 PRINT "Oh well..." -9760 GOSUB 6430 -9762 close #1:close #2:close #3 -9770 STOP -9780 rem *** PITS *** -9790 if l1 < 13 THEN 300 -9791 IF l = 1 and (s(18) = -1 or s(18) = l1) then 300 -9800 rem IS HE GOING TO FALL INTO A PIT? -9810 if l1 = 16 or l1 = 17 or l1 = 19 or l1 = 20 or l1 = 25 or l1 = 47 or l1 = 48 or l1 = 59 or l1 = 60 or l1 = 61 or l1 = 75 or l1 = 76 or l1 = 98 then 9840 -9820 goto 300 -9830 rem he fell into a pit -9840 z59 = 44:gosub 7620 -9850 goto 9540 -9860 rem *** SEEK A "YES" OR "NO" -9870 INPUT a$ -9875 if len(a$) <> 0 then 9880 -9877 a$ = " " -9880 a$ = LOWER$(mid$(a$,1,1)) -9890 if a$ <> "y" and a$ <> "n" then 9930 -9900 if a$ = "y" then 9901 else 9910 -9901 z0 = 1 -9910 if a$ = "n" then 9911 else 9920 -9911 z0 = 0 -9920 goto 9945 -9930 PRINT "Yes or No-"; -9940 goto 9870 -9945 return -9950 rem ---- SHORT NAMES FOR STUFF ---- -9961 data "large gold nugget" -9962 data "bars of silver" -9963 data "precious jewelry" -9964 data "many coins" -9965 data "several diamonds" -9966 data "fragile ming vase" -9967 data "glistening pearl" -9968 data "nest of golden eggs" -9969 data "jewel-encrusted trident" -9970 data "egg-sized emerald" -9971 data "platinum pyramid" -9972 data "golden chain" -9973 data "rare spices" -9974 data "persian rug" -9975 data "treasure chest" -9976 data "water" -9977 data "oil" -9978 data "brass lamp" -9979 data "keys" -9980 data "food" -9981 data "bottle" -9982 data "wicker cage" -9983 data "3-foot black rod" -9984 data "clam" -9985 data "magazine" -9986 data "bear" -9987 data "axe" -9988 data "velvet pillow" -9989 data "shards of pottery" -9990 data "oyster" -9991 data "bird" -9992 data "troll" -9993 data "dragon" -9994 data "snake" -9995 data "dwarf" -9996 data "rock" -9997 data "stairs" -9998 data "steps" -9999 data "house" -10000 data "grate" -10001 data "stream" -10002 data "room" -10003 data "bridge" -10004 data "pit" -10005 data "volcano" -10006 data "road" -10007 data "everything" -12500 rem INITIALIZE MESSAGE INDEX -12501 open "AMESSAGE.IDX" for INPUT as #6 else 12505 -12502 goto 12610 -12504 rem Message indx file doesn't exist, create it -12505 OPEN "AMESSAGE.IDX" FOR OUTPUT AS #6 -12506 fpos = -2:b$ = "":fracnt= 0:lastfra=-1:fseek #3,0 -12520 fpos = fpos+len(b$)+2 -12522 INPUT #3,b$ -12530 if len(b$) = 0 then 12531 else 12540 -12531 b$ = " " : fpos = fpos-1 -12540 if b$="#" then 12591 -12550 if instr(b$,"#") = 0 then 12520 -12560 z4 = val(mid$(b$,2)) -12570 if int(z4) = z4 then 12580 -12571 fracnt = fracnt + 1 -12572 if lastfra = int(z4) then 12574 -12573 indx(int(z4)) = fracnt:lastfra = int(z4):PRINT #6,int(z4);",";FRACNT -12574 fraindx(fracnt) = fpos -12576 goto 12585 -12580 indx(int(z4)) = fpos -12581 PRINT #6,int(z4);",";FPOS -12585 PRINT "*"; -12590 goto 12520 -12591 PRINT #6,-999;",";-999 -12592 FOR I = 1 TO FRACNT -12593 PRINT #6,FRAINDX(I) -12594 NEXT I -12595 PRINT #6,-999 -12596 close #3 -12597 open "AMESSAGE" for INPUT as #3 -12600 GOTO 12670 -12604 REM READ AMESSAGE.IDX FILE INTO ARRAYS -12610 INPUT #6,II,I -12612 PRINT "*"; -12615 IF I=-999 THEN 12630 -12620 INDX(II)=I:GOTO 12610 -12630 II = 0 -12640 INPUT #6,I -12641 PRINT "*"; -12650 IF I=-999 THEN 12670 -12660 II = II +1:FRAINDX(II)=I:GOTO 12640 -12670 CLOSE #6:PRINT:PRINT -12680 RETURN From 6a7eb321024e541915e86d780c55dd57f36025fb Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 13 Sep 2021 00:42:35 -0400 Subject: [PATCH 094/183] Add files via upload --- examples/adventure-fast.bas | 1145 +++++++++++++++++++++++++++++++++++ 1 file changed, 1145 insertions(+) create mode 100644 examples/adventure-fast.bas diff --git a/examples/adventure-fast.bas b/examples/adventure-fast.bas new file mode 100644 index 0000000..a19a645 --- /dev/null +++ b/examples/adventure-fast.bas @@ -0,0 +1,1145 @@ +10 rem ADVENTURE/3000 VERSION 3.2 27 FEB 1979 AT 5:30 PM +11 rem THIS PROGRAM IS RELATIVELY BUG-FREE, BUT ONE STILL MUST +12 rem realize that murphy 'S LAW STILL PREVAILS! +13 rem +14 rem ADVENTURE: PROGRAMMED IN HP/3000 BASIC BY BENJAMIN MOSER +15 rem JAMES MADISON HIGH SCHOOL, VIENNA, VIRGINIA. THE BASIC LAYOUT OF THE +16 rem GAME WAS CONCEIVED BY DON WOODS & WILLIE CROWTHER, BOTH OF M.I.T. +17 rem +18 rem ADVENTURE WAS PORTED TO THE MACINTOSH PLUS BY THE ELIZABETH AND DAVID HUNTER +19 rem IN MARCH 1998 AND THEN TO PYBASIC FOR THE RASPBERRY RP2040 IN JUNE 2021 +20 rem PRINT "Adventure 3.2 on ";date$;" at ";time$ +21 PRINT "Adventure 3.2 for PyBASIC" +22 open "AMESSAGE" for INPUT as #3:open "AMOVING" for INPUT as #4 +23 open "ADESCRIP" for INPUT as #1:open "AITEMS" for INPUT as #2 +44 rem dirs is an array of possible room directions, it replaces file AMOVING +45 dim dirs(100,10) +46 dim indx(303) +47 dim fraindx(10) +50 dim s(99) +51 dim v(100) +52 dim k(200) +53 dim o(15) +70 PRINT:PRINT "Initializing."; +110 rem initialize +130 rem total rooms, items,and keywords +150 l1 = int(RND(1)*4)+1 : l2 = l1 +160 g = 0 : b0 = 1 : sn = 1 : d1 = 1 : d2 = 0 : t = 1 : b1 = 0 : b2 = 0 : p1 = 0: dead = 0 +161 l = 0 : c = 0 : d3 = 0 : b3 = 0 : d0 = 2 : t1 = 100 : t2 = 35 : t3 = 149 : r0 = 0 : c0 = 0: c$="" +162 KC = 1.03 +170 for ii = 1 to 99 +171 s(ii) = 0 : v(ii) = 0 +172 next ii +175 PRINT ".";:v(100) = 0 +182 indx(1) = -1:indx(2)=-1 +190 rem read in possible movement direction array +192 for z2 = 1 to 100 +193 INPUT #4,dx1,dx2,dx3,dx4,dx5,dx6,dx7,dx8,dx9,dx0 +194 dirs(z2,1)=dx1:dirs(z2,2)=dx2:dirs(z2,3)=dx3:dirs(z2,4)=dx4:dirs(z2,5)=dx5 +195 dirs(z2,6)=dx6:dirs(z2,7)=dx7:dirs(z2,8)=dx8:dirs(z2,9)=dx9:dirs(z2,10)=dx0 +196 PRINT "."; +197 next z2 +198 close #4 +200 restore 230 +210 rem read in locations of items +220 for z2 = 1 to T2 step 5 +221 read dx1,dx2,dx3,dx4,dx5:s(z2)=dx1:s(z2+1)=dx2:s(z2+2)=dx3:s(z2+3)=dx4:s(z2+4)=dx5 +222 PRINT "."; +228 next z2 +230 data 18,25,23,24,21 +231 data 52,0,71,74,58 +232 data 59,69,66,82,100 +233 data 7,49,7,7,7 +234 data 7,12,13,40,38 +235 data 69,0,46,0,0 +236 data 15,60,82,22,250 +240 for ii = 1 to 15 step 5 +241 read dx1,dx2,dx3,dx4,dx5:o(ii)=dx1:o(ii+1)=dx2:o(ii+2)=dx3:o(ii+3)=dx4:o(ii+4)=dx5 +242 PRINT "."; +243 next ii +245 data 1,2,2,2,2 +246 data 3,4,3,3,2 +247 data 5,3,2,3,3 +260 rem ASK IF HE WANTS DIRECTIONS +263 z59 = 301:gosub 7620 +264 gosub 9860 +266 if z0 then 267 else 320 +267 z59 = 302:gosub 7620 +280 rem command INPUT routine +300 rem PRINT room, items +310 rem If it's dark don't let him see anything +320 if l1 < 13 or l1 = 58 then 350 +321 if l = 1 and (s(18) = l1 or s(18) = -1) then 350 +330 z59 = 45:gosub 7620 +340 goto 400 +350 on d0+1 gosub 6780,7990,8180 +360 v(l1) = 1 +370 gosub 6680 +375 if dead = 1 then 9540 +380 gosub 7800 +390 rem INPUT LOOP --- MULTIPLE COMMAANDS, REMOVE JUNK CHARACTERS +400 if len(c$) > 0 then 500 +410 INPUT ">";c$:c$=upper$(c$) +420 if c$ = "" then 410 +425 PRINT:PRINT +430 c$ = upper$(c$) +449 rem 65-90 = A-Z 48-57 = 0-9 46 = . 44 = , +450 for x = 1 to len(c$) +460 z5 = asc(mid$(c$,x,1)) +470 if (z5 > 64 and z5 < 91) or (z5 > 47 and z5 < 58) or z5 = 44 then 480 else 471 +471 c$ = mid$(c$,1,x-1)+" "+mid$(c$,x+1) +480 next x +490 if mid$(c$,len(c$),1) = "," then 500 +491 c$ = c$+"," +500 z4 = instr(c$,",") +510 a$ = upper$(mid$(c$,1,z4-1)) : c$ = mid$(c$,z4+1) +550 a$ = " "+a$+" " +560 rem search a$ for keywords,puut kwd code into k(x) +570 rem items +580 data 1,"GOLD",1,"NUGGET",2,"BARS",2,"SILVER",3,"JEWELRY",4,"COINS" +590 data 5,"DIAMONDS",6,"MING",6,"VASE",7,"PEARL",8,"EGGS",8,"NEST" +600 data 9,"TRIDENT",10,"EMERALD",11,"PLATINUM",11,"PYRAMID",12,"CHAIN" +610 data 13,"SPICES",14,"PERSIAN",14,"RUG",15,"TREASURE",15,"CHEST" +620 data 16,"WATER",17,"OIL",18,"LAMP",18,"LANTERN",19,"KEYS",20,"FOOD",21,"BOTTLE" +630 data 22,"CAGE",23,"ROD",23,"WAND",24,"CLAM",25,"MAGAZINE",26,"BEAR" +640 data 27,"AXE",28,"VELVET",28,"PILLOW",29,"SHARDS",30,"OYSTER" +650 data 31,"BIRD",32,"TROLL",33,"DRAGON",34,"SNAKE",35,"DWARF" +660 data 36,"ROCK",36,"BOULDER",37,"STAIRS",38,"STEPS",39,"HOUSE",39,"BUILDING" +665 data 40,"GRATE",41,"STREAM",42,"ROOM",43,"BRIDGE",44,"PIT",45,"VOLCANO" +667 data 46,"ROAD",47,"ALL",47,"EVERYTHING" +670 rem DIRECTIONS +680 data 100,"N",100,"NORTH",101,"NE",101,"NORTHEAST",102,"E",102,"EAST" +682 data 103,"SE",103,"SOUTHEAST",104,"S",104,"SOUTH",105,"SW",105,"SOUTHWEST" +684 data 106,"W",106,"WEST",107,"NW",107,"NORTHWEST",108,"U",108,"UP",109,"D",109,"DOWN" +690 rem VERBS +700 data 110,"PLUGH",111,"XYZZY",112,"PLOVER",113,"CROSS",114,"CLIMB",115,"JUMP" +710 data 116,"FILL",117,"EMPTY",117,"POUR",118,"LOOK",118,"L",119,"LIGHT",119,"ON",120,"EXTINGUISH" +720 data 120,"OFF",121,"IN",121,"ENTER",122,"LEAVE",122,"OUT",123,"INVENTORY",123,"I" +730 data 124,"GET",124,"CATCH",124,"TAKE",125,"DROP",125,"DUMP",126,"THROW",127,"ATTACK" +740 data 127,"KILL",128,"FEED",129,"WATER",130,"LOCK",131,"UNLOCK" +750 data 132,"FREE",132,"RELEASE",133,"WAVE",134,"OPEN",135,"CLOSE" +760 data 136,"OIL",137,"EAT",138,"DRINK",139,"FEE FIE FOE FOO" +765 data 140,"SHORT",141,"LONG",142,"BRIEF",143,"QUIT",143,"STOP",143,"END" +770 data 144,"SCORE",145,"SAVE",146,"LOAD",147,"READ",147,"EXAMINE" +775 data 148,"YES",148,"Y",149,"BUG",150,"*" +780 restore 580 +790 for i = 1 to 200 +791 k(i) = 0 +792 next i +800 z1 = 0 : z3 = 0 +810 rem T3=TOTAL NUMBER OF KEYWORDS +820 if z1 > t3 then 900 +830 read z1,b$ +840 b$ = " "+b$+" " +850 rem IF KEYWORD WAS FOUND, NOTE THIS IN K(Z1) +860 if instr(a$,b$) = 0 then goto 820 +861 k(z1) = 1 +870 rem KEYWORDK #Z1 NOT FOUND +880 goto 820 +890 rem EXOTIC WORDS +900 z0 = 36 +910 for x = z0 to 46 +920 if k(x) = 0 then 960 +930 gosub 8390 +940 print "What do you want to do with the ";d$;"?" +950 goto 410 +960 next x +970 for x = 110 to t3 +980 if k(x) = 1 then 1950 +990 next x +1000 rem THEN IT'S A DIRECTION +1010 for d = 1 to 10 +1020 if k(d+99) = 1 then 1070 +1030 next d +1040 rem COMMAND NOT A DIRECTION +1050 goto 1950 +1060 rem CAN HE MOVE THAT WAY? +1070 z2 = dirs(L1,d) +1130 if z2 = 255 then 1470 +1140 if z2 < 1 or z2 > 254 then 1220 +1150 rem NORMAL MOVING +1160 rem CHECK FOR SPECIAL MOVE CONDITIONS +1170 goto 1260 +1180 l2 = l1 : l1 = z2 +1190 if s(35) = l2 then 1191 else 1200 +1191 s(35) = l1 +1200 goto 9780 +1220 z59 = 1 +1221 gosub 7620 +1230 goto 400 +1240 rem SPECIAL ROOM DIRECTIONS +1250 rem GRATE +1260 if L1 = 10 and (d = 10 or d = 5) THEN 1280 +1261 IF L1 = 11 and (d = 9 or d = 3) then 1280 else 1320 +1270 rem IF GRATE IS OPEN (G=0) MOVE HIM +1280 if g = 1 then 1180 +1290 z59 = 10:gosub 7620 +1300 goto 400 +1310 rem CAN'T TAKE NUGGET UPPSTAIRS +1320 if not (l1 = 17 and d = 9 and s(1) = -1) then 1360 +1330 z59 = 38:gosub 7620 +1340 goto 400 +1350 rem CRYSTAL BRIDGE AND FISSURE +1360 if not (l1 = 19 and d = 7 or l1 = 20 and d = 3) then 1410 +1370 if b2 then 1180 +1380 z59 = 3:gosub 7620 +1390 goto 400 +1400 rem MT. KING & SNAKE +1410 if not (l1 = 22 and d <> 3 and d <> 9) then 1690 +1420 if sn = 0 then 1180 +1430 z59 = 50:gosub 7620 +1440 goto 400 +1460 rem bedquilt and random directiosn +1470 if l1 <> 44 then 1590 +1480 if RND(1) > 0.5 then 1510 +1490 z59 = 52:gosub 7620 +1500 goto 400 +1510 restore 1530 +1520 rem ROOMS TO JU +1530 data 33,37,45,92,76 +1540 for z3 = 1 to int(RND(1)*5)+1 +1550 read z2 +1560 next z3 +1570 goto 1180 +1580 rem WITT's END +1590 if l1 <> 39 then 1220 +1600 rem SHOULD WE LET HIM OUT? +1610 if RND(1) < 0.15 then 1650 +1620 rem NO +1630 z59 = 52:gosub 7620 +1640 goto 400 +1650 rem YES +1660 z2 = 38 +1670 goto 1180 +1680 rem Narrow Tunnel +1690 if not (l1 = 57 or l1 = 58) then 1780 +1691 IF K(102)=0 AND K(106)=0 THEN 1780 +1700 for z3 = 1 to t2 +1710 if z3 = 10 then 1750 +1720 if s(z3) <> -1 then 1750 +1730 z59 = 53:gosub 7620 +1740 goto 400 +1750 next z3 +1760 goto 1180 +1770 rem TROLL +1780 IF L1=60 AND D=2 THEN 1790 +1781 IF L1=61 AND D=6 THEN 1790 ELSE 1860 +1790 on t+1 goto 1180,1800,1820,1840 +1800 z59 = 55:gosub 7620 +1810 goto 400 +1820 z59 = 56:gosub 7620 +1821 z59 = 55:gosub 7620 +1830 T=1:goto 400 +1840 t = 2 +1850 goto 1180 +1860 if not (l1 = 73 and d = 1 and d2 = 0) then 1890 +1870 z59 = 57:gosub 7620 +1880 goto 400 +1890 if not (l1 = 82 and s(33) = l1 and d = 1) then 1180 +1900 rem DRAGON +1910 z59 = 51:gosub 7620 +1920 goto 400 +1940 rem OTHER COMMANDS +1950 for z1 = 100 to t3 +1960 if k(z1)=1 then 2090 +1970 next z1 +1980 rem ITEM BO NO VERB? +1990 restore 9961 +2000 for x = 1 to 35 +2010 read d$ +2020 if k(x) = 1 then 940 +2030 next x +2040 restore 2070 +2050 for x = 1 to int(RND(1)*4)+1 +2051 read b$ +2052 next x +2060 PRINT b$ +2070 data "What?","I don't understand.","I can't understand that.","I don't know that word." +2080 goto 400 +2090 z1 = z1-109 +2095 on z1 goto 2120,2220,2300,2390,2570,2640,2680,2860,2930,3000,3080,3130,3290,3450,3590,3920,4160,4430,4630,4800,4950,5060,5190,5410,5560,5750,5810,5920,6000,6090,6230,6270,6310,6350,6410,8970,9080,9220,4490,9330 +2110 goto 2040 +2120 REM *** PLUGH *** +2130 IF L1<>7 THEN 2170 +2140 IF S(35)=L1 THEN S(35)=0 +2150 Z2=26 +2160 GOTO 1180 +2170 IF L1<>26 THEN 2200 +2180 Z2=7 +2190 GOTO 1180 +2200 z59 = 2:gosub 7620 +2210 GOTO 400 +2220 REM *** XYZZY *** +2230 IF L1<>7 THEN 2270 +2240 IF S(35)=L1 THEN S(35)=0 +2250 Z2=13 +2260 GOTO 1180 +2270 IF L1<>13 THEN 2200 +2280 Z2=7 +2290 GOTO 1180 +2300 REM *** PLOVER *** (CAN'T BRING EMERALD WITH HIM) +2310 IF L1>26 THEN 2360 +2320 IF S(35)<>L1 THEN 2330 +2325 S(35) = 0 +2330 IF S(10)<>-1 THEN 2340 +2335 S(10) = L1 +2340 Z2 = 58 +2350 GOTO 1180 +2360 IF L1<>58 THEN 2200 +2370 Z2=26 +2380 GOTO 1180 +2390 REM *** CROSS *** +2400 IF L1<>19 THEN 2470 +2410 IF B2<>0 THEN 2440 +2420 z59 = 3:gosub 7620 +2430 GOTO 400 +2440 D=7 +2450 REM JUST GIVE NEW DIRECTION, USE MOVE ROUTINE +2460 GOTO 1070 +2470 IF L1<>20 THEN 2510 +2480 IF B2=0 THEN 2420 +2490 D=3 +2500 GOTO 1070 +2510 IF L1<>60 THEN 2540 +2520 D=2 +2530 GOTO 1070 +2540 IF L1<>61 THEN 2200 +2550 D=6 +2560 GOTO 1070 +2570 REM *** CLIMB *** +2580 IF L1<>50 THEN 2200 +2590 REM CAN HE CLIMB BEANSTALK? +2600 IF P1<2 THEN 2200 +2610 REM YES +2620 Z2=70 +2630 GOTO 1180 +2640 REM *** JUMP *** STRICTLY SUICIDAL +2650 IF L1<>16 AND L1<>19 AND L1<>20 AND L1<>27 THEN 2200 +2660 z59 = 4:gosub 7620 +2670 GOTO 9550 +2680 REM FILL +2690 IF S(21)=-1 THEN 2730 +2700 B$="bottle" +2710 PRINT "You don't have the ";b$ +2720 goto 410 +2730 IF B0=0 THEN 2760 +2740 z59 = 5:gosub 7620 +2750 GOTO 410 +2760 IF L1<>7 AND L1<>8 AND L1<>9 AND L1<>35 AND L1<>74 AND L1<>81 THEN 2790 +2770 B0=1:S(16)=-1 +2780 GOTO 2840 +2790 IF L1=49 THEN 2830 +2800 B$="oil" +2810 PRINT "I see no ";B$;" here." +2820 GOTO 400 +2830 B0=2:S(17)=-1 +2840 PRINT "The bottle is now filled." +2850 GOTO 400 +2860 REM *** EMPTY *** +2870 IF S(21)=-1 THEN 2890 +2880 GOTO 2700 +2890 REM EMPTY BOTTLE (ASSUMED FULL) +2900 S(B0+15)=0:B0=0 +2910 PRINT "Emptied" +2920 GOTO 400 +2930 rem *** LOOK *** +2940 if l1 < 13 or l1 = 58 then 2970 +2941 if l = 1 and (s(18) = l1 or s(18) = -1) then 2970 +2950 z59 = 45:gosub 7620 +2960 goto 400 +2970 gosub 8050 +2980 gosub 6680 +2990 goto 400 +3000 rem *** LIGHT *** +3010 if s(18) = -1 then 3040 +3020 b$ = "lamp" +3030 goto 2710 +3040 l = 1 +3050 b$ = "on" +3060 PRINT "The lamp is now ";b$ +3070 goto 2940 +3080 REM *** OFF (EXTINGUSIH) *** +3090 IF S(18)=-1 THEN 3110 +3100 GOTO 3020 +3110 L=0:B$="off" +3120 GOTO 3060 +3130 REM *** ENTER *** +3140 IF L1<>6 THEN 3180 +3150 REM TO HOUSE +3160 D=3 +3170 GOTO 1070 +3180 IF L1<>68 THEN 3240 +3190 REM TO BARREN ROOM +3200 D=3 +3210 GOTO 1070 +3240 FOR D=10 TO 1 step -1 +3250 Z2 = DIRS(L1,D) +3260 IF Z2>0 AND Z2<101 THEN 1150 +3270 NEXT D +3280 GOTO 2200 +3290 REM ** LEAVE *** +3300 IF L1<>7 THEN 3340 +3310 REM LEAVE HOUSE +3320 D=7 +3330 GOTO 1070 +3340 IF L1<>69 THEN 3400 +3350 REM LEAVE BARREN ROOM +3360 D = 7 +3370 GOTO 1070 +3400 FOR D = 1 TO 10 +3410 Z2 = DIRS(L1,D) +3420 IF Z2>0 AND Z2<101 THEN 1150 +3430 NEXT D +3440 GOTO 2200 +3450 REM *** INVENTORY *** +3470 Z0=0 +3480 PRINT "You are carrying:"; +3490 FOR X=1 TO T2 +3510 IF S(X)<>-1 THEN 3540 +3511 restore 9960+x:read b$ +3520 PRINT B$ +3530 Z0 = Z0 + 1 +3540 NEXT X +3550 IF Z0=0 THEN 3551 else 3560 +3551 PRINT "nothing." +3560 PRINT +3570 GOTO 400 +3590 rem *** GET *** +3630 if k(47) = 1 then 3680 +3640 gosub 8280 +3650 if z8 > 0 then 3680 +3660 PRINT "Get what?" +3670 goto 2040 +3680 for z3 = 1 to t2 +3710 if k(47) = 1 then 3730 +3720 if k(z3) = 0 then 3900 +3730 if s(z3) <> l1 then 3750 +3740 if s(z3) = l1 then 3790 +3750 if k(47) = 1 then 3900 +3760 restore 9960+z3:read a$:PRINT a$;" not here." +3770 goto 3900 +3780 rem MUST CHECK NOW FOR LEGALITY OF TAKING ITEM +3790 z8 = 0 +3800 for x = 1 to t2 +3810 if s(x) <> -1 then 3820 +3811 z8 = z8+1 +3820 next x +3830 if z8 < 7 then 3870 +3840 rem CARRYING TOO MUCH +3850 z59 = 54:gosub 7620 +3860 goto 410 +3870 goto 6880 +3880 s(z3) = -1 +3890 restore 9960+z3:read a$:PRINT a$;":taken." +3900 next z3 +3910 goto 400 +3920 REM *** DROP *** +3940 if k(47) = 1 then 4000 +3950 gosub 8280 +3960 IF Z8>0 THEN 4000 +3970 PRINT "Drop what?" +3980 GOTO 2040 +4000 FOR Z3=1 TO T2 +4030 IF K(47)=1 THEN 4060 +4040 IF K(Z3)<>1 THEN 4140 +4050 IF S(Z3)=0 THEN 4140 +4060 IF S(Z3)=-1 THEN 4100 +4070 IF K(47)=1 THEN 4140 +4080 restore 9960+z3:read b$:PRINT "You don't have the ";B$ +4090 GOTO 4140 +4100 REM STILL NEED TO ELABORATE ON DROP (BIRD IN CAGE, BOTTLE) +4110 GOTO 7380 +4120 restore 9960+z3:read b$:PRINT B$;":dropped." +4130 S(Z3)=L1 +4140 NEXT Z3 +4150 GOTO 400 +4160 REM *** THROW *** +4170 GOSUB 8280 +4180 IF Z8>0 THEN 4210 +4190 PRINT "Throw what?" +4200 GOTO 2040 +4210 IF S(Z3)<>-1 THEN 2710 +4220 IF NOT (Z3<16 AND S(32)=L1) THEN 4260 +4230 REM THROW TREASURE TO TROLL +4240 z59 = 27:gosub 7620 +4241 S(Z3)=0:T=3:GOTO 400 +4260 IF NOT (Z3=27 AND S(32)=L1) THEN 4300 +4270 REM TRYING TO BUTCHER TROLL? +4280 z59 = 26:gosub 7620 +4281 S(27)=L1:GOTO 400 +4300 IF NOT (Z3=27 AND S(35)=L1) THEN 4380 +4310 REM TRYING TO KILL DWARF +4320 IF RND(1)>0.5 THEN 4360 +4330 z59 = 29:gosub 7620 +4340 GOSUB 8650 +4350 GOTO 4410 +4360 z59 = 30:gosub 7620 +4361 S(35)=0:GOTO 4410 +4380 REM NOTHING SPECIAL, JUST DROP ITEM +4390 IF S(35)<>L1 THEN 4400 +4391 GOSUB 8550 +4400 PRINT "Thrown." +4410 S(Z3) = L1 +4415 if dead = 1 then 9540 +4420 GOTO 400 +4430 REM *** ATTACK *** +4440 GOSUB 8280 +4450 IF NOT (Z3=33 AND S(Z3)=L1 AND L1=82) THEN 4520 +4460 REM HE CAN KILL DRAGON +4470 z59 = 68:gosub 7620 +4480 GOTO 410 +4490 IF L1<>82 THEN 2040 +4500 z59 = 69:gosub 7620 +4501 S(33)=0:D1=0:GOTO 400 +4520 IF S(32)<>L1 THEN 4560 +4530 REM TRYING TO MUNGE TROLL +4540 Z9=FNA(25):GOTO 400 +4560 IF NOT (Z3=26 OR Z3>30) THEN 4600 +4570 REM DANGEROUS TO ATTACK THESE +4580 z59 = 70:gosub 7620 +4590 GOTO 400 +4600 REM NOTHING TO ATTACK +4610 z59 = 71:gosub 7620 +4620 GOTO 400 +4630 REM *** FEED *** +4640 GOSUB 8280 +4650 IF Z3<>35 THEN 4690 +4660 REM CAN'T FEED DWARF! +4670 z59 = 24:gosub 7620 +4680 GOTO 400 +4690 IF S(20) = -1 THEN 4720 +4700 B$ = "FOOD":GOTO 2710 +4720 IF L1=69 THEN 4760 +4730 PRINT "I can't feed it." +4740 z59 = 23:gosub 7620 +4750 GOTO 400 +4760 IF S(20)=L1 THEN 7600 +4770 B1=1:S(20)=0:z59 = 6:gosub 7620 +4790 GOTO 400 +4800 REM *** WATER *** +4810 IF S(16) = -1 THEN 4840 +4820 B$ = "water":GOTO 2710 +4840 IF L1<>50 THEN 2200 +4850 REM GOTO P1+1 OF 4860,4890,4920 +4851 IF P1 = 0 THEN 4860 +4852 IF P1 = 1 THEN 4890 +4853 IF P1 = 2 THEN 4920 +4860 z59 = 7:gosub 7620 +4870 P1=1:S(16)=0:B0=0:GOTO 400 +4890 z59 = 8:gosub 7620 +4900 P1=2:S(16)=0:B0=0:GOTO 400 +4920 z59 = 9:gosub 7620 +4930 P1=0:S(16)=0:B0=0:GOTO 400 +4950 REM *** LOCK *** +4960 IF L1=10 OR L1=11 THEN 4990 +4970 REM NOTHING LOCKABLE +4980 GOTO 2200 +4990 IF S(19)=-1 THEN 5020 +5000 B$="keys":goto 2710 +5020 G=0:z59 = 10:gosub 7620 +5040 GOTO 400 +5060 REM *** UNLOCK *** +5070 IF S(19)<>-1 THEN 5000 +5080 IF L1<>10 AND L1<>11 THEN 5120 +5090 G=1:z59 = 11:gosub 7620 +5110 GOTO 400 +5120 IF L1<>69 THEN 2200 +5130 IF B1>0 THEN 5160 +5140 z59 = 12:gosub 7620 +5150 goto 400 +5160 IF C<>0 THEN 5170 +5165 C=1:B1=2 +5170 z59 = 13:gosub 7620 +5180 GOTO 400 +5190 REM *** FREE *** +5200 IF K(31) = 1 THEN 5240 +5210 REM CAN'T FREE ANYTHING BUT BIRD +5220 z59 = 2:gosub 7620 +5230 GOTO 410 +5240 IF S(31)<>-1 THEN 5220 +5250 S(31) = L1:B3=0 +5260 PRINT "Freed." +5270 IF L1<>22 THEN 5350 +5280 IF SN<>1 THEN 400 +5290 B$="snake" +5300 PRINT "The little bird attacks the green ";B$;" and" +5310 IF L1=82 THEN 5380 +5320 PRINT "drives it off" +5330 SN=0:S(34)=0:GOTO 400 +5350 IF L1<>82 THEN 400 +5360 B$="dragon":GOTO 5300 +5380 PRINT "gets burned to a crisp" +5390 S(31)=0 +5400 GOTO 400 +5410 REM *** WAVE *** +5420 IF K(23) <> 1 THEN 2200 +5430 IF S(23)=-1 THEN 5460 +5440 B$="rod":GOTO 2710 +5460 REM IS HERE NEAR FISSURE +5470 IF L1<>19 AND L1<>20 THEN 2200 +5480 REM yes +5490 REM GOTO B2+1 OF 5500,5530 +5491 IF B2=0 THEN 5500 +5492 IF B2=1 THEN 5530 +5500 z59 = 14:gosub 7620 +5510 B2=1:GOTO 400 +5530 z59 = 15:gosub 7620 +5540 B2=0:GOTO 400 +5560 REM *** OPEN *** +5570 GOSUB 8280 +5580 IF Z3>0 THEN 5610 +5590 PRINT "Open ";:goto 2040 +5610 IF Z3=40 THEN 5070 +5620 IF S(Z3)=L1 THEN 5650 +5630 PRINT "I see no ";b$;" here.":goto 400 +5650 if z3=24 THEN 5680 +5660 PRINT "I don't know how to open a ";B$:GOTO 400 +5680 IF S(9)=-1 THEN 5710 +5690 z59 = 16:gosub 7620 +5700 GOTO 400 +5710 IF S(Z3) = 0 THEN 2200 +5720 REM HE'S OPENED CLAM, SO PRINT DESCRIPTION OF THIS +5730 REM PUT PEARL IN CUL-DE-SAC +5740 S(7)=43:S(24)=0:S(30)=L1:z59 = 17:gosub 7620 +5750 GOTO 400 +5760 REM *** CLOSE *** +5770 GOSUB 8280 +5780 IF Z3=40 THEN 4960 +5790 z59 = 18:gosub 7620 +5800 GOTO 400 +5810 REM OIL +5820 IF K(17)=0 THEN 2200 +5830 IF S(17)=-1 THEN 5860 +5840 B$="oil":GOTO 5630 +5860 IF L1<>73 THEN 2200 +5870 REM IS DOOR STILL RUSTED +5880 IF D2=1 THEN 2200 +5890 D2=1:S(17)=0:B0=0:z59 = 19:gosub 7620 +5900 GOTO 400 +5910 REM *** EAT *** +5920 IF K(20) = 1 THEN 5950 +5930 z59 = 20:gosub 7620 +5940 GOTO 410 +5950 Z3=20:GOSUB 8490 +5970 IF Z5=0 THEN 410 +5980 z59 = 73:gosub 7620 +5381 S(20)=0:B0=0:GOTO 400 +6000 REM *** DRINK *** +6010 IF K(16) =1 THEN 6040 +6020 z59 = 21:gosub 7620 +6030 GOTO 410 +6040 Z3=16:GOSUB 8490 +6060 IF Z5=0 THEN 410 +6070 z59 = 22:gosub 7620 +6071 S(17)=0:B0=0:GOTO 400 +6090 REM *** FEE FIE FOE FOO *** +6100 IF L1=71 THEN 6130 +6110 z59 = 2:gosub 7620 +6120 GOTO 410 +6130 IF S(8)<>L1 THEN 6180 +6140 REM MAKE NEST VANISH +6150 z59 = 79:gosub 7620 +6160 S(8)=0:GOTO 400 +6180 REM IF S(8)=0 THEN 6110 +6190 S(8)=L1 +6200 rem MAKE NEST RE-APPEAR +6210 z59 = 81:gosub 7620 +6220 GOTO 400 +6230 REM *** SHORT *** +6240 PRINT "Short descriptions" +6250 D0=0:GOTO 400 +6270 REM *** LONG *** +6280 PRINT "Long descriptions" +6290 D0=1:GOTO 400 +6310 REM *** BRIEF *** +6320 PRINT "OK, I'll only describe the room in detail the first time." +6330 D0=2:GOTO 400 +6350 REM *** QUIT *** +6360 PRINT "Save game"; +6370 GOSUB 9860 +6380 IF Z0=1 THEN 8970 +6390 GOTO 9750 +6400 REM SCORE *** +6410 GOSUB 6430 +6420 GOTO 400 +6430 REM PRINT OUT SCORE DATA +6440 GOSUB 6510 +6450 PRINT "Your score is now ";S0 +6451 PRINT "You have explored ";(Z9/T1)*T1;"% of the cave." +6460 RESTORE 6470 +6470 DATA "beginner","novice","experienced","advanced","expert" +6480 Z9 = INT((S0-1)/100) +6481 IF Z9 <= 4 THEN 6483 +6482 Z9=4 +6483 FOR Z0=0 TO Z9 +6484 READ D$ +6485 NEXT Z0 +6490 PRINT "That makes you a ";D$;" adventurer." +6500 RETURN +6510 REM COMPUTE CURRENT SCORE +6520 RESTORE 230 +6530 Z9=0:S0=0 +6540 FOR Z0=1 TO 15 +6550 READ Z1 +6560 IF Z1=0 THEN 6590 +6570 IF V(Z1)<>1 THEN 6580 +6575 S0=S0+4*O(Z0) +6580 IF S(Z0)<>7 THEN 6590 +6585 S0=S0+4*O(Z0) +6590 NEXT Z0 +6600 S0=(G=1)*10 + S0:S0=(SN=0)*20 + S0:S0=(D1=0)*30 + S0:S0=(T=0)*30 + S0:S0=(B1=2)*20 + S0 +6605 S0=(B2=1)*20 + S0:S0=(P1=2)*20 + S0:S0=(D2=1)*20 + S0:S0=(C=1)*20 + S0 +6610 FOR Z0 = 1 TO T1 +6620 IF V(Z0)<>1 THEN 6630 +6621 S0=S0+1:Z9=Z9+1 +6630 NEXT Z0 +6640 RETURN +6660 rem list items at location l1 +6680 fseek #2,0 +6690 for z1 = 1 to t2 +6700 INPUT #2,a$ +6710 if s(z1) <> l1 then 6720 +6711 PRINT a$ +6720 next z1 +6721 IF S(26)<>-1 THEN 6730 +6722 z59 = 67:gosub 7620 +6730 rem CHECK FOR DWARF,PIRATE +6740 gosub 8560 +6745 if dead = 1 then 6770 +6750 gosub 8800 +6760 PRINT +6770 return +6775 rem Print Short room description +6780 fseek #1,0 +6820 for z1 = 1 to l1 +6830 INPUT #1,a$ +6840 next z1 +6850 v(l1) = 1 +6860 PRINT a$ +6870 return +6880 rem SPECIAL GETS +6890 if not (z3 = 24 or z3 = 30 or z3 > 31) then 6930 +6900 rem CAN'T GET THESE FOR SOME REASON +6910 z59 = 61:gosub 7620 +6920 goto 400 +6930 if not (z3 = 12 and c = 0) then 6970 +6940 rem CHAIN +6950 z59 = 58:gosub 7620 +6960 goto 3900 +6970 rem BEAR IS HE FED? UNLOCKED? +6980 if not (z3 = 26 and b1 <> 2) then 7010 +6990 z59 = 61:gosub 7620 +7000 goto 3900 +7010 if not (z3 = 14 and d1 = 1) then 7050 +7020 rem DRAGON AND RUG +7030 z59 = 59:gosub 7620 +7040 goto 3900 +7050 if not (z3 = 16 or z3 = 17) then 7090 +7060 rem OIL AND WATER DO SAME AS FILL +7070 PRINT "Why not say 'fill'?" +7080 goto 3900 +7090 if not (z3 = 22 and b3) then 7140 +7100 rem TAKE BIRD SINCE IT'S IN CAGE +7110 s(31) = -1:PRINT "Bird and ";:goto 3880 +7140 if z3 <> 31 then 7310 +7150 rem GETTING BIRD +7160 if b3 <> 1 then 7210 +7170 rem TAKE CAGE, SINCE BIRD IS IN IT +7180 PRINT "Cage and ";:s(22) = -1:goto 3880 +7210 if s(22) = -1 then 7240 +7220 b$ = "cage":goto 2810 +7240 if s(23) = -1 then 7280 +7250 rem OK TO TAKE BIRD +7260 s(31) = -1 : b3 = 1:goto 3890 +7280 rem ROD SCARES BIRD +7290 z59 = 37:gosub 7620 +7300 goto 3900 +7310 rem BOTTLE FULL? IF SO, GET CONTENTS +7320 if not (z3 = 21 and b0) then 7360 +7330 PRINT "Contents and the "; +7340 s(b0+15) = -1 +7360 goto 3880 +7370 rem SPECIAL "DROP" +7380 IF Z3<>31 THEN 7440 +7390 REM BIRD IN CAGE +7400 S(31)=L1:S(22)=L1:B3=1 +7410 IF Z3<>31 THEN 7420 +7415 PRINT "Cage and "; +7420 IF Z3=22 THEN 7430 +7425 PRINT "Bird and "; +7430 goto 4120 +7440 if z3=22 and b3=1 then 7400 +7450 IF Z3<>21 THEN 7520 +7460 REM BOTTLE +7470 IF B0=0 THEN 4120 +7480 REM BOTTLE IS FULL, DO DROP CONTENTS TOO +7490 PRINT "Contents and "; +7500 S(15+B0)=L1:GOTO 4120 +7520 IF NOT (Z3=16 OR Z3=17) THEN 7541 +7530 PRINT "Try saying 'empty'":goto 4140 +7541 IF Z3<>26 OR T<>1 OR (L1<>60 AND L1<>61) THEN 7550 +7542 z59 = 28:gosub 7620 +7543 T=0:S(26)=L1:S(32)=0:GOTO 400 +7550 IF Z3<>6 THEN 4120 +7560 IF S(28)=L1 THEN 7600 +7570 REM GOODBYE, FRAGILE VASE! +7580 z59 = 43:gosub 7620 +7581 S(6)=0:S(29)=L1:GOTO 400 +7600 z59 = 60:gosub 7620 +7610 GOTO 4120 +7619 rem PRINT MESSAGE +7620 if indx(1) >= 0 then 7640 +7630 gosub 12500 +7640 z59 = int(z59):xtmp = indx(z59) +7645 if z59 <> 2 and z59 <> 61 then 7660 +7650 xtmp = fraindx(xtmp+min(int(RND(1)*5),5)) +7660 fseek #3,xtmp +7670 INPUT #3,b1$ +7672 if len(b1$) = 0 then 7673 else 7690 +7673 b1$ = " " +7690 if instr(b1$,"#") = 0 then 7670 +7700 z4 = val(mid$(b1$,2)) +7705 if int(z4) = z59 then 7720 +7710 if int(z4) < z59 then 7670 +7712 if int(z4) > z59 then 7760 +7720 INPUT #3,b1$ +7730 if mid$(b1$,1,1) = "#" then 7770 +7740 PRINT b1$ +7750 goto 7720 +7760 PRINT "NO DESC. # ";z59;" IN FILE AMESSAGE" +7770 return +7800 rem +7810 rem SITUATION DESCRIPTIONS +7820 rem +7830 rem GRATE +7840 if l1 = 10 or l1 = 11 then 7842 else 7860 +7842 z59 = (g+10):gosub 7620 +7850 rem CRYSTAL BRIDGE +7860 if (l1 = 19 or l1 = 20) and b2 = 1 then 7861 else 7880 +7861 z59 = 14:gosub 7620 +7870 rem PLUGH NOISE +7880 if l1 = 26 and RND(1) > 0.3 then 7881 else 7900 +7881 z59 = 41:gosub 7620 +7890 rem IRON DOOR +7900 if l1 = 73 and d2 = 0 then 7901 else 7920 +7901 z59 = 57:gosub 7620 +7910 rem TROLL +7920 if (l1 = 60 or l1 = 61) and t = 1 then 7921 else 7940 +7921 z59 = 63:gosub 7620 +7930 rem BEAR +7940 if l1 = 69 and b1 = 0 then 7941 else 7950 +7941 z59 = 64:gosub 7620 +7950 if l1 = 69 and b1 = 1 then 7951 else 7970 +7951 z59 = 66:gosub 7620 +7960 rem PLANT IN PIT +7970 if l1 = 48 or l1 = 50 then 7971 else 7980 +7971 z59 = 47+p1:gosub 7620 +7980 return +7990 rem +8000 rem PRINT long room description from "amessage" file +8010 rem description is noormally l1+200 except for +8020 rem maze or forest +8030 rem set v(l1)=1 so as not to repeat long desc(brief mode +8040 v(l1) = 1 +8050 if l1 > 4 then 8080 +8060 z59 = 200:gosub 7620 +8070 goto 8130 +8080 if not (l1 > 88 and l1 < 98 or l1 = 99) then 8110 +8090 z59 = 288:gosub 7620 +8100 goto 8130 +8110 rem normal description +8120 z59 = 200+l1:gosub 7620 +8130 return +8180 rem always give long descriptio nfor forest and maze +8190 if l1 < 5 or (l1 > 88 and l1 < 98) or l1 = 99 then 8220 +8200 if v(l1)=1 then 8201 else 8220 +8201 gosub 6780 +8202 goto 8235 +8210 rem he hassn't seen this room, so give a long desc. +8220 v(l1) = 1 +8230 gosub 7990 +8235 return +8240 rem +8250 rem fetch first item code in k(1 to t2) +8260 rem z8=total # of items found in list +8270 rem z3=item code first found +8280 z8 = 0 : z3 = 0: d$ = "" +8300 for z5 = 1 to 45 +8320 if k(z5) = 0 then 8360 +8330 z8 = z8+1 +8340 restore 9960+z5:read b$:d$ = b$ +8350 if k(z5)=1 and z8 = 1 then 8352 else 8360 +8352 z3 = z5 +8360 next z5 +8370 b$ = d$ +8380 return +8390 rem FIND FIRST ITEM AT ROOM +8400 x1 = 0 +8410 FOR Z1=1 TO 47 +8415 if x1=1 then 8440 +8430 IF K(Z1) = 1 THEN 8431 else 8440 +8431 restore 9960+z1:read d$:x1=1 +8440 NEXT Z1 +8450 RETURN +8460 REM +8470 REM MAKE SURE HE'S CARRYING ITEM * Z3 +8480 REM +8490 IF S(Z3)=-1 THEN 8530 +8500 PRINT "You don't have the ";A$ +8510 Z5=0 +8520 RETURN +8530 Z5=1 +8540 RETURN +8550 rem *** DWARF *** +8560 if d3 <> 0 then 8640 +8570 rem SHOULD DWARF GIVE AWAY AXE? +8580 if l1 < 13 then 8790 +8590 if RND(1) > 0.05 then 8790 +8600 rem GIVE AWAY AXE +8610 z59 = 80:gosub 7620 +8620 s(27) = l1 : d3 = 1 +8630 goto 8790 +8640 rem SHOULD DWARF ATTACK? +8650 if L1 >= 13 then 8660 +8652 s(35) = 0:goto 8790 +8660 if s(35) <> L1 then 8770 +8670 if RND(1) > 0.5 then 8790 +8680 rem YES! +8690 z59 = 32:gosub 7620 +8700 rem DOES THE KNIFE KILL THE PLAYER? +8705 KC = KC - 0.02 +8706 IF KC >= 0.5 THEN 8710 +8707 KC = 0.5 +8710 if RND(1) <= KC then 8750 +8720 rem YES +8730 PRINT "It gets you!" +8740 dead = 1 : goto 8790 +8750 PRINT "It misses!" +8760 goto 8790 +8770 rem SHOULD WE PUT A DWARF HERE? +8780 if RND(1) >= 0.1 then 8790 +8785 s(35) = l1 +8786 z59=31:gosub 7620 +8790 return +8800 rem *** PIRATE *** +8810 rem FIRST, DOES HE HAVE ANYTHING WORTH STEALING? +8820 z3 = 0 +8830 if l1 < 13 then 8960 +8840 for x = 1 to 15 +8850 if s(x) <> -1 then 8860 +8855 z3 = z3+1 +8860 next x +8870 if z3 < int(RND(1)*4)+1 then 8960 +8880 rem SHOULD WE RIP OFF HIS VALUABLES? +8890 if RND(1) < 0.05 then 8920 +8900 z59 = 34:gosub 7620 +8910 goto 8960 +8920 z59 = 33:gosub 7620 +8930 for x = 1 to 15 +8940 if s(x) <> -1 then 8950 +8945 s(x) = 100 +8950 next x +8960 return +8970 REM *** SAVE GAME *** +8980 INPUT "What do you want to call the save file? ";A$ +8990 OPEN A$ FOR OUTPUT AS #5 ELSE 9010 +9000 GOTO 9030 +9010 PRINT "File ";a$;" not created" +9020 GOTO 410 +9030 PRINT #5,T1;",";T2;",";T3;",";L1;",";L2;",";G;",";B0;",";SN;",";D1;",";D2;",";D0;",";T;",";B1;",";B2;",";P1;",";L;",";C;",";D3;",";B3;",";R0;",";KC +9040 FOR X=1 TO 99 +9041 PRINT #5,S(X);",";V(X) +9042 NEXT X +9043 PRINT #5,V(100) +9044 CLOSE #5 +9050 PRINT "Game saved" +9051 C0 = 0 +9060 if k(143) = 1 then 9750 +9070 GOTO 410 +9080 REM *** LOAD OLD GAME *** +9090 IF C0=0 THEN 9120 +9100 PRINT "You already have a loaded game!" +9110 GOTO 410 +9120 INPUT "Save file name? ";A$ +9130 OPEN A$ FOR INPUT AS #5 ELSE 9150 +9140 GOTO 9170 +9150 PRINT "Unable to use file ";A$ +9160 GOTO 410 +9170 INPUT #5,T1,T2,T3,L1,L2,G,B0,SN,D1,D2,D0,T,B1,B2,P1,L,C,D3,B3,R0,KCX$ +9171 kc = val(kcx$) +9180 FOR X=1 TO 99 +9191 INPUT #5,SX,VX:s(x)=sx:v(x)=vx +9192 NEXT X +9193 INPUT #5,vx:v(100)=vx +9194 CLOSE #5 +9200 C0=1 +9210 GOTO 300 +9220 rem *** READ THE MAGAZINE *** +9230 GOSUB 8280 +9240 IF Z3=25 THEN 9270 +9250 z59 = 74:gosub 7620 +9260 GOTO 410 +9270 IF S(25)=-1 THEN 9300 +9280 B$="magazine" +9290 GOTO 2710 +9300 REM OK, LET HIM READ IT +9310 z59 = 303:gosub 7620 +9320 GOTO 400 +9330 REM *** BUG *** +9340 A$ = "ADVBUGS.TXT" +9341 OPEN A$ FOR APPEND AS #5 ELSE 9150 +9390 INPUT "Your name: ";A$ +9400 A$=A$+" "+DATE$ +9410 PRINT #5,A$ +9420 PRINT "Enter your gripe in up to five lines (hit return to quit):" +9430 FOR Z0=1 TO 5 +9440 PRINT Z0; +9450 INPUT A$ +9460 IF A$="" THEN 9490 +9470 PRINT #5,A$ +9480 NEXT Z0 +9490 PRINT "Message recorded. Thank you!" +9500 CLOSE #5 +9510 GOTO 410 +9540 REM REINCARNATE HIM +9550 R0=R0+1 +9560 IF R0=1 THEN 9580 +9561 IF R0=2 THEN 9610 +9562 IF R0=3 THEN 9740 +9570 REM ASK HIM IF HE WANTS TO BE REINCARNATED +9580 z59 = 75:gosub 7620 +9590 GOSUB 9860 +9600 GOTO 9630 +9610 z59 = 77:gosub 7620 +9620 GOTO 9580 +9630 IF Z0=0 THEN 9750 +9640 z59 = 76:gosub 7620 +9650 REM PUT HIM BACK IN HOUSE, REARRANGE HIS STUFF +9660 S(18)=7:L=0:DEAD=0:KC=1.03 +9670 FOR X=1 TO T2 +9680 IF S(X)<>-1 THEN 9690 +9685 S(X)=L1 +9690 NEXT X +9700 REM WE'VE PUT THE LAMP IN HOUSE AND OTHER ITEMS WHERE HE DIED +9710 L1=INT(RND(1)*4)+1:L2=L1 +9720 GOTO 320 +9730 REM THIRD DEATH--END OF GAME +9740 z59 = 78:gosub 7620 +9750 PRINT "Oh well..." +9760 GOSUB 6430 +9762 close #1:close #2:close #3 +9770 STOP +9780 rem *** PITS *** +9790 if l1 < 13 THEN 300 +9791 IF l = 1 and (s(18) = -1 or s(18) = l1) then 300 +9800 rem IS HE GOING TO FALL INTO A PIT? +9810 if l1 = 16 or l1 = 17 or l1 = 19 or l1 = 20 or l1 = 25 or l1 = 47 or l1 = 48 or l1 = 59 or l1 = 60 or l1 = 61 or l1 = 75 or l1 = 76 or l1 = 98 then 9840 +9820 goto 300 +9830 rem he fell into a pit +9840 z59 = 44:gosub 7620 +9850 goto 9540 +9860 rem *** SEEK A "YES" OR "NO" +9870 INPUT a$ +9875 if len(a$) <> 0 then 9880 +9877 a$ = " " +9880 a$ = LOWER$(mid$(a$,1,1)) +9890 if a$ <> "y" and a$ <> "n" then 9930 +9900 if a$ = "y" then 9901 else 9910 +9901 z0 = 1 +9910 if a$ = "n" then 9911 else 9920 +9911 z0 = 0 +9920 goto 9945 +9930 PRINT "Yes or No-"; +9940 goto 9870 +9945 return +9950 rem ---- SHORT NAMES FOR STUFF ---- +9961 data "large gold nugget" +9962 data "bars of silver" +9963 data "precious jewelry" +9964 data "many coins" +9965 data "several diamonds" +9966 data "fragile ming vase" +9967 data "glistening pearl" +9968 data "nest of golden eggs" +9969 data "jewel-encrusted trident" +9970 data "egg-sized emerald" +9971 data "platinum pyramid" +9972 data "golden chain" +9973 data "rare spices" +9974 data "persian rug" +9975 data "treasure chest" +9976 data "water" +9977 data "oil" +9978 data "brass lamp" +9979 data "keys" +9980 data "food" +9981 data "bottle" +9982 data "wicker cage" +9983 data "3-foot black rod" +9984 data "clam" +9985 data "magazine" +9986 data "bear" +9987 data "axe" +9988 data "velvet pillow" +9989 data "shards of pottery" +9990 data "oyster" +9991 data "bird" +9992 data "troll" +9993 data "dragon" +9994 data "snake" +9995 data "dwarf" +9996 data "rock" +9997 data "stairs" +9998 data "steps" +9999 data "house" +10000 data "grate" +10001 data "stream" +10002 data "room" +10003 data "bridge" +10004 data "pit" +10005 data "volcano" +10006 data "road" +10007 data "everything" +12500 rem INITIALIZE MESSAGE INDEX +12501 open "AMESSAGE.IDX" for INPUT as #6 else 12505 +12502 goto 12610 +12504 rem Message indx file doesn't exist, create it +12505 OPEN "AMESSAGE.IDX" FOR OUTPUT AS #6 +12506 fpos = -2:b$ = "":fracnt= 0:lastfra=-1:fseek #3,0 +12520 fpos = fpos+len(b$)+2 +12522 INPUT #3,b$ +12530 if len(b$) = 0 then 12531 else 12540 +12531 b$ = " " : fpos = fpos-1 +12540 if b$="#" then 12591 +12550 if instr(b$,"#") = 0 then 12520 +12560 z4 = val(mid$(b$,2)) +12570 if int(z4) = z4 then 12580 +12571 fracnt = fracnt + 1 +12572 if lastfra = int(z4) then 12574 +12573 indx(int(z4)) = fracnt:lastfra = int(z4):PRINT #6,int(z4);",";FRACNT +12574 fraindx(fracnt) = fpos +12576 goto 12585 +12580 indx(int(z4)) = fpos +12581 PRINT #6,int(z4);",";FPOS +12585 PRINT "*"; +12590 goto 12520 +12591 PRINT #6,-999;",";-999 +12592 FOR I = 1 TO FRACNT +12593 PRINT #6,FRAINDX(I) +12594 NEXT I +12595 PRINT #6,-999 +12596 close #3 +12597 open "AMESSAGE" for INPUT as #3 +12600 GOTO 12670 +12604 REM READ AMESSAGE.IDX FILE INTO ARRAYS +12610 INPUT #6,II,I +12612 PRINT "*"; +12615 IF I=-999 THEN 12630 +12620 INDX(II)=I:GOTO 12610 +12630 II = 0 +12640 INPUT #6,I +12641 PRINT "*"; +12650 IF I=-999 THEN 12670 +12660 II = II +1:FRAINDX(II)=I:GOTO 12640 +12670 CLOSE #6:PRINT:PRINT +12680 RETURN From a936e59ddf4d0686df8a6e1fd7b0444d923cc45d Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 13 Sep 2021 17:56:12 -0400 Subject: [PATCH 095/183] Issue with Dwarf at troll bridge --- examples/AMESSAGE | 12 +++++++++++- examples/adventure-fast.bas | 12 ++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/examples/AMESSAGE b/examples/AMESSAGE index 2a1c352..9c08ebf 100644 --- a/examples/AMESSAGE +++ b/examples/AMESSAGE @@ -527,6 +527,17 @@ You are near a large pit beside a large stalactite in the maze. The stalactite is about 30 feet long, allowing you to descend it. However, due to the smoothness of the stalactite you will probably be unable to climb back up again. +#299 +The threatening little dwarf pulls out a sharp, nasty knife but +suddenly freezes. + +The burly troll notices the little dwarf and appears to become very angry. + +The little dwarf frantically looks from side to side, the burly troll +takes a sudden step towards the little dwarf. + +The little dwarf makes an odd squeal, steps back and trips on an outcropping, +falling with a soft *thud* before scrambling out of sight in a cloud of dust. #300 Dead end. #301 @@ -561,7 +572,6 @@ There are some useful commands that you should know about: Other helpful functions include: * multiple commands on one input line, ie: "Go south, get car, keys, Start car." (example only) -** oops, the PyBasic version doesn't support multiple commands :( -------------------------------------------------------------- "Adventure" is a version of the game created by Willy Crowther and Dan Woods at M.I.T. diff --git a/examples/adventure-fast.bas b/examples/adventure-fast.bas index a19a645..6bd92b2 100644 --- a/examples/adventure-fast.bas +++ b/examples/adventure-fast.bas @@ -26,7 +26,7 @@ 150 l1 = int(RND(1)*4)+1 : l2 = l1 160 g = 0 : b0 = 1 : sn = 1 : d1 = 1 : d2 = 0 : t = 1 : b1 = 0 : b2 = 0 : p1 = 0: dead = 0 161 l = 0 : c = 0 : d3 = 0 : b3 = 0 : d0 = 2 : t1 = 100 : t2 = 35 : t3 = 149 : r0 = 0 : c0 = 0: c$="" -162 KC = 1.03 +162 KC = 1.02 170 for ii = 1 to 99 171 s(ii) = 0 : v(ii) = 0 172 next ii @@ -903,13 +903,16 @@ 8650 if L1 >= 13 then 8660 8652 s(35) = 0:goto 8790 8660 if s(35) <> L1 then 8770 +8661 if (l1 <> 60 and l1 <> 61) or t <> 1 then 8670 +8662 z59 = 299:gosub 7620 +8663 s(35) = 0: goto 8790 8670 if RND(1) > 0.5 then 8790 8680 rem YES! 8690 z59 = 32:gosub 7620 8700 rem DOES THE KNIFE KILL THE PLAYER? 8705 KC = KC - 0.02 -8706 IF KC >= 0.5 THEN 8710 -8707 KC = 0.5 +8706 IF KC >= 0.75 THEN 8710 +8707 KC = 0.75 8710 if RND(1) <= KC then 8750 8720 rem YES 8730 PRINT "It gets you!" @@ -917,7 +920,8 @@ 8750 PRINT "It misses!" 8760 goto 8790 8770 rem SHOULD WE PUT A DWARF HERE? -8780 if RND(1) >= 0.1 then 8790 +8780 if RND(1) >= 0.05 then 8790 +8781 if (l1 = 60 or l1 = 61) and t = 1 then 8790 8785 s(35) = l1 8786 z59=31:gosub 7620 8790 return From a209257c4d59739874c8039113984ae8c3239a80 Mon Sep 17 00:00:00 2001 From: richpl Date: Tue, 14 Sep 2021 21:37:23 +0100 Subject: [PATCH 096/183] Initial commit --- examples/eliza.bas | 236 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 examples/eliza.bas diff --git a/examples/eliza.bas b/examples/eliza.bas new file mode 100644 index 0000000..18d1bc1 --- /dev/null +++ b/examples/eliza.bas @@ -0,0 +1,236 @@ +1 GOSUB 1300 +5 PRINT TAB ( 16 ) ; "**************************" +10 PRINT TAB ( 26 ) ; "ELIZA" +20 PRINT TAB ( 20 ) ; "CREATIVE COMPUTING" +30 PRINT TAB ( 18 ) ; "MORRISTOWN, NEW JERSEY" : PRINT +40 PRINT TAB ( 19 ) ; "ADAPTED FOR IBM PC BY" +50 PRINT TAB ( 12 ) ; "PATRICIA DANIELSON AND PAUL HASHFIELD" +53 PRINT : PRINT TAB ( 6 ) ; "PLEASE DON'T USE COMMAS OR PERIODS IN YOUR INPUTS" : PRINT +55 PRINT TAB ( 16 ) ; "*************************" +60 PRINT : PRINT : PRINT +80 REM*****INITIALIZATION********** +100 DIM S ( 36 ) , R ( 36 ) , N ( 36 ) +105 DIM KEYWORD$ ( 36 ) , WORDIN$ ( 7 ) , WORDOUT$ ( 7 ) , REPLIES$ ( 112 ) +107 REM NEED TO PRE-DECLARE BEFORE USE +108 P$ = "NULL" +110 N1 = 36 : N2 = 14 : N3 = 112 +112 FOR X = 1 TO N1 +113 READ TEMP$: KEYWORD$ ( X ) = TEMP$ +114 NEXT X +115 FOR X = 1 TO N2 / 2 +116 READ TEMP$: WORDIN$ ( X ) = TEMP$ : READ TEMP$: WORDOUT$ ( X ) = TEMP$ +117 NEXT X +118 FOR X = 1 TO N3 +119 READ TEMP$: REPLIES$ ( X ) = TEMP$ +120 NEXT X +130 FOR X = 1 TO N1 +140 READ TEMP: S ( X ) = TEMP: READ TEMP: L = TEMP +142 R ( X ) = S ( X ) : N ( X ) = S ( X ) + L - 1 +150 NEXT X +160 PRINT "HI! I'M ELIZA. WHAT'S YOUR PROBLEM?" +170 REM *********************************** +180 REM *******USER INPUT SECTION********** +190 REM *********************************** +200 INPUT I$ +201 I$ = " " + I$ + " " +210 REM GET RID OF APOSTROPHES +220 FOR L = 1 TO LEN ( I$ ) +230 REM IF MID$(I$,L,1)="'"THEN I$=LEFT$(I$,L-1)+RIGHT$(I$,LEN(I$)-L):GOTO 230 +240 IF L + 4 > LEN ( I$ ) THEN 250 +241 IF MID$ ( I$ , L , 4 ) <> "SHUT" THEN 250 +242 PRINT "O.K. IF YOU FEEL THAT WAY I'LL SHUT UP...." +243 END +250 NEXT L +255 IF I$ = P$ THEN 256 ELSE 260 +256 PRINT "PLEASE DON'T REPEAT YOURSELF!": GOTO 170 +260 REM *********************************** +270 REM ********FIND KEYWORD IN I$********* +280 REM *********************************** +300 FOR K = 1 TO N1 +320 FOR L = 1 TO LEN ( I$ ) - LEN ( KEYWORD$ ( K ) ) + 1 +340 IF MID$ ( I$ , L , LEN ( KEYWORD$ ( K ) ) ) <> KEYWORD$ ( K ) THEN 350 +341 IF K <> 13 THEN 349 +342 IF MID$ ( I$ , L , LEN ( KEYWORD$ ( 29 ) ) ) = KEYWORD$ ( 29 ) THEN 343 ELSE 349 +343 K = 29 +349 F$ = KEYWORD$ ( K ) : GOTO 390 +350 NEXT L +360 NEXT K +370 K = 36 : GOTO 570 : REM WE DIDN'T FIND ANY KEYWORDS +380 REM ****************************************** +390 REM **TAKE PART OF STRING AND CONJUGATE IT**** +400 REM **USING THE LIST OF STRINGS TO BE SWAPPED* +410 REM ****************************************** +430 C$ = " " + RIGHT$ ( I$ , LEN ( I$ ) - LEN ( F$ ) - L + 1 ) + " " +440 FOR X = 1 TO N2 / 2 +460 FOR L = 1 TO LEN ( C$ ) +470 IF L + LEN ( WORDIN$ ( X ) ) > LEN ( C$ ) THEN 510 +480 IF MID$ ( C$ , L , LEN ( WORDIN$ ( X ) ) ) <> WORDIN$ ( X ) THEN 510 +490 C$ = LEFT$ ( C$ , L - 1 ) + WORDOUT$ ( X ) + RIGHT$ ( C$ , LEN ( C$ ) - L - LEN ( WORDIN$ ( X ) ) + 1 ) +495 L = L + LEN ( WORDOUT$ ( X ) ) +500 GOTO 540 +510 IF L + LEN ( WORDOUT$ ( X ) ) > LEN ( C$ ) THEN 540 +520 IF MID$ ( C$ , L , LEN ( WORDOUT$ ( X ) ) ) <> WORDOUT$ ( X ) THEN 540 +530 C$ = LEFT$ ( C$ , L - 1 ) + WORDIN$ ( X ) + RIGHT$ ( C$ , LEN ( C$ ) - L - LEN ( WORDOUT$ ( X ) ) + 1 ) +535 L = L + LEN ( WORDIN$ ( X ) ) +540 NEXT L +550 NEXT X +551 IF MID$ ( C$ , 2 , 1 ) = " " THEN 552 ELSE 555 +552 C$ = RIGHT$ ( C$ , LEN ( C$ ) - 1 ) : REM ONLY 1 SPACE +555 FOR L = 1 TO LEN ( C$ ) +556 IF MID$ ( C$ , L , 1 ) = "!" THEN 557 ELSE 558 +557 C$ = LEFT$ ( C$ , L - 1 ) + RIGHT$ ( C$ , LEN ( C$ ) - L ) : GOTO 556 +558 NEXT L +560 REM ********************************************** +570 REM **NOW USING THE KEYWORD NUMBER (K) GET REPLY** +580 REM ********************************************** +600 F$ = REPLIES$ ( R ( K ) ) +610 R ( K ) = R ( K ) + 1 +615 IF R ( K ) > N ( K ) THEN 617 ELSE 620 +617 R ( K ) = S ( K ) +620 IF RIGHT$ ( F$ , 1 ) <> "*" THEN 621 ELSE 625 +621 PRINT F$ : P$ = I$ : GOTO 170 +625 IF C$ <> " " THEN 630 +626 PRINT "YOU WILL HAVE TO ELABORATE MORE FOR ME TO HELP YOU" +627 GOTO 170 +630 PRINT LEFT$ ( F$ , LEN ( F$ ) - 1 ) ; C$ +640 P$ = I$ : GOTO 170 +1000 REM ******************************* +1010 REM *****PROGRAM DATA FOLLOWS****** +1020 REM ******************************* +1030 REM *********KEYWORDS************** +1049 REM ******************************* +1050 DATA "CAN YOU " , "CAN I " , "YOU ARE " , "YOU'RE " , "I DON'T " , "I FEEL " +1060 DATA "WHY DON'T YOU " , "WHY CAN'T I " , "ARE YOU " , "I CAN'T " , "I AM " , "I'M " +1070 DATA "YOU " , "I WANT " , "WHAT " , "HOW " , "WHO " , "WHERE " , "WHEN " , "WHY " +1080 DATA "NAME " , "CAUSE " , "SORRY " , "DREAM " , "HELLO " , "HI " , "MAYBE " +1090 DATA "NO" , "YOUR " , "ALWAYS " , "THINK " , "ALIKE " , "YES " , "FRIEND " +1100 DATA "COMPUTER" , "NOKEYFOUND" +1200 REM ********************************* +1210 REM ***STRING DATA FOR CONJUGATIONS** +1220 REM ********************************* +1230 DATA " ARE " , " AM " , " WERE " , " WAS " , " YOU " , " I " , " YOUR" , " MY " +1235 DATA " I'VE " , " YOU'VE " , " I'M " , " YOU'RE " +1240 DATA " ME " , " YOU " +1300 REM ****************************** +1310 REM *********REPLIES************** +1320 REM ****************************** +1330 DATA "DON'T YOU BELIEVE THAT I CAN*" +1340 DATA "PERHAPS YOU WOULD LIKE TO BE LIKE ME*" +1350 DATA "YOU WANT ME TO BE ABLE TO*" +1360 DATA "PERHAPS YOU DON'T WANT TO*" +1365 DATA "DO YOU WANT TO BE ABLE TO*" +1370 DATA "WHAT MAKES YOU THINK I AM*" +1380 DATA "DOES IT PLEASE YOU TO BELIEVE I AM*" +1390 DATA "PERHAPS YOU WOULD LIKE TO BE*" +1400 DATA "DO YOU SOMETIMES WISH YOU WERE*" +1410 DATA "DON'T YOU REALLY*" +1420 DATA "WHY DON'T YOU*" +1430 DATA "DO YOU WISH TO BE ABLE TO*" +1440 DATA "DOES THAT TROUBLE YOU*" +1450 DATA "DO YOU OFTEN FEEL*" +1460 DATA "DO YOU OFTEN FEEL*" +1470 DATA "DO YOU ENJOY FEELING*" +1480 DATA "DO YOU REALLY BELIEVE I DON'T*" +1490 DATA "PERHAPS IN GOOD TIME I WILL*" +1500 DATA "DO YOU WANT ME TO*" +1510 DATA "DO YOU THINK YOU SHOULD BE ABLE TO*" +1520 DATA "WHY CAN'T YOU*" +1530 DATA "WHY ARE YOU INTERESTED IN WHETHER OR NOT I AM*" +1540 DATA "WOULD YOU PREFER IF I WERE NOT*" +1550 DATA "PERHAPS IN YOUR FANTASIES I AM*" +1560 DATA "HOW DO YOU KNOW YOU CAN'T*" +1570 DATA "HAVE YOU TRIED?" +1580 DATA "PERHAPS YOU CAN NOW*" +1590 DATA "DID YOU COME TO ME BECAUSE YOU ARE*" +1600 DATA "HOW LONG HAVE YOU BEEN*" +1610 DATA "DO YOU BELIEVE IT IS NORMAL TO BE*" +1620 DATA "DO YOU ENJOY BEING*" +1630 DATA "WE WERE DISCUSSING YOU--NOT ME." +1640 DATA "OH, I*" +1650 DATA "YOU'RE NOT REALLY TALKING ABOUT ME, ARE YOU?" +1660 DATA "WHAT WOULD IT MEAN TO YOU IF YOU GOT*" +1670 DATA "WHY DO YOU WANT*" +1680 DATA "SUPPOSE YOU SOON GOT*" +1690 DATA "WHAT IF YOU NEVER GOT*" +1700 DATA "I SOMETIMES ALSO WANT*" +1710 DATA "WHY DO YOU ASK?" +1720 DATA "DOES THAT QUESTION INTEREST YOU?" +1730 DATA "WHAT ANSWER WOULD PLEASE YOU THE MOST?" +1740 DATA "WHAT DO YOU THINK?" +1750 DATA "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?" +1760 DATA "WHAT IS IT THAT YOU REALLY WANT TO KNOW?" +1770 DATA "HAVE YOU ASKED ANYONE ELSE?" +1780 DATA "HAVE YOU ASKED SUCH QUESTIONS BEFORE?" +1790 DATA "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?" +1800 DATA "NAMES DON'T INTEREST ME." +1810 DATA "I DON'T CARE ABOUT NAMES --PLEASE GO ON." +1820 DATA "IS THAT THE REAL REASON?" +1830 DATA "DON'T ANY OTHER REASONS COME TO MIND?" +1840 DATA "DOES THAT REASON EXPLAIN ANYTHING ELSE?" +1850 DATA "WHAT OTHER REASONS MIGHT THERE BE?" +1860 DATA "PLEASE DON'T APOLOGIZE!" +1870 DATA "APOLOGIES ARE NOT NECESSARY." +1880 DATA "WHAT FEELINGS DO YOU HAVE WHEN YOU APOLOGIZE?" +1890 DATA "DON'T BE SO DEFENSIVE!" +1900 DATA "WHAT DOES THAT DREAM SUGGEST TO YOU?" +1910 DATA "DO YOU DREAM OFTEN?" +1920 DATA "WHAT PERSONS APPEAR IN YOUR DREAMS?" +1930 DATA "ARE YOU DISTURBED BY YOUR DREAMS?" +1940 DATA "HOW DO YOU DO ...PLEASE STATE YOUR PROBLEM." +1950 DATA "YOU DON'T SEEM QUITE CERTAIN." +1960 DATA "WHY THE UNCERTAIN TONE?" +1970 DATA "CAN'T YOU BE MORE POSITIVE?" +1980 DATA "YOU AREN'T SURE?" +1990 DATA "DON'T YOU KNOW?" +2000 DATA "ARE YOU SAYING NO JUST TO BE NEGATIVE?" +2010 DATA "YOU ARE BEING A BIT NEGATIVE." +2020 DATA "WHY NOT?" +2030 DATA "ARE YOU SURE?" +2040 DATA "WHY NO?" +2050 DATA "WHY ARE YOU CONCERNED ABOUT MY*" +2060 DATA "WHAT ABOUT YOUR OWN*" +2070 DATA "CAN YOU THINK OF A SPECIFIC EXAMPLE?" +2080 DATA "WHEN?" +2090 DATA "WHAT ARE YOU THINKING OF?" +2100 DATA "REALLY, ALWAYS?" +2110 DATA "DO YOU REALLY THINK SO?" +2120 DATA "BUT YOU ARE NOT SURE YOU*" +2130 DATA "DO YOU DOUBT YOU*" +2140 DATA "IN WHAT WAY?" +2150 DATA "WHAT RESEMBLANCE DO YOU SEE?" +2160 DATA "WHAT DOES THE SIMILARITY SUGGEST TO YOU?" +2170 DATA "WHAT OTHER CONNECTIONS DO YOU SEE?" +2180 DATA "COULD THERE REALLY BE SOME CONNECTION?" +2190 DATA "HOW?" +2200 DATA "YOU SEEM QUITE POSITIVE." +2210 DATA "ARE YOU SURE?" +2220 DATA "I SEE." +2230 DATA "I UNDERSTAND." +2240 DATA "WHY DO YOU BRING UP THE TOPIC OF FRIENDS?" +2250 DATA "DO YOUR FRIENDS WORRY YOU?" +2260 DATA "DO YOUR FRIENDS PICK ON YOU?" +2270 DATA "ARE YOU SURE YOU HAVE ANY FRIENDS?" +2280 DATA "DO YOU IMPOSE ON YOUR FRIENDS?" +2290 DATA "PERHAPS YOUR LOVE FOR FRIENDS WORRIES YOU." +2300 DATA "DO COMPUTERS WORRY YOU?" +2310 DATA "ARE YOU TALKING ABOUT ME IN PARTICULAR?" +2320 DATA "ARE YOU FRIGHTENED BY MACHINES?" +2330 DATA "WHY DO YOU MENTION COMPUTERS?" +2340 DATA "WHAT DO YOU THINK MACHINES HAVE TO DO WITH YOUR PROBLEM?" +2350 DATA "DON'T YOU THINK COMPUTERS CAN HELP PEOPLE?" +2360 DATA "WHAT IS IT ABOUT MACHINES THAT WORRIES YOU?" +2370 DATA "SAY, DO YOU HAVE ANY PSYCHOLOGICAL PROBLEMS?" +2380 DATA "WHAT DOES THAT SUGGEST TO YOU?" +2390 DATA "I SEE." +2400 DATA "I'M NOT SURE I UNDERSTAND YOU FULLY." +2410 DATA "COME COME ELUCIDATE YOUR THOUGHTS." +2420 DATA "CAN YOU ELABORATE ON THAT?" +2430 DATA "THAT IS QUITE INTERESTING." +2500 REM ************************* +2510 REM *****DATA FOR FINDING RIGHT REPLIES +2520 REM ************************* +2530 DATA 1 , 3 , 4 , 2 , 6 , 4 , 6 , 4 , 10 , 4 , 14 , 3 , 17 , 3 , 20 , 2 , 22 , 3 , 25 , 3 +2540 DATA 28 , 4 , 28 , 4 , 32 , 3 , 35 , 5 , 40 , 9 , 40 , 9 , 40 , 9 , 40 , 9 , 40 , 9 , 40 , 9 +2550 DATA 49 , 2 , 51 , 4 , 55 , 4 , 59 , 4 , 63 , 1 , 63 , 1 , 64 , 5 , 69 , 5 , 74 , 2 , 76 , 4 +2560 DATA 80 , 3 , 83 , 7 , 90 , 3 , 93 , 6 , 99 , 7 , 106 , 6 +2570 RETURN \ No newline at end of file From b3f21cca68a4eed155aa01710bd0a2d1f03b300b Mon Sep 17 00:00:00 2001 From: richpl Date: Tue, 14 Sep 2021 21:38:16 +0100 Subject: [PATCH 097/183] Added ELIZA and collaborator acknowledgements --- README.md | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7bf31f3..78ac527 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ The interpreter can be invoked as follows: $ python interpreter.py ``` +Although this started of as a personal project, it has been enhanced considerably by some other Github users. You can see them in the list of contributors! It's very much a group endeavour now. + ## Operators A limited range of arithmetic expressions are provided. Addition and subtraction have the lowest precedence, @@ -53,6 +55,8 @@ but this can be changed with parentheses. Additional numerical operations may be performed using numeric functions (see below). +Not also that + does extra duty as a string concatenation operator. + ## Commands Programs may be listed using the **LIST** command: @@ -784,7 +788,7 @@ Seeds may not produce the same result on another platform. Some functions are provided to help you manipulate strings. Functions that return a string have a '$' suffix like string variables. -**NOTE** For compatibility with older basic dialetcs, all string indexes are 1 based. +Note that unlike some other BASIC variants, string positions start at *0*. The functions are: @@ -802,9 +806,9 @@ at position *start* and end at *end*. Returns 0 if no match found. * **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y*. If z is provided, that number of characters is returned, if omitted the entire rest of the string is returned -* **LEFT$**(x$, y) - Returns the left most y characters from string x$. If y * exceeds the length of x$, the entire string will be returned. +* **LEFT$**(x$, y) - Returns the left most r characters from string x$. If y * exceeds the length of x$, the entire string will be returned. -* **RIGHT$**(x$, y) - Returns the right most y characters from string x$. If y * exceeds the length of x$, the entire string will be returned. +* **RIGHT$**(x$, y) - Returns the right most r characters from string x$. If y * exceeds the length of x$, the entire string will be returned. * **STR$**(x) - Returns a string representation of numeric value *x*. @@ -814,6 +818,7 @@ at position *start* and end at *end*. Returns 0 if no match found. * **TAB**(x) - Generates a string containing x spaces with no CR/LF +**NOTE** For compatibility with older basic dialetcs, all string indexes are 1 based. Examples for **ASC**, **CHR$** and **STR$** ``` @@ -826,6 +831,14 @@ A - 65 90 ``` +Strings may also be concatenated using the '+' operator: + +``` +> 10 PRINT "Hello" + " there" +> RUN +Hello there +``` + ## Example programs A number of example BASIC programs have been supplied in the repository, in the examples directory: @@ -844,6 +857,8 @@ calculate the corresponding factorial *N!*. * *bagels.bas* - A guessing game. +* *eliza.bas* - A port of the early chatbot, posing as a therapist, originally created by Joseph Weizenbaum in 1964. + ## Informal grammar definition **ABS**(*numerical-expression*) - Calculates the absolute value of the result of *numerical-expression* @@ -881,12 +896,10 @@ will read starting at file position *filepos* **IF$**(*expression*, *string-expression*, *string-expression*) - Evaluates *expression* and returns the value of the result of the first *string-expression* if true, or the second if false. -**INPUT** [*#filenum*,|*input-prompt*;] *simple-variable-list* - Processes user or file input presented as a comma separated list +**INPUT** [*#filenum*,|*input-prompt*:] *simple-variable-list* - Processes user or file input presented as a comma separated list **INSTR**(*hackstack-string-expression*, *needle-string-expression*[, *start-numeric-expression*[, *end-numeric-expression*]]) - Returns position of first *needle-string-expression* inside first *hackstack-string-expression*, optionally start searching at position given by *start-numeric-expression* and optionally ending at position given by *end-numeric-expression*. Returns -1 if no match found. -**LEFT$**(*string-expression*, *char-count*) - Takes the result of *string-expression* and returns the left-most *char-count* characters. If *char-count* exceeds string length the entire string is returned. - **LEN**(*string-expression*) - Returns the length of the result of *string-expression* [**LET**] *variable* = *numeric-expression* | *string-expression* - Assigns a value to a simple variable or array variable @@ -919,7 +932,7 @@ on the next line. **POW**(*base*, *exponent*) - Calculates the result of raising the base to the power of the exponent -**PRINT** [*#filenum*,]*print-list* - Prints a semicolon separated list of literals or variables to the screen or to a file. Included CR/LF by default, but this can be suppressed by ending the statement with a semicolon. +**PRINT** [*#filenum*,]*print-list* - Prints a comma separated list of literals or variables to the screen or to a file **RANDOMIZE** [*numeric-expression*] - Resets random number generator to an unpredictable sequence. With optional seed (*numeric expression*), the sequence is predictable. @@ -932,9 +945,7 @@ optional seed (*numeric expression*), the sequence is predictable. **RESTORE** *line-number* - sets the line number that the next **READ** will start loading constants from. *line-number* must refer to a **DATA** statement -**RIGHT$**(*string-expression*, *char-count*) - Takes the result of *string-expression* and returns the right-most *char-count* characters. If *char-count* exceeds string length, the entire string is returned. - -**RND**(*mode*) - For mode values >= 0 generates a pseudo random number N, where 0 <= N < 1. For values < 0 reseeds the PRNG +**RND** - Generates a pseudo random number N, where 0 <= N < 1 **RNDINT**(*lo-numerical-expression*, *hi-numerical-expression*) - Generates a pseudo random integer N, where *lo-numerical-expression* <= N <= *hi-numerical-expression* From 8dc690f3f200c8d47c371a34caad9f37178f4ff8 Mon Sep 17 00:00:00 2001 From: richpl Date: Wed, 15 Sep 2021 17:07:32 +0100 Subject: [PATCH 098/183] Minor fix --- examples/eliza.bas | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/eliza.bas b/examples/eliza.bas index 18d1bc1..486522b 100644 --- a/examples/eliza.bas +++ b/examples/eliza.bas @@ -1,4 +1,3 @@ -1 GOSUB 1300 5 PRINT TAB ( 16 ) ; "**************************" 10 PRINT TAB ( 26 ) ; "ELIZA" 20 PRINT TAB ( 20 ) ; "CREATIVE COMPUTING" @@ -232,5 +231,4 @@ 2530 DATA 1 , 3 , 4 , 2 , 6 , 4 , 6 , 4 , 10 , 4 , 14 , 3 , 17 , 3 , 20 , 2 , 22 , 3 , 25 , 3 2540 DATA 28 , 4 , 28 , 4 , 32 , 3 , 35 , 5 , 40 , 9 , 40 , 9 , 40 , 9 , 40 , 9 , 40 , 9 , 40 , 9 2550 DATA 49 , 2 , 51 , 4 , 55 , 4 , 59 , 4 , 63 , 1 , 63 , 1 , 64 , 5 , 69 , 5 , 74 , 2 , 76 , 4 -2560 DATA 80 , 3 , 83 , 7 , 90 , 3 , 93 , 6 , 99 , 7 , 106 , 6 -2570 RETURN \ No newline at end of file +2560 DATA 80 , 3 , 83 , 7 , 90 , 3 , 93 , 6 , 99 , 7 , 106 , 6 \ No newline at end of file From c6ff5fc315cdabb48fc6f4b000603cb891902803 Mon Sep 17 00:00:00 2001 From: richpl Date: Thu, 16 Sep 2021 16:26:04 +0100 Subject: [PATCH 099/183] Fix accidentally backed out changes --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 78ac527..a8053d6 100644 --- a/README.md +++ b/README.md @@ -788,7 +788,7 @@ Seeds may not produce the same result on another platform. Some functions are provided to help you manipulate strings. Functions that return a string have a '$' suffix like string variables. -Note that unlike some other BASIC variants, string positions start at *0*. +**NOTE** For compatibility with older basic dialetcs, all string indexes are 1 based. The functions are: @@ -806,9 +806,9 @@ at position *start* and end at *end*. Returns 0 if no match found. * **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y*. If z is provided, that number of characters is returned, if omitted the entire rest of the string is returned -* **LEFT$**(x$, y) - Returns the left most r characters from string x$. If y * exceeds the length of x$, the entire string will be returned. +* **LEFT$**(x$, y) - Returns the left most y characters from string x$. If y * exceeds the length of x$, the entire string will be returned. -* **RIGHT$**(x$, y) - Returns the right most r characters from string x$. If y * exceeds the length of x$, the entire string will be returned. +* **RIGHT$**(x$, y) - Returns the right most y characters from string x$. If y * exceeds the length of x$, the entire string will be returned. * **STR$**(x) - Returns a string representation of numeric value *x*. @@ -896,10 +896,12 @@ will read starting at file position *filepos* **IF$**(*expression*, *string-expression*, *string-expression*) - Evaluates *expression* and returns the value of the result of the first *string-expression* if true, or the second if false. -**INPUT** [*#filenum*,|*input-prompt*:] *simple-variable-list* - Processes user or file input presented as a comma separated list +**INPUT** [*#filenum*,|*input-prompt*;] *simple-variable-list* - Processes user or file input presented as a comma separated list **INSTR**(*hackstack-string-expression*, *needle-string-expression*[, *start-numeric-expression*[, *end-numeric-expression*]]) - Returns position of first *needle-string-expression* inside first *hackstack-string-expression*, optionally start searching at position given by *start-numeric-expression* and optionally ending at position given by *end-numeric-expression*. Returns -1 if no match found. +**LEFT$**(*string-expression*, *char-count*) - Takes the result of *string-expression* and returns the left-most *char-count* characters. If *char-count* exceeds string length the entire string is returned. + **LEN**(*string-expression*) - Returns the length of the result of *string-expression* [**LET**] *variable* = *numeric-expression* | *string-expression* - Assigns a value to a simple variable or array variable @@ -932,7 +934,7 @@ on the next line. **POW**(*base*, *exponent*) - Calculates the result of raising the base to the power of the exponent -**PRINT** [*#filenum*,]*print-list* - Prints a comma separated list of literals or variables to the screen or to a file +**PRINT** [*#filenum*,]*print-list* - Prints a semicolon separated list of literals or variables to the screen or to a file. Included CR/LF by default, but this can be suppressed by ending the statement with a semicolon. **RANDOMIZE** [*numeric-expression*] - Resets random number generator to an unpredictable sequence. With optional seed (*numeric expression*), the sequence is predictable. @@ -945,7 +947,9 @@ optional seed (*numeric expression*), the sequence is predictable. **RESTORE** *line-number* - sets the line number that the next **READ** will start loading constants from. *line-number* must refer to a **DATA** statement -**RND** - Generates a pseudo random number N, where 0 <= N < 1 +**RIGHT$**(*string-expression*, *char-count*) - Takes the result of *string-expression* and returns the right-most *char-count* characters. If *char-count* exceeds string length, the entire string is returned. + +**RND**(*mode*) - For mode values >= 0 generates a pseudo random number N, where 0 <= N < 1. For values < 0 reseeds the PRNG **RNDINT**(*lo-numerical-expression*, *hi-numerical-expression*) - Generates a pseudo random integer N, where *lo-numerical-expression* <= N <= *hi-numerical-expression* From 86f627996febfc006b7def76d89464f7b88ba208 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 20 Sep 2021 20:20:12 -0400 Subject: [PATCH 100/183] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a8053d6..49aaa90 100644 --- a/README.md +++ b/README.md @@ -816,7 +816,9 @@ at position *start* and end at *end*. Returns 0 if no match found. * **VAL**(x$) - Attempts to convert *x$* to a numeric value. If *x$* is not numeric, returns 0. -* **TAB**(x) - Generates a string containing x spaces with no CR/LF +* **TAB**(x) - When included in a **PRINT** statement *print-list*, specifies the position *x* on the line where the next text will be printed. If the specified position *x* is less than the current print position a newline is printed and the print location is set to the specified column. If the **TAB** function is used anywhere other than on a **PRINT** statement, it will return a string containing *x* spaces with no CR/LF + + **NOTE** For compatibility with older basic dialetcs, all string indexes are 1 based. From 14ae953e845da45374522ef11a3487388efc9997 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 20 Sep 2021 20:21:04 -0400 Subject: [PATCH 101/183] Tab without tuple flag --- basicparser.py | 64 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/basicparser.py b/basicparser.py index fc7b71d..68e7d28 100644 --- a/basicparser.py +++ b/basicparser.py @@ -102,6 +102,9 @@ def __init__(self, basicdata): # loop variable self.last_flowsignal = None + # Set to keep track of print column across multiple print statements + self.__prnt_column = 0 + #file handle list self.__file_handles = {} @@ -279,11 +282,31 @@ def __printstmt(self): # Check there are items to print if not self.__tokenindex >= len(self.__tokenlist): + prntTab = (self.__token.category == Token.TAB) self.__logexpr() - if fileIO: - self.__file_handles[filenum].write('%s' %(self.__operand_stack.pop())) + + #if type(self.__operand_stack[-1]) == tuple and self.__operand_stack[-1][0] == "TAB": + if prntTab: + if self.__prnt_column > len(self.__operand_stack[-1]): + if fileIO: + self.__file_handles[filenum].write("\n") + else: + print() + self.__prnt_column = 0 + + current_pr_column = len(self.__operand_stack[-1]) - self.__prnt_column + self.__prnt_column = len(self.__operand_stack.pop()) - 1 + if current_pr_column > 1: + if fileIO: + self.__file_handles[filenum].write(" "*(current_pr_column-1)) + else: + print(" "*(current_pr_column-1), end="") else: - print(self.__operand_stack.pop(), end='') + self.__prnt_column += len(str(self.__operand_stack[-1])) + if fileIO: + self.__file_handles[filenum].write('%s' %(self.__operand_stack.pop())) + else: + print(self.__operand_stack.pop(), end='') while self.__token.category == Token.SEMICOLON: if self.__tokenindex == len(self.__tokenlist) - 1: @@ -291,17 +314,36 @@ def __printstmt(self): # a newline.. a-la ms-basic return self.__advance() + prntTab = (self.__token.category == Token.TAB) self.__logexpr() - if fileIO: - self.__file_handles[filenum].write('%s' %(self.__operand_stack.pop())) + + #if type(self.__operand_stack[-1]) == tuple and self.__operand_stack[-1][0] == "TAB": + if prntTab: + if self.__prnt_column > len(self.__operand_stack[-1]): + if fileIO: + self.__file_handles[filenum].write("\n") + else: + print() + self.__prnt_column = 0 + current_pr_column = len(self.__operand_stack[-1]) - self.__prnt_column + if fileIO: + self.__file_handles[filenum].write(" "*(current_pr_column-1)) + else: + print(" "*(current_pr_column-1), end="") + self.__prnt_column = len(self.__operand_stack.pop()) - 1 else: - print(self.__operand_stack.pop(), end='') + self.__prnt_column += len(str(self.__operand_stack[-1])) + if fileIO: + self.__file_handles[filenum].write('%s' %(self.__operand_stack.pop())) + else: + print(self.__operand_stack.pop(), end='') # Final newline if fileIO: self.__file_handles[filenum].write("\n") else: print() + self.__prnt_column = 0 def __letstmt(self): """Parses a LET statement, @@ -1621,13 +1663,13 @@ def __evaluate_function(self, category): return value.lower() elif category == Token.TAB: - # Return a string of value spaces - if not isinstance(value, int): + if isinstance(value, int): + return (" "*value) + + else: raise TypeError("Invalid type supplied to TAB in line " + str(self.__line_number)) - return " " * value - else: raise SyntaxError("Unrecognised function in line " + str(self.__line_number)) @@ -1646,4 +1688,4 @@ def __randomizestmt(self): random.seed(seed) else: - random.seed(int(monotonic())) + random.seed(int(monotonic())) \ No newline at end of file From 95cc47cd61359402a23f4207e73c7bddc3b4d737 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 20 Sep 2021 20:21:20 -0400 Subject: [PATCH 102/183] Add files via upload --- examples/REGRESSION.TXT | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 examples/REGRESSION.TXT diff --git a/examples/REGRESSION.TXT b/examples/REGRESSION.TXT new file mode 100644 index 0000000..53f83a3 --- /dev/null +++ b/examples/REGRESSION.TXT @@ -0,0 +1,2 @@ +0123456789Hello World! +This is second line for testing From f6c9abeddb4a5b9ed67f1d77d61826e083686b7f Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 20 Sep 2021 20:27:33 -0400 Subject: [PATCH 103/183] minor code cleanup --- basicparser.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/basicparser.py b/basicparser.py index 68e7d28..15dbc5e 100644 --- a/basicparser.py +++ b/basicparser.py @@ -285,7 +285,6 @@ def __printstmt(self): prntTab = (self.__token.category == Token.TAB) self.__logexpr() - #if type(self.__operand_stack[-1]) == tuple and self.__operand_stack[-1][0] == "TAB": if prntTab: if self.__prnt_column > len(self.__operand_stack[-1]): if fileIO: @@ -317,7 +316,6 @@ def __printstmt(self): prntTab = (self.__token.category == Token.TAB) self.__logexpr() - #if type(self.__operand_stack[-1]) == tuple and self.__operand_stack[-1][0] == "TAB": if prntTab: if self.__prnt_column > len(self.__operand_stack[-1]): if fileIO: @@ -1664,7 +1662,7 @@ def __evaluate_function(self, category): elif category == Token.TAB: if isinstance(value, int): - return (" "*value) + return " "*value else: raise TypeError("Invalid type supplied to TAB in line " + From 720bea5e9fb09d79aca6a34ededeaf0126cab353 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 20 Sep 2021 20:28:44 -0400 Subject: [PATCH 104/183] oops, wrong file --- examples/REGRESSION.TXT | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 examples/REGRESSION.TXT diff --git a/examples/REGRESSION.TXT b/examples/REGRESSION.TXT deleted file mode 100644 index 53f83a3..0000000 --- a/examples/REGRESSION.TXT +++ /dev/null @@ -1,2 +0,0 @@ -0123456789Hello World! -This is second line for testing From 7213477c2eaa647978183e6b3687aa6563532dc7 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 20 Sep 2021 20:28:58 -0400 Subject: [PATCH 105/183] Add files via upload --- examples/regression.bas | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/regression.bas b/examples/regression.bas index 3ce9284..cf96feb 100644 --- a/examples/regression.bas +++ b/examples/regression.bas @@ -91,6 +91,8 @@ 1010 READ I , J , K , L 1020 PRINT "the next line should print 4561.5:" 1030 PRINT I; J ; K ; L +1040 PRINT "The next lines should print: 'Hello World' and then 'AGAIN' under the word 'World'" +1050 PRINT "Hel" ; : PRINT "lo" ; TAB ( 7 ) ; "World" ; TAB ( 7 ) ; "AGAIN" 1610 PRINT "*** Finished ***" 1620 STOP 1630 REM A SUBROUTINE TEST From fdec2dfe8eb6c609a6953ea2474aa4f6ddf88169 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 20 Sep 2021 21:09:20 -0400 Subject: [PATCH 106/183] Tab testing found some porting issues --- examples/PyBStartrek.bas | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/examples/PyBStartrek.bas b/examples/PyBStartrek.bas index c1670f6..5dbffc0 100644 --- a/examples/PyBStartrek.bas +++ b/examples/PyBStartrek.bas @@ -500,7 +500,7 @@ 4080 INPUT"Computer active and awaiting command ";CM$:H8=1 4090 FOR K1= 1 TO 6 4100 IF MID$(CM$,1,3)<>MID$(CM1$,3*K1-2,3) THEN 4120 -4110 ON K1 GOTO 4230,4400,4490,4750,4550,4210 +4110 ON K1 GOTO 4250,4400,4490,4750,4550,4210 4120 NEXT K1 4121 gosub 4130 4122 goto 4080 @@ -513,17 +513,15 @@ 4190 PRINT" REG = Galaxy 'region name' map":PRINT 4191 return 4200 REM setup to change cum gal record to galaxy map -4210 GOSUB 840 -4211 H8=0:G5=1:PRINT" the galaxy":GOTO 4290 -4250 GOSUB 840 -4260 PRINT:PRINT" "; +4210 H8=0:G5=1:PRINT" the galaxy":GOTO 4290 +4250 PRINT:PRINT" "; 4270 PRINT "Computer record of galaxy for quadrant ";Q1;",";Q2 4280 PRINT 4290 PRINT" 1 2 3 4 5 6 7 8" 4300 O1$=" ----- ----- ----- ----- ----- ----- ----- -----" 4310 PRINT O1$ 4311 FOR I=1 TO 8 -4312 PRINT I," ";:IF H8=0 THEN 4350 +4312 PRINT I;" ";:IF H8=0 THEN 4350 4320 FOR J=1 TO 8 4321 PRINT" ";:IF Z(I,J)<>0 THEN 4330 4322 PRINT"***";:GOTO 4340 @@ -543,8 +541,7 @@ 4373 rem 'POKE 1229,0 POKE 1237,1 4380 GOTO 1520 4390 REM status report -4400 GOSUB 840 -4401 PRINT" Status Report":X$="":IF K9<=1 THEN 4410 +4400 PRINT" Status Report":X$="":IF K9<=1 THEN 4410 4402 X$="s" 4410 PRINT"Klingon";X$;" left: ";K9 4420 PRINT"Mission must be completed in ";0.1*INT((T0+T9-T)*10);" stardates" @@ -555,8 +552,7 @@ 4460 PRINT"Your stupidity has left you on your own in" 4470 PRINT" the galaxy -- you have no starbases left!":GOTO 3180 4480 REM torpedo, base nav, d/d calculator -4490 GOSUB 840 -4491 IF K3<=0 THEN 2550 +4490 IF K3<=0 THEN 2550 4500 X$="":IF K3<=1 THEN 4510 4501 X$="s" 4510 PRINT"From ENTERPRISE to Klingon battle cruiser";X$ @@ -564,8 +560,7 @@ 4521 IF K(I,3)<=0 THEN 4740 4530 W1=K(I,1):X=K(I,2) 4540 C1=S1:A=S2:GOTO 4590 -4550 GOSUB 840 -4551 PRINT"Direction/Distance Calculator:" +4550 PRINT"Direction/Distance Calculator:" 4560 PRINT"You are at quadrant ";Q1;",";Q2;" sector ";S1;",";S2 4570 PRINT"Please enter ":INPUT" initial coordinates (x,y) ";C1,A 4580 INPUT" Final coordinates (x,y) ";W1,X @@ -589,8 +584,7 @@ 4730 PRINT"Distance = ";:cc1=SQR(x*X+A*A):PRINT cc1:IF H8=1 THEN 1520 4740 NEXT I 4741 GOTO 1520 -4750 GOSUB 840 -4751 IF B3=0 THEN 4770 +4750 IF B3=0 THEN 4770 4752 PRINT"From ENTERPRISE to Starbase:":W1=B4:X=B5 4760 GOTO 4540 4770 PRINT"Mr. Spock reports, 'Sensors show no starbases in this"; From 3ee924b5e39b6e2a1a77c1e697b7cf9654ace6ff Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 20 Sep 2021 23:57:34 -0400 Subject: [PATCH 107/183] end-point bug for tab eol --- basicparser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/basicparser.py b/basicparser.py index 15dbc5e..9ab96b3 100644 --- a/basicparser.py +++ b/basicparser.py @@ -286,7 +286,7 @@ def __printstmt(self): self.__logexpr() if prntTab: - if self.__prnt_column > len(self.__operand_stack[-1]): + if self.__prnt_column >= len(self.__operand_stack[-1]): if fileIO: self.__file_handles[filenum].write("\n") else: @@ -317,7 +317,7 @@ def __printstmt(self): self.__logexpr() if prntTab: - if self.__prnt_column > len(self.__operand_stack[-1]): + if self.__prnt_column >= len(self.__operand_stack[-1]): if fileIO: self.__file_handles[filenum].write("\n") else: From dda3d4004ddc4b86b10ac4ae4834e88dbd6be300 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 21 Sep 2021 23:53:15 -0400 Subject: [PATCH 108/183] Docking bug --- examples/PyBStartrek.bas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/PyBStartrek.bas b/examples/PyBStartrek.bas index 5dbffc0..7e393bf 100644 --- a/examples/PyBStartrek.bas +++ b/examples/PyBStartrek.bas @@ -456,7 +456,7 @@ 3761 NEXT I 3762 IF Z3=1 THEN 3770 3763 D0=0:GOTO 3790 -3770 D0=1:CC$="docked":E=E0:P=P0 +3770 D0=1:C$="docked":E=E0:P=P0 3780 PRINT"Shields dropped for docking purposes":S=0:GOTO 3810 3790 IF K3<=0 THEN 3800 3791 C$="*red*":GOTO 3810 From df0c7f67650e9d98c7a8128cb36d934f0e4d6e13 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 10 Oct 2021 13:14:05 +0100 Subject: [PATCH 109/183] General tidy up --- README.md | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 49aaa90..f89b6eb 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ but this can be changed with parentheses. Additional numerical operations may be performed using numeric functions (see below). -Not also that + does extra duty as a string concatenation operator. +Not also that '+' does extra duty as a string concatenation operator, while '*' can be used to repeat strings. ## Commands @@ -70,17 +70,16 @@ Programs may be listed using the **LIST** command: The list command can take arguments to refine the line selection listed -`LIST 50` Lists only line 50 +`LIST 50` Lists only line 50. -`LIST 50-100` Lists lines 50 through 100 inclusive +`LIST 50-100` Lists lines 50 through 100 inclusive. `LIST 50 100` Also Lists lines 50 through 100 inclusive, almost any delimiter -works here +works here. -`LIST -100` Lists from the start of the program through line 100 inclusive - -`LIST 50-` Lists from line 50 to the end of the program +`LIST -100` Lists from the start of the program through line 100 inclusive. +`LIST 50-` Lists from line 50 to the end of the program. A program is executed using the **RUN** command: @@ -105,12 +104,12 @@ The program may be re-loaded from disk using the **LOAD** command, again specify Program read from file > ``` - When loading or saving, the .bas extension is assumed if not provided. If you are loading a simple name (alpha/numbers only) and in the working dir, quotes can be omitted: + ``` > LOAD regression ``` -Will load regression.bas from the current working directory. +will load regression.bas from the current working directory. Individual program statements may be deleted by entering their line number only: @@ -125,7 +124,6 @@ Individual program statements may be deleted by entering their line number only: 20 PRINT "Goodbye" > ``` - The program may be erased entirely from memory using the **NEW** command: ``` @@ -557,8 +555,6 @@ I is greater than J > ``` - - Allowable relational operators are: * '=' (equal, note that in BASIC the same operator is used for assignment) @@ -705,7 +701,6 @@ Hello World! > ``` - ### Numeric functions Selected numeric functions are provided, and may be used with any numeric expression. For example, @@ -818,10 +813,6 @@ at position *start* and end at *end*. Returns 0 if no match found. * **TAB**(x) - When included in a **PRINT** statement *print-list*, specifies the position *x* on the line where the next text will be printed. If the specified position *x* is less than the current print position a newline is printed and the print location is set to the specified column. If the **TAB** function is used anywhere other than on a **PRINT** statement, it will return a string containing *x* spaces with no CR/LF - - -**NOTE** For compatibility with older basic dialetcs, all string indexes are 1 based. - Examples for **ASC**, **CHR$** and **STR$** ``` > 10 I = 65 @@ -840,6 +831,12 @@ Strings may also be concatenated using the '+' operator: > RUN Hello there ``` +Strings may be repeated using the '*' operator: + +``` +> 10 PRINT "Hello " * 5 +> RUN +Hello Hello Hello Hello Hello ## Example programs @@ -855,11 +852,11 @@ calculate the corresponding factorial *N!*. * *PyBStartrek.bas* - A port of the 1971 Star Trek text based strategy game. -* *adventure-fast.bas* - A port of a 1979 text based adventure game. +* *adventure-fast.bas* - A port of a 1979 text based Microsoft Adventure game. -* *bagels.bas* - A guessing game. +* *bagels.bas* - A guessing game, which made its first appearnce in the book 'BASIC Computer Games' in 1978. -* *eliza.bas* - A port of the early chatbot, posing as a therapist, originally created by Joseph Weizenbaum in 1964. +* *eliza.bas* - A port of the early chatbot, posing as a therapist, originally created by Joseph Weizenbaum in 1964.This BASIC version can trace its lineage back to an implementation originally developed by Jeff Shrager in 1973. ## Informal grammar definition From 4f84b796e28c1059de8756ccceb520e84a2ab074 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 10 Oct 2021 13:15:21 +0100 Subject: [PATCH 110/183] Formatting fix --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f89b6eb..e9f6b66 100644 --- a/README.md +++ b/README.md @@ -837,6 +837,7 @@ Strings may be repeated using the '*' operator: > 10 PRINT "Hello " * 5 > RUN Hello Hello Hello Hello Hello +``` ## Example programs From 8fab117abccd3c2cc24e69563d5513eb57431db1 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 10 Oct 2021 15:12:53 +0100 Subject: [PATCH 111/183] Initial commit --- examples/oregon.bas | 721 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 721 insertions(+) create mode 100644 examples/oregon.bas diff --git a/examples/oregon.bas b/examples/oregon.bas new file mode 100644 index 0000000..b0e55e2 --- /dev/null +++ b/examples/oregon.bas @@ -0,0 +1,721 @@ +10 REM PROGRAM NAME - OREGON VERSION:01/01/78 +20 REM ORIGINAL PROGRAMMING BY BILL HEINEMANN - 1971 +30 REM SUPPORT RESEARCH AND MATERIALS BY DON RAVITSCH, +40 REM MINNESOTA EDUCATIONAL COMPUTING CONSORTIUM STAFF +50 REM CDC CYBER 70/73-26 BASIC 3.1 +60 REM DOCUMENTATION BOOKLET 'OREGON' AVAILABLE FROM +61 REM MECC SUPPORT SERVICES +62 REM 2520 BROADWAY DRIVE +63 REM ST. PAUL MN 55113 +80 REM +150 REM *FOR THE MEANING OF THE VARIABLES USED, LIST LINES 6470-6790* +155 REM +160 PRINT "DO YOU NEED INSTRUCTIONS (YES/NO)" ; +165 REM PYBASIC CAN'T DO STRING OPERATIONS ON ARRAYS +170 REM DIM C$(5) +180 REM RANDOMIZE REMOVED +190 INPUT C$ +200 IF C$ = "NO" THEN 690 +210 PRINT +220 PRINT +230 REM ***INSTRUCTIONS*** +240 PRINT "THIS PROGRAM SIMULATES A TRIP OVER THE OREGON TRAIL FROM" +250 PRINT "INDEPENDENCE, MISSOURI TO OREGON CITY, OREGON IN 1847." +260 PRINT "YOUR FAMILY OF FIVE WILL COVER THE 2040 MILE OREGON TRAIL" +270 PRINT "IN 5-6 MONTHS --- IF YOU MAKE IT ALIVE." +280 PRINT +290 PRINT "YOU HAD SAVED $900 TO SPEND FOR THE TRIP, AND YOU'VE JUST" +300 PRINT " PAID $200 FOR A WAGON." +310 PRINT "YOU WILL NEED TO SPEND THE REST OF YOUR MONEY ON THE" +320 PRINT " FOLLOWING ITEMS:" +330 PRINT +340 PRINT " OXEN - YOU CAN SPEND $200-$300 ON YOUR TEAM" +350 PRINT " THE MORE YOU SPEND, THE FASTER YOU'LL GO" +360 PRINT " BECAUSE YOU'LL HAVE BETTER ANIMALS" +370 PRINT +380 PRINT " FOOD - THE MORE YOU HAVE, THE LESS CHANCE THERE" +390 PRINT " IS OF GETTING SICK" +400 PRINT +410 PRINT " AMMUNITION - $1 BUYS A BELT OF 50 BULLETS" +420 PRINT " YOU WILL NEED BULLETS FOR ATTACKS BY ANIMALS" +430 PRINT " AND BANDITS, AND FOR HUNTING FOOD" +440 PRINT +450 PRINT " CLOTHING - THIS IS ESPECIALLY IMPORTANT FOR THE COLD" +460 PRINT " WEATHER YOU WILL ENCOUNTER WHEN CROSSING" +470 PRINT " THE MOUNTAINS" +480 PRINT +490 PRINT " MISCELLANEOUS SUPPLIES - THIS INCLUDES MEDICINE AND" +500 PRINT " OTHER THINGS YOU WILL NEED FOR SICKNESS" +510 PRINT " AND EMERGENCY REPAIRS" +520 PRINT +530 PRINT +540 PRINT "YOU CAN SPEND ALL YOUR MONEY BEFORE YOU START YOUR TRIP -" +550 PRINT "OR YOU CAN SAVE SOME OF YOUR CASH TO SPEND AT FORTS ALONG" +560 PRINT "THE WAY WHEN YOU RUN LOW. HOWEVER, ITEMS COST MORE AT" +570 PRINT "THE FORTS. YOU CAN ALSO GO HUNTING ALONG THE WAY TO GET" +580 PRINT "MORE FOOD." +590 PRINT "WHENEVER YOU HAVE TO USE YOUR TRUSTY RIFLE ALONG THE WAY," +600 PRINT "YOU WILL BE TOLD TO TYPE IN A WORD (ONE THAT SOUNDS LIKE A " +610 PRINT "GUN SHOT). THE FASTER YOU TYPE IN THAT WORD AND HIT THE" +620 PRINT "" "RETURN" " KEY, THE BETTER LUCK YOU'LL HAVE WITH YOUR GUN." +630 PRINT +640 PRINT "AT EACH TURN, ALL ITEMS ARE SHOWN IN DOLLAR AMOUNTS" +650 PRINT "EXCEPT BULLETS" +660 PRINT "WHEN ASKED TO ENTER MONEY AMOUNTS, DON'T USE A " "$" "." +670 PRINT +680 PRINT "GOOD LUCK!!!" +690 PRINT +700 PRINT +710 PRINT "HOW GOOD A SHOT ARE YOU WITH YOUR RIFLE?" +720 PRINT " (1) ACE MARKSMAN, (2) GOOD SHOT, (3) FAIR TO MIDDLIN'" +730 PRINT " (4) NEED MORE PRACTICE, (5) SHAKY KNEES" +740 PRINT "ENTER ONE OF THE ABOVE -- THE BETTER YOU CLAIM YOU ARE, THE" +750 PRINT "FASTER YOU'LL HAVE TO BE WITH YOUR GUN TO BE SUCCESSFUL." +760 INPUT D9 +770 IF D9 > 5 THEN 790 +780 GOTO 810 +790 D9 = 0 +800 REM ***INITIAL PURCHASES*** +810 X1 = - 1 +820 REM LINES 820-829 MODIFIED BY CHRISTOPHER PEDERSEN (AUG 10, 2018) +821 REM FOR COMPATIBILITY WITH THE CHIPMUNK BASIC INTERPRETER +822 REM CHIPMUNK BASIC: http://www.nicholson.com/rhn/basic/ +823 D3 = 0 +824 M9 = 0 +825 M = 0 +826 F2 = 0 +827 F1 = 0 +828 S4 = 0 +829 K8 = 0 +830 PRINT +840 PRINT +850 PRINT "HOW MUCH DO YOU WANT TO SPEND ON YOUR OXEN TEAM" ; +860 INPUT A +870 IF A >= 200 THEN 900 +880 PRINT "NOT ENOUGH" +890 GOTO 850 +900 IF A <= 300 THEN 930 +910 PRINT "TOO MUCH" +920 GOTO 850 +930 PRINT "HOW MUCH DO YOU WANT TO SPEND ON FOOD" ; +940 INPUT F +950 IF F >= 0 THEN 980 +960 PRINT "IMPOSSIBLE" +970 GOTO 930 +980 PRINT "HOW MUCH DO YOU WANT TO SPEND ON AMMUNITION" ; +990 INPUT B +1000 IF B >= 0 THEN 1030 +1010 PRINT "IMPOSSIBLE" +1020 GOTO 980 +1030 PRINT "HOW MUCH DO YOU WANT TO SPEND ON CLOTHING" ; +1040 INPUT C +1050 IF C >= 0 THEN 1080 +1060 PRINT "IMPOSSIBLE" +1070 GOTO 1030 +1080 PRINT "HOW MUCH DO YOU WANT TO SPEND ON MISCELLANEOUS SUPPLIES" ; +1090 INPUT M1 +1100 IF M1 >= 0 THEN 1130 +1110 PRINT "IMPOSSIBLE" +1120 GOTO 1080 +1130 T = 700 - A - F - B - C - M1 +1140 IF T >= 0 THEN 1170 +1150 PRINT "YOU OVERSPENT--YOU ONLY HAD $700 TO SPEND. BUY AGAIN" +1160 GOTO 830 +1170 B = 50 * B +1180 PRINT "AFTER ALL YOUR PURCHASES. YOU NOW HAVE " ; T ; " DOLLARS LEFT" +1190 PRINT +1200 PRINT "MONDAY MARCH 29 1847" +1210 PRINT +1220 GOTO 1750 +1230 IF M >= 2040 THEN 5430 +1240 REM ***SETTING DATE*** +1250 D3 = D3 + 1 +1260 PRINT +1270 PRINT "MONDAY " ; +1280 IF D3 > 10 THEN 1300 +1290 ON D3 GOTO 1310 , 1330 , 1350 , 1370 , 1390 , 1410 , 1430 , 1450 , 1470 , 1490 +1300 ON D3 - 10 GOTO 1510 , 1530 , 1550 , 1570 , 1590 , 1610 , 1630 , 1650 , 1670 , 1690 +1310 PRINT "APRIL 12 " ; +1320 GOTO 1720 +1330 PRINT "APRIL 26 " ; +1340 GOTO 1720 +1350 PRINT "MAY 10 " ; +1360 GOTO 1720 +1370 PRINT "MAY 24 " ; +1380 GOTO 1720 +1390 PRINT "JUNE 7 " ; +1400 GOTO 1720 +1410 PRINT "JUNE 21 " ; +1420 GOTO 1720 +1430 PRINT "JULY 5 " ; +1440 GOTO 1720 +1450 PRINT "JULY 19 " ; +1460 GOTO 1720 +1470 PRINT "AUGUST 2 " ; +1480 GOTO 1720 +1490 PRINT "AUGUST 16 " ; +1500 GOTO 1720 +1510 PRINT "AUGUST 31 " ; +1520 GOTO 1720 +1530 PRINT "SEPTEMBER 13 " ; +1540 GOTO 1720 +1550 PRINT "SEPTEMBER 27 " ; +1560 GOTO 1720 +1570 PRINT "OCTOBER 11 " ; +1580 GOTO 1720 +1590 PRINT "OCTOBER 25 " ; +1600 GOTO 1720 +1610 PRINT "NOVEMBER 8 " ; +1620 GOTO 1720 +1630 PRINT "NOVEMBER 22 " ; +1640 GOTO 1720 +1650 PRINT "DECEMBER 6 " ; +1660 GOTO 1720 +1670 PRINT "DECEMBER 20 " ; +1680 GOTO 1720 +1690 PRINT "YOU HAVE BEEN ON THE TRAIL TOO LONG ------" +1700 PRINT "YOUR FAMILY DIES IN THE FIRST BLIZZARD OF WINTER" +1710 GOTO 5170 +1720 PRINT "1847" +1730 PRINT +1740 REM ***BEGINNING EACH TURN*** +1750 IF F >= 0 THEN 1770 +1760 F = 0 +1770 IF B >= 0 THEN 1790 +1780 B = 0 +1790 IF C >= 0 THEN 1810 +1800 C = 0 +1810 IF M1 >= 0 THEN 1830 +1820 M1 = 0 +1830 IF F >= 13 THEN 1850 +1840 PRINT "YOU'D BETTER DO SOME HUNTING OR BUY FOOD AND SOON!!!!" +1850 F = INT ( F ) +1860 B = INT ( B ) +1870 C = INT ( C ) +1880 M1 = INT ( M1 ) +1890 T = INT ( T ) +1900 M = INT ( M ) +1910 M2 = M +1920 IF S4 = 1 THEN 1950 +1930 IF K8 = 1 THEN 1950 +1940 GOTO 1990 +1950 T = T - 20 +1960 IF T < 0 THEN 5080 +1970 PRINT "DOCTOR'S BILL IS $20" +1980 REM LINES 1980-1982 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH MODERN BASIC +1981 LET S4 = 0 +1982 LET K8 = S4 +1990 IF M9 = 1 THEN 2020 +2000 PRINT "TOTAL MILEAGE: " ; M +2010 GOTO 2040 +2020 PRINT "TOTAL MILEAGE: 950" +2030 M9 = 0 +2040 REM LINES 2040-2050 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH MODERN BASIC +2041 PRINT "FOOD: " ; F +2042 PRINT "BULLETS: " ; B +2043 PRINT "CLOTHING: " ; C +2044 PRINT "MISC. SUPPLIES: " ; M1 +2050 PRINT "CASH: $" ; T +2060 IF X1 = - 1 THEN 2170 +2070 X1 = X1 * ( - 1 ) +2080 PRINT "DO YOU WANT TO (1) STOP AT THE NEXT FORT, (2) HUNT, " ; +2090 PRINT "OR (3) CONTINUE" +2100 INPUT X +2110 REM IF X>2 THEN 2150 +2120 REM IF X<1 THEN 2150 +2130 REM LET X=INT(X) +2140 GOTO 2270 +2150 LET X = 3 +2160 GOTO 2270 +2170 PRINT "DO YOU WANT TO (1) HUNT, OR (2) CONTINUE" +2180 INPUT X +2190 IF X = 1 THEN 2210 +2200 LET X = 2 +2210 LET X = X + 1 +2220 IF X = 3 THEN 2260 +2230 IF B > 39 THEN 2260 +2240 PRINT "TOUGH---YOU NEED MORE BULLETS TO GO HUNTING" +2250 GOTO 2170 +2260 X1 = X1 * ( - 1 ) +2270 ON X GOTO 2290 , 2540 , 2720 +2280 REM ***STOPPING AT FORT*** +2290 PRINT "ENTER WHAT YOU WISH TO SPEND ON THE FOLLOWING" +2300 PRINT "FOOD" ; +2310 GOSUB 2330 +2320 GOTO 2410 +2330 INPUT P +2340 IF P < 0 THEN 2400 +2350 T = T - P +2360 IF T >= 0 THEN 2400 +2370 PRINT "YOU DON'T HAVE THAT MUCH--KEEP YOUR SPENDING DOWN" +2380 T = T + P +2390 P = 0 +2400 RETURN +2410 F = F + 2 / 3 * P +2420 PRINT "AMMUNITION" ; +2430 GOSUB 2330 +2440 LET B = INT ( B + 2 / 3 * P * 50 ) +2450 PRINT "CLOTHING" ; +2460 GOSUB 2330 +2470 C = C + 2 / 3 * P +2480 PRINT "MISCELLANEOUS SUPPLIES" ; +2490 GOSUB 2330 +2500 M1 = M1 + 2 / 3 * P +2510 M = M - 45 +2520 GOTO 2720 +2530 REM ***HUNTING*** +2540 IF B > 39 THEN 2570 +2550 PRINT "TOUGH---YOU NEED MORE BULLETS TO GO HUNTING" +2560 GOTO 2080 +2570 M = M - 45 +2580 GOSUB 6140 +2590 IF B1 <= 1 THEN 2660 +2600 IF 100 + RND ( 1 ) < 13 * B1 THEN 2710 +2610 F = F + 48 - 2 * B1 +2620 PRINT "NICE SHOT--RIGHT ON TARGET--GOOD EATIN' TONIGHT!!" +2630 B = B - 10 - 3 * B1 +2640 GOTO 2720 +2650 REM **BELLS IN LINE 2660** +2660 PRINT "RIGHT BETWEEN THE EYES---YOU GOT A BIG ONE!!!!" +2670 PRINT "FULL BELLIES TONIGHT!" +2680 F = F + 52 + RND ( 1 ) * 6 +2690 B = B - 10 - RND ( 1 ) * 4 +2700 GOTO 2720 +2710 PRINT "YOU MISSED---AND YOUR DINNER GOT AWAY....." +2720 IF F >= 13 THEN 2750 +2730 GOTO 5060 +2740 REM ***EATING*** +2750 PRINT "DO YOU WANT TO EAT (1) POORLY (2) MODERATELY" +2760 PRINT "OR (3) WELL" ; +2770 INPUT E +2780 IF E > 3 THEN 2750 +2790 IF E < 1 THEN 2750 +2800 LET E = INT ( E ) +2810 LET F = F - 8 - 5 * E +2820 IF F >= 0 THEN 2860 +2830 F = F + 8 + 5 * E +2840 PRINT "YOU CAN'T EAT THAT WELL" +2850 GOTO 2750 +2860 LET M = M + 200 + ( A - 220 ) / 5 + 10 * RND ( 1 ) +2870 REM LINES 2870-2872 MODIFIED BY C.D.P. FOR COMPATIBILITY W/ CHIPMUNK BASIC +2871 C1 = 0 +2872 L1 = C1 +2880 REM ***RIDERS ATTACK*** +2889 REM LINE 2890 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH CHIPMUNK BASIC +2900 PRINT "RIDERS AHEAD. THEY " ; +2910 S5 = 0 +2919 REM ALL RND(-1) FUNCTION CALLS HAVE BEEN CHANGED TO RND(1) BY C.D.P. +2930 PRINT "DON'T " ; +2940 S5 = 1 +2950 PRINT "LOOK HOSTILE" +2960 PRINT "TACTICS" +2970 PRINT "(1) RUN (2) ATTACK (3) CONTINUE (4) CIRCLE WAGONS" +2990 S5 = 1 - S5 +3000 INPUT T1 +3010 IF T1 < 1 THEN 2970 +3020 IF T1 > 4 THEN 2970 +3030 T1 = INT ( T1 ) +3040 IF S5 = 1 THEN 3330 +3050 IF T1 > 1 THEN 3110 +3060 M = M + 20 +3070 M1 = M1 - 15 +3080 B = B - 150 +3090 A = A - 40 +3100 GOTO 3470 +3110 IF T1 > 2 THEN 3240 +3120 GOSUB 6140 +3130 B = B - B1 * 40 - 80 +3140 IF B1 > 1 THEN 3170 +3150 PRINT "NICE SHOOTING---YOU DROVE THEM OFF" +3160 GOTO 3470 +3170 IF B1 <= 4 THEN 3220 +3180 PRINT "LOUSY SHOT---YOU GOT KNIFED" +3190 K8 = 1 +3200 PRINT "YOU HAVE TO SEE OL' DOC BLANCHARD" +3210 GOTO 3470 +3220 PRINT "KINDA SLOW WITH YOUR COLT .45" +3230 GOTO 3470 +3240 IF T1 > 3 THEN 3290 +3260 LET B = B - 150 +3270 M1 = M1 - 15 +3280 GOTO 3470 +3290 GOSUB 6140 +3300 B = B - B1 * 30 - 80 +3310 M = M - 25 +3320 GOTO 3140 +3330 IF T1 > 1 THEN 3370 +3340 M = M + 15 +3350 A = A - 10 +3360 GOTO 3470 +3370 IF T1 > 2 THEN 3410 +3380 M = M - 5 +3390 B = B - 100 +3400 GOTO 3470 +3410 IF T1 > 3 THEN 3430 +3420 GOTO 3470 +3430 M = M - 20 +3440 GOTO 3470 +3450 PRINT "THEY DID NOT ATTACK" +3460 GOTO 3550 +3470 IF S5 = 0 THEN 3500 +3480 PRINT "RIDERS WERE FRIENDLY, BUT CHECK FOR POSSIBLE LOSSES" +3490 GOTO 3550 +3500 PRINT "RIDERS WERE HOSTILE--CHECK FOR LOSES" +3510 IF B >= 0 THEN 3550 +3520 PRINT "YOU RAN OUT OF BULLETS AND GOT MASSACRED BY THE RIDERS" +3530 GOTO 5170 +3540 REM ***SELECTION OF EVENTS*** +3550 LET D1 = 0 +3560 RESTORE 3620 +3570 R1 = 100 * RND ( 1 ) +3580 LET D1 = D1 + 1 +3590 IF D1 = 16 THEN 4670 +3600 READ D +3610 IF R1 > D THEN 3580 +3620 DATA 6 , 11 , 13 , 15 , 17 , 22 , 32 , 35 , 37 , 42 , 44 , 54 , 64 , 69 , 95 +3630 IF D1 > 10 THEN 3650 +3640 ON D1 GOTO 3660 , 3700 , 3740 , 3790 , 3820 , 3850 , 3880 , 3960 , 4130 , 4190 +3650 ON D1 - 10 GOTO 4220 , 4290 , 4340 , 4560 , 4610 , 4670 +3660 PRINT "WAGON BREAKS DOWN--LOSE TIME AND SUPPLIES FIXING IT" +3670 LET M = M - 15 - 5 * RND ( 1 ) +3680 LET M1 = M1 - 8 +3690 GOTO 4710 +3700 PRINT "0X INJURES LEG---SLOWS YOU DOWN REST OF TRIP" +3710 LET M = M - 25 +3720 LET A = A - 20 +3730 GOTO 4710 +3740 PRINT "BACK LUCK---YOUR DAUGHTER BROKE HER ARM" +3750 PRINT "YOU HAD TO STOP AND USE SUPPLIES TO MAKE A SLING" +3760 M = M - 5 - 4 * RND ( 1 ) +3770 M1 = M1 - 2 - 3 * RND ( 1 ) +3780 GOTO 4710 +3790 PRINT "OX WANDERS OFF---SPEND TIME LOOKING FOR IT" +3800 M = M - 17 +3810 GOTO 4710 +3820 PRINT "YOUR SON GETS LOST---SPEND HALF THE DAY LOOKING FOR HIM" +3830 M = M - 10 +3840 GOTO 4710 +3850 PRINT "UNSAFE WATER--LOSE TIME LOOKING FOR CLEAN SPRING" +3860 LET M = M - 10 * RND ( 1 ) - 2 +3870 GOTO 4710 +3880 IF M > 950 THEN 4490 +3890 PRINT "HEAVY RAINS---TIME AND SUPPLIES LOST" +3910 F = F - 10 +3920 B = B - 500 +3930 M1 = M1 - 15 +3940 M = M - 10 * RND ( 1 ) - 5 +3950 GOTO 4710 +3960 PRINT "BANDITS ATTACK" +3970 GOSUB 6140 +3980 B = B - 20 * B1 +3990 IF B >= 0 THEN 4030 +4000 PRINT "YOU RAN OUT OF BULLETS---THEY GET LOTS OF CASH" +4010 T = T / 3 +4020 GOTO 4040 +4030 IF B1 <= 1 THEN 4100 +4040 PRINT "YOU GOT SHOT IN THE LEG AND THEY TOOK ONE OF YOUR OXEN" +4050 K8 = 1 +4060 PRINT "BETTER HAVE A DOC LOOK AT YOUR WOUND" +4070 M1 = M1 - 5 +4080 A = A - 20 +4090 GOTO 4710 +4100 PRINT "QUICKEST DRAW OUTSIDE OF DODGE CITY!!!" +4110 PRINT "YOU GOT 'EM!" +4120 GOTO 4710 +4130 PRINT "THERE WAS A FIRE IN YOUR WAGON--FOOD AND SUPPLIES DAMAGE!" +4140 F = F - 40 +4150 B = B - 400 +4160 LET M1 = M1 - RND ( 1 ) * 8 - 3 +4170 M = M - 15 +4180 GOTO 4710 +4190 PRINT "LOSE YOUR WAY IN HEAVY FOG---TIME IS LOST" +4200 M = M - 10 - 5 * RND ( 1 ) +4210 GOTO 4710 +4220 PRINT "YOU KILLED A POISONOUS SNAKE AFTER IT BIT YOU" +4230 B = B - 10 +4240 M1 = M1 - 5 +4250 IF M1 >= 0 THEN 4280 +4260 PRINT "YOU DIE OF SNAKEBITE SINCE YOU HAVE NO MEDICINE" +4270 GOTO 5170 +4280 GOTO 4710 +4290 PRINT "WAGON GETS SWAMPED FORDING RIVER--LOSE FOOD AND CLOTHES" +4300 F = F - 30 +4310 C = C - 20 +4320 M = M - 20 - 20 * RND ( 1 ) +4330 GOTO 4710 +4340 PRINT "WILD ANIMALS ATTACK!" +4350 GOSUB 6140 +4360 IF B > 39 THEN 4410 +4370 PRINT "YOU WERE TOO LOW ON BULLETS--" +4380 PRINT "THE WOLVES OVERPOWERED YOU" +4390 K8 = 1 +4400 GOTO 5120 +4410 IF B1 > 2 THEN 4440 +4420 PRINT "NICE SHOOTIN' PARTNER---THEY DIDN'T GET MUCH" +4430 GOTO 4450 +4440 PRINT "SLOW ON THE DRAW---THEY GOT AT YOUR FOOD AND CLOTHES" +4450 B = B - 20 * B1 +4460 C = C - B1 * 4 +4470 F = F - B1 * 8 +4480 GOTO 4710 +4490 PRINT "COLD WEATHER---BRRRRRRR!---YOU " ; +4500 IF C > 22 + 4 * RND ( 1 ) THEN 4530 +4510 PRINT "DONT'T " ; +4520 C1 = 1 +4530 PRINT "HAVE ENOUGH CLOTHING TO KEEP YOU WARM" +4540 IF C1 = 0 THEN 4710 +4550 GOTO 6300 +4560 PRINT "HAIL STORM---SUPPLIES DAMAGED" +4570 M = M - 5 - RND ( 1 ) * 10 +4580 B = B - 200 +4590 M1 = M1 - 4 - RND ( 1 ) * 3 +4600 GOTO 4710 +4610 IF E = 1 THEN 6300 +4620 IF E = 3 THEN 4650 +4640 GOTO 4710 +4650 PRINT "HELPFUL INDIANS SHOW YOU WHERE TO FIND MORE FOOD" +4680 F = F + 14 +4690 GOTO 4710 +4700 REM ***MOUNTAINS*** +4710 IF M <= 950 THEN 1230 +4719 REM LINE 4720 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH CHIPMUNK BASIC +4730 PRINT "RUGGED MOUNTAINS" +4750 PRINT "YOU GOT LOST---LOSE VALUABLE TIME TRYING TO FIND TRAIL!" +4760 M = M - 60 +4770 GOTO 4860 +4790 PRINT "WAGON DAMAGED!---LOSE TIME AND SUPPLIES" +4800 M1 = M1 - 5 +4810 B = B - 200 +4820 M = M - 20 - 30 * RND ( 1 ) +4830 GOTO 4860 +4840 PRINT "THE GOING GETS SLOW" +4860 IF F1 = 1 THEN 4900 +4870 F1 = 1 +4890 PRINT "YOU MADE IT SAFELY THROUGH SOUTH PASS--NO SNOW" +4900 IF M < 1700 THEN 4940 +4910 IF F2 = 1 THEN 4940 +4920 F2 = 1 +4940 IF M > 950 THEN 1230 +4950 M9 = 1 +4960 GOTO 1230 +4970 PRINT "BLIZZARD IN MOUNTAIN PASS--TIME AND SUPPLIES LOST" +4980 L1 = 1 +4990 F = F - 25 +5000 M1 = M1 - 10 +5010 B = B - 300 +5020 M = M - 30 - 40 * RND ( 1 ) +5030 IF C < 18 + 2 * RND ( 1 ) THEN 6300 +5040 GOTO 4940 +5050 REM ***DYING*** +5060 PRINT "YOU RAN OUT OF FOOD AND STARVED TO DEATH" +5070 GOTO 5170 +5080 LET T = 0 +5090 PRINT "YOU CAN'T AFFORD A DOCTOR" +5100 GOTO 5120 +5110 PRINT "YOU RAN OUT OF MEDICAL SUPPLIES" +5120 PRINT "YOU DIED OF " ; +5130 IF K8 = 1 THEN 5160 +5140 PRINT "PNEUMONIA" +5150 GOTO 5170 +5160 PRINT "INJURIES" +5170 PRINT +5180 PRINT "DUE TO YOUR UNFORTUNATE SITUATION, THERE ARE A FEW" +5190 PRINT "FORMALITIES WE MUST GO THROUGH" +5200 PRINT +5210 PRINT "WOULD YOU LIKE A MINISTER?" +5220 INPUT C$ +5230 PRINT "WOULD YOU LIKE A FANCY FUNERAL?" +5240 INPUT C$ +5250 PRINT "WOULD YOU LIKE US TO INFORM YOUR NEXT OF KIN?" +5260 INPUT C$ +5270 IF C$ = "YES" THEN 5310 +5280 PRINT "BUT YOUR AUNT SADIE IN ST. LOUIS IS REALLY WORRIED ABOUT YOU" +5290 PRINT +5300 GOTO 5330 +5310 PRINT "THAT WILL BE $4.50 FOR THE TELEGRAPH CHARGE." +5320 PRINT +5330 PRINT "WE THANK YOU FOR THIS INFORMATION AND WE ARE SORRY YOU" +5340 PRINT "DIDN'T MAKE IT TO THE GREAT TERRITORY OF OREGON" +5350 PRINT "BETTER LUCK NEXT TIME" +5360 PRINT +5370 PRINT +5380 PRINT TAB ( 30 ) ; "SINCERELY" +5390 PRINT +5400 PRINT TAB ( 17 ) ; "THE OREGON CITY CHAMBER OF COMMERCE" +5409 REM 'STOP' COMMAND BELOW CHANGED TO 'END' BY C.D.P. +5410 END +5420 REM ***FINAL TURN*** +5430 F9 = ( 2040 - M2 ) / ( M - M2 ) +5440 F = F + ( 1 - F9 ) * ( 8 + 5 * E ) +5450 PRINT +5460 REM **BELLS IN LINES 5470,5480** +5470 PRINT "YOU FINALLY ARRIVED AT OREGON CITY" +5480 PRINT "AFTER 2040 LONG MILES---HOORAY!!!!!" +5490 PRINT "A REAL PIONEER!" +5500 PRINT +5510 F9 = INT ( F9 * 14 ) +5520 D3 = D3 * 14 + F9 +5530 F9 = F9 + 1 +5540 IF F9 < 8 THEN 5560 +5550 F9 = F9 - 7 +5560 ON F9 GOTO 5570 , 5590 , 5610 , 5630 , 5650 , 5670 , 5690 +5570 PRINT "MONDAY " ; +5580 GOTO 5700 +5590 PRINT "TUESDAY " ; +5600 GOTO 5700 +5610 PRINT "WEDNESDAY " ; +5620 GOTO 5700 +5630 PRINT "THURSDAY " ; +5640 GOTO 5700 +5650 PRINT "FRIDAY " ; +5660 GOTO 5700 +5670 PRINT "SATURDAY " ; +5680 GOTO 5700 +5690 PRINT "SUNDAY " ; +5700 IF D3 > 124 THEN 5740 +5710 D3 = D3 - 93 +5720 PRINT "JULY " ; D3 ; " 1847" +5730 GOTO 5920 +5740 IF D3 > 155 THEN 5780 +5750 D3 = D3 - 124 +5760 PRINT "AUGUST " ; D3 ; " 1847" +5770 GOTO 5920 +5780 IF D3 > 185 THEN 5820 +5790 D3 = D3 - 155 +5800 PRINT "SEPTEMBER " ; D3 ; " 1847" +5810 GOTO 5920 +5820 IF D3 > 216 THEN 5860 +5830 D3 = D3 - 185 +5840 PRINT "OCTOBER " ; D3 ; " 1847" +5850 GOTO 5920 +5860 IF D3 > 246 THEN 5900 +5870 D3 = D3 - 216 +5880 PRINT "NOVEMBER " ; D3 ; " 1847" +5890 GOTO 5920 +5900 D3 = D3 - 246 +5910 PRINT "DECEMBER " ; D3 ; " 1847" +5920 PRINT +5930 REM LINE 5930 REMOVED BY C.D.P. FOR COMPATIBILITY WITH MODERN BASIC +5940 IF B > 0 THEN 5960 +5950 LET B = 0 +5960 IF C > 0 THEN 5980 +5970 LET C = 0 +5980 IF M1 > 0 THEN 6000 +5990 LET M1 = 0 +6000 IF T > 0 THEN 6020 +6010 LET T = 0 +6020 IF F > 0 THEN 6040 +6030 LET F = 0 +6040 REM LINES 6040-6045 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH MODERN BASIC +6041 PRINT "FOOD: " ; F +6042 PRINT "BULLETS: " ; B +6043 PRINT "CLOTHING: " ; C +6044 PRINT "MISC. SUPP.: " ; M1 +6045 PRINT "CASH: " ; T +6050 PRINT +6060 PRINT TAB ( 11 ) ; "PRESIDENT JAMES K. POLK SENDS YOU HIS" +6070 PRINT TAB ( 17 ) ; "HEARTIEST CONGRATULATIONS" +6080 PRINT +6090 PRINT TAB ( 11 ) ; "AND WISHES YOU A PROSPEROUS LIFE AHEAD" +6100 PRINT +6110 PRINT TAB ( 22 ) ; "AT YOUR NEW HOME" +6119 REM 'STOP' COMMAND BELOW CHANGED TO 'END' BY C.D.P. +6120 END +6130 REM ***SHOOTING SUB-ROUTINE*** +6131 REM THE METHOD OF TIMING THE SHOOTING (LINES 6210-6240) +6132 REM WILL VARY FROM SYSTEM TO SYSTEM. FOR EXAMPLE, H-P +6133 REM USERS WILL PROBABLY PREFER TO USE THE 'ENTER' STATEMENT. +6134 REM IF TIMING ON THE USER'S SYSTEM IS HIGHLY SUSCEPTIBLE +6135 REM TO SYSTEM RESPONSE TIME, THE FORMULA IN LINE 6240 CAN +6136 REM BE TAILORED TO ACCOMMODATE THIS BY EITHER INCREASING +6137 REM OR DECREASING THE 'SHOOTING' TIME RECORDED BY THE SYSTEM +6140 DIM S$ ( 5 ) +6150 S$ ( 1 ) = "BANG" +6160 S$ ( 2 ) = "BLAM" +6170 S$ ( 3 ) = "POW" +6180 S$ ( 4 ) = "WHAM" +6190 S6 = INT ( RND ( 1 ) * 4 + 1 ) +6200 PRINT "TYPE " ; S$ ( S6 ) +6201 REM NO TIMER FUNCTION IN PYBASIC, SO RANDOM ELEMENT INTRODUCED +6202 REM I'M AWARE THAT THIS CHANGES THE ELEMENT OF 'SKILL' TO +6203 REM ONE OF LUCK FOR HUNTING +6206 REM 'CLK(0)' FUNCTION CHANGED TO 'TIMER' BY C.D.P. FOR COMPATIBILITY +6207 REM B3 = TIMER changed to B3 = TIMER + 2 BY C.D.P. TO MAKE HUNTING... +6208 REM ...EASIER ON MODERN COMPUTERS. SEE NOTES FROM ORIGINAL AUTHOR ON +6209 REM ... ON TWEAKING HUNTING DIFFICULTY +6210 REM B3 = TIMER + 2 +6220 INPUT C$ +6229 REM 'CLK(0)' FUNCTION CHANGED TO 'TIMER' BY C.D.P. FOR COMPATIBILITY +6230 REM B1 = TIMER +6240 REM B1 = ( ( B1 - B3 ) * 3600 ) - ( D9 - 1 ) +6244 REM IT'S A PYBASIC BODGE, BUT GENERATE RANDOM B1 +6245 B1 = INT(RND(1)*9)+1 +6250 PRINT +6255 IF B1 > 0 THEN 6260 +6257 B1 = 0 +6260 IF C$ = S$ ( S6 ) THEN 6280 +6270 B1 = 9 +6280 RETURN +6290 REM ***ILLNESS SUB-ROUTINE*** +6300 IF 100 * RND ( 1 ) < 10 + 35 * ( E - 1 ) THEN 6370 +6305 REM LOOP INTRODUCED BECAUSE THERE IS NO EXPONENT OPERATOR IN PYBASIC +6309 REM LINE 6310 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH CHIPMUNK BASIC +6310 REM IF 100*RND(1)<100-(40/4^(E-1)) THEN 6410 +6311 ANSWER = 40 / 4 +6312 INCREMENT = 40 / 4 +6313 FOR I = 1 TO E - 2 +6314 FOR J = 1 TO 40 / 4 - 1 +6315 ANSWER = ANSWER + INCREMENT +6316 NEXT J +6317 INCREMENT = ANSWER +6318 NEXT I +6319 IF 100 * RND ( 1 ) < 100 - ANSWER THEN 6410 +6320 PRINT "SERIOUS ILLNESS---" +6330 PRINT "YOU MUST STOP FOR MEDICAL ATTENTION" +6340 M1 = M1 - 10 +6350 S4 = 1 +6360 GOTO 6440 +6370 PRINT "WILD ILLNESS---MEDICINE USED" +6380 M = M - 5 +6390 M1 = M1 - 2 +6400 GOTO 6440 +6410 PRINT "BAD ILLNESS---MEDICINE USED" +6420 M = M - 5 +6430 M1 = M1 - 5 +6440 IF M1 < 0 THEN 5110 +6450 IF L1 = 1 THEN 4940 +6460 GOTO 4710 +6470 REM ***IDENTIFICATION OF VARIABLES IN THE PROGRAM*** +6480 REM A = AMOUNT SPENT ON ANIMALS +6490 REM B = AMOUNT SPENT ON AMMUNITION +6500 REM B1 = ACTUAL RESPONSE TIME FOR INPUTTING "BANG" +6510 REM B3 = CLOCK TIME AT START OF INPUTTING "BANG" +6520 REM C = AMOUNT SPENT ON CLOTHING +6530 REM C1 = FLAG FOR INSUFFICIENT CLOTHING IN COLD WEATHER +6540 REM C$ = YES/N0 RESPONSE TO QUESTIONS +6550 REM D1 = COUNTER IN GENERATING EVENTS +6560 REM D3 = TURN NUMBER FOR SETTING DATE +6570 REM D4 = CURRENT DATE +6580 REM D9 = CHOICE OF SHOOTING EXPERTISE LEVEL +6590 REM E = CHOICE OF EATING +6600 REM F = AMOUNT SPENT ON FOOD +6610 REM F1 = FLAG FOR CLEARING SOUTH PASS +6620 REM F2 = FLAG FOR CLEARING BLUE MOUNTAINS +6630 REM F9 = FRACTION OF 2 WEEKS TRAVELED ON FINAL TURN +6640 REM K8 = FLAG FOR INJURY +6650 REM L1 = FLAG FOR BLIZZARD +6660 REM M = TOTAL MILEAGE WHOLE TRIP +6670 REM M1 = AMOUNT SPENT ON MISCELLANEOUS SUPPLIES +6680 REM M2 = TOTAL MILEAGE UP THROUGH PREVIOUS TURN +6690 REM M9 = FLAG FOR CLEARING SOUTH PASS IN SETTING MILEAGE +6700 REM P = AMOUNT SPENT ON ITEMS AT FORT +6710 REM R1 = RANDOM NUMBER IN CHOOSING EVENTS +6720 REM S4 = FLAG FOR ILLNESS +6730 REM S5 = ""HOSTILITY OF RIDERS"" FACTOR +6740 REM S6 = SHOOTING WORD SELECTOR +6750 REM S$ = VARIATIONS OF SHOOTING WORD +6760 REM T = CASH LEFT OVER AFTER INITIAL PURCHASES +6770 REM T1 = CHOICE OF TACTICS WHEN ATTACKED +6780 REM X = CHOICE OF ACTION FOR EACH TURN +6790 REM X1 = FLAG FOR FORT OPTION +6800 END From 8b1884e495ca3a1e5ce87a9bd003b1e2254cd7f6 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 10 Oct 2021 15:15:39 +0100 Subject: [PATCH 112/183] Added Oregon Trail --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e9f6b66..0ada0b6 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ Multiple statements may appear on one line separated by a colon: ``` 10 FOR I = 1 to 10: PRINT I: NEXT ``` -Will need to be decomposed to individual lines +will need to be decomposed to individual lines. ### Variables @@ -859,6 +859,8 @@ calculate the corresponding factorial *N!*. * *eliza.bas* - A port of the early chatbot, posing as a therapist, originally created by Joseph Weizenbaum in 1964.This BASIC version can trace its lineage back to an implementation originally developed by Jeff Shrager in 1973. +* *oregon.bas* - A port (of a port by the looks of it) of The Oregon Trail. This is a text based adventure game, originally developed by Don Rawitsch, Bill Heinemann, and Paul Dillenberger in 1971. This could still be a bit buggy, the listing I found wasn't the greatest. + ## Informal grammar definition **ABS**(*numerical-expression*) - Calculates the absolute value of the result of *numerical-expression* From 08417c4ee7c9da232d89b07d4b42c0e4c66b1565 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 10 Oct 2021 15:16:55 +0100 Subject: [PATCH 113/183] Fix minor typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0ada0b6..5d9a340 100644 --- a/README.md +++ b/README.md @@ -855,9 +855,9 @@ calculate the corresponding factorial *N!*. * *adventure-fast.bas* - A port of a 1979 text based Microsoft Adventure game. -* *bagels.bas* - A guessing game, which made its first appearnce in the book 'BASIC Computer Games' in 1978. +* *bagels.bas* - A guessing game, which made its first appearance in the book 'BASIC Computer Games' in 1978. -* *eliza.bas* - A port of the early chatbot, posing as a therapist, originally created by Joseph Weizenbaum in 1964.This BASIC version can trace its lineage back to an implementation originally developed by Jeff Shrager in 1973. +* *eliza.bas* - A port of the early chatbot, posing as a therapist, originally created by Joseph Weizenbaum in 1964. This BASIC version can trace its lineage back to an implementation originally developed by Jeff Shrager in 1973. * *oregon.bas* - A port (of a port by the looks of it) of The Oregon Trail. This is a text based adventure game, originally developed by Don Rawitsch, Bill Heinemann, and Paul Dillenberger in 1971. This could still be a bit buggy, the listing I found wasn't the greatest. From 373c7e7bfbbe30cf846aa3534f6d77bf916ab242 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 10 Oct 2021 17:01:11 +0100 Subject: [PATCH 114/183] Replaced loop with POW --- examples/oregon.bas | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/examples/oregon.bas b/examples/oregon.bas index b0e55e2..c30c6bb 100644 --- a/examples/oregon.bas +++ b/examples/oregon.bas @@ -657,20 +657,10 @@ 6270 B1 = 9 6280 RETURN 6290 REM ***ILLNESS SUB-ROUTINE*** -6300 IF 100 * RND ( 1 ) < 10 + 35 * ( E - 1 ) THEN 6370 -6305 REM LOOP INTRODUCED BECAUSE THERE IS NO EXPONENT OPERATOR IN PYBASIC +6300 IF 100 * RND ( 1 ) < 10 + 35 * ( E - 1 ) THEN 6370 6309 REM LINE 6310 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH CHIPMUNK BASIC -6310 REM IF 100*RND(1)<100-(40/4^(E-1)) THEN 6410 -6311 ANSWER = 40 / 4 -6312 INCREMENT = 40 / 4 -6313 FOR I = 1 TO E - 2 -6314 FOR J = 1 TO 40 / 4 - 1 -6315 ANSWER = ANSWER + INCREMENT -6316 NEXT J -6317 INCREMENT = ANSWER -6318 NEXT I -6319 IF 100 * RND ( 1 ) < 100 - ANSWER THEN 6410 -6320 PRINT "SERIOUS ILLNESS---" +6310 IF 100*RND(1)<100-POW(40/4,E-1) THEN 6410 +6320 PRINT "SERIOUS ILLNESS---" 6330 PRINT "YOU MUST STOP FOR MEDICAL ATTENTION" 6340 M1 = M1 - 10 6350 S4 = 1 From bd573200bd9a8df4e8d2a2d4992d5754e3613809 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 10 Oct 2021 14:26:00 -0400 Subject: [PATCH 115/183] Test for working loop variable on NEXT statement --- examples/regression.bas | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/regression.bas b/examples/regression.bas index cf96feb..24076bb 100644 --- a/examples/regression.bas +++ b/examples/regression.bas @@ -93,6 +93,13 @@ 1030 PRINT I; J ; K ; L 1040 PRINT "The next lines should print: 'Hello World' and then 'AGAIN' under the word 'World'" 1050 PRINT "Hel" ; : PRINT "lo" ; TAB ( 7 ) ; "World" ; TAB ( 7 ) ; "AGAIN" +1060 PRINT "This loop should count from 1 to 10" +1070 FOR I = 1 TO 10 +1080 FOR J = 1 TO 3 +1090 PRINT I +1100 GOTO 1130 +1120 NEXT J +1130 NEXT I 1610 PRINT "*** Finished ***" 1620 STOP 1630 REM A SUBROUTINE TEST From 44b6f14f4d61c425f5fabed14dc3e4c8e8741e96 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 10 Oct 2021 14:27:46 -0400 Subject: [PATCH 116/183] Use loop variable on NEXT Per issue #52 --- basicparser.py | 12 ++++++++++-- flowsignal.py | 4 ++-- program.py | 9 ++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/basicparser.py b/basicparser.py index 9ab96b3..298eb83 100644 --- a/basicparser.py +++ b/basicparser.py @@ -1150,7 +1150,7 @@ def __forstmt(self): ftarget=loop_variable) else: # Set up and return the flow signal - return FlowSignal(ftype=FlowSignal.LOOP_BEGIN) + return FlowSignal(ftype=FlowSignal.LOOP_BEGIN,floop_var=loop_variable) def __nextstmt(self): """Processes a NEXT statement that terminates @@ -1163,7 +1163,15 @@ def __nextstmt(self): self.__advance() # Advance past NEXT token - return FlowSignal(ftype=FlowSignal.LOOP_REPEAT) + # Process the loop variable initialisation + loop_variable = self.__token.lexeme # Save lexeme of + # the current token + + if loop_variable.endswith('$'): + raise SyntaxError('Syntax error: Loop variable is not numeric' + + ' in line ' + str(self.__line_number)) + + return FlowSignal(ftype=FlowSignal.LOOP_REPEAT,floop_var=loop_variable) def __ongosubstmt(self): """Process the ON-GOSUB statement diff --git a/flowsignal.py b/flowsignal.py index a86827a..33b01fa 100644 --- a/flowsignal.py +++ b/flowsignal.py @@ -73,7 +73,7 @@ class FlowSignal: # been processed. There should be therefore be no ftarget value specified STOP = 6 - def __init__(self, ftarget=None, ftype=SIMPLE_JUMP): + def __init__(self, ftarget=None, ftype=SIMPLE_JUMP, floop_var=None): """Creates a new FlowSignal for a branch. If the jump target is supplied, then the branch is assumed to be either a GOTO or conditional branch and the type is assigned as @@ -103,4 +103,4 @@ def __init__(self, ftarget=None, ftype=SIMPLE_JUMP): self.ftype = ftype self.ftarget = ftarget - + self.floop_var = floop_var diff --git a/program.py b/program.py index 965bc06..920358d 100644 --- a/program.py +++ b/program.py @@ -147,9 +147,11 @@ def __init__(self): self.__next_stmt = 0 # Initialise return stack for subroutine returns - # and loop returns self.__return_stack = [] + # return dictionary for loop returns + self.__return_loop = {} + # Setup DATA object self.__data = BASICData() @@ -370,7 +372,8 @@ def execute(self): # that it can be returned to when the loop # repeats #self.__return_stack.append(self.get_next_line_number()) - self.__return_stack.append(line_numbers[index]) + #self.__return_stack.append(line_numbers[index]) + self.__return_loop[flowsignal.floop_var] = line_numbers[index] # Continue to the next statement in the loop index = index + 1 @@ -415,7 +418,7 @@ def execute(self): # Loop repeat encountered # Pop the loop start address from the stack try: - index = line_numbers.index(self.__return_stack.pop()) + index = line_numbers.index(self.__return_loop.pop(flowsignal.floop_var)) except ValueError: raise RuntimeError("Invalid loop exit in line " + From 2a59976d2c55a4476351053e3f0e8da86da3118c Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 10 Oct 2021 15:17:05 -0400 Subject: [PATCH 117/183] Loop variable error reporting fix --- program.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program.py b/program.py index 920358d..b51b29f 100644 --- a/program.py +++ b/program.py @@ -424,7 +424,7 @@ def execute(self): raise RuntimeError("Invalid loop exit in line " + str(self.get_next_line_number())) - except IndexError: + except KeyError: raise RuntimeError("NEXT encountered without corresponding " + "FOR loop in line " + str(self.get_next_line_number())) From 8e2f803c87a67c2de59b49a5e0eeaf81dcb94d35 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 11 Oct 2021 00:02:57 -0400 Subject: [PATCH 118/183] clean up old unnecessary comment --- program.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/program.py b/program.py index b51b29f..ed7f7a7 100644 --- a/program.py +++ b/program.py @@ -371,8 +371,6 @@ def execute(self): # Put loop line number on the stack so # that it can be returned to when the loop # repeats - #self.__return_stack.append(self.get_next_line_number()) - #self.__return_stack.append(line_numbers[index]) self.__return_loop[flowsignal.floop_var] = line_numbers[index] # Continue to the next statement in the loop @@ -479,4 +477,4 @@ def set_next_line_number(self, line_number): :param line_number: The new line number """ - self.__next_stmt = line_number \ No newline at end of file + self.__next_stmt = line_number From 52ebda33949b9b671798337ca562e24419c1fba2 Mon Sep 17 00:00:00 2001 From: richpl Date: Mon, 11 Oct 2021 21:25:36 +0100 Subject: [PATCH 119/183] Initial commit --- examples/life.bas | 94 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 examples/life.bas diff --git a/examples/life.bas b/examples/life.bas new file mode 100644 index 0000000..760d7cc --- /dev/null +++ b/examples/life.bas @@ -0,0 +1,94 @@ +2 PRINT TAB(34);"LIFE" +4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN NEW JERSEY" +6 PRINT: PRINT: PRINT +8 PRINT "ENTER YOUR PATTERN, AS A SERIES OF SPACES" +9 PRINT "AND STARS, LINE BY LINE. FINISH BY TYPING" +10 PRINT "'DONE' ON THE FINAL LINE:" +11 X1=1: Y1=1: X2=24: Y2=70: G=0: P=0 +12 DIM A(24, 70): DIM B$(24) +15 I9=1 +20 C=1 +30 INPUT TEMP$ +35 B$(C) = TEMP$ +40 IF B$(C)="DONE" THEN 45 ELSE 50 +45 B$(C)="": GOTO 80 +46 REM ORIGINAL VERSION REQUIRED USER TO WRITE +47 REM DOTS INSTEAD OF SPACES, WHICH WOULD THEN BE +48 REM CONVERTED TO SPACES +50 REM IF LEFT$(B$(C), 1)="." THEN 55 ELSE 60 +55 REM B$(C)=" "+RIGHT$(B$(C), LEN(B$(C))-1) +60 C=C+1 +70 GOTO 30 +80 C=C-1: L=0 +90 FOR X=1 TO C-1 +100 IF LEN(B$(X))>L THEN 105 ELSE 110 +105 L=LEN(B$(X)) +110 NEXT X +120 X1=INT(11-C/2) +130 Y1=INT(33-L/2) +135 REM TRANSCRIBE INPUT PATTERN INTO ARRAY +140 FOR X=1 TO C +150 FOR Y=1 TO LEN(B$(X)) +160 IF MID$(B$(X), Y, 1)<>" " THEN 165 ELSE 170 +165 A(X1+X, Y1+Y)=1: P=P+1 +170 NEXT Y +180 NEXT X +200 PRINT: PRINT: PRINT +210 PRINT "GENERATION: "; G; " POPULATION: "; P; +212 IF I9=0 THEN 213 ELSE 215 +213 PRINT " INVALID!"; +215 X3=24: Y3=70: X4=1: Y4=1: P=0 +220 G=G+1 +230 FOR X=X1 TO X2 +240 PRINT +250 FOR Y=Y1 TO Y2 +253 IF A(X, Y)=2 THEN 254 ELSE 256 +254 A(X, Y)=0: GOTO 270 +256 IF A(X, Y)=3 THEN 258 ELSE 260 +258 A(X, Y)=1: GOTO 261 +260 IF A(X, Y)<>1 THEN 270 +261 PRINT TAB(Y);"*"; +262 IF XX4 THEN 265 ELSE 266 +265 X4=X +266 IF YY4 THEN 269 ELSE 270 +269 Y4=Y +270 NEXT Y +290 NEXT X +295 FOR X=X2+1 TO 24 +296 PRINT +297 NEXT X +299 X1=X3: X2=X4: Y1=Y3: Y2=Y4 +301 IF X1<3 THEN 302 ELSE 303 +302 X1=3: I9=-1 +303 IF X2>22 THEN 304 ELSE 305 +304 X2=22: I9=-1 +305 IF Y1<3 THEN 306 ELSE 307 +306 Y1=3: I9=-1 +307 IF Y2>68 THEN 308 ELSE 309 +308 Y2=68: I9=-1 +309 P=0 +500 FOR X=X1-1 TO X2+1 +510 FOR Y=Y1-1 TO Y2+1 +520 C=0 +530 FOR I=X-1 TO X+1 +540 FOR J=Y-1 TO Y+1 +550 IF A(I, J)=1 OR A(I, J)=2 THEN 555 ELSE 560 +555 C=C+1 +560 NEXT J +570 NEXT I +580 IF A(X, Y)=0 THEN 610 +590 IF C<3 or C>4 THEN 592 ELSE 595 +592 A(X, Y)=2: GOTO 600 +595 P=P+1 +600 GOTO 620 +610 IF C=3 THEN 615 ELSE 620 +615 A(X, Y)=3: P=P+1 +620 NEXT Y +630 NEXT X +635 X1=X1-1: Y1=Y1-1: X2=X2+1: Y2=Y2+1 +640 GOTO 210 +650 END \ No newline at end of file From 439cce8b0a67be6ca5c0e14c4a8af83e9d77dce5 Mon Sep 17 00:00:00 2001 From: richpl Date: Mon, 11 Oct 2021 21:28:29 +0100 Subject: [PATCH 120/183] Added Conway's Game of Life --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5d9a340..2ad127f 100644 --- a/README.md +++ b/README.md @@ -861,6 +861,8 @@ calculate the corresponding factorial *N!*. * *oregon.bas* - A port (of a port by the looks of it) of The Oregon Trail. This is a text based adventure game, originally developed by Don Rawitsch, Bill Heinemann, and Paul Dillenberger in 1971. This could still be a bit buggy, the listing I found wasn't the greatest. +* *life.bas* - An implementation of Conway's Game of Life. This version is a port of the BASIC program which appeared in 'BASIC Computer Games' in 1978. + ## Informal grammar definition **ABS**(*numerical-expression*) - Calculates the absolute value of the result of *numerical-expression* From 994c3df8306d9af4854e782268ac0ea8291f52a7 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 15 Oct 2021 00:01:04 -0400 Subject: [PATCH 121/183] IF generalization --- basicparser.py | 80 ++++++++++++++++++++++++++++++++++++++++---------- flowsignal.py | 10 +++++-- interpreter.py | 2 +- program.py | 2 ++ 4 files changed, 74 insertions(+), 20 deletions(-) diff --git a/basicparser.py b/basicparser.py index 298eb83..dd23eed 100644 --- a/basicparser.py +++ b/basicparser.py @@ -124,24 +124,65 @@ def parse(self, tokenlist, line_number): # Remember the line number to aid error reporting self.__line_number = line_number self.__tokenlist = [] + self.__tokenindex = 0 + linetokenindex = 0 for token in tokenlist: - if token.category == token.COLON: + # If statements will always be the last statment processed on a line so + # any colons found after an IF are part of the condition execution statements + # and will be processed in the recursive call to parse + if token.category == token.IF: + # process IF statement to move __tokenidex to the code block + # of the THEN or ELSE and then call PARSE recursivly to process that code block + # this will terminate the token loop by RETURNing to the calling module + # + # **Warning** if an IF stmt is used in the THEN code block or multiple IF statement are used + # in a THEN or ELSE block the block grouping is ambiguious and logical processing may not + # function as expected. There is no ambiguity when single IF statements are placed within ELSE blocks + linetokenindex += self.__tokenindex + self.__tokenindex = 0 + self.__tokenlist = tokenlist[linetokenindex:] + + # Assign the first token + self.__token = self.__tokenlist[0] + flow = self.__stmt() # process IF statement + if flow and (flow.ftype == FlowSignal.EXECUTE): + # recursive call to process THEN/ELSE block + try: + return self.parse(tokenlist[linetokenindex+self.__tokenindex:],line_number) + except RuntimeError as err: + raise RuntimeError(str(err)+' in line ' + str(self.__line_number)) + else: + # branch on original syntax 'IF cond THEN lineno [ELSE lineno]' + # in this syntax the then or else code block is not a legal basic statement + # so recursive processing can't be used + return flow + elif token.category == token.COLON: + # Found a COLON, process tokens found to this point + linetokenindex += self.__tokenindex self.__tokenindex = 0 # Assign the first token self.__token = self.__tokenlist[self.__tokenindex] + flow = self.__stmt() if flow: return flow + linetokenindex += 1 self.__tokenlist = [] + elif token.category == token.ELSE: + # if we find an ELSE we must be in a recursive call and be processing a THEN block + # since we're processing the THEN block we are done if we hit an ELSE + break else: self.__tokenlist.append(token) - + # reached end of statement, process tokens collected since last COLON (or from start if no COLONs) + linetokenindex += self.__tokenindex self.__tokenindex = 0 # Assign the first token self.__token = self.__tokenlist[self.__tokenindex] + return self.__stmt() def __advance(self): @@ -311,6 +352,7 @@ def __printstmt(self): if self.__tokenindex == len(self.__tokenlist) - 1: # If a semicolon ends this line, don't print # a newline.. a-la ms-basic + self.__advance() return self.__advance() prntTab = (self.__token.category == Token.TAB) @@ -1031,31 +1073,37 @@ def __ifstmt(self): # Process the THEN part and save the jump value self.__consume(Token.THEN) - if self.__token.category == Token.GOTO: - self.__advance() # Advance past optional GOTO + if self.__token.category != Token.UNSIGNEDINT: + if saveval: + return FlowSignal(ftype=FlowSignal.EXECUTE) + else: + self.__expr() - self.__expr() - then_jump = self.__operand_stack.pop() + # Jump if the expression evaluated to True + if saveval: + # Set up and return the flow signal + return FlowSignal(ftarget=self.__operand_stack.pop()) - # Jump if the expression evaluated to True - if saveval: - # Set up and return the flow signal - return FlowSignal(ftarget=then_jump) + # advance to ELSE + while self.__tokenindex < len(self.__tokenlist) and self.__token.category != Token.ELSE: + self.__advance() # See if there is an ELSE part if self.__token.category == Token.ELSE: self.__advance() - if self.__token.category == Token.GOTO: - self.__advance() # Advance past optional GOTO + if self.__token.category != Token.UNSIGNEDINT: + return FlowSignal(ftype=FlowSignal.EXECUTE) + else: - self.__expr() + self.__expr() - # Set up and return the flow signal - return FlowSignal(ftarget=self.__operand_stack.pop()) + # Set up and return the flow signal + return FlowSignal(ftarget=self.__operand_stack.pop()) else: # No ELSE action + #return FlowSignal(ftype=FlowSignal.SKIP) return None def __forstmt(self): @@ -1694,4 +1742,4 @@ def __randomizestmt(self): random.seed(seed) else: - random.seed(int(monotonic())) \ No newline at end of file + random.seed(int(monotonic())) diff --git a/flowsignal.py b/flowsignal.py index 33b01fa..b966c1d 100644 --- a/flowsignal.py +++ b/flowsignal.py @@ -73,6 +73,9 @@ class FlowSignal: # been processed. There should be therefore be no ftarget value specified STOP = 6 + # Indicates that a conditional result block should be executed + EXECUTE = 7 + def __init__(self, ftarget=None, ftype=SIMPLE_JUMP, floop_var=None): """Creates a new FlowSignal for a branch. If the jump target is supplied, then the branch is assumed to be @@ -85,11 +88,12 @@ def __init__(self, ftarget=None, ftype=SIMPLE_JUMP, floop_var=None): :param ftarget: The associated value :param ftype: Either GOSUB, SIMPLE_JUMP, RETURN, LOOP_BEGIN, LOOP_SKIP or STOP + :param floop_var: The loop variable of a FOR/NEXT loop """ if ftype not in [self.GOSUB, self.SIMPLE_JUMP, self.LOOP_BEGIN, self.LOOP_REPEAT, self.RETURN, - self.LOOP_SKIP, self.STOP]: + self.LOOP_SKIP, self.STOP, self.EXECUTE]: raise TypeError("Invalid flow signal type supplied: " + str(ftype)) if ftarget == None and \ @@ -98,9 +102,9 @@ def __init__(self, ftarget=None, ftype=SIMPLE_JUMP, floop_var=None): if ftarget != None and \ ftype in [self.RETURN, self.LOOP_BEGIN, self.LOOP_REPEAT, - self.STOP]: + self.STOP, self.EXECUTE]: raise TypeError("Target wrongly supplied for flow signal " + str(ftype)) self.ftype = ftype self.ftarget = ftarget - self.floop_var = floop_var + self.floop_var = floop_var \ No newline at end of file diff --git a/interpreter.py b/interpreter.py index d81814d..caddf20 100644 --- a/interpreter.py +++ b/interpreter.py @@ -133,4 +133,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/program.py b/program.py index ed7f7a7..d847cd7 100644 --- a/program.py +++ b/program.py @@ -371,6 +371,8 @@ def execute(self): # Put loop line number on the stack so # that it can be returned to when the loop # repeats + #self.__return_stack.append(self.get_next_line_number()) + #self.__return_stack.append(line_numbers[index]) self.__return_loop[flowsignal.floop_var] = line_numbers[index] # Continue to the next statement in the loop From 28732a2fb45d80844fc0d4fef75215e4fef6bbdc Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 15 Oct 2021 00:01:52 -0400 Subject: [PATCH 122/183] IF generalization tests --- examples/regression.bas | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/examples/regression.bas b/examples/regression.bas index 24076bb..1960ef9 100644 --- a/examples/regression.bas +++ b/examples/regression.bas @@ -100,6 +100,35 @@ 1100 GOTO 1130 1120 NEXT J 1130 NEXT I +1140 INPUT "Enter T for the THEN blocks to execute, E for the ELSE (T/E): " ; ANS$ +1150 ANS$ = UPPER$ ( ANS$ ) +1160 IF ANS$ = "T" THEN 1190 +1170 PRINT "1170: Did not enter T" +1180 GOTO 1200 +1190 PRINT "1190: Entered T" +1200 IF ANS$ = "T" THEN GOTO 1230 +1210 PRINT "1210: Did not enter T" +1220 GOTO 1240 +1230 PRINT "1230: Entered T" +1240 IF ANS$ = "T" THEN 1280 ELSE 1260 +1250 PRINT "ERROR - Should not be at line 1250" +1260 PRINT "1260: Did not enter T" +1270 GOTO 1290 +1280 PRINT "1280: Entered T" +1290 IF ANS$ = "T" THEN GOTO 1330 ELSE GOTO 1310 +1300 PRINT "ERROR - Should not be at line 1300" +1310 PRINT "1310: Did not enter T" +1320 GOTO 1340 +1330 PRINT "1330: Entered T" +1340 IF ANS$ = "T" THEN PRINT "1340: Entered T" +1350 IF ANS$ <> "T" THEN PRINT "1350: Did not enter T" +1360 IF ANS$ = "T" THEN PRINT "1360: Entered T" ELSE PRINT "1360: Did not enter T" +1370 IF ANS$ = "T" THEN PRINT "1370: Entered T" : GOTO 1390 +1380 IF ANS$ <> "T" THEN PRINT "1380: Did not enter T" +1390 IF ANS$ = "T" THEN PRINT "1390: Entered " ; : PRINT "T" ELSE PRINT "1390: Did " ; : PRINT "not enter T" +1400 IF ANS$ = "T" THEN PRINT "1400: Entered T" ELSE PRINT "1400: Did " ; : PRINT "not enter T" +1410 IF ANS$ = "T" THEN PRINT "1410: Entered " ; : PRINT "T" ELSE PRINT "1410: Did not enter T" +1420 PRINT "Compount Stmt w/conditionals ";:IF ANS$ = "T" THEN PRINT "1420: Entered " ; : PRINT "T" ELSE PRINT "1420: Did " ; : PRINT "not enter T" 1610 PRINT "*** Finished ***" 1620 STOP 1630 REM A SUBROUTINE TEST From 1d4aabe87f5c84462f6d6d1e7b3005565fc6c69e Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 15 Oct 2021 00:23:17 -0400 Subject: [PATCH 123/183] Updates for IF statement generalization --- README.md | 120 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 2ad127f..6dd8415 100644 --- a/README.md +++ b/README.md @@ -491,10 +491,16 @@ be replaced by the start value, it will not be evaluated. After the completion of the loop, the loop variable value will be the end value + step value (unless the loop is exited using a **GOTO** statement). -### Conditional branching +### Conditionals -Conditional branches are implemented using the **IF-THEN-ELSE** statement. The expression is evaluated and the appropriate jump -made depending upon the result of the evaluation. +Conditionals are implemented using the **IF-THEN-ELSE** statement. The expression is evaluated and the appropriate +statements executed depending upon the result of the evaluation. If a positive integer is supplied as +the **THEN** or the **ELSE** statement, a branch will be performed to the indicated line number. + +Note that the **ELSE** clause is optional and may be omitted. In this case, the **THEN** branch is taken if the +expression evaluates to true, otherwise the next statement is executed. + +**Conditional branching example:** ``` > 10 REM PRINT THE GREATEST NUMBER @@ -510,51 +516,49 @@ made depending upon the result of the evaluation. > ``` -Note that the **ELSE** clause is optional and may be omitted. In this case, the **THEN** branch is taken if the -expression evaluates to true, otherwise the following statement is executed. - -You can optionally give the **GOTO** keyword before your line numbers. This is for compatibility with other BASIC dialects. e.g. `40 IF I > J THEN GOTO 50 ELSE GOTO 70` - -The **ON GOTO|GOSUB** *expr* *line1,line2,...* statement will call a subroutine or branch to a line number in the list of line numbers corresponding to the ordinal -value of the evaluated *expr*. The first line number corresponds with an *expr* value of 1. *expr* must evaluate to an integer value. - If *expr* evaluates to less than 1 or greater than the number of provided line numbers execution continues on the next -statement without making a subroutine call or branch: +The following code segement is equivelant to the segment above: ``` -> 20 LET J = 2 -> 30 ON J GOSUB 100,200,300 -> 40 STOP -> 100 REM THE 1ST SUBROUTINE -> 110 PRINT "J is ONE" -> 120 RETURN -> 200 REM THE 2ND SUBROUTINE -> 210 PRINT "J is TWO" -> 220 RETURN -> 300 REM THE 3RD SUBROUTINE -> 310 PRINT "J is THREE" -> 320 RETURN +> 10 REM PRINT THE GREATEST NUMBER +> 20 LET I = 10 +> 30 LET J = 20 +> 40 IF I > J THEN PRINT I ELSE PRINT J +> 80 REM FINISHED > RUN -J is TWO +20 > ``` -It is also possible to call a subroutine depending upon the result of a conditional expression using the **IFF** function (see Ternary Functions below). In -the example below, if the expression evaluates to true, **IFF** returns a 1 and the subroutine is called, otherwise **IFF** returns a 0 and execution -continues to the next statement without making the call: +A **THEN** or **ELSE** can be supplied multiple statements if they are seperated by tha colon "**:**". ``` -> 10 LET I = 10 -> 20 LET J = 5 -> 30 ON IFF (I > J, 1, 0) GOSUB 100 -> 40 STOP -> 100 REM THE SUBROUTINE -> 110 PRINT "I is greater than J" -> 120 RETURN +> 10 REM PRINT THE GREATEST NUMBER +> 20 LET I = 10 +> 30 LET J = 20 +> 40 IF I > J THEN LET L = I:PRINT I ELSE LET L = J:PRINT J +> 50 PRINT L +> 80 REM FINISHED > RUN -I is greater than J +20 +20 > ``` +Note that should an **IF-THEN-ELSE** stmt be used in a **THEN** code block or multiple **IF-THEN-ELSE** statements +are used in either a single **THEN** or **ELSE** code block, the block grouping is ambiguious and logical processing +may not function as expected. There is no ambiguity when single **IF-THEN-ELSE** statements are placed within **ELSE** +blocks. + +Ambiguous: +``` +> 100 IF I > J THEN IF J >= 100 THEN PRINT "I > 100" else PRINT "Not clear which **IF** this belongs to" +``` + +Not Ambiguous: +``` +> 100 IF I < J THEN PRINT "I is less than J" ELSE IF J > 100 THEN PRINT "I > 100" ELSE PRINT "J <= 100" +``` + Allowable relational operators are: * '=' (equal, note that in BASIC the same operator is used for assignment) @@ -600,6 +604,48 @@ Expressions can be inside brackets to change the order of evaluation. Compare th Test failed! ``` +### ON GOTO, ON GOSUB + +The **ON GOTO|GOSUB** *expr* *line1,line2,...* statement will call a subroutine or branch to a line number in the list of line numbers corresponding to the ordinal +value of the evaluated *expr*. The first line number corresponds with an *expr* value of 1. *expr* must evaluate to an integer value. + If *expr* evaluates to less than 1 or greater than the number of provided line numbers execution continues on the next +statement without making a subroutine call or branch: + +``` +> 20 LET J = 2 +> 30 ON J GOSUB 100,200,300 +> 40 STOP +> 100 REM THE 1ST SUBROUTINE +> 110 PRINT "J is ONE" +> 120 RETURN +> 200 REM THE 2ND SUBROUTINE +> 210 PRINT "J is TWO" +> 220 RETURN +> 300 REM THE 3RD SUBROUTINE +> 310 PRINT "J is THREE" +> 320 RETURN +> RUN +J is TWO +> +``` + +It is also possible to call a subroutine depending upon the result of a conditional expression using the **IFF** function (see Ternary Functions below). In +the example below, if the expression evaluates to true, **IFF** returns a 1 and the subroutine is called, otherwise **IFF** returns a 0 and execution +continues to the next statement without making the call: + +``` +> 10 LET I = 10 +> 20 LET J = 5 +> 30 ON IFF (I > J, 1, 0) GOSUB 100 +> 40 STOP +> 100 REM THE SUBROUTINE +> 110 PRINT "I is greater than J" +> 120 RETURN +> RUN +I is greater than J +> +``` + ### Ternary Functions As an alternative to branching, Ternary functions are provided. @@ -894,7 +940,7 @@ will read starting at file position *filepos* **GOTO** *line-number* - Unconditional branch -**IF** *expression* **THEN** *line-number* [**ELSE** *line-number*] - Conditional branch +**IF** *expression* **THEN** *line-number*|*basic-statement(s)* [**ELSE** *line-number*|*basic-statement(s)*] - Conditional **IFF**(*expression*, *numeric-expression*, *numeric-expression*) - Evaluates *expression* and returns the value of the result of the first *numeric-expression* if true, or the second if false. From 6730e4bdfa178f6920adc2810945d2366da0a2ce Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 15 Oct 2021 00:31:51 -0400 Subject: [PATCH 124/183] IF generalization --- basicparser.py | 1 - program.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/basicparser.py b/basicparser.py index dd23eed..7162fa0 100644 --- a/basicparser.py +++ b/basicparser.py @@ -1103,7 +1103,6 @@ def __ifstmt(self): else: # No ELSE action - #return FlowSignal(ftype=FlowSignal.SKIP) return None def __forstmt(self): diff --git a/program.py b/program.py index d847cd7..ed7f7a7 100644 --- a/program.py +++ b/program.py @@ -371,8 +371,6 @@ def execute(self): # Put loop line number on the stack so # that it can be returned to when the loop # repeats - #self.__return_stack.append(self.get_next_line_number()) - #self.__return_stack.append(line_numbers[index]) self.__return_loop[flowsignal.floop_var] = line_numbers[index] # Continue to the next statement in the loop From cf1e661eced45ca3293ed2027351c7e45a39fa0b Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 24 Oct 2021 19:48:29 +0200 Subject: [PATCH 125/183] Fix typos discovered by codespell --- README.md | 22 +++++++++++----------- basicparser.py | 10 +++++----- examples/AMESSAGE | 2 +- examples/adventure-fast.bas | 2 +- examples/oregon.bas | 2 +- examples/regression.bas | 2 +- lexer.py | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 6dd8415..f6f7ef7 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,7 @@ many dimensions the array has, and the sizes of those dimensions: Note that the index of each dimension always starts at *zero*, but for compatibility with some basic dialects the bounds of each dimension will be -expanded by one to enable element access inlcuding the len. So in the above example, +expanded by one to enable element access including the len. So in the above example, valid index values for array *A* will be *0, 1*, *2* or *3* for each dimension. Arrays may have a maximum of three dimensions. @@ -282,7 +282,7 @@ statements before the end of the program an error will be displayed. This is to in a state where a variable has not been assigned a value, but nevertheless an attempt to use that variable is made later on in the program. -Normally each **DATA** statement is consumed sequently by **READ** statements however, the **RESTORE** statment can +Normally each **DATA** statement is consumed sequently by **READ** statements however, the **RESTORE** statement can be used to override this order and set the line number of the **DATA** statement that will be used by the next **READ** statement. If the *line-number* used in a **RESTORE** statement does not refer to a **DATA** statement an error will be displayed. @@ -516,7 +516,7 @@ expression evaluates to true, otherwise the next statement is executed. > ``` -The following code segement is equivelant to the segment above: +The following code segment is equivalent to the segment above: ``` > 10 REM PRINT THE GREATEST NUMBER @@ -529,7 +529,7 @@ The following code segement is equivelant to the segment above: > ``` -A **THEN** or **ELSE** can be supplied multiple statements if they are seperated by tha colon "**:**". +A **THEN** or **ELSE** can be supplied multiple statements if they are separated by a colon "**:**". ``` > 10 REM PRINT THE GREATEST NUMBER @@ -545,7 +545,7 @@ A **THEN** or **ELSE** can be supplied multiple statements if they are seperated ``` Note that should an **IF-THEN-ELSE** stmt be used in a **THEN** code block or multiple **IF-THEN-ELSE** statements -are used in either a single **THEN** or **ELSE** code block, the block grouping is ambiguious and logical processing +are used in either a single **THEN** or **ELSE** code block, the block grouping is ambiguous and logical processing may not function as expected. There is no ambiguity when single **IF-THEN-ELSE** statements are placed within **ELSE** blocks. @@ -713,26 +713,26 @@ within an **INPUT** statement, only simple variables. ### File Input/Output -Data can be read from or written to files using the **OPEN**, **FSEEK**, **INPUT**, **PRINT** and **CLOSE** statments. +Data can be read from or written to files using the **OPEN**, **FSEEK**, **INPUT**, **PRINT** and **CLOSE** statements. When a file is opened using the syntax **OPEN** "*filename*" **FOR INPUT|OUTPUT|APPEND AS** *#filenum* [**ELSE** *linenum*] a -file number (*#filenum*) is assigned to the file, which if specfied as the first argument of an **INPUT** or **PRINT** -statment, will direct the input or output to the file. +file number (*#filenum*) is assigned to the file, which if specified as the first argument of an **INPUT** or **PRINT** +statement, will direct the input or output to the file. If there is an error opening a file and the optional **ELSE** option has been specified, program control will branch to the specified line number, if the **ELSE** has not been provided an error message will be displayed. If a file is opened for **OUTPUT** which does not exist, the file will be created, if the file does exist, its contents will be erased and any new **PRINT** output will replace it. If a file is opened for **APPEND** an error will occur if the file -doesn't exist (or the **ELSE** branch will occur if specified). If the file does exist, any **PRINT** statments will add to the end +doesn't exist (or the **ELSE** branch will occur if specified). If the file does exist, any **PRINT** statements will add to the end of the file. -If an input prompt is specfied on an **INPUT** statement being used for file I/O (i.e. *#filenum* is specified) an error +If an input prompt is specified on an **INPUT** statement being used for file I/O (i.e. *#filenum* is specified) an error will be displayed. The **FSEEK** *#filenum*,*filepos* statement will position the file pointer for the next **INPUT** statement. -The **CLOSE** *#filenum* statment will close the file. +The **CLOSE** *#filenum* statement will close the file. ``` > 10 OPEN "FILE.TXT" FOR OUTPUT AS #1 diff --git a/basicparser.py b/basicparser.py index 7162fa0..96c0867 100644 --- a/basicparser.py +++ b/basicparser.py @@ -127,16 +127,16 @@ def parse(self, tokenlist, line_number): self.__tokenindex = 0 linetokenindex = 0 for token in tokenlist: - # If statements will always be the last statment processed on a line so + # If statements will always be the last statement processed on a line so # any colons found after an IF are part of the condition execution statements # and will be processed in the recursive call to parse if token.category == token.IF: # process IF statement to move __tokenidex to the code block - # of the THEN or ELSE and then call PARSE recursivly to process that code block + # of the THEN or ELSE and then call PARSE recursively to process that code block # this will terminate the token loop by RETURNing to the calling module # # **Warning** if an IF stmt is used in the THEN code block or multiple IF statement are used - # in a THEN or ELSE block the block grouping is ambiguious and logical processing may not + # in a THEN or ELSE block the block grouping is ambiguous and logical processing may not # function as expected. There is no ambiguity when single IF statements are placed within ELSE blocks linetokenindex += self.__tokenindex self.__tokenindex = 0 @@ -451,7 +451,7 @@ def __assignmentstmt(self): self.__advance() if self.__token.category == Token.LEFTPAREN: - # We are assiging to an array + # We are assigning to an array self.__arrayassignmentstmt(left) else: @@ -604,7 +604,7 @@ def __openstmt(self): else: raise SyntaxError('Invalid Open access mode in line ' + str(self.__line_number)) - self.__advance() # Advance past acess type + self.__advance() # Advance past access type if self.__token.lexeme != "AS": raise SyntaxError('Expecting AS in line ' + str(self.__line_number)) diff --git a/examples/AMESSAGE b/examples/AMESSAGE index 9c08ebf..e8522f5 100644 --- a/examples/AMESSAGE +++ b/examples/AMESSAGE @@ -561,7 +561,7 @@ killed by some rather unfriendly inhabitants of the cave. There are some useful commands that you should know about: --- Brief, Long, and Short - these commands control the amount of --- detail you get in descriptions. ---- Look (or L) - gives a detailled descriptio nof your surroundings. +--- Look (or L) - gives a detailed description of your surroundings. --- Inventory (or I) tells you what you are carrying. --- Quit, Stop or End - these are self-explanatory. --- Save - by typing this, you may save your game and continue it at diff --git a/examples/adventure-fast.bas b/examples/adventure-fast.bas index 6bd92b2..1fe3602 100644 --- a/examples/adventure-fast.bas +++ b/examples/adventure-fast.bas @@ -850,7 +850,7 @@ 8110 rem normal description 8120 z59 = 200+l1:gosub 7620 8130 return -8180 rem always give long descriptio nfor forest and maze +8180 rem always give long description for forest and maze 8190 if l1 < 5 or (l1 > 88 and l1 < 98) or l1 = 99 then 8220 8200 if v(l1)=1 then 8201 else 8220 8201 gosub 6780 diff --git a/examples/oregon.bas b/examples/oregon.bas index c30c6bb..844d3f3 100644 --- a/examples/oregon.bas +++ b/examples/oregon.bas @@ -460,7 +460,7 @@ 4480 GOTO 4710 4490 PRINT "COLD WEATHER---BRRRRRRR!---YOU " ; 4500 IF C > 22 + 4 * RND ( 1 ) THEN 4530 -4510 PRINT "DONT'T " ; +4510 PRINT "DON'T " ; 4520 C1 = 1 4530 PRINT "HAVE ENOUGH CLOTHING TO KEEP YOU WARM" 4540 IF C1 = 0 THEN 4710 diff --git a/examples/regression.bas b/examples/regression.bas index 1960ef9..c60a30a 100644 --- a/examples/regression.bas +++ b/examples/regression.bas @@ -128,7 +128,7 @@ 1390 IF ANS$ = "T" THEN PRINT "1390: Entered " ; : PRINT "T" ELSE PRINT "1390: Did " ; : PRINT "not enter T" 1400 IF ANS$ = "T" THEN PRINT "1400: Entered T" ELSE PRINT "1400: Did " ; : PRINT "not enter T" 1410 IF ANS$ = "T" THEN PRINT "1410: Entered " ; : PRINT "T" ELSE PRINT "1410: Did not enter T" -1420 PRINT "Compount Stmt w/conditionals ";:IF ANS$ = "T" THEN PRINT "1420: Entered " ; : PRINT "T" ELSE PRINT "1420: Did " ; : PRINT "not enter T" +1420 PRINT "Compound Stmt w/conditionals ";:IF ANS$ = "T" THEN PRINT "1420: Entered " ; : PRINT "T" ELSE PRINT "1420: Did " ; : PRINT "not enter T" 1610 PRINT "*** Finished ***" 1620 STOP 1630 REM A SUBROUTINE TEST diff --git a/lexer.py b/lexer.py index 61d36e0..bde1234 100644 --- a/lexer.py +++ b/lexer.py @@ -146,7 +146,7 @@ def tokenize(self, stmt): else: token.category = Token.NAME - # Remark Statments - process rest of statement without checks + # Remark Statements - process rest of statement without checks if token.lexeme == "REM": while c!= '': token.lexeme += c # Append the current char to the lexeme From 56f32eb2d80f13b5937965e219d57c7128382b0f Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 15 Nov 2021 03:41:35 -0500 Subject: [PATCH 126/183] Fix for OPEN ELSE bug --- basicparser.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/basicparser.py b/basicparser.py index 96c0867..1bd6976 100644 --- a/basicparser.py +++ b/basicparser.py @@ -121,6 +121,7 @@ def parse(self, tokenlist, line_number): how to branch if necessary, None otherwise """ + # Remember the line number to aid error reporting self.__line_number = line_number self.__tokenlist = [] @@ -170,8 +171,9 @@ def parse(self, tokenlist, line_number): linetokenindex += 1 self.__tokenlist = [] - elif token.category == token.ELSE: - # if we find an ELSE we must be in a recursive call and be processing a THEN block + elif token.category == token.ELSE and self.__tokenlist[0].category != token.OPEN: + # if we find an ELSE and we are not processing an OPEN statement, we must + # be in a recursive call and be processing a THEN block # since we're processing the THEN block we are done if we hit an ELSE break else: From afc2bc5d785adaa3378fc5a1ab2133bd6155d906 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 15 Nov 2021 03:42:24 -0500 Subject: [PATCH 127/183] Add test for OPEN ELSE statment --- examples/regression.bas | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/regression.bas b/examples/regression.bas index c60a30a..554d08d 100644 --- a/examples/regression.bas +++ b/examples/regression.bas @@ -58,6 +58,8 @@ 580 FSEEK #2,10 590 INPUT #2,A$ 600 PRINT A$ +610 OPEN "NOFILE.X7Z" FOR INPUT AS #2 ELSE 620 +615 PRINT "***This Message should NOT be Displayed***" 620 N = 0 630 I = 7 640 PRINT "This loop should count to 5 in increments of 1 twice:" From 823b2ccb83d0daa12cc4fd2d72546ff4448824e3 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 30 Jan 2022 18:23:31 +0000 Subject: [PATCH 128/183] Fixed Issue #61, array initialisation --- basicparser.py | 56 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/basicparser.py b/basicparser.py index 1bd6976..3d48915 100644 --- a/basicparser.py +++ b/basicparser.py @@ -28,16 +28,18 @@ """ class BASICArray: - def __init__(self, dimensions): + def __init__(self, dimensions, elem_type): """Initialises the object with the specified number of dimensions. Maximum number of dimensions is three :param dimensions: List of array dimensions and their corresponding sizes + :param elem_type: Indicates whether the elements are strings ('str') + or numbers ('num') """ - self.dims = min(3,len(dimensions)) + self.dims = min(3, len(dimensions)) if self.dims == 0: raise SyntaxError("Zero dimensional array specified") @@ -55,19 +57,36 @@ def __init__(self, dimensions): # MSBASIC: Overdim by one, as some dialects are 1 based and expect # to use the last item at index = size if self.dims == 1: - self.data = [0 for x in range(dimensions[0] + 1)] + if elem_type == 'num': + self.data = [0 for x in range(dimensions[0] + 1)] + else: + self.data = ['' for x in range(dimensions[0] + 1)] elif self.dims == 2: - self.data = [ - [0 for x in range(dimensions[1] + 1)] for x in range(dimensions[0] + 1) - ] + if elem_type == 'num': + self.data = [ + [0 for x in range(dimensions[1] + 1)] for x in range(dimensions[0] + 1) + ] + else: + self.data = [ + ['' for x in range(dimensions[1] + 1)] for x in range(dimensions[0] + 1) + ] else: - self.data = [ - [ - [0 for x in range(dimensions[2] + 1)] - for x in range(dimensions[1] + 1) + if elem_type == 'num': + self.data = [ + [ + [0 for x in range(dimensions[2] + 1)] + for x in range(dimensions[1] + 1) + ] + for x in range(dimensions[0] + 1) + ] + else: + self.data = [ + [ + ['' for x in range(dimensions[2] + 1)] + for x in range(dimensions[1] + 1) + ] + for x in range(dimensions[0] + 1) ] - for x in range(dimensions[0] + 1) - ] def pretty_print(self): print(str(self.data)) @@ -487,7 +506,7 @@ def __dimstmt(self): # Extract the array name, append a suffix so # that we can distinguish from simple variables # in the symbol table - name = self.__token.lexeme + "_array" + name = self.__token.lexeme + '_array' self.__advance() # Advance past array name self.__consume(Token.LEFTPAREN) @@ -507,12 +526,17 @@ def __dimstmt(self): if len(dimensions) > 3: raise SyntaxError( - "Maximum number of array dimensions is three " - + "in line " + 'Maximum number of array dimensions is three ' + + 'in line ' + str(self.__line_number) ) - self.__symbol_table[name] = BASICArray(dimensions) + # Ensure array is initialised with correct values + # depending upon type + if name.endswith('$_array'): + self.__symbol_table[name] = BASICArray(dimensions, 'str') + else: + self.__symbol_table[name] = BASICArray(dimensions, 'num') if self.__tokenindex == len(self.__tokenlist): # We have parsed the last token here... From 123edae51fa33ad87c4ac64f298691a4da5f4fa4 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 30 Jan 2022 18:24:12 +0000 Subject: [PATCH 129/183] Explain array initialisation --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f6f7ef7..06c415d 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,9 @@ Note that the index of each dimension always starts at *zero*, but for compatibility with some basic dialects the bounds of each dimension will be expanded by one to enable element access including the len. So in the above example, valid index values for array *A* will be *0, 1*, *2* or *3* -for each dimension. Arrays may have a maximum of three dimensions. +for each dimension. Arrays may have a maximum of three dimensions. Numeric arrays will +be initialised with each element set to zero, while string arrays will be initialised +with each element set to the empty string "". As for simple variables, a string array has its name suffixed by a '$' character, while a numeric array does not carry a suffix. An attempt to assign a string value to a numeric array or vice versa will generate an error. From 3172cd610a55bed63f759d867dad61fe82ce73e3 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 30 Jan 2022 15:19:09 -0500 Subject: [PATCH 130/183] Changes for micropython compatibility --- basicparser.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/basicparser.py b/basicparser.py index 3d48915..9c026ec 100644 --- a/basicparser.py +++ b/basicparser.py @@ -19,7 +19,10 @@ from flowsignal import FlowSignal import math import random -from time import monotonic +try: + from time import ticks_ms as monotonic +except: + from time import monotonic """Implements a BASIC array, which may have up @@ -865,7 +868,7 @@ def __readstmt(self): elif not left.endswith('$'): try: numeric = float(right) - if numeric.is_integer(): + if int(numeric) == numeric: numeric = int(numeric) self.__symbol_table[left] = numeric @@ -1711,7 +1714,7 @@ def __evaluate_function(self, category): elif category == Token.VAL: try: numeric = float(value) - if numeric.is_integer(): + if int(numeric) == numeric: return int(numeric) return numeric From 98a44c5b2ae2b2d8ca1cf589e6e8423f962b3320 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 30 Jan 2022 15:24:31 -0500 Subject: [PATCH 131/183] Ignore blank unnumbered lines --- program.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/program.py b/program.py index ed7f7a7..13d1939 100644 --- a/program.py +++ b/program.py @@ -244,17 +244,18 @@ def add_stmt(self, tokenlist): numbered program statement """ - try: - line_number = int(tokenlist[0].lexeme) - if tokenlist[1].lexeme == "DATA": - self.__data.addData(line_number,tokenlist[1:]) - self.__program[line_number] = [tokenlist[1],] - else: - self.__program[line_number] = tokenlist[1:] + if len(tokenlist) > 0: + try: + line_number = int(tokenlist[0].lexeme) + if tokenlist[1].lexeme == "DATA": + self.__data.addData(line_number,tokenlist[1:]) + self.__program[line_number] = [tokenlist[1],] + else: + self.__program[line_number] = tokenlist[1:] - except TypeError as err: - raise TypeError("Invalid line number: " + - str(err)) + except TypeError as err: + raise TypeError("Invalid line number: " + + str(err)) def line_numbers(self): """Returns a list of all the From 866a1381167f2346b516d8a60fcf615749ae3394 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 30 Jan 2022 15:27:22 -0500 Subject: [PATCH 132/183] Full parser version, not speed optimized --- examples/{adventure-fast.bas => adventure.bas} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{adventure-fast.bas => adventure.bas} (100%) diff --git a/examples/adventure-fast.bas b/examples/adventure.bas similarity index 100% rename from examples/adventure-fast.bas rename to examples/adventure.bas From eb8cfc48dbeea79110de084fe09abc18a9ef31f0 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 30 Jan 2022 16:03:14 -0500 Subject: [PATCH 133/183] Added missing lines found during testing --- examples/oregon.bas | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/examples/oregon.bas b/examples/oregon.bas index 844d3f3..7a97c7c 100644 --- a/examples/oregon.bas +++ b/examples/oregon.bas @@ -471,30 +471,39 @@ 4590 M1 = M1 - 4 - RND ( 1 ) * 3 4600 GOTO 4710 4610 IF E = 1 THEN 6300 -4620 IF E = 3 THEN 4650 +4620 IF E = 3 THEN 4650 +4630 IF RND(1) > 0.25 THEN 6300 4640 GOTO 4710 -4650 PRINT "HELPFUL INDIANS SHOW YOU WHERE TO FIND MORE FOOD" +4650 IF RND(1) < 0.5 THEN 6300 +4660 GOTO 4720 +4670 PRINT "HELPFUL INDIANS SHOW YOU WHERE TO FIND MORE FOOD" 4680 F = F + 14 4690 GOTO 4710 4700 REM ***MOUNTAINS*** 4710 IF M <= 950 THEN 1230 -4719 REM LINE 4720 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH CHIPMUNK BASIC -4730 PRINT "RUGGED MOUNTAINS" +4719 REM LINE 4720 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH CHIPMUNK BASIC +4720 IF RND(1)*10 > 9-(POW((M/100-15),2)+72)/(POW((M/100-15),2)+12) THEN 4860 +4730 PRINT "RUGGED MOUNTAINS" +4740 IF RND(1) > 0.1 THEN 4780 4750 PRINT "YOU GOT LOST---LOSE VALUABLE TIME TRYING TO FIND TRAIL!" 4760 M = M - 60 -4770 GOTO 4860 +4770 GOTO 4860 +4780 IF RND(1) > 0.11 THEN 4840 4790 PRINT "WAGON DAMAGED!---LOSE TIME AND SUPPLIES" 4800 M1 = M1 - 5 4810 B = B - 200 4820 M = M - 20 - 30 * RND ( 1 ) 4830 GOTO 4860 4840 PRINT "THE GOING GETS SLOW" +4850 M = M - 45 - RND(1) / 0.02 4860 IF F1 = 1 THEN 4900 4870 F1 = 1 +4880 IF RND(1) < 0.8 THEN 4970 4890 PRINT "YOU MADE IT SAFELY THROUGH SOUTH PASS--NO SNOW" 4900 IF M < 1700 THEN 4940 4910 IF F2 = 1 THEN 4940 4920 F2 = 1 +4930 IF RND(1) < 0.7 THEN 4970 4940 IF M > 950 THEN 1230 4950 M9 = 1 4960 GOTO 1230 From 7b1227e2d6876543f64a8d414e9cba20a56009da Mon Sep 17 00:00:00 2001 From: richpl Date: Mon, 1 Jul 2024 19:39:28 +0100 Subject: [PATCH 134/183] Clarified floating point format --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06c415d..646c8b6 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ are all invalid. Numeric variables have no suffix, whereas string variables are always suffixed by '$'. Note that 'I' and 'I$' are considered to be separate variables. Note that string literals must always be enclosed within double quotes (not single quotes). -Using no quotes will result in a syntax error. +Using no quotes will result in a syntax error. In addition, for floating point numbers less than one (e.g. 0.67), the decimal point must always be prefixed by a zero (e.g. .67 will be flagged as a syntax error). Array variables are defined using the **DIM** statement, which explicitly lists how many dimensions the array has, and the sizes of those dimensions: From a0f14cc771e895ab63a88d6088be502a2dcb761f Mon Sep 17 00:00:00 2001 From: Greg Whitehead Date: Sun, 13 Oct 2024 20:46:05 -0700 Subject: [PATCH 135/183] Fix for bug where "IF THEN " statements were leaving the line number on the operand stack, causing a memory leak --- basicparser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/basicparser.py b/basicparser.py index 9c026ec..d96121f 100644 --- a/basicparser.py +++ b/basicparser.py @@ -1107,11 +1107,12 @@ def __ifstmt(self): return FlowSignal(ftype=FlowSignal.EXECUTE) else: self.__expr() + target = self.__operand_stack.pop() # Jump if the expression evaluated to True if saveval: # Set up and return the flow signal - return FlowSignal(ftarget=self.__operand_stack.pop()) + return FlowSignal(ftarget=target) # advance to ELSE while self.__tokenindex < len(self.__tokenlist) and self.__token.category != Token.ELSE: From 5c8192df5e5756fa5fdc71290791dbe81be2df64 Mon Sep 17 00:00:00 2001 From: richpl Date: Mon, 9 Dec 2024 22:41:05 +0000 Subject: [PATCH 136/183] Initial commit, work in progress --- examples/Pneuma.bas | 221 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 examples/Pneuma.bas diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas new file mode 100644 index 0000000..5c9fb5e --- /dev/null +++ b/examples/Pneuma.bas @@ -0,0 +1,221 @@ +10 REM Pneuma - A space adventure +20 REM ========== backstory and instructions ========== +30 PRINT "***** Pneuma - A space adventure *****" : PRINT +40 PRINT "Movement: [go] a[ft], f[orward], p[ort], s[tarboard], u[p] or d[own]" +45 PRINT "Actions: get, take, drop, examine, look, i[nventory], q[uit]" +50 PRINT +60 PRINT "You wake up in your bunk, in the sleeping quarters of the starship Pneuma. You can't" +65 PRINT "remember much. You went to bed feeling sick and after a feverish few hours tossing and" +70 PRINT "turning, feeling like you were burning up, you eventually fell asleep." : PRINT +75 PRINT "Now, your sheets and night clothes are damp with sweat, and you have a raging thirst." +80 PRINT "You have a sore throat and the mother of all headaches, like your brain has been boiling" +85 PRINT "in your skull." : PRINT +95 REM ========== set up environment =========== +100 RC = 18 : REM room count +110 DIM LO$ ( RC ) +120 INV = 0 : LO$ ( INV ) = "Inventory" +130 GAL = 1 : LO$ ( GAL ) = "Galley" +140 REC = 2 : LO$ ( REC ) = "Recreation/Dining Room" +150 ARM = 3 : LO$ ( ARM ) = "Armoury" +160 BDG = 4 : LO$ ( BDG ) = "Bridge" +170 SLP = 5 : LO$ ( SLP ) = "Sleeping Quarters" +180 MED = 6 : LO$ ( MED ) = "Medical Centre" +190 GYM = 7 : LO$ ( GYM ) = "Gymnasium" +200 LAC = 8 : LO$ ( LAC ) = "Lower Aft Corridor" +210 ENG = 9 : LO$ ( ENG ) = "Engine Room" +220 STO = 10 : LO$ ( STO ) = "Storeroom" +230 MEN = 11 : LO$ ( MEN ) = "Menagerie" +240 LAB = 12 : LO$ ( LAB ) = "Laboratory" +250 LFC = 13 : LO$ ( LFC ) = "Lower Forward Corridor" +260 POD = 14 : LO$ ( POD ) = "Pod Bay" +270 AMC = 15 : LO$ ( AMC ) = "Aft Main Corridor" +271 MMC = 16 : LO$ ( MMC ) = "Mid Main Corridor" +272 FMC = 17 : LO$ ( FMC ) = "Forward Main Corridor" +275 REM encoded room exits, two digits per direction f, a, p, s, u, d +280 DIM EX$ ( RC ) +281 EX$ ( GAL ) = "020015000008" +282 EX$ ( REC ) = "000116000000" +283 EX$ ( ARM ) = "000017000000" +284 EX$ ( BDG ) = "001700000000" +285 EX$ ( SLP ) = "000015000000" +286 EX$ ( MED ) = "070016000000" +287 EX$ ( GYM ) = "000617000013" +288 EX$ ( LAC ) = "100000090100" +289 EX$ ( ENG ) = "000008000000" +290 EX$ ( STO ) = "120800000000" +291 EX$ ( MEN ) = "000010000000" +292 EX$ ( LAB ) = "001000130000" +293 EX$ ( LFC ) = "140012000700" +294 EX$ ( POD ) = "001300000000" +295 EX$ ( AMC ) = "160001050000" +296 EX$ ( MMC ) = "171502060000" +297 EX$ ( FMC ) = "041603070000" +300 OC = 4 : REM object count +310 DIM OB$ ( OC ) +320 PULSE = 0 : OB$ ( PULSE ) = "Pulse rifle" +330 SUIT = 1 : OB$ ( SUIT ) = "Space suit" +340 FOOD = 2 : OB$ ( FOOD ) = "Rotting food" +400 REM object locations +410 REM location 0 = player's inventory +420 DIM OL ( OC ) +430 OL ( PULSE ) = ARM +440 OL ( SUIT ) = POD +450 OL ( FOOD ) = GAL +500 REM setup room descriptions +510 GOSUB 3000 +650 PL = 5 : REM player location +700 REM ========== main loop ========== +701 REM show room details +703 PRINT "You are in the " ; LO$ ( PL ) : PRINT +705 GOSUB 4010 +706 PRINT "Objects visible:" +707 FOR I = 0 TO OC - 1 : IF OL ( I ) = PL THEN PRINT OB$ ( I ) : NEXT I +708 PRINT +710 INPUT "What now? " ; I$ +715 PRINT +716 MOVE = 1 +720 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "get " THEN MOVE = 0 : GOSUB 1400 +730 IF LEFT$ ( LOWER$ ( I$ ) , 5 ) = "take " THEN MOVE = 0 : GOSUB 1700 +740 IF LEFT$ ( LOWER$ ( I$ ) , 5 ) = "drop " THEN MOVE = 0 : GOSUB 2000 +750 IF LEFT$ ( LOWER$ ( I$ ) , 8 ) = "examine " THEN MOVE = 0 : GOSUB 2300 +760 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "look" THEN MOVE = 0 : GOSUB 4010 +770 IF LOWER$ ( I$ ) = "i" OR LOWER$ ( I$ ) = "inventory" THEN MOVE = 0 : GOSUB 1000 +780 IF LEFT$ ( LOWER$ ( I$ ) , 1 ) = "q" THEN GOSUB 2600 +785 IF LEFT$ ( LOWER$ ( I$ ) , 3 ) = "go " THEN GOSUB 1100 +790 IF LOWER$ ( I$ ) = "f" OR LOWER$ ( I$ ) = "forward" THEN GOSUB 1200 +800 IF LOWER$ ( I$ ) = "a" OR LOWER$ ( I$ ) = "aft" THEN GOSUB 1200 +810 IF LOWER$ ( I$ ) = "p" OR LOWER$ ( I$ ) = "port" THEN GOSUB 1200 +820 IF LOWER$ ( I$ ) = "s" OR LOWER$ ( I$ ) = "starboard" THEN GOSUB 1200 +830 IF LOWER$ ( I$ ) = "u" OR LOWER$ ( I$ ) = "up" THEN GOSUB 1200 +840 IF LOWER$ ( I$ ) = "d" OR LOWER$ ( I$ ) = "down" THEN GOSUB 1200 +895 IF MOVE = 1 THEN GOTO 700 ELSE GOTO 708 +900 STOP +995 REM ========== actions ========== +1000 REM list the player's inventory +1005 PRINT "You have the following items:" : PRINT +1010 FOR I = 0 TO OC - 1 +1020 IF OL ( I ) = 0 THEN PRINT OB$ ( I ) +1030 NEXT I +1040 RETURN +1100 REM fully written out move (e.g. 'go aft') +1110 D$ = MID$ ( LOWER$(I$) , 4 , 1 ) +1120 GOSUB 1300 +1130 RETURN +1200 REM abbreviated move (e.g. 'a' or 'aft') +1210 D$ = LOWER$( I$ ) +1220 GOSUB 1300 +1230 RETURN +1300 REM go to the player's new location (PL) +1310 IF D$ = "f" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 1 , 2 ) ) +1320 IF D$ = "a" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 3 , 2 ) ) +1330 IF D$ = "p" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 5 , 2 ) ) +1340 IF D$ = "s" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 7 , 2 ) ) +1350 IF D$ = "d" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 11 , 2 ) ) +1355 IF NPL = 0 THEN PRINT "You can't go that way." : PRINT ELSE PL = NPL +1360 RETURN +1400 REM get command +1700 REM take command +2000 REM drop command +2300 REM examine command +2600 REM quit command +2610 PRINT "Farewell spacefarer ..." +2620 STOP +3000 REM room descriptions +3010 DIM RD$ ( RC, 5 ) +3020 RD$ ( INV, 1 ) = "" +3021 RD$ ( INV, 2 ) = "" +3022 RD$ ( INV, 3 ) = "" +3023 RD$ ( INV, 4 ) = "" +3024 RD$ ( INV, 5 ) = "" +3030 RD$ ( GAL, 1 ) = "The galley contains gleaming, stainless steel cupboards along the aft wall. A food" +3040 RD$ ( GAL, 2 ) = "preparation surface is on the port wall, currently covered in rotting food. A chef" +3050 RD$ ( GAL, 3 ) = "stands at the work surface, methodically chopping food even though everything has" +3060 RD$ ( GAL, 4 ) = "already been thoroughly diced. There are doors in the starboard and forward walls and" +3070 RD$ ( GAL, 5 ) = "a stairway leads downwards in the far corner." +3080 RD$ ( REC, 1 ) = "Space is clearly at a premium in this ship. The room doubles as both a dining and" +3090 RD$ ( REC, 2 ) = "recreation area. Long tables for dining are located on the port side, while couches" +3100 RD$ ( REC, 3 ) = "and low tables are scattered around the remaining space. There are doors in the aft" +3110 RD$ ( REC, 4 ) = "and starboard walls." +3120 RD$ ( REC, 5 ) = "" +3130 RD$ ( ARM, 1 ) = "Locked cabinets line the starboard wall. Each cabinet has a prominently displayed" +3140 RD$ ( ARM, 2 ) = "notice on its door reading 'Weapons to be removed only when authorised by the Chief" +3150 RD$ ( ARM, 3 ) = "Security Officer'. However, the door to one cupboard has been prized open, it is" +3160 RD$ ( ARM, 4 ) = "warped and bent. This cupboard appears to be empty. The only exit is a starboard door." +3170 RD$ ( ARM, 5 ) = "" +3180 RD$ ( BDG, 1 ) = "The bridge is the heart of the ship. A vast array of glowing screens and switches fill" +3190 RD$ ( BDG, 2 ) = "every surface. The screens are complex graphics providing detailed information about" +3200 RD$ ( BDG, 3 ) = "the status of every system on the ship. Many of them are showing red warning symbols." +3210 RD$ ( BDG, 4 ) = "An aft exit leads back into the main corridor." +3220 RD$ ( BDG, 5 ) = "" +3230 RD$ ( SLP, 1 ) = "The sleeping quarters is filled with bunks, one up, one down. Several of the bunks" +3240 RD$ ( SLP, 2 ) = "contain sleeping forms, some gently shoring. The room has a partition to separate" +3250 RD$ ( SLP, 3 ) = "male and female bunks. Against the forward wall are two corresponding sets of heads." +3260 RD$ ( SLP, 4 ) = "The room is messy, with discarded personal items everywhere ... on bunks, on the floor." +3270 RD$ ( SLP, 5 ) = "There is a door in the port wall." +3280 RD$ ( MED, 1 ) = "The medical centre looks relatively normal, but there is evidence of discarded items" +3290 RD$ ( MED, 2 ) = "lying around the room. Blood filled syringes are scattered on a workbench, as well as" +3300 RD$ ( MED, 3 ) = "some bloodied bandanges. The words 'I'm losing myself' are scrawled messily in blood on" +3310 RD$ ( MED, 4 ) = "one wall. In the corner you can see a medical scanner and next to it, a terminal. On the" +3320 RD$ ( MED, 5 ) = "terminal screen are the words 'Medical Log'. Exits lead port and forward." +3330 RD$ ( GYM, 1 ) = "The gymnasium is full of exercise equipment. A woman is running furiously on a treadmill." +3340 RD$ ( GYM, 2 ) = "She looks exhausted and emaciated, but she keeps running at top speed, almost at a sprint." +3350 RD$ ( GYM, 3 ) = "Her eyes remain fixed on the treadmill console. There are aft and port exists, as well as" +3360 RD$ ( GYM, 4 ) = "a stairwell leading to the lower deck in the far corner." +3370 RD$ ( GYM, 5 ) = "" +3380 RD$ ( LAC, 1 ) = "This is a featureless, utilitarian corridor. A stairwell leads upwards. There are also" +3390 RD$ ( LAC, 2 ) = "exits leading starboard and forward." +3400 RD$ ( LAC, 3 ) = "" +3410 RD$ ( LAC, 4 ) = "" +3420 RD$ ( LAC, 5 ) = "" +3430 RD$ ( ENG, 1 ) = "The engine room is characterised by a continual rumble, as though incredible energies are" +3440 RD$ ( ENG, 2 ) = "barely being contained. There is a console in the far corner, festooned with controls and" +3450 RD$ ( ENG, 3 ) = "engine readouts. A single exit leads out into the corridor." +3460 RD$ ( ENG, 4 ) = "" +3470 RD$ ( ENG, 5 ) = "" +3480 RD$ ( STO, 1 ) = "The storeroom is full of crates, most neatly stacked, but with some scattered across the" +3490 RD$ ( STO, 2 ) = "floor, their contents spilling out. Along the port wall is a door marked 'Test specimens'." +3500 RD$ ( STO, 3 ) = "From behind the door, strange animal noises are audible ... snuffling sounds and the" +3510 RD$ ( STO, 4 ) = "occasional primate shriek. A dead man wearing a scuffed and torn lab coat is lying facedown" +3520 RD$ ( STO, 5 ) = "in front of the specimen door. Two other exists lead forward and aft." +3530 RD$ ( MEN, 1 ) = "The room is a hellhole. Cages stand open, while various animals roam about: chimpanzees," +3540 RD$ ( MEN, 2 ) = "dogs, and rats. Some of the rats are dead, having been savaged and eviscerated. The floor" +3550 RD$ ( MEN, 3 ) = "and walls are smeared with animal faeces, and the smell is almost overpowering. A single" +3560 RD$ ( MEN, 4 ) = "port door leads back into the storeroom." +3560 RD$ ( MEN, 5 ) = "" +3570 RD$ ( LAB, 1 ) = "The laboratory is full of scientific equipment, chemical glassware, electronic analysers," +3580 RD$ ( LAB, 2 ) = "fume cupboards, and two couches. The place looks disorded, like the rest of the ship, the" +3590 RD$ ( LAB, 3 ) = "result of frenetic activity. A number of experiments seem to be in progress, with logbooks" +3600 RD$ ( LAB, 4 ) = "and tablets covered in dense calculations and notes. Whatever has been happening in here," +3610 RD$ ( LAB, 5 ) = "it has been done with extreme urgency. Exits lead aft and to starboard." +3620 RD$ ( LFC, 1 ) = "This is a featureless, utilitarian corridor. A stairwell leads upwards. There are also" +3630 RD$ ( LFC, 2 ) = "doors leading to port and forward." +3640 RD$ ( LFC, 3 ) = "" +3650 RD$ ( LFC, 4 ) = "" +3660 RD$ ( LFC, 5 ) = "" +3670 RD$ ( POD, 1 ) = "You are in a large room, with a row of spacesuits hanging on the port wall. At the forward" +3680 RD$ ( POD, 2 ) = "end of the room is a small, two seater vehicle, capable of operating in space outside the" +3690 RD$ ( POD, 3 ) = "main ship for limited periods. In front of the small ship is the pod bay door, leading out" +3700 RD$ ( POD, 4 ) = "into space. There is a single exit leading aft." +3710 RD$ ( POD, 5 ) = "" +3720 RD$ ( AMC, 1 ) = "The main corridor stretches away from you towards the front of the ship. It is featureless" +3730 RD$ ( AMC, 2 ) = "and utilitarian. The lighting is dim. You can see doors either side of you, to port and" +3740 RD$ ( AMC, 3 ) = "to starboard." +3750 RD$ ( AMC, 4 ) = "" +3760 RD$ ( AMC, 5 ) = "" +3770 RD$ ( MMC, 1 ) = "The main corridor stretches away from you both forward and aft. It is featureless" +3780 RD$ ( MMC, 2 ) = "and utilitarian. The lighting is dim. You can see doors either side of you, to port and" +3790 RD$ ( MMC, 3 ) = "to starboard." +3800 RD$ ( MMC, 4 ) = "" +3810 RD$ ( MMC, 5 ) = "" +3820 RD$ ( FMC, 1 ) = "The main corridor stretches away from you towards the rear of the ship. It is featureless" +3830 RD$ ( FMC, 2 ) = "and utilitarian. The lighting is dim. You can see doors either side of you, to port and" +3840 RD$ ( FMC, 3 ) = "to starboard, as well as a third door leading forward." +3850 RD$ ( FMC, 4 ) = "" +3860 RD$ ( FMC, 5 ) = "" +4000 RETURN +4010 REM print room description +4020 FOR LINE = 1 TO 5 +4030 IF RD$(PL, LINE) <> "" THEN PRINT RD$(PL, LINE) +4040 NEXT LINE +4050 PRINT +4060 RETURN From 5c1484d73b73b768c75e6f38c097e37b729d5b61 Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 14 Dec 2024 23:35:47 +0000 Subject: [PATCH 137/183] Debugged room moves, work in progress --- examples/Pneuma.bas | 51 ++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 5c9fb5e..7ebb0a2 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -1,8 +1,11 @@ 10 REM Pneuma - A space adventure 20 REM ========== backstory and instructions ========== -30 PRINT "***** Pneuma - A space adventure *****" : PRINT +30 PRINT "********************************" : PRINT +35 PRINT " Pneuma - A space adventure" : PRINT +37 PRINT "********************************": PRINT 40 PRINT "Movement: [go] a[ft], f[orward], p[ort], s[tarboard], u[p] or d[own]" -45 PRINT "Actions: get, take, drop, examine, look, i[nventory], q[uit]" +45 PRINT "Actions: get, take, drop, examine, look, i[nventory], q[uit]" +46 PRINT "To repeat these instructions: help" 50 PRINT 60 PRINT "You wake up in your bunk, in the sleeping quarters of the starship Pneuma. You can't" 65 PRINT "remember much. You went to bed feeling sick and after a feverish few hours tossing and" @@ -33,16 +36,16 @@ 272 FMC = 17 : LO$ ( FMC ) = "Forward Main Corridor" 275 REM encoded room exits, two digits per direction f, a, p, s, u, d 280 DIM EX$ ( RC ) -281 EX$ ( GAL ) = "020015000008" -282 EX$ ( REC ) = "000116000000" -283 EX$ ( ARM ) = "000017000000" +281 EX$ ( GAL ) = "020000150008" +282 EX$ ( REC ) = "000100160000" +283 EX$ ( ARM ) = "000000170000" 284 EX$ ( BDG ) = "001700000000" 285 EX$ ( SLP ) = "000015000000" 286 EX$ ( MED ) = "070016000000" 287 EX$ ( GYM ) = "000617000013" 288 EX$ ( LAC ) = "100000090100" 289 EX$ ( ENG ) = "000008000000" -290 EX$ ( STO ) = "120800000000" +290 EX$ ( STO ) = "120800110000" 291 EX$ ( MEN ) = "000010000000" 292 EX$ ( LAB ) = "001000130000" 293 EX$ ( LFC ) = "140012000700" @@ -50,11 +53,11 @@ 295 EX$ ( AMC ) = "160001050000" 296 EX$ ( MMC ) = "171502060000" 297 EX$ ( FMC ) = "041603070000" -300 OC = 4 : REM object count +300 OC = 3 : REM object count 310 DIM OB$ ( OC ) -320 PULSE = 0 : OB$ ( PULSE ) = "Pulse rifle" -330 SUIT = 1 : OB$ ( SUIT ) = "Space suit" -340 FOOD = 2 : OB$ ( FOOD ) = "Rotting food" +320 PULSE = 0 : OB$ ( PULSE ) = "pulse rifle" +330 SUIT = 1 : OB$ ( SUIT ) = "space suit" +340 FOOD = 2 : OB$ ( FOOD ) = "rotting food" 400 REM object locations 410 REM location 0 = player's inventory 420 DIM OL ( OC ) @@ -67,10 +70,8 @@ 700 REM ========== main loop ========== 701 REM show room details 703 PRINT "You are in the " ; LO$ ( PL ) : PRINT -705 GOSUB 4010 -706 PRINT "Objects visible:" -707 FOR I = 0 TO OC - 1 : IF OL ( I ) = PL THEN PRINT OB$ ( I ) : NEXT I -708 PRINT +705 GOSUB 4010 : REM print room description +707 GOSUB 4070 : REM print objects 710 INPUT "What now? " ; I$ 715 PRINT 716 MOVE = 1 @@ -79,6 +80,7 @@ 740 IF LEFT$ ( LOWER$ ( I$ ) , 5 ) = "drop " THEN MOVE = 0 : GOSUB 2000 750 IF LEFT$ ( LOWER$ ( I$ ) , 8 ) = "examine " THEN MOVE = 0 : GOSUB 2300 760 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "look" THEN MOVE = 0 : GOSUB 4010 +765 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "help" THEN MOVE = 0 : GOSUB 4130 770 IF LOWER$ ( I$ ) = "i" OR LOWER$ ( I$ ) = "inventory" THEN MOVE = 0 : GOSUB 1000 780 IF LEFT$ ( LOWER$ ( I$ ) , 1 ) = "q" THEN GOSUB 2600 785 IF LEFT$ ( LOWER$ ( I$ ) , 3 ) = "go " THEN GOSUB 1100 @@ -88,7 +90,7 @@ 820 IF LOWER$ ( I$ ) = "s" OR LOWER$ ( I$ ) = "starboard" THEN GOSUB 1200 830 IF LOWER$ ( I$ ) = "u" OR LOWER$ ( I$ ) = "up" THEN GOSUB 1200 840 IF LOWER$ ( I$ ) = "d" OR LOWER$ ( I$ ) = "down" THEN GOSUB 1200 -895 IF MOVE = 1 THEN GOTO 700 ELSE GOTO 708 +895 IF MOVE = 1 THEN GOTO 700 ELSE PRINT : GOTO 710 900 STOP 995 REM ========== actions ========== 1000 REM list the player's inventory @@ -110,6 +112,7 @@ 1320 IF D$ = "a" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 3 , 2 ) ) 1330 IF D$ = "p" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 5 , 2 ) ) 1340 IF D$ = "s" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 7 , 2 ) ) +1345 IF D$ = "u" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 9 , 2 ) ) 1350 IF D$ = "d" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 11 , 2 ) ) 1355 IF NPL = 0 THEN PRINT "You can't go that way." : PRINT ELSE PL = NPL 1360 RETURN @@ -143,7 +146,7 @@ 3160 RD$ ( ARM, 4 ) = "warped and bent. This cupboard appears to be empty. The only exit is a starboard door." 3170 RD$ ( ARM, 5 ) = "" 3180 RD$ ( BDG, 1 ) = "The bridge is the heart of the ship. A vast array of glowing screens and switches fill" -3190 RD$ ( BDG, 2 ) = "every surface. The screens are complex graphics providing detailed information about" +3190 RD$ ( BDG, 2 ) = "every surface. On the screens are complex graphics providing detailed information about" 3200 RD$ ( BDG, 3 ) = "the status of every system on the ship. Many of them are showing red warning symbols." 3210 RD$ ( BDG, 4 ) = "An aft exit leads back into the main corridor." 3220 RD$ ( BDG, 5 ) = "" @@ -175,12 +178,12 @@ 3480 RD$ ( STO, 1 ) = "The storeroom is full of crates, most neatly stacked, but with some scattered across the" 3490 RD$ ( STO, 2 ) = "floor, their contents spilling out. Along the port wall is a door marked 'Test specimens'." 3500 RD$ ( STO, 3 ) = "From behind the door, strange animal noises are audible ... snuffling sounds and the" -3510 RD$ ( STO, 4 ) = "occasional primate shriek. A dead man wearing a scuffed and torn lab coat is lying facedown" +3510 RD$ ( STO, 4 ) = "occasional primate shriek. A dead man wearing a scuffed and torn lab coat is lying face down" 3520 RD$ ( STO, 5 ) = "in front of the specimen door. Two other exists lead forward and aft." 3530 RD$ ( MEN, 1 ) = "The room is a hellhole. Cages stand open, while various animals roam about: chimpanzees," 3540 RD$ ( MEN, 2 ) = "dogs, and rats. Some of the rats are dead, having been savaged and eviscerated. The floor" 3550 RD$ ( MEN, 3 ) = "and walls are smeared with animal faeces, and the smell is almost overpowering. A single" -3560 RD$ ( MEN, 4 ) = "port door leads back into the storeroom." +3555 RD$ ( MEN, 4 ) = "port door leads back into the storeroom." 3560 RD$ ( MEN, 5 ) = "" 3570 RD$ ( LAB, 1 ) = "The laboratory is full of scientific equipment, chemical glassware, electronic analysers," 3580 RD$ ( LAB, 2 ) = "fume cupboards, and two couches. The place looks disorded, like the rest of the ship, the" @@ -217,5 +220,15 @@ 4020 FOR LINE = 1 TO 5 4030 IF RD$(PL, LINE) <> "" THEN PRINT RD$(PL, LINE) 4040 NEXT LINE -4050 PRINT +4050 REM PRINT 4060 RETURN +4070 REM print objects +4080 FOR I = 0 TO OC-1 +4090 IF OL ( I ) = PL THEN PRINT : PRINT "You can see: ";OB$ ( I ) +4100 NEXT I +4110 PRINT +4120 RETURN +4130 REM print help +4140 PRINT "For movement, try [go] a[ft], f[orward], p[ort], s[tarboard], u[p] or d[own]" +4150 PRINT "For actions, try get, take, drop, examine, look, i[nventory], q[uit]" +4160 RETURN From cff10f79699b210b5a127f2da4e720dbc99247e0 Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 14 Dec 2024 23:40:59 +0000 Subject: [PATCH 138/183] Fixed citation for adventure.bas --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 646c8b6..b97583c 100644 --- a/README.md +++ b/README.md @@ -901,7 +901,8 @@ calculate the corresponding factorial *N!*. * *PyBStartrek.bas* - A port of the 1971 Star Trek text based strategy game. -* *adventure-fast.bas* - A port of a 1979 text based Microsoft Adventure game. +* *adventure.bas* - A port of the original 1976 text based adventure game, originally developed for the PDP-10 by Will Crowther, +and expanded by Don Woods. * *bagels.bas* - A guessing game, which made its first appearance in the book 'BASIC Computer Games' in 1978. From 92df3bffb30a0cc43731b7b7cd53d4c0c38f7b24 Mon Sep 17 00:00:00 2001 From: richpl Date: Wed, 18 Dec 2024 22:01:34 +0000 Subject: [PATCH 139/183] Added dropping and picking up objects This is a work in progress --- examples/Pneuma.bas | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 7ebb0a2..678312e 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -2,10 +2,8 @@ 20 REM ========== backstory and instructions ========== 30 PRINT "********************************" : PRINT 35 PRINT " Pneuma - A space adventure" : PRINT -37 PRINT "********************************": PRINT -40 PRINT "Movement: [go] a[ft], f[orward], p[ort], s[tarboard], u[p] or d[own]" -45 PRINT "Actions: get, take, drop, examine, look, i[nventory], q[uit]" -46 PRINT "To repeat these instructions: help" +40 PRINT "********************************": PRINT +45 PRINT "To get instructions, type 'help'" 50 PRINT 60 PRINT "You wake up in your bunk, in the sleeping quarters of the starship Pneuma. You can't" 65 PRINT "remember much. You went to bed feeling sick and after a feverish few hours tossing and" @@ -66,12 +64,11 @@ 450 OL ( FOOD ) = GAL 500 REM setup room descriptions 510 GOSUB 3000 -650 PL = 5 : REM player location +650 PL = 5 : REM initial player location 700 REM ========== main loop ========== 701 REM show room details 703 PRINT "You are in the " ; LO$ ( PL ) : PRINT 705 GOSUB 4010 : REM print room description -707 GOSUB 4070 : REM print objects 710 INPUT "What now? " ; I$ 715 PRINT 716 MOVE = 1 @@ -90,7 +87,7 @@ 820 IF LOWER$ ( I$ ) = "s" OR LOWER$ ( I$ ) = "starboard" THEN GOSUB 1200 830 IF LOWER$ ( I$ ) = "u" OR LOWER$ ( I$ ) = "up" THEN GOSUB 1200 840 IF LOWER$ ( I$ ) = "d" OR LOWER$ ( I$ ) = "down" THEN GOSUB 1200 -895 IF MOVE = 1 THEN GOTO 700 ELSE PRINT : GOTO 710 +895 IF MOVE = 1 THEN GOTO 700 ELSE GOTO 710 900 STOP 995 REM ========== actions ========== 1000 REM list the player's inventory @@ -117,8 +114,34 @@ 1355 IF NPL = 0 THEN PRINT "You can't go that way." : PRINT ELSE PL = NPL 1360 RETURN 1400 REM get command +1405 F=-1: R$="" +1410 R$ = MID$(I$, 5) : REM R$ is the requested object +1420 REM get the object ID +1430 FOR I= 0 TO OC-1 +1440 IF OB$(I) = R$ THEN F=I : REM object exists +1450 NEXT I +1460 REM can't find the item? +1470 IF F=-1 THEN PRINT "Can't see that item here" : PRINT : GOTO 1540 +1480 IF OL(F) <> PL THEN PRINT "That item doesn't appear to be around here" : PRINT : GOTO 1540 +1490 IF OL(F)=0 THEN PRINT "You already have that item" : PRINT: GOTO 1540 +1520 OL(F)=0 : REM add the item to the inventory +1530 PRINT "You've picked up ";OB$(F) : PRINT +1540 RETURN 1700 REM take command +1710 F=-1: R$="" +1720 R$ = MID$(I$, 6) : REM R$ is the requested object +1730 GOTO 1420 : REM use the same logic as the get command 2000 REM drop command +2010 F=-1: R$="" +2020 R$ = MID$(I$, 6) : REM R$ is the requested object +2030 FOR I= 0 TO OC-1 +2040 IF OB$(I) = R$ THEN F=I : REM object exists +2050 NEXT I +2060 REM can't find it? +2070 IF F=-1 THEN PRINT "You've never seen that" : PRINT: GOTO 1540 +2080 IF OL(F) <> 0 THEN PRINT "You aren't carrying that" : PRINT: GOTO 2110 +2090 OL(F) = PL : PRINT "You've dropped ";OB$(F): PRINT: REM add the item to the current room +2110 RETURN 2300 REM examine command 2600 REM quit command 2610 PRINT "Farewell spacefarer ..." @@ -220,7 +243,7 @@ 4020 FOR LINE = 1 TO 5 4030 IF RD$(PL, LINE) <> "" THEN PRINT RD$(PL, LINE) 4040 NEXT LINE -4050 REM PRINT +4055 GOSUB 4070 4060 RETURN 4070 REM print objects 4080 FOR I = 0 TO OC-1 From f57e7a8a2156e6c2a3d6f0314d3bad7126ad8863 Mon Sep 17 00:00:00 2001 From: richpl Date: Fri, 20 Dec 2024 22:22:14 +0000 Subject: [PATCH 140/183] Added examination function Work in progress --- examples/Pneuma.bas | 53 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 678312e..0e70abc 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -62,8 +62,16 @@ 430 OL ( PULSE ) = ARM 440 OL ( SUIT ) = POD 450 OL ( FOOD ) = GAL +455 IC = 1 : REM interactive object count +457 DIM IO$ (IC) +459 MEDLOG = 0 : IO$ ( MEDLOG ) = "medical log" +475 REM interative object locations +477 DIM IL ( IC ) +479 IL ( MEDLOG) = MED 500 REM setup room descriptions 510 GOSUB 3000 +520 REM setup up interative descriptions +530 GOSUB 5000 650 PL = 5 : REM initial player location 700 REM ========== main loop ========== 701 REM show room details @@ -115,7 +123,7 @@ 1360 RETURN 1400 REM get command 1405 F=-1: R$="" -1410 R$ = MID$(I$, 5) : REM R$ is the requested object +1410 R$ = MID$(LOWER$(I$), 5) : REM R$ is the requested object 1420 REM get the object ID 1430 FOR I= 0 TO OC-1 1440 IF OB$(I) = R$ THEN F=I : REM object exists @@ -129,11 +137,11 @@ 1540 RETURN 1700 REM take command 1710 F=-1: R$="" -1720 R$ = MID$(I$, 6) : REM R$ is the requested object +1720 R$ = MID$(LOWER$(I$), 6) : REM R$ is the requested object 1730 GOTO 1420 : REM use the same logic as the get command 2000 REM drop command 2010 F=-1: R$="" -2020 R$ = MID$(I$, 6) : REM R$ is the requested object +2020 R$ = MID$(LOWER$(I$), 6) : REM R$ is the requested object 2030 FOR I= 0 TO OC-1 2040 IF OB$(I) = R$ THEN F=I : REM object exists 2050 NEXT I @@ -143,6 +151,16 @@ 2090 OL(F) = PL : PRINT "You've dropped ";OB$(F): PRINT: REM add the item to the current room 2110 RETURN 2300 REM examine command +2310 F=-1 : R$="" +2320 R$ = MID$(LOWER$(I$), 9) : REM R$ is the object to examine +2330 FOR I = 0 TO IC-1 +2340 IF IO$(I) = R$ THEN F=I : REM object exists +2350 NEXT I +2360 REM can't find it? +2370 IF F=-1 THEN PRINT "You can't examine that" : PRINT : GOTO 2400 +2380 IF IL(F) <> PL THEN PRINT "There isn't one of these here" : PRINT : GOTO 2400 +2390 GOSUB 6000 : REM print result of examination +2400 RETURN 2600 REM quit command 2610 PRINT "Farewell spacefarer ..." 2620 STOP @@ -255,3 +273,32 @@ 4140 PRINT "For movement, try [go] a[ft], f[orward], p[ort], s[tarboard], u[p] or d[own]" 4150 PRINT "For actions, try get, take, drop, examine, look, i[nventory], q[uit]" 4160 RETURN +5000 REM interactive item descriptions +5010 DIM ID$(IC, 20) +5020 ID$( MEDLOG, 1 ) = "The last few entries of the medical log are still visible on the screen:" +5030 ID$( MEDLOG, 2 ) = " " +5040 ID$( MEDLOG, 3 ) = "'2142-6-13: Three days since chimpanzee Nova was innoculated with agent #53." +5050 ID$( MEDLOG, 4 ) = " However, subject Nova showing no signs of adaptation to planetary destination" +5060 ID$( MEDLOG, 5 ) = " atmosphere. In fact, she appears listless, though punctuated with periods of" +5070 ID$( MEDLOG, 6 ) = " extreme agression." +5080 ID$( MEDLOG, 7 ) = " " +5090 ID$( MEDLOG, 8 ) = " 2142-6-14: This morning, Dr Pearson was bitten by Nova. Very quickly he exhibited" +5100 ID$( MEDLOG, 9 ) = " signs of a high fever and is now resting in his quarters." +5110 ID$( MEDLOG, 10) = " " +5120 ID$( MEDLOG, 11) = " 2142-6-15: After a brief period of catatonia, Pearson got up on his own, returned" +5130 ID$( MEDLOG, 12) = " to the medical bay and began to compulsively prepare more agent #53 samples. He" +5140 ID$( MEDLOG, 13) = " is otherwise uncommunicative." +5150 ID$( MEDLOG, 14) = " " +5160 ID$( MEDLOG, 15) = " 2142-6-17: Pearson's compulsive behaviour continues unabated. We have a hypothesis" +5170 ID$( MEDLOG, 16) = " for what the virus, which we have now designated HO-1, does to the brain. However," +5180 ID$( MEDLOG, 17) = " we are struggling to isolate Pearson and may have to seal the medical bay." +5190 ID$( MEDLOG, 18) = " " +5200 ID$ (MEDLOG, 19) = " 2142-6-17: Hollow (HO-1) confirmed as airborne, and other cases appearing around the ship." +5210 ID$ (MEDLOG, 20) = " Shipwide lockdown declared but crew cohesion and discipline already breaking down.'" +5220 RETURN +6000 REM print interative object description +6010 FOR LINE = 1 TO 20 +6020 IF ID$(F, LINE) <> "" THEN PRINT ID$(F, LINE) +6030 NEXT LINE +6035 PRINT +6040 RETURN From 214d564b79cbfee03923b880e1008679fbc1b7e0 Mon Sep 17 00:00:00 2001 From: richpl Date: Tue, 24 Dec 2024 10:59:30 +0000 Subject: [PATCH 141/183] Added more interactive items Work in progress --- examples/Pneuma.bas | 215 +++++++++++++++++++++++++++++--------------- 1 file changed, 143 insertions(+), 72 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 0e70abc..7019757 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -10,69 +10,14 @@ 70 PRINT "turning, feeling like you were burning up, you eventually fell asleep." : PRINT 75 PRINT "Now, your sheets and night clothes are damp with sweat, and you have a raging thirst." 80 PRINT "You have a sore throat and the mother of all headaches, like your brain has been boiling" -85 PRINT "in your skull." : PRINT -95 REM ========== set up environment =========== -100 RC = 18 : REM room count -110 DIM LO$ ( RC ) -120 INV = 0 : LO$ ( INV ) = "Inventory" -130 GAL = 1 : LO$ ( GAL ) = "Galley" -140 REC = 2 : LO$ ( REC ) = "Recreation/Dining Room" -150 ARM = 3 : LO$ ( ARM ) = "Armoury" -160 BDG = 4 : LO$ ( BDG ) = "Bridge" -170 SLP = 5 : LO$ ( SLP ) = "Sleeping Quarters" -180 MED = 6 : LO$ ( MED ) = "Medical Centre" -190 GYM = 7 : LO$ ( GYM ) = "Gymnasium" -200 LAC = 8 : LO$ ( LAC ) = "Lower Aft Corridor" -210 ENG = 9 : LO$ ( ENG ) = "Engine Room" -220 STO = 10 : LO$ ( STO ) = "Storeroom" -230 MEN = 11 : LO$ ( MEN ) = "Menagerie" -240 LAB = 12 : LO$ ( LAB ) = "Laboratory" -250 LFC = 13 : LO$ ( LFC ) = "Lower Forward Corridor" -260 POD = 14 : LO$ ( POD ) = "Pod Bay" -270 AMC = 15 : LO$ ( AMC ) = "Aft Main Corridor" -271 MMC = 16 : LO$ ( MMC ) = "Mid Main Corridor" -272 FMC = 17 : LO$ ( FMC ) = "Forward Main Corridor" -275 REM encoded room exits, two digits per direction f, a, p, s, u, d -280 DIM EX$ ( RC ) -281 EX$ ( GAL ) = "020000150008" -282 EX$ ( REC ) = "000100160000" -283 EX$ ( ARM ) = "000000170000" -284 EX$ ( BDG ) = "001700000000" -285 EX$ ( SLP ) = "000015000000" -286 EX$ ( MED ) = "070016000000" -287 EX$ ( GYM ) = "000617000013" -288 EX$ ( LAC ) = "100000090100" -289 EX$ ( ENG ) = "000008000000" -290 EX$ ( STO ) = "120800110000" -291 EX$ ( MEN ) = "000010000000" -292 EX$ ( LAB ) = "001000130000" -293 EX$ ( LFC ) = "140012000700" -294 EX$ ( POD ) = "001300000000" -295 EX$ ( AMC ) = "160001050000" -296 EX$ ( MMC ) = "171502060000" -297 EX$ ( FMC ) = "041603070000" -300 OC = 3 : REM object count -310 DIM OB$ ( OC ) -320 PULSE = 0 : OB$ ( PULSE ) = "pulse rifle" -330 SUIT = 1 : OB$ ( SUIT ) = "space suit" -340 FOOD = 2 : OB$ ( FOOD ) = "rotting food" -400 REM object locations -410 REM location 0 = player's inventory -420 DIM OL ( OC ) -430 OL ( PULSE ) = ARM -440 OL ( SUIT ) = POD -450 OL ( FOOD ) = GAL -455 IC = 1 : REM interactive object count -457 DIM IO$ (IC) -459 MEDLOG = 0 : IO$ ( MEDLOG ) = "medical log" -475 REM interative object locations -477 DIM IL ( IC ) -479 IL ( MEDLOG) = MED +85 PRINT "in your skull. In fact, you're no longer sure exactly where you are ... you seem to be" +90 PRINT "suffering from some sort of amnesia ...": PRINT +95 REM set up environment +100 GOSUB 2700 500 REM setup room descriptions 510 GOSUB 3000 520 REM setup up interative descriptions 530 GOSUB 5000 -650 PL = 5 : REM initial player location 700 REM ========== main loop ========== 701 REM show room details 703 PRINT "You are in the " ; LO$ ( PL ) : PRINT @@ -103,6 +48,7 @@ 1010 FOR I = 0 TO OC - 1 1020 IF OL ( I ) = 0 THEN PRINT OB$ ( I ) 1030 NEXT I +1035 PRINT 1040 RETURN 1100 REM fully written out move (e.g. 'go aft') 1110 D$ = MID$ ( LOWER$(I$) , 4 , 1 ) @@ -164,7 +110,72 @@ 2600 REM quit command 2610 PRINT "Farewell spacefarer ..." 2620 STOP -3000 REM room descriptions +2700 REM ========== set up environment =========== +2705 RC = 18 : REM room count +2710 DIM LO$ ( RC ) +2715 INV = 0 : LO$ ( INV ) = "Inventory" +2720 GAL = 1 : LO$ ( GAL ) = "Galley" +2725 REC = 2 : LO$ ( REC ) = "Recreation/Dining Room" +2730 ARM = 3 : LO$ ( ARM ) = "Armoury" +2735 BDG = 4 : LO$ ( BDG ) = "Bridge" +2740 SLP = 5 : LO$ ( SLP ) = "Sleeping Quarters" +2745 MED = 6 : LO$ ( MED ) = "Medical Centre" +2750 GYM = 7 : LO$ ( GYM ) = "Gymnasium" +2755 LAC = 8 : LO$ ( LAC ) = "Lower Aft Corridor" +2760 ENG = 9 : LO$ ( ENG ) = "Engine Room" +2765 STO = 10 : LO$ ( STO ) = "Storeroom" +2770 MEN = 11 : LO$ ( MEN ) = "Menagerie" +2775 LAB = 12 : LO$ ( LAB ) = "Laboratory" +2780 LFC = 13 : LO$ ( LFC ) = "Lower Forward Corridor" +2785 POD = 14 : LO$ ( POD ) = "Pod Bay" +2790 AMC = 15 : LO$ ( AMC ) = "Aft Main Corridor" +2795 MMC = 16 : LO$ ( MMC ) = "Mid Main Corridor" +2800 FMC = 17 : LO$ ( FMC ) = "Forward Main Corridor" +2805 REM encoded room exits, two digits per direction f, a, p, s, u, d +2810 DIM EX$ ( RC ) +2815 EX$ ( GAL ) = "020000150008" +2820 EX$ ( REC ) = "000100160000" +2825 EX$ ( ARM ) = "000000170000" +2830 EX$ ( BDG ) = "001700000000" +2835 EX$ ( SLP ) = "000015000000" +2840 EX$ ( MED ) = "070016000000" +2845 EX$ ( GYM ) = "000617000013" +2850 EX$ ( LAC ) = "100000090100" +2855 EX$ ( ENG ) = "000008000000" +2860 EX$ ( STO ) = "120800110000" +2865 EX$ ( MEN ) = "000010000000" +2870 EX$ ( LAB ) = "001000130000" +2875 EX$ ( LFC ) = "140012000700" +2880 EX$ ( POD ) = "001300000000" +2890 EX$ ( AMC ) = "160001050000" +2895 EX$ ( MMC ) = "171502060000" +2900 EX$ ( FMC ) = "041603070000" +2905 OC = 3 : REM object count +2910 DIM OB$ ( OC ) +2915 PULSE = 0 : OB$ ( PULSE ) = "pulse rifle" +2920 SUIT = 1 : OB$ ( SUIT ) = "space suit" +2925 FOOD = 2 : OB$ ( FOOD ) = "rotting food" +2930 REM object locations +2932 REM location 0 = player's inventory +2934 DIM OL ( OC ) +2936 OL ( PULSE ) = ARM +2938 OL ( SUIT ) = POD +2940 OL ( FOOD ) = GAL +2942 IC = 4 : REM interactive object count +2944 DIM IO$ (IC) +2946 MEDLOG = 0 : IO$ ( MEDLOG ) = "medical log" +2948 PORTHOLE = 1 : IO$ (PORTHOLE) = "porthole" +2950 CONSOLE = 2 : IO$(CONSOLE) = "console" +2952 ENGINE = 3 :IO$(ENGINE)= "engine control" +2960 REM interative object locations +2962 DIM IL ( IC ) +2964 IL ( MEDLOG) = MED +2966 IL (PORTHOLE) = POD +2968 IL (CONSOLE) = BDG +2970 IL (ENGINE) = ENG +2980 PL = 5 : REM initial player location +2990 RETURN +3000 REM ========== room descriptions ========== 3010 DIM RD$ ( RC, 5 ) 3020 RD$ ( INV, 1 ) = "" 3021 RD$ ( INV, 2 ) = "" @@ -189,8 +200,8 @@ 3180 RD$ ( BDG, 1 ) = "The bridge is the heart of the ship. A vast array of glowing screens and switches fill" 3190 RD$ ( BDG, 2 ) = "every surface. On the screens are complex graphics providing detailed information about" 3200 RD$ ( BDG, 3 ) = "the status of every system on the ship. Many of them are showing red warning symbols." -3210 RD$ ( BDG, 4 ) = "An aft exit leads back into the main corridor." -3220 RD$ ( BDG, 5 ) = "" +3210 RD$ ( BDG, 4 ) = "There is a console directly in front of you, a pilot gripping the throttle." +3220 RD$ ( BDG, 5 ) = "An aft exit leads back into the main corridor." 3230 RD$ ( SLP, 1 ) = "The sleeping quarters is filled with bunks, one up, one down. Several of the bunks" 3240 RD$ ( SLP, 2 ) = "contain sleeping forms, some gently shoring. The room has a partition to separate" 3250 RD$ ( SLP, 3 ) = "male and female bunks. Against the forward wall are two corresponding sets of heads." @@ -212,8 +223,8 @@ 3410 RD$ ( LAC, 4 ) = "" 3420 RD$ ( LAC, 5 ) = "" 3430 RD$ ( ENG, 1 ) = "The engine room is characterised by a continual rumble, as though incredible energies are" -3440 RD$ ( ENG, 2 ) = "barely being contained. There is a console in the far corner, festooned with controls and" -3450 RD$ ( ENG, 3 ) = "engine readouts. A single exit leads out into the corridor." +3440 RD$ ( ENG, 2 ) = "barely being contained. There is an engine control in the far corner, festooned with" +3450 RD$ ( ENG, 3 ) = "switches and engine readouts. A single exit leads out into the corridor." 3460 RD$ ( ENG, 4 ) = "" 3470 RD$ ( ENG, 5 ) = "" 3480 RD$ ( STO, 1 ) = "The storeroom is full of crates, most neatly stacked, but with some scattered across the" @@ -239,7 +250,7 @@ 3670 RD$ ( POD, 1 ) = "You are in a large room, with a row of spacesuits hanging on the port wall. At the forward" 3680 RD$ ( POD, 2 ) = "end of the room is a small, two seater vehicle, capable of operating in space outside the" 3690 RD$ ( POD, 3 ) = "main ship for limited periods. In front of the small ship is the pod bay door, leading out" -3700 RD$ ( POD, 4 ) = "into space. There is a single exit leading aft." +3700 RD$ ( POD, 4 ) = "into space. There is a porthole on the far wall. There is a single exit leading aft." 3710 RD$ ( POD, 5 ) = "" 3720 RD$ ( AMC, 1 ) = "The main corridor stretches away from you towards the front of the ship. It is featureless" 3730 RD$ ( AMC, 2 ) = "and utilitarian. The lighting is dim. You can see doors either side of you, to port and" @@ -257,7 +268,7 @@ 3850 RD$ ( FMC, 4 ) = "" 3860 RD$ ( FMC, 5 ) = "" 4000 RETURN -4010 REM print room description +4010 REM ========== print room description ========== 4020 FOR LINE = 1 TO 5 4030 IF RD$(PL, LINE) <> "" THEN PRINT RD$(PL, LINE) 4040 NEXT LINE @@ -269,11 +280,12 @@ 4100 NEXT I 4110 PRINT 4120 RETURN -4130 REM print help -4140 PRINT "For movement, try [go] a[ft], f[orward], p[ort], s[tarboard], u[p] or d[own]" -4150 PRINT "For actions, try get, take, drop, examine, look, i[nventory], q[uit]" +4130 REM ========== print help ========== +4140 PRINT "For movement, try [go] a[ft], f[orward], p[ort], s[tarboard], u[p] or d[own]." +4150 PRINT "For actions, try get, take, drop, examine, look, i[nventory], q[uit]." +4155 PRINT "To examine, pick up or drop items, refer to them exactly as they are printed." 4160 RETURN -5000 REM interactive item descriptions +5000 REM ========== interactive item descriptions ========== 5010 DIM ID$(IC, 20) 5020 ID$( MEDLOG, 1 ) = "The last few entries of the medical log are still visible on the screen:" 5030 ID$( MEDLOG, 2 ) = " " @@ -295,8 +307,67 @@ 5190 ID$( MEDLOG, 18) = " " 5200 ID$ (MEDLOG, 19) = " 2142-6-17: Hollow (HO-1) confirmed as airborne, and other cases appearing around the ship." 5210 ID$ (MEDLOG, 20) = " Shipwide lockdown declared but crew cohesion and discipline already breaking down.'" -5220 RETURN -6000 REM print interative object description +5220 ID$ (PORTHOLE, 1 ) = "Looking through the porthole, you seen a spacesuit clad figure floating outside." +5230 ID$ (PORTHOLE, 2 ) = "Although he is tethered to an anchor point on the ship's hull, he is otherwise floating" +5240 ID$ (PORTHOLE, 3 ) = "freely, his arms and legs splayed out to his sides." +5250 ID$ (PORTHOLE, 4 ) = " " +5260 ID$ (PORTHOLE, 5 ) = "Peering at the figure more closely, as he slowly rotates, a light from the ship" +5270 ID$ (PORTHOLE, 6 ) = "briefly illuminates his face. He's clearly dead, his oxygen ran out some time ago." +5280 ID$ (PORTHOLE, 7 ) = "His expression is frozen in a rictus of pain. He was screaming almost until the end ..." +5290 ID$ (PORTHOLE, 8 ) = "" +5300 ID$ (PORTHOLE, 9 ) = "" +5310 ID$ (PORTHOLE, 10 ) = "" +5320 ID$ (PORTHOLE, 11 ) = "" +5330 ID$ (PORTHOLE, 12 ) = "" +5340 ID$ (PORTHOLE, 13 ) = "" +5342 ID$ (PORTHOLE, 14 ) = "" +5344 ID$ (PORTHOLE, 15 ) = "" +5346 ID$ (PORTHOLE, 16 ) = "" +5348 ID$ (PORTHOLE, 17 ) = "" +5350 ID$ (PORTHOLE, 18 ) = "" +5352 ID$ (PORTHOLE, 19 ) = "" +5354 ID$ (PORTHOLE, 20 ) = "" +5356 ID$ (CONSOLE, 1 ) = "The console shows the state of the ship's engines. They are in overdrive. The pilot" +5358 ID$ (CONSOLE, 2 ) = "appears to have the throttle jammed wide open with his right arm. The muscles in his" +5360 ID$ (CONSOLE, 3 ) = "forearm are taught, there's no way he's going to release the throttle. He left hand" +5362 ID$ (CONSOLE, 4 ) = "works its way around the console switches. After a few moments you realise that he is" +5364 ID$ (CONSOLE, 5 ) = "executing the same sequence of switches over and over again." +5366 ID$ (CONSOLE, 6 ) = " " +5368 ID$ (CONSOLE, 7 ) = "You speak to the pilot but he is unresponsive. Mentally he is somewhere else. He is" +5370 ID$ (CONSOLE, 8 ) = "mumbling to himself but you cannot make out the words, although he appears to be" +5372 ID$ (CONSOLE, 9 ) = "reciting some sort of launch checklist." +5374 ID$ (CONSOLE, 10 ) = " " +5376 ID$ (CONSOLE, 11 ) = "One thing is certain, the ship is out of control, careering through space at maximum" +5378 ID$ (CONSOLE, 12 ) = "speed. Rescue will be impossible unless you can find a way to shut down the engines." +5380 ID$ (CONSOLE, 13 ) = "" +5382 ID$ (CONSOLE, 14 ) = "" +5384 ID$ (CONSOLE, 15 ) = "" +5386 ID$ (CONSOLE, 16 ) = "" +5388 ID$ (CONSOLE, 17 ) = "" +5390 ID$ (CONSOLE, 18 ) = "" +5392 ID$ (CONSOLE, 19 ) = "" +5394 ID$ (CONSOLE, 20 ) = "" +5296 ID$ (ENGINE, 1 ) = "The control panel is grubby, smeared with oil and grime. This is clearly" +5298 ID$ (ENGINE, 2 ) = "the engineering heart of the ship. Most of the readouts mean nothing to you," +5300 ID$ (ENGINE, 3 ) = "whatever your duties were on this ship, you were clearly not a warp engineer." +5302 ID$ (ENGINE, 4 ) = " " +5304 ID$ (ENGINE, 5 ) = "Many of the lights on the front panel are flashing red. Not all is well with" +5306 ID$ (ENGINE, 6 ) = "the Pneuma. Even to your untrained eye, it's obvious that the ship's engines appear to" +5308 ID$ (ENGINE, 7 ) = "be on the point of burnout, having been run at full capacity for many hours." +5310 ID$ (ENGINE, 8 ) = " " +5312 ID$ (ENGINE, 9 ) = "To the top left of the panel, a screen reads:" +5314 ID$ (ENGINE, 11 ) = " " +5316 ID$ (ENGINE, 12 ) = "'WARNING: CORE BREACH IMMINENT'" +5318 ID$ (ENGINE, 13 ) = " " +5320 ID$ (ENGINE, 14 ) = "Just below the screen is a large red button, shielded by a cover that can be" +5322 ID$ (ENGINE, 15 ) = "flipped aside." +5324 ID$ (ENGINE, 16 ) = "" +5326 ID$ (ENGINE, 17 ) = "" +5328 ID$ (ENGINE, 18 ) = "" +5330 ID$ (ENGINE, 19 ) = "" +5332 ID$ (ENGINE, 20 ) = "" +5990 RETURN +6000 REM ========== print interative object description ========== 6010 FOR LINE = 1 TO 20 6020 IF ID$(F, LINE) <> "" THEN PRINT ID$(F, LINE) 6030 NEXT LINE From cf415bc61bbb72781d9710194c753f5a45605243 Mon Sep 17 00:00:00 2001 From: richpl Date: Tue, 24 Dec 2024 21:12:07 +0000 Subject: [PATCH 142/183] Refactored descriptions to use DATA statements Work in progress --- examples/Pneuma.bas | 315 ++++++++++++++++++++------------------------ 1 file changed, 145 insertions(+), 170 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 7019757..2a2a5a1 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -176,97 +176,99 @@ 2980 PL = 5 : REM initial player location 2990 RETURN 3000 REM ========== room descriptions ========== -3010 DIM RD$ ( RC, 5 ) -3020 RD$ ( INV, 1 ) = "" -3021 RD$ ( INV, 2 ) = "" -3022 RD$ ( INV, 3 ) = "" -3023 RD$ ( INV, 4 ) = "" -3024 RD$ ( INV, 5 ) = "" -3030 RD$ ( GAL, 1 ) = "The galley contains gleaming, stainless steel cupboards along the aft wall. A food" -3040 RD$ ( GAL, 2 ) = "preparation surface is on the port wall, currently covered in rotting food. A chef" -3050 RD$ ( GAL, 3 ) = "stands at the work surface, methodically chopping food even though everything has" -3060 RD$ ( GAL, 4 ) = "already been thoroughly diced. There are doors in the starboard and forward walls and" -3070 RD$ ( GAL, 5 ) = "a stairway leads downwards in the far corner." -3080 RD$ ( REC, 1 ) = "Space is clearly at a premium in this ship. The room doubles as both a dining and" -3090 RD$ ( REC, 2 ) = "recreation area. Long tables for dining are located on the port side, while couches" -3100 RD$ ( REC, 3 ) = "and low tables are scattered around the remaining space. There are doors in the aft" -3110 RD$ ( REC, 4 ) = "and starboard walls." -3120 RD$ ( REC, 5 ) = "" -3130 RD$ ( ARM, 1 ) = "Locked cabinets line the starboard wall. Each cabinet has a prominently displayed" -3140 RD$ ( ARM, 2 ) = "notice on its door reading 'Weapons to be removed only when authorised by the Chief" -3150 RD$ ( ARM, 3 ) = "Security Officer'. However, the door to one cupboard has been prized open, it is" -3160 RD$ ( ARM, 4 ) = "warped and bent. This cupboard appears to be empty. The only exit is a starboard door." -3170 RD$ ( ARM, 5 ) = "" -3180 RD$ ( BDG, 1 ) = "The bridge is the heart of the ship. A vast array of glowing screens and switches fill" -3190 RD$ ( BDG, 2 ) = "every surface. On the screens are complex graphics providing detailed information about" -3200 RD$ ( BDG, 3 ) = "the status of every system on the ship. Many of them are showing red warning symbols." -3210 RD$ ( BDG, 4 ) = "There is a console directly in front of you, a pilot gripping the throttle." -3220 RD$ ( BDG, 5 ) = "An aft exit leads back into the main corridor." -3230 RD$ ( SLP, 1 ) = "The sleeping quarters is filled with bunks, one up, one down. Several of the bunks" -3240 RD$ ( SLP, 2 ) = "contain sleeping forms, some gently shoring. The room has a partition to separate" -3250 RD$ ( SLP, 3 ) = "male and female bunks. Against the forward wall are two corresponding sets of heads." -3260 RD$ ( SLP, 4 ) = "The room is messy, with discarded personal items everywhere ... on bunks, on the floor." -3270 RD$ ( SLP, 5 ) = "There is a door in the port wall." -3280 RD$ ( MED, 1 ) = "The medical centre looks relatively normal, but there is evidence of discarded items" -3290 RD$ ( MED, 2 ) = "lying around the room. Blood filled syringes are scattered on a workbench, as well as" -3300 RD$ ( MED, 3 ) = "some bloodied bandanges. The words 'I'm losing myself' are scrawled messily in blood on" -3310 RD$ ( MED, 4 ) = "one wall. In the corner you can see a medical scanner and next to it, a terminal. On the" -3320 RD$ ( MED, 5 ) = "terminal screen are the words 'Medical Log'. Exits lead port and forward." -3330 RD$ ( GYM, 1 ) = "The gymnasium is full of exercise equipment. A woman is running furiously on a treadmill." -3340 RD$ ( GYM, 2 ) = "She looks exhausted and emaciated, but she keeps running at top speed, almost at a sprint." -3350 RD$ ( GYM, 3 ) = "Her eyes remain fixed on the treadmill console. There are aft and port exists, as well as" -3360 RD$ ( GYM, 4 ) = "a stairwell leading to the lower deck in the far corner." -3370 RD$ ( GYM, 5 ) = "" -3380 RD$ ( LAC, 1 ) = "This is a featureless, utilitarian corridor. A stairwell leads upwards. There are also" -3390 RD$ ( LAC, 2 ) = "exits leading starboard and forward." -3400 RD$ ( LAC, 3 ) = "" -3410 RD$ ( LAC, 4 ) = "" -3420 RD$ ( LAC, 5 ) = "" -3430 RD$ ( ENG, 1 ) = "The engine room is characterised by a continual rumble, as though incredible energies are" -3440 RD$ ( ENG, 2 ) = "barely being contained. There is an engine control in the far corner, festooned with" -3450 RD$ ( ENG, 3 ) = "switches and engine readouts. A single exit leads out into the corridor." -3460 RD$ ( ENG, 4 ) = "" -3470 RD$ ( ENG, 5 ) = "" -3480 RD$ ( STO, 1 ) = "The storeroom is full of crates, most neatly stacked, but with some scattered across the" -3490 RD$ ( STO, 2 ) = "floor, their contents spilling out. Along the port wall is a door marked 'Test specimens'." -3500 RD$ ( STO, 3 ) = "From behind the door, strange animal noises are audible ... snuffling sounds and the" -3510 RD$ ( STO, 4 ) = "occasional primate shriek. A dead man wearing a scuffed and torn lab coat is lying face down" -3520 RD$ ( STO, 5 ) = "in front of the specimen door. Two other exists lead forward and aft." -3530 RD$ ( MEN, 1 ) = "The room is a hellhole. Cages stand open, while various animals roam about: chimpanzees," -3540 RD$ ( MEN, 2 ) = "dogs, and rats. Some of the rats are dead, having been savaged and eviscerated. The floor" -3550 RD$ ( MEN, 3 ) = "and walls are smeared with animal faeces, and the smell is almost overpowering. A single" -3555 RD$ ( MEN, 4 ) = "port door leads back into the storeroom." -3560 RD$ ( MEN, 5 ) = "" -3570 RD$ ( LAB, 1 ) = "The laboratory is full of scientific equipment, chemical glassware, electronic analysers," -3580 RD$ ( LAB, 2 ) = "fume cupboards, and two couches. The place looks disorded, like the rest of the ship, the" -3590 RD$ ( LAB, 3 ) = "result of frenetic activity. A number of experiments seem to be in progress, with logbooks" -3600 RD$ ( LAB, 4 ) = "and tablets covered in dense calculations and notes. Whatever has been happening in here," -3610 RD$ ( LAB, 5 ) = "it has been done with extreme urgency. Exits lead aft and to starboard." -3620 RD$ ( LFC, 1 ) = "This is a featureless, utilitarian corridor. A stairwell leads upwards. There are also" -3630 RD$ ( LFC, 2 ) = "doors leading to port and forward." -3640 RD$ ( LFC, 3 ) = "" -3650 RD$ ( LFC, 4 ) = "" -3660 RD$ ( LFC, 5 ) = "" -3670 RD$ ( POD, 1 ) = "You are in a large room, with a row of spacesuits hanging on the port wall. At the forward" -3680 RD$ ( POD, 2 ) = "end of the room is a small, two seater vehicle, capable of operating in space outside the" -3690 RD$ ( POD, 3 ) = "main ship for limited periods. In front of the small ship is the pod bay door, leading out" -3700 RD$ ( POD, 4 ) = "into space. There is a porthole on the far wall. There is a single exit leading aft." -3710 RD$ ( POD, 5 ) = "" -3720 RD$ ( AMC, 1 ) = "The main corridor stretches away from you towards the front of the ship. It is featureless" -3730 RD$ ( AMC, 2 ) = "and utilitarian. The lighting is dim. You can see doors either side of you, to port and" -3740 RD$ ( AMC, 3 ) = "to starboard." -3750 RD$ ( AMC, 4 ) = "" -3760 RD$ ( AMC, 5 ) = "" -3770 RD$ ( MMC, 1 ) = "The main corridor stretches away from you both forward and aft. It is featureless" -3780 RD$ ( MMC, 2 ) = "and utilitarian. The lighting is dim. You can see doors either side of you, to port and" -3790 RD$ ( MMC, 3 ) = "to starboard." -3800 RD$ ( MMC, 4 ) = "" -3810 RD$ ( MMC, 5 ) = "" -3820 RD$ ( FMC, 1 ) = "The main corridor stretches away from you towards the rear of the ship. It is featureless" -3830 RD$ ( FMC, 2 ) = "and utilitarian. The lighting is dim. You can see doors either side of you, to port and" -3840 RD$ ( FMC, 3 ) = "to starboard, as well as a third door leading forward." -3850 RD$ ( FMC, 4 ) = "" -3860 RD$ ( FMC, 5 ) = "" +3010 DIM RD$ ( RC, 5 ) +3015 REM inventory +3020 DATA "", "", "", "", "" +3025 REM galley +3030 DATA "The galley contains gleaming, stainless steel cupboards along the aft wall. A food" +3040 DATA "preparation surface is on the port wall, currently covered in rotting food. A chef" +3050 DATA "stands at the work surface, methodically chopping food even though everything has" +3060 DATA "already been thoroughly diced. There are doors in the starboard and forward walls and" +3070 DATA "a stairway leads downwards in the far corner." +3075 REM recreation room +3080 DATA "Space is clearly at a premium in this ship. The room doubles as both a dining and" +3090 DATA "recreation area. Long tables for dining are located on the port side, while couches" +3100 DATA "and low tables are scattered around the remaining space. There are doors in the aft" +3110 DATA "and starboard walls.", "" +3120 REM armoury +3130 DATA "Locked cabinets line the starboard wall. Each cabinet has a prominently displayed" +3140 DATA "notice on its door reading 'Weapons to be removed only when authorised by the Chief" +3150 DATA "Security Officer'. However, the door to one cupboard has been prized open, it is" +3160 DATA "warped and bent. This cupboard appears to be empty. The only exit is a starboard door.", "" +3170 REM bridge +3180 DATA "The bridge is the heart of the ship. A vast array of glowing screens and switches fill" +3190 DATA "every surface. On the screens are complex graphics providing detailed information about" +3200 DATA "the status of every system on the ship. Many of them are showing red warning symbols." +3210 DATA "There is a *console* directly in front of you, a pilot gripping the throttle." +3220 DATA "An aft exit leads back into the main corridor." +3225 REM sleeping quarters +3230 DATA "The sleeping quarters is filled with bunks, one up, one down. Several of the bunks" +3240 DATA "contain sleeping forms, some gently shoring. The room has a partition to separate" +3250 DATA "male and female bunks. Against the forward wall are two corresponding sets of heads." +3260 DATA "The room is messy, with discarded personal items everywhere ... on bunks, on the floor." +3270 DATA "There is a door in the port wall." +3275 REM medical centre +3280 DATA "The medical centre looks relatively normal, but there is evidence of discarded items" +3290 DATA "lying around the room. Blood filled syringes are scattered on a workbench, as well as" +3300 DATA "some bloodied bandanges. The words 'I'm losing myself' are scrawled messily in blood on" +3310 DATA "one wall. In the corner you can see a medical scanner and next to it, a terminal. On the" +3320 DATA "terminal screen is a portion of the *medical log*. Exits lead port and forward." +3325 REM gymnasium +3330 DATA "The gymnasium is full of exercise equipment. A woman is running furiously on a treadmill." +3340 DATA "She looks exhausted and emaciated, but she keeps running at top speed, almost at a sprint." +3350 DATA "Her eyes remain fixed on the treadmill console. There are aft and port exists, as well as" +3360 DATA "a stairwell leading to the lower deck in the far corner.", "" +3370 REM lower aft corridor +3380 DATA "This is a featureless, utilitarian corridor. A stairwell leads upwards. There are also" +3390 DATA "exits leading starboard and forward.", "", "", "" +3400 REM engine room +3430 DATA "The engine room is characterised by a continual rumble, as though incredible energies are" +3440 DATA "barely being contained. There is an *engine control* in the far corner, festooned with" +3450 DATA "switches and engine readouts. A single exit leads out into the corridor.", "", "" +3460 REM storeroom +3480 DATA "The storeroom is full of crates, most neatly stacked, but with some scattered across the" +3490 DATA "floor, their contents spilling out. Along the port wall is a door marked 'Test specimens'." +3500 DATA "From behind the door, strange animal noises are audible ... snuffling sounds and the" +3510 DATA "occasional primate shriek. A dead man wearing a scuffed and torn lab coat is lying face down" +3520 DATA "in front of the specimen door. Two other exists lead forward and aft." +3525 REM menagerie +3530 DATA "The room is a hellhole. Cages stand open, while various animals roam about: chimpanzees," +3540 DATA "dogs, and rats. Some of the rats are dead, having been savaged and eviscerated. The floor" +3550 DATA "and walls are smeared with animal faeces, and the smell is almost overpowering. A single" +3555 DATA "port door leads back into the storeroom.", "" +3560 REM laboratory +3570 DATA "The laboratory is full of scientific equipment, chemical glassware, electronic analysers," +3580 DATA "fume cupboards, and two couches. The place looks disorded, like the rest of the ship, the" +3590 DATA "result of frenetic activity. A number of experiments seem to be in progress, with logbooks" +3600 DATA "and tablets covered in dense calculations and notes. Whatever has been happening in here," +3610 DATA "it has been done with extreme urgency. Exits lead aft and to starboard." +3615 REM lower forward corridor +3620 DATA "This is a featureless, utilitarian corridor. A stairwell leads upwards. There are also" +3630 DATA "doors leading to port and forward.", "", "", "" +3660 REM pod bay +3670 DATA "You are in a large room, with a row of spacesuits hanging on the port wall. At the forward" +3680 DATA "end of the room is a small, two seater vehicle, capable of operating in space outside the" +3690 DATA "main ship for limited periods. In front of the small ship is the pod bay door, leading out" +3700 DATA "into space. There is a *porthole* on the far wall. There is a single exit leading aft.", "" +3710 REM aft main corridor +3720 DATA "The main corridor stretches away from you towards the front of the ship. It is featureless" +3730 DATA "and utilitarian. The lighting is dim. You can see doors either side of you, to port and" +3740 DATA "to starboard.", "", "" +3760 REM mid main corridor +3770 DATA "The main corridor stretches away from you both forward and aft. It is featureless" +3780 DATA "and utilitarian. The lighting is dim. You can see doors either side of you, to port and" +3790 DATA "to starboard.", "", "" +3810 REM forward main corridor +3820 DATA "The main corridor stretches away from you towards the rear of the ship. It is featureless" +3830 DATA "and utilitarian. The lighting is dim. You can see doors either side of you, to port and" +3840 DATA "to starboard, as well as a third door leading forward.", "", "" +3860 REM assign to room descriptions +3870 FOR ROOM = 0 to RC-1 +3880 FOR I = 1 TO 5 +3885 READ DESC$ : RD$ (ROOM, I) = DESC$ +3886 REM PRINT DESC$ +3890 NEXT I +3900 NEXT ROOM 4000 RETURN 4010 REM ========== print room description ========== 4020 FOR LINE = 1 TO 5 @@ -287,85 +289,58 @@ 4160 RETURN 5000 REM ========== interactive item descriptions ========== 5010 DIM ID$(IC, 20) -5020 ID$( MEDLOG, 1 ) = "The last few entries of the medical log are still visible on the screen:" -5030 ID$( MEDLOG, 2 ) = " " -5040 ID$( MEDLOG, 3 ) = "'2142-6-13: Three days since chimpanzee Nova was innoculated with agent #53." -5050 ID$( MEDLOG, 4 ) = " However, subject Nova showing no signs of adaptation to planetary destination" -5060 ID$( MEDLOG, 5 ) = " atmosphere. In fact, she appears listless, though punctuated with periods of" -5070 ID$( MEDLOG, 6 ) = " extreme agression." -5080 ID$( MEDLOG, 7 ) = " " -5090 ID$( MEDLOG, 8 ) = " 2142-6-14: This morning, Dr Pearson was bitten by Nova. Very quickly he exhibited" -5100 ID$( MEDLOG, 9 ) = " signs of a high fever and is now resting in his quarters." -5110 ID$( MEDLOG, 10) = " " -5120 ID$( MEDLOG, 11) = " 2142-6-15: After a brief period of catatonia, Pearson got up on his own, returned" -5130 ID$( MEDLOG, 12) = " to the medical bay and began to compulsively prepare more agent #53 samples. He" -5140 ID$( MEDLOG, 13) = " is otherwise uncommunicative." -5150 ID$( MEDLOG, 14) = " " -5160 ID$( MEDLOG, 15) = " 2142-6-17: Pearson's compulsive behaviour continues unabated. We have a hypothesis" -5170 ID$( MEDLOG, 16) = " for what the virus, which we have now designated HO-1, does to the brain. However," -5180 ID$( MEDLOG, 17) = " we are struggling to isolate Pearson and may have to seal the medical bay." -5190 ID$( MEDLOG, 18) = " " -5200 ID$ (MEDLOG, 19) = " 2142-6-17: Hollow (HO-1) confirmed as airborne, and other cases appearing around the ship." -5210 ID$ (MEDLOG, 20) = " Shipwide lockdown declared but crew cohesion and discipline already breaking down.'" -5220 ID$ (PORTHOLE, 1 ) = "Looking through the porthole, you seen a spacesuit clad figure floating outside." -5230 ID$ (PORTHOLE, 2 ) = "Although he is tethered to an anchor point on the ship's hull, he is otherwise floating" -5240 ID$ (PORTHOLE, 3 ) = "freely, his arms and legs splayed out to his sides." -5250 ID$ (PORTHOLE, 4 ) = " " -5260 ID$ (PORTHOLE, 5 ) = "Peering at the figure more closely, as he slowly rotates, a light from the ship" -5270 ID$ (PORTHOLE, 6 ) = "briefly illuminates his face. He's clearly dead, his oxygen ran out some time ago." -5280 ID$ (PORTHOLE, 7 ) = "His expression is frozen in a rictus of pain. He was screaming almost until the end ..." -5290 ID$ (PORTHOLE, 8 ) = "" -5300 ID$ (PORTHOLE, 9 ) = "" -5310 ID$ (PORTHOLE, 10 ) = "" -5320 ID$ (PORTHOLE, 11 ) = "" -5330 ID$ (PORTHOLE, 12 ) = "" -5340 ID$ (PORTHOLE, 13 ) = "" -5342 ID$ (PORTHOLE, 14 ) = "" -5344 ID$ (PORTHOLE, 15 ) = "" -5346 ID$ (PORTHOLE, 16 ) = "" -5348 ID$ (PORTHOLE, 17 ) = "" -5350 ID$ (PORTHOLE, 18 ) = "" -5352 ID$ (PORTHOLE, 19 ) = "" -5354 ID$ (PORTHOLE, 20 ) = "" -5356 ID$ (CONSOLE, 1 ) = "The console shows the state of the ship's engines. They are in overdrive. The pilot" -5358 ID$ (CONSOLE, 2 ) = "appears to have the throttle jammed wide open with his right arm. The muscles in his" -5360 ID$ (CONSOLE, 3 ) = "forearm are taught, there's no way he's going to release the throttle. He left hand" -5362 ID$ (CONSOLE, 4 ) = "works its way around the console switches. After a few moments you realise that he is" -5364 ID$ (CONSOLE, 5 ) = "executing the same sequence of switches over and over again." -5366 ID$ (CONSOLE, 6 ) = " " -5368 ID$ (CONSOLE, 7 ) = "You speak to the pilot but he is unresponsive. Mentally he is somewhere else. He is" -5370 ID$ (CONSOLE, 8 ) = "mumbling to himself but you cannot make out the words, although he appears to be" -5372 ID$ (CONSOLE, 9 ) = "reciting some sort of launch checklist." -5374 ID$ (CONSOLE, 10 ) = " " -5376 ID$ (CONSOLE, 11 ) = "One thing is certain, the ship is out of control, careering through space at maximum" -5378 ID$ (CONSOLE, 12 ) = "speed. Rescue will be impossible unless you can find a way to shut down the engines." -5380 ID$ (CONSOLE, 13 ) = "" -5382 ID$ (CONSOLE, 14 ) = "" -5384 ID$ (CONSOLE, 15 ) = "" -5386 ID$ (CONSOLE, 16 ) = "" -5388 ID$ (CONSOLE, 17 ) = "" -5390 ID$ (CONSOLE, 18 ) = "" -5392 ID$ (CONSOLE, 19 ) = "" -5394 ID$ (CONSOLE, 20 ) = "" -5296 ID$ (ENGINE, 1 ) = "The control panel is grubby, smeared with oil and grime. This is clearly" -5298 ID$ (ENGINE, 2 ) = "the engineering heart of the ship. Most of the readouts mean nothing to you," -5300 ID$ (ENGINE, 3 ) = "whatever your duties were on this ship, you were clearly not a warp engineer." -5302 ID$ (ENGINE, 4 ) = " " -5304 ID$ (ENGINE, 5 ) = "Many of the lights on the front panel are flashing red. Not all is well with" -5306 ID$ (ENGINE, 6 ) = "the Pneuma. Even to your untrained eye, it's obvious that the ship's engines appear to" -5308 ID$ (ENGINE, 7 ) = "be on the point of burnout, having been run at full capacity for many hours." -5310 ID$ (ENGINE, 8 ) = " " -5312 ID$ (ENGINE, 9 ) = "To the top left of the panel, a screen reads:" -5314 ID$ (ENGINE, 11 ) = " " -5316 ID$ (ENGINE, 12 ) = "'WARNING: CORE BREACH IMMINENT'" -5318 ID$ (ENGINE, 13 ) = " " -5320 ID$ (ENGINE, 14 ) = "Just below the screen is a large red button, shielded by a cover that can be" -5322 ID$ (ENGINE, 15 ) = "flipped aside." -5324 ID$ (ENGINE, 16 ) = "" -5326 ID$ (ENGINE, 17 ) = "" -5328 ID$ (ENGINE, 18 ) = "" -5330 ID$ (ENGINE, 19 ) = "" -5332 ID$ (ENGINE, 20 ) = "" +5020 REM medical log +5030 DATA "The last few entries of the medical log are still visible on the screen:", " " +5040 DATA "'2142-6-13: Three days since chimpanzee Nova was innoculated with agent #53." +5050 DATA " However, subject Nova showing no signs of adaptation to planetary destination" +5060 DATA " atmosphere. In fact, she appears listless, though punctuated with periods of" +5070 DATA " extreme agression.", " " +5090 DATA " 2142-6-14: This morning, Dr Pearson was bitten by Nova. Very quickly he exhibited" +5100 DATA " signs of a high fever and is now resting in his quarters.", " " +5120 DATA " 2142-6-15: After a brief period of catatonia, Pearson got up on his own, returned" +5130 DATA " to the medical bay and began to compulsively prepare more agent #53 samples. He" +5140 DATA " is otherwise uncommunicative.", " " +5160 DATA " 2142-6-17: Pearson's compulsive behaviour continues unabated. We have a hypothesis" +5170 DATA " for what the virus, which we have now designated HO-1, does to the brain. However," +5180 DATA " we are struggling to isolate Pearson and may have to seal the medical bay.", " " +5200 DATA " 2142-6-17: Hollow (HO-1) confirmed as airborne, and other cases appearing around the ship." +5210 DATA " Shipwide lockdown declared but crew cohesion and discipline already breaking down.'" +5215 REM porthole +5220 DATA "Looking through the porthole, you seen a spacesuit clad figure floating outside." +5230 DATA "Although he is tethered to an anchor point on the ship's hull, he is otherwise floating" +5240 DATA "freely, his arms and legs splayed out to his sides.", " " +5260 DATA "Peering at the figure more closely, as he slowly rotates, a light from the ship" +5270 DATA "briefly illuminates his face. He's clearly dead, his oxygen ran out some time ago." +5280 DATA "His expression is frozen in a rictus of pain. He was screaming almost until the end ..." +5290 DATA "", "", "", "", "", "", "", "", "", "", "", "", "" +5300 REM console +5310 DATA "The console shows the state of the ship's engines. They are in overdrive. The pilot" +5320 DATA "appears to have the throttle jammed wide open with his right arm. The muscles in his" +5340 DATA "forearm are taught, there's no way he's going to release the throttle. He left hand" +5350 DATA "works its way around the console switches. After a few moments you realise that he is" +5360 DATA "executing the same sequence of switches over and over again.", " " +5370 DATA "You speak to the pilot but he is unresponsive. Mentally he is somewhere else. He is" +5380 DATA "mumbling to himself but you cannot make out the words, although he appears to be" +5390 DATA "reciting some sort of launch checklist.", " " +5400 DATA "One thing is certain, the ship is out of control, careering through space at maximum" +5410 DATA "speed. Rescue will be impossible unless you can find a way to shut down the engines." +5420 DATA "", "", "", "", "", "", "", "" +5430 REM engine control +5440 DATA "The control panel is grubby, smeared with oil and grime. This is clearly" +5450 DATA "the engineering heart of the ship. Most of the readouts mean nothing to you," +5460 DATA "whatever your duties were on this ship, you were clearly not a warp engineer.", " " +5470 DATA "Many of the lights on the front panel are flashing red. Not all is well with" +5480 DATA "the Pneuma. Even to your untrained eye, it's obvious that the ship's engines appear to" +5490 DATA "be on the point of burnout, having been run at full capacity for many hours.", " " +5500 DATA "To the top left of the panel, a screen reads:", " " +5510 DATA "'WARNING: CORE BREACH IMMINENT'", " " +5520 DATA "Just below the screen is a large red button, shielded by a cover that can be" +5530 DATA "flipped aside.", "", "", "", "", "", "" +5940 FOR OBJECT = 0 TO IC-1 +5950 FOR I = 1 TO 20 +5960 READ DESC$ : ID$ (OBJECT, I) = DESC$ +5970 NEXT I +5980 NEXT OBJECT 5990 RETURN 6000 REM ========== print interative object description ========== 6010 FOR LINE = 1 TO 20 From bbf6f17052eb5ee8d3c29aa6675720ac7129f219 Mon Sep 17 00:00:00 2001 From: richpl Date: Thu, 26 Dec 2024 18:21:06 +0000 Subject: [PATCH 143/183] Made array indexing consistent Work in progress --- examples/Pneuma.bas | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 2a2a5a1..edb0ba6 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -264,14 +264,14 @@ 3840 DATA "to starboard, as well as a third door leading forward.", "", "" 3860 REM assign to room descriptions 3870 FOR ROOM = 0 to RC-1 -3880 FOR I = 1 TO 5 +3880 FOR I = 0 TO 4 3885 READ DESC$ : RD$ (ROOM, I) = DESC$ 3886 REM PRINT DESC$ 3890 NEXT I 3900 NEXT ROOM 4000 RETURN 4010 REM ========== print room description ========== -4020 FOR LINE = 1 TO 5 +4020 FOR LINE = 0 TO 4 4030 IF RD$(PL, LINE) <> "" THEN PRINT RD$(PL, LINE) 4040 NEXT LINE 4055 GOSUB 4070 @@ -337,13 +337,13 @@ 5520 DATA "Just below the screen is a large red button, shielded by a cover that can be" 5530 DATA "flipped aside.", "", "", "", "", "", "" 5940 FOR OBJECT = 0 TO IC-1 -5950 FOR I = 1 TO 20 +5950 FOR I = 0 TO 19 5960 READ DESC$ : ID$ (OBJECT, I) = DESC$ 5970 NEXT I 5980 NEXT OBJECT 5990 RETURN 6000 REM ========== print interative object description ========== -6010 FOR LINE = 1 TO 20 +6010 FOR LINE = 0 TO 19 6020 IF ID$(F, LINE) <> "" THEN PRINT ID$(F, LINE) 6030 NEXT LINE 6035 PRINT From 25447a915cc6771ddbd355a0528c3ed09d1c7f94 Mon Sep 17 00:00:00 2001 From: richpl Date: Thu, 26 Dec 2024 19:21:20 +0000 Subject: [PATCH 144/183] Added person descriptions Work in progress --- examples/Pneuma.bas | 52 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index edb0ba6..566ce3c 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -32,6 +32,7 @@ 760 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "look" THEN MOVE = 0 : GOSUB 4010 765 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "help" THEN MOVE = 0 : GOSUB 4130 770 IF LOWER$ ( I$ ) = "i" OR LOWER$ ( I$ ) = "inventory" THEN MOVE = 0 : GOSUB 1000 +775 IF LEFT$ ( LOWER$ ( I$ ) , 8 ) = "talk to " THEN MOVE = 0 : GOSUB 2630 780 IF LEFT$ ( LOWER$ ( I$ ) , 1 ) = "q" THEN GOSUB 2600 785 IF LEFT$ ( LOWER$ ( I$ ) , 3 ) = "go " THEN GOSUB 1100 790 IF LOWER$ ( I$ ) = "f" OR LOWER$ ( I$ ) = "forward" THEN GOSUB 1200 @@ -110,6 +111,9 @@ 2600 REM quit command 2610 PRINT "Farewell spacefarer ..." 2620 STOP +2630 REM talk to command +2635 REM **** TBD **** +2695 RETURN 2700 REM ========== set up environment =========== 2705 RC = 18 : REM room count 2710 DIM LO$ ( RC ) @@ -173,15 +177,25 @@ 2966 IL (PORTHOLE) = POD 2968 IL (CONSOLE) = BDG 2970 IL (ENGINE) = ENG -2980 PL = 5 : REM initial player location -2990 RETURN +2972 PC = 3 : REM person count +2974 DIM P$ ( PC ) +2976 CHEF = 0 : P$ ( CHEF ) = "chef" +2978 RUNNER = 1 : P$ ( RUNNER ) = "runner" +2980 PILOT = 2 : P$ ( PILOT ) = "pilot" +2982 REM person locations +2984 DIM PLOC ( PC ) +2986 PLOC ( CHEF ) = GAL +2988 PLOC ( RUNNER ) = GYM +2990 PLOC ( PILOT ) = BDG +2996 PL = 5 : REM initial player location +2998 RETURN 3000 REM ========== room descriptions ========== 3010 DIM RD$ ( RC, 5 ) 3015 REM inventory 3020 DATA "", "", "", "", "" 3025 REM galley 3030 DATA "The galley contains gleaming, stainless steel cupboards along the aft wall. A food" -3040 DATA "preparation surface is on the port wall, currently covered in rotting food. A chef" +3040 DATA "preparation surface is on the port wall, currently covered in rotting food. A *chef*" 3050 DATA "stands at the work surface, methodically chopping food even though everything has" 3060 DATA "already been thoroughly diced. There are doors in the starboard and forward walls and" 3070 DATA "a stairway leads downwards in the far corner." @@ -199,7 +213,7 @@ 3180 DATA "The bridge is the heart of the ship. A vast array of glowing screens and switches fill" 3190 DATA "every surface. On the screens are complex graphics providing detailed information about" 3200 DATA "the status of every system on the ship. Many of them are showing red warning symbols." -3210 DATA "There is a *console* directly in front of you, a pilot gripping the throttle." +3210 DATA "There is a *console* directly in front of you, a *pilot* gripping the throttle." 3220 DATA "An aft exit leads back into the main corridor." 3225 REM sleeping quarters 3230 DATA "The sleeping quarters is filled with bunks, one up, one down. Several of the bunks" @@ -214,10 +228,10 @@ 3310 DATA "one wall. In the corner you can see a medical scanner and next to it, a terminal. On the" 3320 DATA "terminal screen is a portion of the *medical log*. Exits lead port and forward." 3325 REM gymnasium -3330 DATA "The gymnasium is full of exercise equipment. A woman is running furiously on a treadmill." -3340 DATA "She looks exhausted and emaciated, but she keeps running at top speed, almost at a sprint." -3350 DATA "Her eyes remain fixed on the treadmill console. There are aft and port exists, as well as" -3360 DATA "a stairwell leading to the lower deck in the far corner.", "" +3330 DATA "The gymnasium is full of exercise equipment. A female *runner* is sprinting furiously on a" +3340 DATA "treadmill. She looks exhausted and emaciated, but she keeps running at top speed, almost at" +3350 DATA "a sprint. Her eyes remain fixed on the treadmill console. There are aft and port exists," +3360 DATA " as well as a stairwell leading to the lower deck in the far corner.", "" 3370 REM lower aft corridor 3380 DATA "This is a featureless, utilitarian corridor. A stairwell leads upwards. There are also" 3390 DATA "exits leading starboard and forward.", "", "", "" @@ -348,3 +362,25 @@ 6030 NEXT LINE 6035 PRINT 6040 RETURN +7000 REM ========== character speech ========== +7010 REM chef +7015 DIM PD$(PC, 4) +7020 DATA "Carrots, potatoes, I'm going to make a nice beef stew." +7030 DATA "This stew is going to be delicious." +7060 REM runner +7070 DATA "24:50, that's my new personal best. Not much further to go!" +7080 DATA "24:50, that's my new personal best. Not much further to go!" +7110 REM pilot +7120 DATA "Main engines online, supercruise on my mark." +7130 DATA "Five ... four ... three ... two ... one ... mark!" +7140 FOR PERSON = 0 TO PC-1 +7150 FOR I = 0 TO 1 +7160 READ DESC$ : PD$ (PERSON, I) = DESC$ +7170 NEXT I +7180 PD$(PERSON, 2) = PD$(PERSON, 0) +7190 PD$(PERSON, 3) = PD$(PERSON, 1) +7200 NEXT PERSON +7210 RETURN +7500 REM ========== print character speech ========== +7510 REM **** TBD **** + From a93b26685278a9b23bd191e701e4e4116bbfd733 Mon Sep 17 00:00:00 2001 From: richpl Date: Wed, 1 Jan 2025 13:01:32 +0000 Subject: [PATCH 145/183] Completed NPC dialogue, work in progress --- examples/Pneuma.bas | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 566ce3c..8dcacac 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -18,6 +18,8 @@ 510 GOSUB 3000 520 REM setup up interative descriptions 530 GOSUB 5000 +540 REM setup dialogue +550 GOSUB 7000 700 REM ========== main loop ========== 701 REM show room details 703 PRINT "You are in the " ; LO$ ( PL ) : PRINT @@ -112,7 +114,15 @@ 2610 PRINT "Farewell spacefarer ..." 2620 STOP 2630 REM talk to command -2635 REM **** TBD **** +2635 F=-1 : R$ = "" +2640 R$ = MID$(LOWER$(I$), 9) : REM R$ is the person to talk to +2645 FOR I = 0 TO PC-1 +2650 IF P$(I) = R$ THEN F=I : REM person exists +2655 NEXT I +2660 REM can't find them? +2665 IF F=-1 THEN PRINT "You've not met them" : PRINT : GOTO 2695 +2670 IF PLOC(F) <> PL THEN PRINT "They aren't here" : PRINT : GOTO 2695 +2675 GOSUB 7500 : REM print dialogue 2695 RETURN 2700 REM ========== set up environment =========== 2705 RC = 18 : REM room count @@ -382,5 +392,10 @@ 7200 NEXT PERSON 7210 RETURN 7500 REM ========== print character speech ========== -7510 REM **** TBD **** +7510 FOR LINE = 0 TO 4 +7520 PRINT PD$(F, LINE) +7530 NEXT LINE +7550 RETURN + + From d58de49fe785e9a7fbf95c411a32f5928d663cf2 Mon Sep 17 00:00:00 2001 From: Plazma-dot Date: Fri, 3 Jan 2025 09:51:26 -0800 Subject: [PATCH 146/183] Update Banner Updated banner with ASCI art with font: Starwars --- interpreter.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/interpreter.py b/interpreter.py index caddf20..624cf97 100644 --- a/interpreter.py +++ b/interpreter.py @@ -31,15 +31,13 @@ def main(): - banner = ( - """ - PPPP Y Y BBBB AAA SSSS I CCC - P P Y Y B B A A S I C - P P Y Y B B A A S I C - PPPP Y BBBB AAAAA SSSS I C - P Y B B A A S I C - P Y B B A A S I C - P Y BBBB A A SSSS I CCC + banner = (r""" + ._____________ ___________. ___ ___________ ______ + | _ .__ \ / ___ _ \ / \ / | / | + | |_) | \ \/ / | |_) | / ^ \ | (----`| | | ,----' + | ___/ \_ _/ | _ < / /_\ \ \ \ | | | | + | | | | | |_) \/ _____ \----) | | | | `----. + | _| |__| |___________/ \_________/ |____________| """) print(banner) From c28cc32865b90f60e357a49e8a5a4869703413f0 Mon Sep 17 00:00:00 2001 From: richpl Date: Tue, 7 Jan 2025 20:21:23 +0000 Subject: [PATCH 147/183] Added warning for GOSUB gotcha --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index b97583c..a6f17f1 100644 --- a/README.md +++ b/README.md @@ -450,6 +450,19 @@ Subroutines may be nested, that is, a subroutine call may be made within another A subroutine may also be called using the **ON-GOSUB** statement (see Conditional branching below). +Further note that **RETURN** passes control back to the next line numbered statement after the call. Subsequent statements after +the call in the same line will not be executed. For example: + +``` +> 10 PRINT "Print this":GOSUB 100:PRINT "This won't be printed" +> 100 PRINT "Print this too" +> 110 RETURN +> RUN +Print this +Print this too +> +``` + ### Loops Bounded loops are achieved through the use of **FOR-NEXT** statements. The loop is controlled by a numeric From 68992a638575cfb436195fa70dcd2c69b1ade2a2 Mon Sep 17 00:00:00 2001 From: richpl Date: Tue, 7 Jan 2025 20:22:54 +0000 Subject: [PATCH 148/183] Minor correction --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a6f17f1..77937ca 100644 --- a/README.md +++ b/README.md @@ -455,6 +455,7 @@ the call in the same line will not be executed. For example: ``` > 10 PRINT "Print this":GOSUB 100:PRINT "This won't be printed" +> 20 STOP > 100 PRINT "Print this too" > 110 RETURN > RUN From 5aa4990ecc62ae6cac7248eb55056e1b945ac1df Mon Sep 17 00:00:00 2001 From: richpl Date: Tue, 7 Jan 2025 20:25:06 +0000 Subject: [PATCH 149/183] Added two new objects and monster --- examples/Pneuma.bas | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 8dcacac..3631afe 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -24,6 +24,7 @@ 701 REM show room details 703 PRINT "You are in the " ; LO$ ( PL ) : PRINT 705 GOSUB 4010 : REM print room description +706 GOSUB 8000 : REM tracker info if carried 710 INPUT "What now? " ; I$ 715 PRINT 716 MOVE = 1 @@ -164,17 +165,21 @@ 2890 EX$ ( AMC ) = "160001050000" 2895 EX$ ( MMC ) = "171502060000" 2900 EX$ ( FMC ) = "041603070000" -2905 OC = 3 : REM object count +2905 OC = 5 : REM object count 2910 DIM OB$ ( OC ) 2915 PULSE = 0 : OB$ ( PULSE ) = "pulse rifle" 2920 SUIT = 1 : OB$ ( SUIT ) = "space suit" -2925 FOOD = 2 : OB$ ( FOOD ) = "rotting food" +2922 FOOD = 2 : OB$ ( FOOD ) = "rotting food" +2924 TRACKER = 3 : OB$ ( TRACKER ) = "tracker" +2926 SYRINGE = 4 : OB$ ( SYRINGE ) = "syringe" 2930 REM object locations 2932 REM location 0 = player's inventory 2934 DIM OL ( OC ) 2936 OL ( PULSE ) = ARM -2938 OL ( SUIT ) = POD -2940 OL ( FOOD ) = GAL +2937 OL ( SUIT ) = POD +2939 OL ( FOOD ) = GAL +2940 OL ( TRACKER ) = GYM +2941 OL ( SYRINGE ) = MED 2942 IC = 4 : REM interactive object count 2944 DIM IO$ (IC) 2946 MEDLOG = 0 : IO$ ( MEDLOG ) = "medical log" @@ -197,7 +202,8 @@ 2986 PLOC ( CHEF ) = GAL 2988 PLOC ( RUNNER ) = GYM 2990 PLOC ( PILOT ) = BDG -2996 PL = 5 : REM initial player location +2996 PL = SLP : REM initial player location +2997 WPL = MEN : REM wraith-hound initial location 2998 RETURN 3000 REM ========== room descriptions ========== 3010 DIM RD$ ( RC, 5 ) @@ -396,6 +402,26 @@ 7520 PRINT PD$(F, LINE) 7530 NEXT LINE 7550 RETURN +8000 REM ========== wraith-hound tracking ========== +8010 IF OL ( TRACKER ) <> INV THEN GOTO 8180 +8030 IF WPL = GAL THEN LOC$ = "Galley" +8040 IF WPL = SLP THEN LOC$ = "Sleeping Quarters" +8050 IF WPL = REC THEN LOC$ = "Recreation Room" +8060 IF WPL = ARM THEN LOC$ = "Armoury" +8070 IF WPL = MED THEN LOC$ = "Medical Centre" +8080 IF WPL = BDG THEN LOC$ = "Bridge" +8090 IF WPL = STO THEN LOC$ = "Stores" +8100 IF WPL = ENG THEN LOC$ = "Engine Room" +8110 IF WPL = MEN THEN LOC$ = "Menagerie" +8120 IF WPL = LAB THEN LOC$ = "Laboratory" +8130 IF WPL = POD THEN LOC$ = "Pod Bay" +8140 IF WPL = AMC OR WPL = MMC OR WPL = FMC THEN LOC$ = "upper deck main corridor" +8150 IF WPL = LAC THEN LOC$ = "Lower deck aft corridor" +8160 IF WPL = LFC THEN LOC$ = "Lower deck forward corridor" +8165 PRINT "Tracking: Wraith-hound current location is the "; LOC$ +8170 PRINT +8180 RETURN + From 3938f28ef378a981f660fb9697967683096cf61d Mon Sep 17 00:00:00 2001 From: richpl Date: Tue, 7 Jan 2025 22:05:09 +0000 Subject: [PATCH 150/183] Code optimisation --- examples/Pneuma.bas | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 3631afe..eb27ac8 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -403,24 +403,10 @@ 7530 NEXT LINE 7550 RETURN 8000 REM ========== wraith-hound tracking ========== -8010 IF OL ( TRACKER ) <> INV THEN GOTO 8180 -8030 IF WPL = GAL THEN LOC$ = "Galley" -8040 IF WPL = SLP THEN LOC$ = "Sleeping Quarters" -8050 IF WPL = REC THEN LOC$ = "Recreation Room" -8060 IF WPL = ARM THEN LOC$ = "Armoury" -8070 IF WPL = MED THEN LOC$ = "Medical Centre" -8080 IF WPL = BDG THEN LOC$ = "Bridge" -8090 IF WPL = STO THEN LOC$ = "Stores" -8100 IF WPL = ENG THEN LOC$ = "Engine Room" -8110 IF WPL = MEN THEN LOC$ = "Menagerie" -8120 IF WPL = LAB THEN LOC$ = "Laboratory" -8130 IF WPL = POD THEN LOC$ = "Pod Bay" -8140 IF WPL = AMC OR WPL = MMC OR WPL = FMC THEN LOC$ = "upper deck main corridor" -8150 IF WPL = LAC THEN LOC$ = "Lower deck aft corridor" -8160 IF WPL = LFC THEN LOC$ = "Lower deck forward corridor" -8165 PRINT "Tracking: Wraith-hound current location is the "; LOC$ -8170 PRINT -8180 RETURN +8010 IF OL ( TRACKER ) <> INV THEN GOTO 8040 +8020 PRINT "Tracking: Wraith-hound current location is the "; LO$ ( WPL ) +8030 PRINT +8040 RETURN From aa4d4e7df92f49883244096efce9653199a3b7c3 Mon Sep 17 00:00:00 2001 From: richpl Date: Wed, 8 Jan 2025 22:21:19 +0000 Subject: [PATCH 151/183] Added creature proximity calculation --- examples/Pneuma.bas | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index eb27ac8..f05f31e 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -12,6 +12,7 @@ 80 PRINT "You have a sore throat and the mother of all headaches, like your brain has been boiling" 85 PRINT "in your skull. In fact, you're no longer sure exactly where you are ... you seem to be" 90 PRINT "suffering from some sort of amnesia ...": PRINT +92 PRINT "THIS GAME IS UNFINISHED AND IS A WORK IN PROGRESS" : PRINT 95 REM set up environment 100 GOSUB 2700 500 REM setup room descriptions @@ -25,6 +26,7 @@ 703 PRINT "You are in the " ; LO$ ( PL ) : PRINT 705 GOSUB 4010 : REM print room description 706 GOSUB 8000 : REM tracker info if carried +708 GOSUB 8200 : REM wraith-hound proximity check 710 INPUT "What now? " ; I$ 715 PRINT 716 MOVE = 1 @@ -264,8 +266,9 @@ 3525 REM menagerie 3530 DATA "The room is a hellhole. Cages stand open, while various animals roam about: chimpanzees," 3540 DATA "dogs, and rats. Some of the rats are dead, having been savaged and eviscerated. The floor" -3550 DATA "and walls are smeared with animal faeces, and the smell is almost overpowering. A single" -3555 DATA "port door leads back into the storeroom.", "" +3550 DATA "and walls are smeared with animal faeces, and the smell is almost overpowering. A capsule," +3552 DATA "its door ajar, is marked 'BIOWEAPON CONTAINMENT'. The capsule is empty." +3555 DATA "A single door to port leads back into the storeroom." 3560 REM laboratory 3570 DATA "The laboratory is full of scientific equipment, chemical glassware, electronic analysers," 3580 DATA "fume cupboards, and two couches. The place looks disorded, like the rest of the ship, the" @@ -296,7 +299,6 @@ 3870 FOR ROOM = 0 to RC-1 3880 FOR I = 0 TO 4 3885 READ DESC$ : RD$ (ROOM, I) = DESC$ -3886 REM PRINT DESC$ 3890 NEXT I 3900 NEXT ROOM 4000 RETURN @@ -316,6 +318,7 @@ 4140 PRINT "For movement, try [go] a[ft], f[orward], p[ort], s[tarboard], u[p] or d[own]." 4150 PRINT "For actions, try get, take, drop, examine, look, i[nventory], q[uit]." 4155 PRINT "To examine, pick up or drop items, refer to them exactly as they are printed." +4157 PRINT 4160 RETURN 5000 REM ========== interactive item descriptions ========== 5010 DIM ID$(IC, 20) @@ -401,12 +404,27 @@ 7510 FOR LINE = 0 TO 4 7520 PRINT PD$(F, LINE) 7530 NEXT LINE +7535 PRINT 7550 RETURN 8000 REM ========== wraith-hound tracking ========== 8010 IF OL ( TRACKER ) <> INV THEN GOTO 8040 8020 PRINT "Tracking: Wraith-hound current location is the "; LO$ ( WPL ) 8030 PRINT 8040 RETURN +8200 REM ========== wraith-hound proximity check ========== +8210 DIR$ = "" +8220 IF VAL ( MID$ ( EX$ ( PL ) , 1 , 2 ) ) = WPL THEN DIR$ = "forward of you" +8230 IF VAL ( MID$ ( EX$ ( PL ) , 3 , 2 ) ) = WPL THEN DIR$ = "aft of you" +8240 IF VAL ( MID$ ( EX$ ( PL ) , 5 , 2 ) ) = WPL THEN DIR$ = "to port" +8250 IF VAL ( MID$ ( EX$ ( PL ) , 7 , 2 ) ) = WPL THEN DIR$ = "to starboard" +8260 IF VAL ( MID$ ( EX$ ( PL ) , 9 , 2 ) ) = WPL THEN DIR$ = "above you" +8270 IF VAL ( MID$ ( EX$ ( PL ) , 11 , 2 ) ) = WPL THEN DIR$ = "below you" +8280 IF DIR$ <> "" THEN PRINT "You hear a snarling, spitting noise "; DIR$ +8285 PRINT +8290 RETURN + + + From e264aa73258bbc1d4208f0a99c2edc76c9ec5ddc Mon Sep 17 00:00:00 2001 From: richpl Date: Fri, 10 Jan 2025 18:14:59 +0000 Subject: [PATCH 152/183] Added creature movement --- examples/Pneuma.bas | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index f05f31e..7774868 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -27,6 +27,7 @@ 705 GOSUB 4010 : REM print room description 706 GOSUB 8000 : REM tracker info if carried 708 GOSUB 8200 : REM wraith-hound proximity check +709 GOSUB 8400 : REM wraith-hound movement 710 INPUT "What now? " ; I$ 715 PRINT 716 MOVE = 1 @@ -419,9 +420,25 @@ 8250 IF VAL ( MID$ ( EX$ ( PL ) , 7 , 2 ) ) = WPL THEN DIR$ = "to starboard" 8260 IF VAL ( MID$ ( EX$ ( PL ) , 9 , 2 ) ) = WPL THEN DIR$ = "above you" 8270 IF VAL ( MID$ ( EX$ ( PL ) , 11 , 2 ) ) = WPL THEN DIR$ = "below you" -8280 IF DIR$ <> "" THEN PRINT "You hear a snarling, spitting noise "; DIR$ +8280 IF DIR$ <> "" THEN PRINT "You hear a snarling, spitting noise "; DIR$; ". The tracker is pinging ..." 8285 PRINT 8290 RETURN +8400 REM ========== wraith-hound movement ========== +8410 REM decide to move one third of the time +8420 MOVE = RNDINT(1, 3) +8430 IF MOVE <> 1 THEN GOTO 8550 +8440 REM randomly determine direction +8450 DIR = RNDINT(1, 6) +8460 IF DIR = 1 THEN INDEX = 1 +8470 IF DIR = 2 THEN INDEX = 3 +8480 IF DIR = 3 THEN INDEX = 5 +8490 IF DIR = 4 THEN INDEX = 7 +8500 IF DIR = 5 THEN INDEX = 9 +8510 IF DIR = 6 THEN INDEX = 11 +8520 NWPL = VAL ( MID$ ( EX$ ( WPL ) , INDEX , 2 ) ) : REM potential next location +8530 IF NWPL = 0 THEN GOTO 8450 : REM can't go that way +8540 WPL = NWPL +8550 RETURN From f22b5e3ee5eb5be586a6d3c7880b4b785d080424 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 21 Jan 2025 03:43:28 -0500 Subject: [PATCH 153/183] count linux/windows newline chars properly when opening files for append --- basicparser.py | 21 ++++++++++++++++++++- examples/regression.bas | 18 ++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/basicparser.py b/basicparser.py index d96121f..5187e0f 100644 --- a/basicparser.py +++ b/basicparser.py @@ -673,10 +673,29 @@ def __openstmt(self): raise RuntimeError('File '+filename+' could not be opened in line ' + str(self.__line_number)) if accessMode == "r+": + # By checking the 'newlines' attribute the appropriate adjustment for + # the file being opened can be determined. + if hasattr(self.__file_handles[filenum],'newlines'): + try: + # newline attribute is only set after a line is read + self.__file_handles[filenum].readline() + except: + pass + newlines = self.__file_handles[filenum].newlines + else: + newlines = None + self.__file_handles[filenum].seek(0) filelen = 0 + if newlines != None: + newlineAdj = len(newlines) - 1 + else: + # If using version of Python that doesn't have newlines attribute + # use adjustment appropriate for Windows formatted files + newlineAdj = 1 + for lines in self.__file_handles[filenum]: - filelen += len(lines)+1 + filelen += len(lines)+newlineAdj self.__file_handles[filenum].seek(filelen) diff --git a/examples/regression.bas b/examples/regression.bas index 554d08d..14ff75e 100644 --- a/examples/regression.bas +++ b/examples/regression.bas @@ -57,8 +57,22 @@ 570 PRINT "The next line should say 'Hello World!'" 580 FSEEK #2,10 590 INPUT #2,A$ -600 PRINT A$ -610 OPEN "NOFILE.X7Z" FOR INPUT AS #2 ELSE 620 +595 PRINT A$ +600 CLOSE #2 +601 OPEN "REGRESSION.TXT" FOR APPEND AS #2 +602 PRINT "3 text lines starting with 0,T,S and ending with !,g,e should follow:" +603 PRINT #2,"Should be a seperate line" +604 CLOSE #2 +605 OPEN "REGRESSION.TXT" FOR INPUT AS #2 +606 FOR I = 1 TO 2 +607 INPUT #2,A$:PRINT A$ +608 NEXT I +609 INPUT #2,A$ +610 FOR I = 1 TO 25 +611 PRINT MID$(A$,I,1); +612 NEXT I +613 PRINT:CLOSE #2 +614 OPEN "NOFILE.X7Z" FOR INPUT AS #2 ELSE 620 615 PRINT "***This Message should NOT be Displayed***" 620 N = 0 630 I = 7 From 996555861e710e92e7a77f1d58650f3b765933e2 Mon Sep 17 00:00:00 2001 From: richpl Date: Thu, 23 Jan 2025 21:56:13 +0000 Subject: [PATCH 154/183] Added use command --- examples/Pneuma.bas | 63 +++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 7774868..abc29fd 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -37,6 +37,7 @@ 750 IF LEFT$ ( LOWER$ ( I$ ) , 8 ) = "examine " THEN MOVE = 0 : GOSUB 2300 760 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "look" THEN MOVE = 0 : GOSUB 4010 765 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "help" THEN MOVE = 0 : GOSUB 4130 +767 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "use " THEN MOVE = 0 : GOSUB 9000 770 IF LOWER$ ( I$ ) = "i" OR LOWER$ ( I$ ) = "inventory" THEN MOVE = 0 : GOSUB 1000 775 IF LEFT$ ( LOWER$ ( I$ ) , 8 ) = "talk to " THEN MOVE = 0 : GOSUB 2630 780 IF LEFT$ ( LOWER$ ( I$ ) , 1 ) = "q" THEN GOSUB 2600 @@ -82,11 +83,11 @@ 1440 IF OB$(I) = R$ THEN F=I : REM object exists 1450 NEXT I 1460 REM can't find the item? -1470 IF F=-1 THEN PRINT "Can't see that item here" : PRINT : GOTO 1540 -1480 IF OL(F) <> PL THEN PRINT "That item doesn't appear to be around here" : PRINT : GOTO 1540 -1490 IF OL(F)=0 THEN PRINT "You already have that item" : PRINT: GOTO 1540 +1470 IF F=-1 THEN PRINT "Can't see that item here." : PRINT : GOTO 1540 +1480 IF OL(F) <> PL THEN PRINT "That item doesn't appear to be around here." : PRINT : GOTO 1540 +1490 IF OL(F)=0 THEN PRINT "You already have that item." : PRINT: GOTO 1540 1520 OL(F)=0 : REM add the item to the inventory -1530 PRINT "You've picked up ";OB$(F) : PRINT +1530 PRINT "You've picked up ";OB$(F); "." : PRINT 1540 RETURN 1700 REM take command 1710 F=-1: R$="" @@ -99,9 +100,9 @@ 2040 IF OB$(I) = R$ THEN F=I : REM object exists 2050 NEXT I 2060 REM can't find it? -2070 IF F=-1 THEN PRINT "You've never seen that" : PRINT: GOTO 1540 -2080 IF OL(F) <> 0 THEN PRINT "You aren't carrying that" : PRINT: GOTO 2110 -2090 OL(F) = PL : PRINT "You've dropped ";OB$(F): PRINT: REM add the item to the current room +2070 IF F=-1 THEN PRINT "You don't have that." : PRINT: GOTO 1540 +2080 IF OL(F) <> 0 THEN PRINT "You aren't carrying that." : PRINT: GOTO 2110 +2090 OL(F) = PL : PRINT "You've dropped ";OB$(F); ".": PRINT: REM add the item to the current room 2110 RETURN 2300 REM examine command 2310 F=-1 : R$="" @@ -110,8 +111,8 @@ 2340 IF IO$(I) = R$ THEN F=I : REM object exists 2350 NEXT I 2360 REM can't find it? -2370 IF F=-1 THEN PRINT "You can't examine that" : PRINT : GOTO 2400 -2380 IF IL(F) <> PL THEN PRINT "There isn't one of these here" : PRINT : GOTO 2400 +2370 IF F=-1 THEN PRINT "You can't examine that." : PRINT : GOTO 2400 +2380 IF IL(F) <> PL THEN PRINT "There isn't one of these here." : PRINT : GOTO 2400 2390 GOSUB 6000 : REM print result of examination 2400 RETURN 2600 REM quit command @@ -124,8 +125,8 @@ 2650 IF P$(I) = R$ THEN F=I : REM person exists 2655 NEXT I 2660 REM can't find them? -2665 IF F=-1 THEN PRINT "You've not met them" : PRINT : GOTO 2695 -2670 IF PLOC(F) <> PL THEN PRINT "They aren't here" : PRINT : GOTO 2695 +2665 IF F=-1 THEN PRINT "You've not met them." : PRINT : GOTO 2695 +2670 IF PLOC(F) <> PL THEN PRINT "They aren't here." : PRINT : GOTO 2695 2675 GOSUB 7500 : REM print dialogue 2695 RETURN 2700 REM ========== set up environment =========== @@ -214,7 +215,7 @@ 3020 DATA "", "", "", "", "" 3025 REM galley 3030 DATA "The galley contains gleaming, stainless steel cupboards along the aft wall. A food" -3040 DATA "preparation surface is on the port wall, currently covered in rotting food. A *chef*" +3040 DATA "preparation surface is on the port wall, currently covered in rotting food. A chef" 3050 DATA "stands at the work surface, methodically chopping food even though everything has" 3060 DATA "already been thoroughly diced. There are doors in the starboard and forward walls and" 3070 DATA "a stairway leads downwards in the far corner." @@ -232,7 +233,7 @@ 3180 DATA "The bridge is the heart of the ship. A vast array of glowing screens and switches fill" 3190 DATA "every surface. On the screens are complex graphics providing detailed information about" 3200 DATA "the status of every system on the ship. Many of them are showing red warning symbols." -3210 DATA "There is a *console* directly in front of you, a *pilot* gripping the throttle." +3210 DATA "There is a console directly in front of you, a pilot gripping the throttle." 3220 DATA "An aft exit leads back into the main corridor." 3225 REM sleeping quarters 3230 DATA "The sleeping quarters is filled with bunks, one up, one down. Several of the bunks" @@ -245,9 +246,9 @@ 3290 DATA "lying around the room. Blood filled syringes are scattered on a workbench, as well as" 3300 DATA "some bloodied bandanges. The words 'I'm losing myself' are scrawled messily in blood on" 3310 DATA "one wall. In the corner you can see a medical scanner and next to it, a terminal. On the" -3320 DATA "terminal screen is a portion of the *medical log*. Exits lead port and forward." +3320 DATA "terminal screen is a portion of the medical log. Exits lead port and forward." 3325 REM gymnasium -3330 DATA "The gymnasium is full of exercise equipment. A female *runner* is sprinting furiously on a" +3330 DATA "The gymnasium is full of exercise equipment. A female runner is sprinting furiously on a" 3340 DATA "treadmill. She looks exhausted and emaciated, but she keeps running at top speed, almost at" 3350 DATA "a sprint. Her eyes remain fixed on the treadmill console. There are aft and port exists," 3360 DATA " as well as a stairwell leading to the lower deck in the far corner.", "" @@ -256,7 +257,7 @@ 3390 DATA "exits leading starboard and forward.", "", "", "" 3400 REM engine room 3430 DATA "The engine room is characterised by a continual rumble, as though incredible energies are" -3440 DATA "barely being contained. There is an *engine control* in the far corner, festooned with" +3440 DATA "barely being contained. There is an engine control in the far corner, festooned with" 3450 DATA "switches and engine readouts. A single exit leads out into the corridor.", "", "" 3460 REM storeroom 3480 DATA "The storeroom is full of crates, most neatly stacked, but with some scattered across the" @@ -283,7 +284,7 @@ 3670 DATA "You are in a large room, with a row of spacesuits hanging on the port wall. At the forward" 3680 DATA "end of the room is a small, two seater vehicle, capable of operating in space outside the" 3690 DATA "main ship for limited periods. In front of the small ship is the pod bay door, leading out" -3700 DATA "into space. There is a *porthole* on the far wall. There is a single exit leading aft.", "" +3700 DATA "into space. There is a porthole on the far wall. There is a single exit leading aft.", "" 3710 REM aft main corridor 3720 DATA "The main corridor stretches away from you towards the front of the ship. It is featureless" 3730 DATA "and utilitarian. The lighting is dim. You can see doors either side of you, to port and" @@ -311,14 +312,14 @@ 4060 RETURN 4070 REM print objects 4080 FOR I = 0 TO OC-1 -4090 IF OL ( I ) = PL THEN PRINT : PRINT "You can see: ";OB$ ( I ) +4090 IF OL ( I ) = PL THEN PRINT : PRINT "You can see: ";OB$ ( I ); "." 4100 NEXT I 4110 PRINT 4120 RETURN 4130 REM ========== print help ========== 4140 PRINT "For movement, try [go] a[ft], f[orward], p[ort], s[tarboard], u[p] or d[own]." -4150 PRINT "For actions, try get, take, drop, examine, look, i[nventory], q[uit]." -4155 PRINT "To examine, pick up or drop items, refer to them exactly as they are printed." +4150 PRINT "For actions, try get, take, drop, examine, look, use, i[nventory], q[uit]." +4155 PRINT "To examine, pick up, use or drop items, refer to them exactly as they are described." 4157 PRINT 4160 RETURN 5000 REM ========== interactive item descriptions ========== @@ -439,6 +440,28 @@ 8530 IF NWPL = 0 THEN GOTO 8450 : REM can't go that way 8540 WPL = NWPL 8550 RETURN +9000 REM ========== use an item ========== +9010 F=-1: R$="" +9020 R$ = MID$(LOWER$(I$), 5) : REM R$ is the requested object +9025 IF R$ = "button" THEN GOSUB 9500 +9030 REM get the object ID +9040 FOR I= 0 TO OC-1 +9050 IF OB$(I) = R$ THEN F=I : REM object exists +9060 NEXT I +9070 REM can't find the item? +9080 IF F=-1 THEN PRINT "You haven't seen that item." : PRINT : GOTO 9400 +9090 IF OL(F) <> 0 THEN PRINT "You haven't picked up that item." : PRINT : GOTO 9400 +9100 REM item exists and is in inventory +9110 IF F <> SYRINGE THEN GOTO 9140 +9120 OB$( SYRINGE ) = "blood filled syringe" +9130 PRINT "You use the syringe to take a blood sample from your arm." : PRINT +9140 IF F <> ( TRACKER ) THEN GOTO 9400 : REM ** change ** +9150 PRINT "The tracker is always on." : PRINT +9400 RETURN +9500 REM ========== special button routine ========== +9600 RETURN +10000 REM TODO - wraith-hound fight, use rotten food, use rifle, +10010 REM TODO - use space suit, engine shutdown, scientist conversation From bf460a28b4999edce1bc555599f9185b5d2e6cf1 Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 1 Feb 2025 15:42:56 +0000 Subject: [PATCH 155/183] Added use of space suit --- examples/Pneuma.bas | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index abc29fd..2ebba49 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -103,6 +103,7 @@ 2070 IF F=-1 THEN PRINT "You don't have that." : PRINT: GOTO 1540 2080 IF OL(F) <> 0 THEN PRINT "You aren't carrying that." : PRINT: GOTO 2110 2090 OL(F) = PL : PRINT "You've dropped ";OB$(F); ".": PRINT: REM add the item to the current room +2095 IF F = SUIT THEN SUIT_WORN = 0 2110 RETURN 2300 REM examine command 2310 F=-1 : R$="" @@ -208,7 +209,8 @@ 2990 PLOC ( PILOT ) = BDG 2996 PL = SLP : REM initial player location 2997 WPL = MEN : REM wraith-hound initial location -2998 RETURN +2998 SUIT_WORN = 0 : REM is space suit worn? +2999 RETURN 3000 REM ========== room descriptions ========== 3010 DIM RD$ ( RC, 5 ) 3015 REM inventory @@ -453,10 +455,16 @@ 9090 IF OL(F) <> 0 THEN PRINT "You haven't picked up that item." : PRINT : GOTO 9400 9100 REM item exists and is in inventory 9110 IF F <> SYRINGE THEN GOTO 9140 +9112 IF OB$( SYRINGE ) = "blood filled syringe" THEN PRINT "The syringe is full." : PRINT : GOTO 9400 +9115 IF SUIT_WORN = 1 THEN PRINT "You can't puncture a spacesuit with a syringe!" : PRINT : GOTO 9400 9120 OB$( SYRINGE ) = "blood filled syringe" -9130 PRINT "You use the syringe to take a blood sample from your arm." : PRINT -9140 IF F <> ( TRACKER ) THEN GOTO 9400 : REM ** change ** +9130 PRINT "You use the syringe to take a blood sample from your arm." : PRINT : GOTO 9400 +9140 IF F <> TRACKER THEN GOTO 9160 9150 PRINT "The tracker is always on." : PRINT +9160 IF F <> SUIT THEN GOTO 9400 +9170 IF OL(F) <> 0 THEN PRINT "You don't have a space suit." : PRINT : GOTO 9400 +9180 SUIT_WORN = 1 +9190 PRINT "You put on the space suit." : PRINT 9400 RETURN 9500 REM ========== special button routine ========== 9600 RETURN From 24d6d3a239bed6630b33e677282ab3d292d43d4d Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 2 Feb 2025 19:44:02 +0000 Subject: [PATCH 156/183] Added engine shutdown routine --- examples/Pneuma.bas | 78 ++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 2ebba49..4aaa1a6 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -83,9 +83,9 @@ 1440 IF OB$(I) = R$ THEN F=I : REM object exists 1450 NEXT I 1460 REM can't find the item? -1470 IF F=-1 THEN PRINT "Can't see that item here." : PRINT : GOTO 1540 -1480 IF OL(F) <> PL THEN PRINT "That item doesn't appear to be around here." : PRINT : GOTO 1540 -1490 IF OL(F)=0 THEN PRINT "You already have that item." : PRINT: GOTO 1540 +1470 IF F=-1 THEN PRINT "You can't take that." : PRINT : RETURN +1480 IF OL(F) <> PL THEN PRINT "That item doesn't appear to be around here." : PRINT : RETURN +1490 IF OL(F)=0 THEN PRINT "You already have that item." : PRINT: RETURN 1520 OL(F)=0 : REM add the item to the inventory 1530 PRINT "You've picked up ";OB$(F); "." : PRINT 1540 RETURN @@ -100,8 +100,8 @@ 2040 IF OB$(I) = R$ THEN F=I : REM object exists 2050 NEXT I 2060 REM can't find it? -2070 IF F=-1 THEN PRINT "You don't have that." : PRINT: GOTO 1540 -2080 IF OL(F) <> 0 THEN PRINT "You aren't carrying that." : PRINT: GOTO 2110 +2070 IF F=-1 THEN PRINT "You don't have that." : PRINT: RETURN +2080 IF OL(F) <> 0 THEN PRINT "You aren't carrying that." : PRINT: RETURN 2090 OL(F) = PL : PRINT "You've dropped ";OB$(F); ".": PRINT: REM add the item to the current room 2095 IF F = SUIT THEN SUIT_WORN = 0 2110 RETURN @@ -112,8 +112,8 @@ 2340 IF IO$(I) = R$ THEN F=I : REM object exists 2350 NEXT I 2360 REM can't find it? -2370 IF F=-1 THEN PRINT "You can't examine that." : PRINT : GOTO 2400 -2380 IF IL(F) <> PL THEN PRINT "There isn't one of these here." : PRINT : GOTO 2400 +2370 IF F=-1 THEN PRINT "You can't examine that." : PRINT : RETURN +2380 IF IL(F) <> PL THEN PRINT "There isn't one of these here." : PRINT : RETURN 2390 GOSUB 6000 : REM print result of examination 2400 RETURN 2600 REM quit command @@ -126,8 +126,8 @@ 2650 IF P$(I) = R$ THEN F=I : REM person exists 2655 NEXT I 2660 REM can't find them? -2665 IF F=-1 THEN PRINT "You've not met them." : PRINT : GOTO 2695 -2670 IF PLOC(F) <> PL THEN PRINT "They aren't here." : PRINT : GOTO 2695 +2665 IF F=-1 THEN PRINT "You've not met them." : PRINT : RETURN +2670 IF PLOC(F) <> PL THEN PRINT "They aren't here." : PRINT : RETURN 2675 GOSUB 7500 : REM print dialogue 2695 RETURN 2700 REM ========== set up environment =========== @@ -179,7 +179,7 @@ 2926 SYRINGE = 4 : OB$ ( SYRINGE ) = "syringe" 2930 REM object locations 2932 REM location 0 = player's inventory -2934 DIM OL ( OC ) +2934 DIM OL ( OC ) 2936 OL ( PULSE ) = ARM 2937 OL ( SUIT ) = POD 2939 OL ( FOOD ) = GAL @@ -190,7 +190,7 @@ 2946 MEDLOG = 0 : IO$ ( MEDLOG ) = "medical log" 2948 PORTHOLE = 1 : IO$ (PORTHOLE) = "porthole" 2950 CONSOLE = 2 : IO$(CONSOLE) = "console" -2952 ENGINE = 3 :IO$(ENGINE)= "engine control" +2952 ENGINE = 3 : IO$(ENGINE)= "engine control" 2960 REM interative object locations 2962 DIM IL ( IC ) 2964 IL ( MEDLOG) = MED @@ -202,14 +202,15 @@ 2976 CHEF = 0 : P$ ( CHEF ) = "chef" 2978 RUNNER = 1 : P$ ( RUNNER ) = "runner" 2980 PILOT = 2 : P$ ( PILOT ) = "pilot" -2982 REM person locations -2984 DIM PLOC ( PC ) -2986 PLOC ( CHEF ) = GAL -2988 PLOC ( RUNNER ) = GYM -2990 PLOC ( PILOT ) = BDG -2996 PL = SLP : REM initial player location -2997 WPL = MEN : REM wraith-hound initial location -2998 SUIT_WORN = 0 : REM is space suit worn? +2981 REM person locations +2983 DIM PLOC ( PC ) +2985 PLOC ( CHEF ) = GAL +2987 PLOC ( RUNNER ) = GYM +2989 PLOC ( PILOT ) = BDG +2990 PL = SLP : REM initial player location +2992 WPL = MEN : REM wraith-hound initial location +2994 SUIT_WORN = 0 : REM is space suit worn? +2995 SHUTDOWN = 0 : REM are engines shut down? 2999 RETURN 3000 REM ========== room descriptions ========== 3010 DIM RD$ ( RC, 5 ) @@ -356,12 +357,16 @@ 5340 DATA "forearm are taught, there's no way he's going to release the throttle. He left hand" 5350 DATA "works its way around the console switches. After a few moments you realise that he is" 5360 DATA "executing the same sequence of switches over and over again.", " " -5370 DATA "You speak to the pilot but he is unresponsive. Mentally he is somewhere else. He is" +5370 DATA "His eyes are glazed, mentally he is somewhere else. He is" 5380 DATA "mumbling to himself but you cannot make out the words, although he appears to be" 5390 DATA "reciting some sort of launch checklist.", " " +5392 DATA "One screen shows the mission parameters:" +5394 DATA " 1. Explore potentially habitable worlds" +5396 DATA " 2. Conduct research to develop gene therapies to aid adaptation to planetary conditions" +5398 DATA " 3. Conduct research to develop custom biologic weapons systems, to protect colonists", " " 5400 DATA "One thing is certain, the ship is out of control, careering through space at maximum" 5410 DATA "speed. Rescue will be impossible unless you can find a way to shut down the engines." -5420 DATA "", "", "", "", "", "", "", "" +5420 DATA "", "", "" 5430 REM engine control 5440 DATA "The control panel is grubby, smeared with oil and grime. This is clearly" 5450 DATA "the engineering heart of the ship. Most of the readouts mean nothing to you," @@ -445,31 +450,40 @@ 9000 REM ========== use an item ========== 9010 F=-1: R$="" 9020 R$ = MID$(LOWER$(I$), 5) : REM R$ is the requested object -9025 IF R$ = "button" THEN GOSUB 9500 +9025 IF R$ <> "red button" THEN GOTO 9030 +9027 GOSUB 9500 +9028 RETURN 9030 REM get the object ID 9040 FOR I= 0 TO OC-1 9050 IF OB$(I) = R$ THEN F=I : REM object exists 9060 NEXT I 9070 REM can't find the item? -9080 IF F=-1 THEN PRINT "You haven't seen that item." : PRINT : GOTO 9400 -9090 IF OL(F) <> 0 THEN PRINT "You haven't picked up that item." : PRINT : GOTO 9400 +9080 IF F=-1 THEN PRINT "You haven't seen that item." : PRINT : RETURN +9090 IF OL(F) <> 0 THEN PRINT "You haven't picked up that item." : PRINT : RETURN 9100 REM item exists and is in inventory 9110 IF F <> SYRINGE THEN GOTO 9140 -9112 IF OB$( SYRINGE ) = "blood filled syringe" THEN PRINT "The syringe is full." : PRINT : GOTO 9400 -9115 IF SUIT_WORN = 1 THEN PRINT "You can't puncture a spacesuit with a syringe!" : PRINT : GOTO 9400 +9112 IF OB$( SYRINGE ) = "blood filled syringe" THEN PRINT "The syringe is full." : PRINT : RETURN +9115 IF SUIT_WORN = 1 THEN PRINT "You can't puncture a spacesuit with a syringe!" : PRINT : RETURN 9120 OB$( SYRINGE ) = "blood filled syringe" -9130 PRINT "You use the syringe to take a blood sample from your arm." : PRINT : GOTO 9400 +9130 PRINT "You use the syringe to take a blood sample from your arm." : PRINT : RETURN 9140 IF F <> TRACKER THEN GOTO 9160 9150 PRINT "The tracker is always on." : PRINT -9160 IF F <> SUIT THEN GOTO 9400 -9170 IF OL(F) <> 0 THEN PRINT "You don't have a space suit." : PRINT : GOTO 9400 +9160 IF F <> SUIT THEN GOTO 9200 +9170 IF OL(F) <> 0 THEN PRINT "You don't have a space suit." : PRINT : RETURN +9175 IF SUIT_WORN = 1 THEN PRINT "You're already wearing the space suit." : PRINT : RETURN 9180 SUIT_WORN = 1 9190 PRINT "You put on the space suit." : PRINT 9400 RETURN -9500 REM ========== special button routine ========== -9600 RETURN +9500 REM special button routine +9510 IF PL <> ENG THEN PRINT "There is no red button here." : PRINT : RETURN +9520 IF SHUTDOWN = 0 THEN GOTO 9540 +9525 SHUTDOWN = 0 +9530 PRINT "Engine restart initiated ... engines online." : PRINT : RETURN +9540 SHUTDOWN = 1 +9550 PRINT "Engine shutdown initiated ... engines offline." : PRINT +9560 RETURN 10000 REM TODO - wraith-hound fight, use rotten food, use rifle, -10010 REM TODO - use space suit, engine shutdown, scientist conversation +10010 REM TODO - scientist conversation From f875df41374cda05cc5af187a0f45d835694c440 Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 15 Feb 2025 11:38:03 +0000 Subject: [PATCH 157/183] Added finale --- examples/Pneuma.bas | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 4aaa1a6..c649d73 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -318,6 +318,7 @@ 4090 IF OL ( I ) = PL THEN PRINT : PRINT "You can see: ";OB$ ( I ); "." 4100 NEXT I 4110 PRINT +4115 IF PL = LAB THEN GOSUB 9700 4120 RETURN 4130 REM ========== print help ========== 4140 PRINT "For movement, try [go] a[ft], f[orward], p[ort], s[tarboard], u[p] or d[own]." @@ -352,7 +353,7 @@ 5280 DATA "His expression is frozen in a rictus of pain. He was screaming almost until the end ..." 5290 DATA "", "", "", "", "", "", "", "", "", "", "", "", "" 5300 REM console -5310 DATA "The console shows the state of the ship's engines. They are in overdrive. The pilot" +5310 DATA "The console shows the state of the ship's engines. The pilot" 5320 DATA "appears to have the throttle jammed wide open with his right arm. The muscles in his" 5340 DATA "forearm are taught, there's no way he's going to release the throttle. He left hand" 5350 DATA "works its way around the console switches. After a few moments you realise that he is" @@ -364,8 +365,8 @@ 5394 DATA " 1. Explore potentially habitable worlds" 5396 DATA " 2. Conduct research to develop gene therapies to aid adaptation to planetary conditions" 5398 DATA " 3. Conduct research to develop custom biologic weapons systems, to protect colonists", " " -5400 DATA "One thing is certain, the ship is out of control, careering through space at maximum" -5410 DATA "speed. Rescue will be impossible unless you can find a way to shut down the engines." +5400 DATA "If the engines are online, the ship will be careering through space at maximum" +5410 DATA "speed. Rescue will be impossible unless the engines are offline." 5420 DATA "", "", "" 5430 REM engine control 5440 DATA "The control panel is grubby, smeared with oil and grime. This is clearly" @@ -434,7 +435,7 @@ 8400 REM ========== wraith-hound movement ========== 8410 REM decide to move one third of the time 8420 MOVE = RNDINT(1, 3) -8430 IF MOVE <> 1 THEN GOTO 8550 +8430 IF MOVE <> 1 THEN RETURN 8440 REM randomly determine direction 8450 DIR = RNDINT(1, 6) 8460 IF DIR = 1 THEN INDEX = 1 @@ -482,8 +483,36 @@ 9540 SHUTDOWN = 1 9550 PRINT "Engine shutdown initiated ... engines offline." : PRINT 9560 RETURN -10000 REM TODO - wraith-hound fight, use rotten food, use rifle, -10010 REM TODO - scientist conversation +9700 REM ========== laboratory conversation ========== +9710 PRINT "Dr Lascoe is stood in the laboratory. He is armed. He speaks to you." : PRINT +9720 PRINT "'It's you! You were infected with HO-1, but you seem to be normal." +9730 PRINT " Hollow destroys consciousness, it shuts down all self awareness in the brain." +9740 PRINT " The patient still functions to a degree, but they're an automaton, endlessly" +9750 PRINT " running the same mental subroutines like a piece of clockwork until the body" +9760 PRINT " breaks down.'" : PRINT +9770 PRINT "'I've been hiding in here to avoid contagion. Someone released the wraith-hound" +9780 PRINT " from containment, but I've been able to keep it away with this weapon." +9790 PRINT " But if you're immune, I can develop a vaccine from your blood.'" : PRINT +9800 IF OL(SYRINGE) = 0 AND OB$(SYRINGE) = "blood filled syringe" THEN GOTO 9820 +9810 PRINT "'You need to get me a sample of your blood.'" : PRINT : RETURN +9820 PRINT "'Ah, I see you have a blood sample, excellent.'" : PRINT +9830 IF SUIT_WORN = 1 THEN GOTO 9855 +9840 PRINT " I still don't want to take the risk that you are contagious. Put on a space" +9850 PRINT " suit and then I'll let you approach me.'" : PRINT: RETURN +9855 PRINT "'Good, you're wearing a suit to avoid contaminating me.'" : PRINT +9860 IF SHUTDOWN = 1 THEN GOTO 9885 +9870 PRINT "'Rescue will be impossible while the engines are running out of control." +9880 PRINT " You'll need to take them offline.'": PRINT : RETURN +9885 PRINT "'Good job on taking the engines offline, you've saved us both!" +9890 PRINT " I'll work up the serum and we can wait for rescue.'" : PRINT +9900 GOSUB 10000 +9910 RETURN +10000 REM ========== finale ========== +10010 PRINT "Congratulations, you have save Pneuma and helped Dr Lascoe develop" +10020 PRINT "vaccine against HO-1, the Hollow virus." +10030 PRINT : PRINT " THE END" : PRINT +10040 STOP +11000 REM TODO - wraith-hound fight, use rotten food, use rifle From 4494a123c5b68cc96a50d46f5a28583a6e02064f Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 15 Feb 2025 14:38:43 +0000 Subject: [PATCH 158/183] Added library terminal object --- examples/Pneuma.bas | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index c649d73..bb943a5 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -185,18 +185,20 @@ 2939 OL ( FOOD ) = GAL 2940 OL ( TRACKER ) = GYM 2941 OL ( SYRINGE ) = MED -2942 IC = 4 : REM interactive object count +2942 IC = 5 : REM interactive object count 2944 DIM IO$ (IC) 2946 MEDLOG = 0 : IO$ ( MEDLOG ) = "medical log" 2948 PORTHOLE = 1 : IO$ (PORTHOLE) = "porthole" 2950 CONSOLE = 2 : IO$(CONSOLE) = "console" 2952 ENGINE = 3 : IO$(ENGINE)= "engine control" +2954 TERMINAL = 4 : IO$(TERMINAL) = "library terminal" 2960 REM interative object locations 2962 DIM IL ( IC ) 2964 IL ( MEDLOG) = MED 2966 IL (PORTHOLE) = POD 2968 IL (CONSOLE) = BDG 2970 IL (ENGINE) = ENG +2971 IL (TERMINAL) = REC 2972 PC = 3 : REM person count 2974 DIM P$ ( PC ) 2976 CHEF = 0 : P$ ( CHEF ) = "chef" @@ -225,8 +227,8 @@ 3075 REM recreation room 3080 DATA "Space is clearly at a premium in this ship. The room doubles as both a dining and" 3090 DATA "recreation area. Long tables for dining are located on the port side, while couches" -3100 DATA "and low tables are scattered around the remaining space. There are doors in the aft" -3110 DATA "and starboard walls.", "" +3100 DATA "and low tables are scattered around the remaining space. A library terminal is switched" +3105 DATA "on in the corner. There are doors in the aft and starboard walls.", "" 3120 REM armoury 3130 DATA "Locked cabinets line the starboard wall. Each cabinet has a prominently displayed" 3140 DATA "notice on its door reading 'Weapons to be removed only when authorised by the Chief" @@ -379,6 +381,10 @@ 5510 DATA "'WARNING: CORE BREACH IMMINENT'", " " 5520 DATA "Just below the screen is a large red button, shielded by a cover that can be" 5530 DATA "flipped aside.", "", "", "", "", "", "" +5540 REM library terminal +5550 DATA "A ancient text is open in the terminal: 'The Origin of Consciousness in the Breakdown" +5560 DATA "of the Bicameral Mind by Julian Jaynes, 1976.'", "", "", "", "", "", "", "", "", "" +5570 DATA "", "", "", "", "", "", "", "", "" 5940 FOR OBJECT = 0 TO IC-1 5950 FOR I = 0 TO 19 5960 READ DESC$ : ID$ (OBJECT, I) = DESC$ From eb7187517fe269df6956c4afd90eb53a8c7dced9 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 23 Feb 2025 21:42:04 +0000 Subject: [PATCH 159/183] Added monster battle routine --- examples/Pneuma.bas | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index bb943a5..b3eb227 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -26,8 +26,9 @@ 703 PRINT "You are in the " ; LO$ ( PL ) : PRINT 705 GOSUB 4010 : REM print room description 706 GOSUB 8000 : REM tracker info if carried -708 GOSUB 8200 : REM wraith-hound proximity check -709 GOSUB 8400 : REM wraith-hound movement +707 GOSUB 8200 : REM wraith-hound proximity check +708 GOSUB 8400 : REM wraith-hound movement +709 IF WPL = PL THEN GOSUB 11000 : REM fight wraith-hound 710 INPUT "What now? " ; I$ 715 PRINT 716 MOVE = 1 @@ -518,7 +519,33 @@ 10020 PRINT "vaccine against HO-1, the Hollow virus." 10030 PRINT : PRINT " THE END" : PRINT 10040 STOP -11000 REM TODO - wraith-hound fight, use rotten food, use rifle +11000 REM ========== fight ========== +11010 PRINT "The wraith-hound arrives. It is an abomination! A weapon system that" +11020 PRINT "nature would never have created unaided. Everything about it is just" +11030 PRINT "plain wrong on so many levels. One thing is clear, it is designed" +11035 PRINT "purely for hunting and killing." : PRINT +11040 INPUT "Do you r[un] or f[ight]?"; I$ +11050 PRINT +11060 IF LOWER$ ( I$ ) = "f" OR LOWER$ ( I$ ) = "fight" THEN GOTO 11190 +11065 IF LOWER$ ( I$ ) = "r" OR LOWER$ ( I$ ) = "run" THEN GOTO 11070 +11067 PRINT "Command unrecognised, you must fight!" : PRINT : GOTO 11190 +11070 REM run away to a random adjoining room +11080 DIR = RNDINT(1, 6) +11090 IF DIR = 1 THEN INDEX = 1 +11100 IF DIR = 2 THEN INDEX = 3 +11110 IF DIR = 3 THEN INDEX = 5 +11120 IF DIR = 4 THEN INDEX = 7 +11130 IF DIR = 5 THEN INDEX = 9 +11140 IF DIR = 6 THEN INDEX = 11 +11150 NPL = VAL ( MID$ ( EX$ ( PL ) , INDEX , 2 ) ) : REM potential next location +11160 IF NPL = 0 THEN GOTO 11080 : REM can't go that way +11170 PL = NPL +11175 GOSUB 4010 +11180 RETURN +11190 REM fight +11200 REM TODO - wraith-hound fight, use rotten food, use rifle +11210 RETURN + From d5cd50a34075df65893a21a44c0b97679175b33c Mon Sep 17 00:00:00 2001 From: richpl Date: Fri, 28 Feb 2025 22:16:36 +0000 Subject: [PATCH 160/183] Added creature battle --- examples/Pneuma.bas | 47 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index b3eb227..d0afc18 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -26,8 +26,8 @@ 703 PRINT "You are in the " ; LO$ ( PL ) : PRINT 705 GOSUB 4010 : REM print room description 706 GOSUB 8000 : REM tracker info if carried -707 GOSUB 8200 : REM wraith-hound proximity check -708 GOSUB 8400 : REM wraith-hound movement +707 IF WPL <> 99 THEN GOSUB 8200 : REM wraith-hound proximity check +708 IF WPL <> 99 THEN GOSUB 8400 : REM wraith-hound movement 709 IF WPL = PL THEN GOSUB 11000 : REM fight wraith-hound 710 INPUT "What now? " ; I$ 715 PRINT @@ -175,7 +175,7 @@ 2910 DIM OB$ ( OC ) 2915 PULSE = 0 : OB$ ( PULSE ) = "pulse rifle" 2920 SUIT = 1 : OB$ ( SUIT ) = "space suit" -2922 FOOD = 2 : OB$ ( FOOD ) = "rotting food" +2922 KNIFE = 2 : OB$ ( KNIFE ) = "knife" 2924 TRACKER = 3 : OB$ ( TRACKER ) = "tracker" 2926 SYRINGE = 4 : OB$ ( SYRINGE ) = "syringe" 2930 REM object locations @@ -183,7 +183,7 @@ 2934 DIM OL ( OC ) 2936 OL ( PULSE ) = ARM 2937 OL ( SUIT ) = POD -2939 OL ( FOOD ) = GAL +2939 OL ( KNIFE ) = GAL 2940 OL ( TRACKER ) = GYM 2941 OL ( SYRINGE ) = MED 2942 IC = 5 : REM interactive object count @@ -424,7 +424,8 @@ 7535 PRINT 7550 RETURN 8000 REM ========== wraith-hound tracking ========== -8010 IF OL ( TRACKER ) <> INV THEN GOTO 8040 +8010 IF OL ( TRACKER ) <> INV THEN RETURN +8015 IF WPL = 99 THEN PRINT "Tracking: Wraith-hound terminated." : GOTO 8030 8020 PRINT "Tracking: Wraith-hound current location is the "; LO$ ( WPL ) 8030 PRINT 8040 RETURN @@ -539,12 +540,40 @@ 11140 IF DIR = 6 THEN INDEX = 11 11150 NPL = VAL ( MID$ ( EX$ ( PL ) , INDEX , 2 ) ) : REM potential next location 11160 IF NPL = 0 THEN GOTO 11080 : REM can't go that way -11170 PL = NPL +11170 PL = NPL : PRINT "You are in the " ; LO$ ( PL ) : PRINT 11175 GOSUB 4010 11180 RETURN -11190 REM fight -11200 REM TODO - wraith-hound fight, use rotten food, use rifle -11210 RETURN +11190 REM fight +11200 FEROCITY = 60 : REM ferocity factor for wraith-hound +11205 STAMINA = 60 : REM your stamina +11210 IF OL(PULSE) <> INV AND OL(KNIFE) <> INV THEN PRINT "You have no weapons, and must fight bare handed." : PRINT : GOTO 11280 +11220 IF OL(PULSE) = INV AND OL(KNIFE) <> INV THEN PRINT "Luckily, you have a pulse rifle." : PRINT: FEROCITY=50 : GOTO 11280 +11230 IF OL(PULSE) <> INV AND OL(KNIFE) = INV THEN PRINT "You only have a knife with which to fight." : PRINT : FEROCITY = 55 : GOTO 11280 +11240 INPUT "Which weapon do you choose? 1 - Pulse rifle, 2 - Knife: "; WPN +11245 PRINT +11250 IF WPN<1 OR WPN>2 THEN GOTO 11240 +11260 IF WPN=1 THEN FEROCITY = 50 +11270 IF WPN=2 THEN FEROCITY = 55 +11280 REM the battle +11290 IF RND(1)>0.5 THEN PRINT "The wraith-hound attacks!" ELSE PRINT "You attack!" +11300 PRINT +11305 GOSUB 11430 +11310 IF RND(1)>0.5 THEN PRINT "You wound the creature." : PRINT: FEROCITY = FEROCITY-5 : GOTO 11330 +11320 PRINT "The wraith-hound wounds you." : PRINT : STAMINA = STAMINA-5 +11330 GOSUB 11430 +11340 IF FEROCITY > 0 THEN GOTO 11390 +11350 PRINT "You slayed the wraith-hound!" : PRINT +11360 REM remove creature from the game +11370 WPL = 99 : REM inaccessible location +11380 RETURN +11390 IF STAMINA > 0 THEN GOTO 11420 +11400 PRINT "You were slayed by the wraith-hound! You have failed to save the Pneuma." : PRINT +11410 STOP +11420 GOTO 11280 : REM next round of the battle +11430 REM ========== delay loop ========== +11440 FOR T = 1 TO 80000 +11450 NEXT T +11460 RETURN From 96a5a54a15c669a37f1f50ccd9271dedf2858987 Mon Sep 17 00:00:00 2001 From: richpl Date: Fri, 28 Feb 2025 22:22:54 +0000 Subject: [PATCH 161/183] Added ref to new example program --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 77937ca..22f1aa4 100644 --- a/README.md +++ b/README.md @@ -926,6 +926,8 @@ and expanded by Don Woods. * *life.bas* - An implementation of Conway's Game of Life. This version is a port of the BASIC program which appeared in 'BASIC Computer Games' in 1978. +* *Pneuma.bas* - A brief 21st century take on the venerable text adventure. + ## Informal grammar definition **ABS**(*numerical-expression*) - Calculates the absolute value of the result of *numerical-expression* From 1bdd29fce74c08246f5064207e74620a5312ad30 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 2 Mar 2025 21:19:41 +0000 Subject: [PATCH 162/183] Fixed engine control and tracker bugs --- examples/Pneuma.bas | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index d0afc18..37bd918 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -22,12 +22,12 @@ 540 REM setup dialogue 550 GOSUB 7000 700 REM ========== main loop ========== -701 REM show room details +701 IF WPL <> 99 THEN GOSUB 8400 : REM wraith-hound movement +702 REM show room details 703 PRINT "You are in the " ; LO$ ( PL ) : PRINT 705 GOSUB 4010 : REM print room description 706 GOSUB 8000 : REM tracker info if carried 707 IF WPL <> 99 THEN GOSUB 8200 : REM wraith-hound proximity check -708 IF WPL <> 99 THEN GOSUB 8400 : REM wraith-hound movement 709 IF WPL = PL THEN GOSUB 11000 : REM fight wraith-hound 710 INPUT "What now? " ; I$ 715 PRINT @@ -487,9 +487,11 @@ 9510 IF PL <> ENG THEN PRINT "There is no red button here." : PRINT : RETURN 9520 IF SHUTDOWN = 0 THEN GOTO 9540 9525 SHUTDOWN = 0 -9530 PRINT "Engine restart initiated ... engines online." : PRINT : RETURN +9530 PRINT "Engine restart initiated ... engines online." : PRINT +9535 ID$ (ENGINE, 10) = "'WARNING: CORE BREACH IMMINENT'" : RETURN 9540 SHUTDOWN = 1 9550 PRINT "Engine shutdown initiated ... engines offline." : PRINT +9555 ID$ (ENGINE, 10) = "'ENGINES OFFLINE'" 9560 RETURN 9700 REM ========== laboratory conversation ========== 9710 PRINT "Dr Lascoe is stood in the laboratory. He is armed. He speaks to you." : PRINT From b3341e3e5dc4a7e1955fcf72e28d48923f6c8513 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 2 Mar 2025 21:41:40 +0000 Subject: [PATCH 163/183] Minor typo fixes --- examples/Pneuma.bas | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/Pneuma.bas b/examples/Pneuma.bas index 37bd918..0d342fc 100644 --- a/examples/Pneuma.bas +++ b/examples/Pneuma.bas @@ -12,7 +12,6 @@ 80 PRINT "You have a sore throat and the mother of all headaches, like your brain has been boiling" 85 PRINT "in your skull. In fact, you're no longer sure exactly where you are ... you seem to be" 90 PRINT "suffering from some sort of amnesia ...": PRINT -92 PRINT "THIS GAME IS UNFINISHED AND IS A WORK IN PROGRESS" : PRINT 95 REM set up environment 100 GOSUB 2700 500 REM setup room descriptions @@ -251,13 +250,13 @@ 3280 DATA "The medical centre looks relatively normal, but there is evidence of discarded items" 3290 DATA "lying around the room. Blood filled syringes are scattered on a workbench, as well as" 3300 DATA "some bloodied bandanges. The words 'I'm losing myself' are scrawled messily in blood on" -3310 DATA "one wall. In the corner you can see a medical scanner and next to it, a terminal. On the" -3320 DATA "terminal screen is a portion of the medical log. Exits lead port and forward." +3310 DATA "one wall. In the corner you can see a terminal. On the terminal screen is a portion" +3320 DATA "of the medical log. Exits lead port and forward." 3325 REM gymnasium 3330 DATA "The gymnasium is full of exercise equipment. A female runner is sprinting furiously on a" 3340 DATA "treadmill. She looks exhausted and emaciated, but she keeps running at top speed, almost at" -3350 DATA "a sprint. Her eyes remain fixed on the treadmill console. There are aft and port exists," -3360 DATA " as well as a stairwell leading to the lower deck in the far corner.", "" +3350 DATA "a sprint. Her eyes remain fixed on the treadmill console. There are aft and port exits," +3360 DATA "as well as a stairwell leading to the lower deck in the far corner.", "" 3370 REM lower aft corridor 3380 DATA "This is a featureless, utilitarian corridor. A stairwell leads upwards. There are also" 3390 DATA "exits leading starboard and forward.", "", "", "" @@ -270,7 +269,7 @@ 3490 DATA "floor, their contents spilling out. Along the port wall is a door marked 'Test specimens'." 3500 DATA "From behind the door, strange animal noises are audible ... snuffling sounds and the" 3510 DATA "occasional primate shriek. A dead man wearing a scuffed and torn lab coat is lying face down" -3520 DATA "in front of the specimen door. Two other exists lead forward and aft." +3520 DATA "in front of the specimen door. Two other exits lead forward and aft." 3525 REM menagerie 3530 DATA "The room is a hellhole. Cages stand open, while various animals roam about: chimpanzees," 3540 DATA "dogs, and rats. Some of the rats are dead, having been savaged and eviscerated. The floor" @@ -547,7 +546,7 @@ 11180 RETURN 11190 REM fight 11200 FEROCITY = 60 : REM ferocity factor for wraith-hound -11205 STAMINA = 60 : REM your stamina +11205 STAMINA = 50 : REM your stamina 11210 IF OL(PULSE) <> INV AND OL(KNIFE) <> INV THEN PRINT "You have no weapons, and must fight bare handed." : PRINT : GOTO 11280 11220 IF OL(PULSE) = INV AND OL(KNIFE) <> INV THEN PRINT "Luckily, you have a pulse rifle." : PRINT: FEROCITY=50 : GOTO 11280 11230 IF OL(PULSE) <> INV AND OL(KNIFE) = INV THEN PRINT "You only have a knife with which to fight." : PRINT : FEROCITY = 55 : GOTO 11280 From a9d7d92a7a005847fb26187609e68207de7d278c Mon Sep 17 00:00:00 2001 From: Ken Perry Date: Sat, 12 Apr 2025 23:48:17 -0400 Subject: [PATCH 164/183] Fixed parsing float when first character is point: Example of one that does not work before this commit: 10 a=.5 20 print "a should be ";.5;" look a is ";a --- lexer.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lexer.py b/lexer.py index bde1234..4e35680 100644 --- a/lexer.py +++ b/lexer.py @@ -99,9 +99,12 @@ def tokenize(self, stmt): break # Process numbers - elif c.isdigit(): + elif c.isdigit() or c == '.': token.category = Token.UNSIGNEDINT found_point = False + if c == '.': + token.category = Token.UNSIGNEDFLOAT + found_point = True # Consume all of the digits, including any decimal point while True: @@ -112,7 +115,7 @@ def tokenize(self, stmt): # and this is not the first decimal point if not c.isdigit(): if c == '.': - if not found_point: + if found_point is False: found_point = True token.category = Token.UNSIGNEDFLOAT From ddc37cbf6f63206d20971ca9fedd7c2f0cd8fd12 Mon Sep 17 00:00:00 2001 From: richpl Date: Fri, 25 Apr 2025 23:21:15 +0100 Subject: [PATCH 165/183] Removed floating point issue --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 22f1aa4..5732098 100644 --- a/README.md +++ b/README.md @@ -1080,7 +1080,6 @@ control flow changes to the Program object, is used consistently throughout the * It is not possible to renumber a program. This would require considerable extra functionality. * Negative values are printed with a space (e.g. '- 5') in program listings because of tokenization. This does not affect functionality. -* Decimal values less than one must be expressed with a leading zero (i.e. 0.34 rather than .34) * User input values cannot be directly assigned to array variables in an **INPUT** or **READ** statement * Strings representing numbers (e.g. "10") can actually be assigned to numeric variables in **INPUT** and **READ** statements without an error, Python will silently convert them to integers. From 616ea8234d286525b824364506d62d42505991c0 Mon Sep 17 00:00:00 2001 From: Ken Perry <=> Date: Mon, 26 May 2025 15:09:15 -0400 Subject: [PATCH 166/183] fixed the readme to match the change to decimal numbers with or without zero --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5732098..2f81fab 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ are all invalid. Numeric variables have no suffix, whereas string variables are always suffixed by '$'. Note that 'I' and 'I$' are considered to be separate variables. Note that string literals must always be enclosed within double quotes (not single quotes). -Using no quotes will result in a syntax error. In addition, for floating point numbers less than one (e.g. 0.67), the decimal point must always be prefixed by a zero (e.g. .67 will be flagged as a syntax error). +Using no quotes will result in a syntax error. Array variables are defined using the **DIM** statement, which explicitly lists how many dimensions the array has, and the sizes of those dimensions: From 15f74e106fe6a2e557af5bc3aefce3b22be4ce97 Mon Sep 17 00:00:00 2001 From: Ken Perry <=> Date: Mon, 26 May 2025 15:21:01 -0400 Subject: [PATCH 167/183] fixed undefined variable functionality --- basicparser.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/basicparser.py b/basicparser.py index 5187e0f..8a60689 100644 --- a/basicparser.py +++ b/basicparser.py @@ -1025,9 +1025,14 @@ def __factor(self): self.__operand_stack.append(self.__sign*self.__symbol_table[self.__token.lexeme]) else: - raise RuntimeError('Name ' + self.__token.lexeme + ' is not defined' + - ' in line ' + str(self.__line_number)) - + # default variables values for undefined variables. + if self.__token.lexeme.endswith ("$"): + # default string + self.__operand_stack.append("") + else: + #default int + self.__operand_stack.append(0) + self.__advance() elif self.__token.category == Token.LEFTPAREN: From 2f4d799a5189fe35b4d829f894e46f6961d69dba Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 21 Oct 2025 22:13:48 -0400 Subject: [PATCH 168/183] update garbage collection info in README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2f81fab..2381608 100644 --- a/README.md +++ b/README.md @@ -263,8 +263,7 @@ Empty array value returned in line 30 > ``` -As in all implementations of BASIC, there is no garbage collection (not surprising since all variables -have global scope)! +Since **all PyBasic variables have a global scope** and string memory is managed by the python interpreter, there is no garbage collection in PyBasic. ### Program constants From 513b99d5fcad23e6d2eb4b1c57d7ba7db696f778 Mon Sep 17 00:00:00 2001 From: Ken Perry Date: Tue, 25 Nov 2025 16:39:16 -0500 Subject: [PATCH 169/183] While wend addition includes tests in examples --- README.md | 43 +++++++++++ basicparser.py | 44 ++++++++++- basictoken.py | 7 +- examples/while_tests.bas | 160 +++++++++++++++++++++++++++++++++++++++ flowsignal.py | 18 ++++- program.py | 102 +++++++++++++++++++++++++ 6 files changed, 369 insertions(+), 5 deletions(-) create mode 100644 examples/while_tests.bas diff --git a/README.md b/README.md index 2381608..53c98d1 100644 --- a/README.md +++ b/README.md @@ -506,6 +506,49 @@ be replaced by the start value, it will not be evaluated. After the completion of the loop, the loop variable value will be the end value + step value (unless the loop is exited using a **GOTO** statement). +Conditional loops are achieved through the use of **WHILE-WEND** statements. The loop continues to execute +as long as the specified condition remains true. The condition is evaluated before each iteration, so if +the condition is false initially, the loop body will not execute at all. + +``` +> 10 LET I = 1 +> 20 WHILE I <= 3 +> 30 PRINT "Count: "; I +> 40 LET I = I + 1 +> 50 WEND +> RUN +Count: 1 +Count: 2 +Count: 3 +> +``` + +WHILE loops may be nested within one another and can also be nested within FOR loops and vice versa. +The loop will terminate when the condition becomes false or when exited using a **GOTO** statement. + +**Example of nested WHILE loops:** + +``` +> 10 LET I = 1 +> 20 WHILE I <= 2 +> 30 PRINT "Outer: "; I +> 40 LET J = 1 +> 50 WHILE J <= 2 +> 60 PRINT " Inner: "; J +> 70 LET J = J + 1 +> 80 WEND +> 90 LET I = I + 1 +> 100 WEND +> RUN +Outer: 1 + Inner: 1 + Inner: 2 +Outer: 2 + Inner: 1 + Inner: 2 +> +``` + ### Conditionals Conditionals are implemented using the **IF-THEN-ELSE** statement. The expression is evaluated and the appropriate diff --git a/basicparser.py b/basicparser.py index 8a60689..17ef514 100644 --- a/basicparser.py +++ b/basicparser.py @@ -239,7 +239,7 @@ def __stmt(self): """ if self.__token.category in [Token.FOR, Token.IF, Token.NEXT, - Token.ON]: + Token.ON, Token.WHILE, Token.WEND]: return self.__compoundstmt() else: @@ -1108,6 +1108,12 @@ def __compoundstmt(self): elif self.__token.category == Token.ON: return self.__ongosubstmt() + elif self.__token.category == Token.WHILE: + return self.__whilestmt() + + elif self.__token.category == Token.WEND: + return self.__wendstmt() + def __ifstmt(self): """Parses if-then-else statements @@ -1274,6 +1280,42 @@ def __nextstmt(self): return FlowSignal(ftype=FlowSignal.LOOP_REPEAT,floop_var=loop_variable) + def __whilestmt(self): + """Parses WHILE loops + + :return: The FlowSignal to indicate that + a WHILE loop start has been processed + + """ + + self.__advance() # Advance past WHILE token + self.__logexpr() # Evaluate the condition + + # Save result of expression + condition_result = self.__operand_stack.pop() + + # Check if the condition is true + if condition_result: + # Condition is true, execute the loop body + return FlowSignal(ftype=FlowSignal.WHILE_BEGIN) + else: + # Condition is false, skip the loop + return FlowSignal(ftype=FlowSignal.WHILE_SKIP) + + def __wendstmt(self): + """Processes a WEND statement that terminates + a WHILE loop + + :return: A FlowSignal indicating that the loop + should repeat (return to WHILE) + + """ + + self.__advance() # Advance past WEND token + + # Return signal to repeat the WHILE loop + return FlowSignal(ftype=FlowSignal.WHILE_REPEAT) + def __ongosubstmt(self): """Process the ON-GOSUB statement diff --git a/basictoken.py b/basictoken.py index 7aba639..5861f84 100644 --- a/basictoken.py +++ b/basictoken.py @@ -119,6 +119,8 @@ class BASICToken: SEMICOLON = 86 # SEMICOLON LEFT = 87 # LEFT$ function RIGHT = 88 # RIGHT$ function + WHILE = 89 # WHILE keyword + WEND = 90 # WEND keyword # Displayable names for each token category catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN', @@ -137,7 +139,7 @@ class BASICToken: 'MAX', 'MIN', 'INSTR', 'AND', 'OR', 'NOT', 'PI', 'RNDINT', 'OPEN', 'HASH', 'CLOSE', 'FSEEK', 'APPEND', 'OUTPUT', 'RESTORE', 'RNDINT', 'TAB', 'SEMICOLON', - 'LEFT', 'RIGHT'] + 'LEFT', 'RIGHT', 'WHILE', 'WEND'] smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN, '+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE, @@ -174,7 +176,8 @@ class BASICToken: 'CLOSE': CLOSE, 'FSEEK': FSEEK, 'APPEND': APPEND, 'OUTPUT':OUTPUT, 'RESTORE': RESTORE, 'TAB': TAB, - 'LEFT$': LEFT, 'RIGHT$': RIGHT} + 'LEFT$': LEFT, 'RIGHT$': RIGHT, + 'WHILE': WHILE, 'WEND': WEND} # Functions diff --git a/examples/while_tests.bas b/examples/while_tests.bas new file mode 100644 index 0000000..34fa98f --- /dev/null +++ b/examples/while_tests.bas @@ -0,0 +1,160 @@ +1 REM =============================================== +2 REM WHILE-WEND LOOP COMPREHENSIVE TEST SUITE +3 REM =============================================== +4 REM +5 REM This program tests all aspects of WHILE-WEND +6 REM loop functionality in PyBasic +7 REM =============================================== +8 REM +10 PRINT "=== WHILE-WEND Loop Test Suite ===" +20 PRINT "" +21 REM +22 REM =============================================== +23 REM Test 1: Basic WHILE Loop +24 REM =============================================== +30 PRINT "Test 1: Basic WHILE Loop" +40 PRINT "Expected: Count 1 through 5" +50 PRINT "------------------------" +60 LET I = 1 +70 WHILE I <= 5 +80 PRINT "Count: "; I +90 LET I = I + 1 +100 WEND +110 PRINT "Basic WHILE loop completed" +120 PRINT "" +121 REM +122 REM =============================================== +123 REM Test 2: WHILE Loop with False Condition +124 REM =============================================== +130 PRINT "Test 2: WHILE Loop Skip Test" +140 PRINT "Expected: Only skip message, no loop output" +150 PRINT "----------------------------------------" +160 LET X = 10 +170 WHILE X < 5 +180 PRINT "This should NOT print!" +190 WEND +200 PRINT "WHILE loop was correctly skipped" +210 PRINT "" +211 REM +212 REM =============================================== +213 REM Test 3: Nested WHILE Loops +214 REM =============================================== +220 PRINT "Test 3: Nested WHILE Loops" +230 PRINT "Expected: Outer 1-3, Inner 1-2 for each" +240 PRINT "-------------------------------------" +250 LET I = 1 +260 WHILE I <= 3 +270 PRINT "Outer loop: "; I +280 LET J = 1 +290 WHILE J <= 2 +300 PRINT " Inner loop: "; J +310 LET J = J + 1 +320 WEND +330 LET I = I + 1 +340 WEND +350 PRINT "Nested WHILE loops completed" +360 PRINT "" +361 REM +362 REM =============================================== +363 REM Test 4: WHILE with Complex Conditions +364 REM =============================================== +370 PRINT "Test 4: Complex Condition Test" +380 PRINT "Expected: A=1,B=5 through A=3,B=5" +390 PRINT "--------------------------------" +400 LET A = 1 +410 LET B = 5 +420 WHILE A < B AND A < 4 +430 PRINT "A = "; A; ", B = "; B +440 LET A = A + 1 +450 WEND +460 PRINT "Complex condition test completed" +470 PRINT "" +471 REM +472 REM =============================================== +473 REM Test 5: WHILE Loop with Countdown +474 REM =============================================== +480 PRINT "Test 5: Countdown WHILE Loop" +490 PRINT "Expected: Countdown from 5 to 1" +500 PRINT "-----------------------------" +510 LET COUNT = 5 +520 WHILE COUNT > 0 +530 PRINT "Countdown: "; COUNT +540 LET COUNT = COUNT - 1 +550 WEND +560 PRINT "Countdown completed!" +570 PRINT "" +571 REM +572 REM =============================================== +573 REM Test 6: WHILE with String Variables +574 REM =============================================== +580 PRINT "Test 6: WHILE with String Conditions" +590 PRINT "Expected: Process items until STOP" +600 PRINT "--------------------------------" +610 LET ITEM$ = "START" +620 LET COUNTER = 0 +630 WHILE ITEM$ <> "STOP" +640 LET COUNTER = COUNTER + 1 +650 PRINT "Processing item "; COUNTER +660 IF COUNTER = 3 THEN LET ITEM$ = "STOP" +670 WEND +680 PRINT "String condition test completed" +690 PRINT "" +691 REM +692 REM =============================================== +693 REM Test 7: Mixed FOR and WHILE Loops +694 REM =============================================== +700 PRINT "Test 7: Mixed FOR and WHILE Loops" +710 PRINT "Expected: FOR loop with nested WHILE" +720 PRINT "--------------------------------" +730 FOR K = 1 TO 2 +740 PRINT "FOR loop iteration: "; K +750 LET M = 1 +760 WHILE M <= 2 +770 PRINT " WHILE iteration: "; M +780 LET M = M + 1 +790 WEND +800 NEXT K +810 PRINT "Mixed loop test completed" +820 PRINT "" +821 REM +822 REM =============================================== +823 REM Test 8: WHILE with Mathematical Operations +824 REM =============================================== +830 PRINT "Test 8: WHILE with Math Operations" +840 PRINT "Expected: Powers of 2 up to 16" +850 PRINT "-----------------------------" +860 LET POWER = 1 +870 WHILE POWER <= 16 +880 PRINT "2^"; LOG(POWER)/LOG(2); " = "; POWER +890 LET POWER = POWER * 2 +900 WEND +910 PRINT "Mathematical operations test completed" +920 PRINT "" +921 REM +922 REM =============================================== +923 REM Final Summary +924 REM =============================================== +930 PRINT "=======================================" +940 PRINT "ALL WHILE-WEND TESTS COMPLETED!" +950 PRINT "If you see this message, all tests" +960 PRINT "have executed successfully!" +970 PRINT "=======================================" +975 REM +976 REM =============================================== +977 REM Test 9: WHILE-WEND Validation Tests +978 REM =============================================== +980 PRINT "Test 9: WHILE-WEND Validation" +985 PRINT "Expected: Tests that require proper structure" +990 PRINT "--------------------------------------------" +1000 PRINT "Note: Missing WEND validation occurs at load time" +1010 PRINT "GOTO breaking WHILE loops causes runtime errors" +1020 PRINT "This ensures proper WHILE-WEND structure" +1030 PRINT "Validation tests completed" +1040 PRINT "" +1050 PRINT "=======================================" +1060 PRINT "ALL WHILE-WEND TESTS COMPLETED!" +1070 PRINT "VALIDATION: Missing WEND detected at load" +1080 PRINT "VALIDATION: GOTO in WHILE causes error" +1090 PRINT "All features working correctly!" +1100 PRINT "=======================================" +1110 END \ No newline at end of file diff --git a/flowsignal.py b/flowsignal.py index b966c1d..214eb68 100644 --- a/flowsignal.py +++ b/flowsignal.py @@ -76,6 +76,18 @@ class FlowSignal: # Indicates that a conditional result block should be executed EXECUTE = 7 + # Indicates the start of a WHILE loop where the condition + # is true and the loop body should be executed + WHILE_BEGIN = 8 + + # Indicates the end of a WHILE loop and that execution should + # return to the beginning of the loop to re-evaluate the condition + WHILE_REPEAT = 9 + + # Indicates that a WHILE loop should be skipped because + # the condition is false + WHILE_SKIP = 10 + def __init__(self, ftarget=None, ftype=SIMPLE_JUMP, floop_var=None): """Creates a new FlowSignal for a branch. If the jump target is supplied, then the branch is assumed to be @@ -93,7 +105,8 @@ def __init__(self, ftarget=None, ftype=SIMPLE_JUMP, floop_var=None): if ftype not in [self.GOSUB, self.SIMPLE_JUMP, self.LOOP_BEGIN, self.LOOP_REPEAT, self.RETURN, - self.LOOP_SKIP, self.STOP, self.EXECUTE]: + self.LOOP_SKIP, self.STOP, self.EXECUTE, + self.WHILE_BEGIN, self.WHILE_REPEAT, self.WHILE_SKIP]: raise TypeError("Invalid flow signal type supplied: " + str(ftype)) if ftarget == None and \ @@ -102,7 +115,8 @@ def __init__(self, ftarget=None, ftype=SIMPLE_JUMP, floop_var=None): if ftarget != None and \ ftype in [self.RETURN, self.LOOP_BEGIN, self.LOOP_REPEAT, - self.STOP, self.EXECUTE]: + self.STOP, self.EXECUTE, self.WHILE_BEGIN, + self.WHILE_REPEAT]: raise TypeError("Target wrongly supplied for flow signal " + str(ftype)) self.ftype = ftype diff --git a/program.py b/program.py index 13d1939..1072831 100644 --- a/program.py +++ b/program.py @@ -151,6 +151,10 @@ def __init__(self): # return dictionary for loop returns self.__return_loop = {} + + # WHILE loop tracking stack - separate from GOSUB return stack + # Each entry contains the line number of the WHILE statement + self.__while_stack = [] # Setup DATA object self.__data = BASICData() @@ -292,11 +296,39 @@ def __execute(self, line_number): except RuntimeError as err: raise RuntimeError(str(err)) + def __validate_while_wend(self): + """Validate that all WHILE statements have matching WEND statements""" + while_stack = [] + line_numbers = self.line_numbers() + + for line_num in line_numbers: + statement = self.__program[line_num] + if statement and len(statement) > 0: + if statement[0].category == Token.WHILE: + while_stack.append(line_num) + elif statement[0].category == Token.WEND: + if not while_stack: + raise RuntimeError(f"WEND without matching WHILE at line {line_num}") + while_stack.pop() + + if while_stack: + raise RuntimeError(f"WHILE without matching WEND at line {while_stack[0]}") + + def __clear_while_stack_on_goto(self, current_line, target_line): + """Clear WHILE loop stack when GOTO jumps out of loops""" + # Simple solution: GOTO clears the WHILE stack completely + # This prevents infinite loops caused by corrupted loop state + # Traditional BASIC behavior - GOTO breaks loop structures + self.__while_stack.clear() + def execute(self): """Execute the program""" self.__parser = BASICParser(self.__data) self.__data.restore(0) # reset data pointer + + # Validate WHILE-WEND pairs before execution + self.__validate_while_wend() line_numbers = self.line_numbers() @@ -318,6 +350,11 @@ def execute(self): if flowsignal: if flowsignal.ftype == FlowSignal.SIMPLE_JUMP: # GOTO or conditional branch encountered + # Clear WHILE loop stack if we're jumping out of loops + target_line = flowsignal.ftarget + current_line = self.get_next_line_number() + self.__clear_while_stack_on_goto(current_line, target_line) + try: index = line_numbers.index(flowsignal.ftarget) @@ -429,6 +466,71 @@ def execute(self): self.set_next_line_number(line_numbers[index]) + elif flowsignal.ftype == FlowSignal.WHILE_BEGIN: + # WHILE loop start encountered + # Put loop line number on the WHILE stack so + # that it can be returned to when the loop repeats + self.__while_stack.append(line_numbers[index]) + + # Continue to the next statement in the loop + index = index + 1 + + if index < len(line_numbers): + self.set_next_line_number(line_numbers[index]) + + else: + # Reached end of program + raise RuntimeError("Program terminated within a WHILE loop") + + elif flowsignal.ftype == FlowSignal.WHILE_SKIP: + # WHILE condition is false, so skip + # all statements within loop and move past the corresponding + # WEND statement + index = index + 1 + wend_count = 0 + while index < len(line_numbers): + next_line_number = line_numbers[index] + temp_tokenlist = self.__program[next_line_number] + + if temp_tokenlist[0].category == Token.WHILE: + # Found nested WHILE, increment counter + wend_count += 1 + elif temp_tokenlist[0].category == Token.WEND: + if wend_count == 0: + # Found matching WEND statement + # Move to the statement after this WEND, if there is one + index = index + 1 + if index < len(line_numbers): + next_line_number = line_numbers[index] # Statement after the WEND + self.set_next_line_number(next_line_number) + break + else: + # This WEND belongs to a nested WHILE + wend_count -= 1 + + index = index + 1 + + # Check we have not reached end of program + if index >= len(line_numbers): + # Terminate the program + break + + elif flowsignal.ftype == FlowSignal.WHILE_REPEAT: + # WHILE repeat encountered (WEND statement) + # Pop the loop start address from the WHILE stack + try: + index = line_numbers.index(self.__while_stack.pop()) + + except ValueError: + raise RuntimeError("Invalid WHILE loop exit in line " + + str(self.get_next_line_number())) + + except IndexError: + raise RuntimeError("WEND encountered without corresponding " + + "WHILE loop in line " + str(self.get_next_line_number())) + + self.set_next_line_number(line_numbers[index]) + else: index = index + 1 From 920589e91ec8e642cce1f32cb918af96bc63d8f5 Mon Sep 17 00:00:00 2001 From: Ken Perry Date: Mon, 24 Nov 2025 21:33:08 -0500 Subject: [PATCH 170/183] Added renumber --- README.md | 34 ++++++++- basictoken.py | 5 +- interpreter.py | 40 +++++++++++ program.py | 186 ++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 261 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2381608..724f9f7 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,38 @@ The program may be erased entirely from memory using the **NEW** command: > ``` +Program line numbers can be renumbered using the **RENUMBER** command: + +``` +> 10 A = 1 +> 20 IF A = 1 THEN 100 +> 30 GOTO 10 +> 100 PRINT "DONE" +> LIST +10 A = 1 +20 IF A = 1 THEN 100 +30 GOTO 10 +100 PRINT "DONE" +> RENUMBER 100,100 +Program renumbered +> LIST +100 A = 1 +200 IF A = 1 THEN 400 +300 GOTO 100 +400 PRINT "DONE" +> +``` + +The RENUMBER command supports various parameter combinations: + +* **RENUMBER** - Renumber whole program starting at 10 with increments of 10 +* **RENUMBER 100** - Start renumbering at 100 with increments of 10 +* **RENUMBER 50,5** - Start at 50 with increments of 5 +* **RENUMBER 100,10,200,300** - Renumber only lines 200-300, starting at 100 +* **RENUMBER ,,200** - Renumber lines 200 and above using defaults + +The RENUMBER command automatically updates line number references in GOTO, GOSUB, IF...THEN, ON...GOTO, ON...GOSUB, and RESTORE statements while preserving line numbers in string literals and comments. + Finally, it is possible to terminate the interpreter by issuing the **EXIT** command: ``` @@ -168,7 +200,7 @@ Program terminated ### Statement structure As per usual in old school BASIC, all program statements must be prefixed with a line number which indicates the order in which the -statements may be executed. There is no renumber command to allow all line numbers to be modified. A statement may be modified or +statements may be executed. A statement may be modified or replaced by re-entering a statement with the same line number: ``` diff --git a/basictoken.py b/basictoken.py index 7aba639..a9bbe34 100644 --- a/basictoken.py +++ b/basictoken.py @@ -119,6 +119,7 @@ class BASICToken: SEMICOLON = 86 # SEMICOLON LEFT = 87 # LEFT$ function RIGHT = 88 # RIGHT$ function + RENUMBER = 91 # RENUMBER command # Displayable names for each token category catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN', @@ -137,7 +138,7 @@ class BASICToken: 'MAX', 'MIN', 'INSTR', 'AND', 'OR', 'NOT', 'PI', 'RNDINT', 'OPEN', 'HASH', 'CLOSE', 'FSEEK', 'APPEND', 'OUTPUT', 'RESTORE', 'RNDINT', 'TAB', 'SEMICOLON', - 'LEFT', 'RIGHT'] + 'LEFT', 'RIGHT', 'RENUMBER'] smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN, '+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE, @@ -174,7 +175,7 @@ class BASICToken: 'CLOSE': CLOSE, 'FSEEK': FSEEK, 'APPEND': APPEND, 'OUTPUT':OUTPUT, 'RESTORE': RESTORE, 'TAB': TAB, - 'LEFT$': LEFT, 'RIGHT$': RIGHT} + 'LEFT$': LEFT, 'RIGHT$': RIGHT,'RENUMBER': RENUMBER} # Functions diff --git a/interpreter.py b/interpreter.py index 624cf97..1974b24 100644 --- a/interpreter.py +++ b/interpreter.py @@ -117,6 +117,46 @@ def main(): elif tokenlist[0].category == Token.NEW: program.delete() + # Renumber the program + elif tokenlist[0].category == Token.RENUMBER: + try: + if len(tokenlist) == 1: + # RENUMBER with no arguments + program.renumber() + else: + # Parse comma-separated arguments, handling blank parameters + args = [] + current_arg = "" + + for i in range(1, len(tokenlist)): + if tokenlist[i].category == Token.COMMA: + # Process the accumulated argument + if current_arg.strip(): + args.append(int(current_arg.strip())) + else: + args.append(None) # Blank parameter + current_arg = "" + elif tokenlist[i].category == Token.UNSIGNEDINT: + current_arg += tokenlist[i].lexeme + + # Process the final argument if any + if current_arg.strip(): + args.append(int(current_arg.strip())) + elif len(tokenlist) > 1 and tokenlist[-1].category == Token.COMMA: + args.append(None) # Trailing comma means blank parameter + + # Convert args list to proper parameters for renumber() + # RENUMBER [newStart][,increment][,oldStart][,oldEnd] + new_start = args[0] if len(args) > 0 and args[0] is not None else 10 + increment = args[1] if len(args) > 1 and args[1] is not None else 10 + old_start = args[2] if len(args) > 2 and args[2] is not None else None + old_end = args[3] if len(args) > 3 and args[3] is not None else None + + program.renumber(new_start, increment, old_start, old_end) + print("Program renumbered") + except Exception as e: + print(f"RENUMBER error: {e}", file=stderr) + # Unrecognised input else: print("Unrecognised input", file=stderr) diff --git a/program.py b/program.py index 13d1939..e30b58c 100644 --- a/program.py +++ b/program.py @@ -21,7 +21,7 @@ """ -from basictoken import BASICToken as Token +from basictoken import BASICToken as Token, BASICToken from basicparser import BASICParser from flowsignal import FlowSignal from lexer import Lexer @@ -479,3 +479,187 @@ def set_next_line_number(self, line_number): """ self.__next_stmt = line_number + + def renumber(self, new_start=10, increment=10, old_start=None, old_end=None): + """Renumber the program according to BASIC RENUMBER command specification + + :param new_start: First line number assigned during renumbering (default: 10) + :param increment: Amount added to each successive line number (default: 10) + :param old_start: Lowest line number to renumber (default: first line) + :param old_end: Highest line number to renumber (default: last line) + """ + + # Validate parameters + if increment == 0: + raise ValueError("Increment cannot be zero") + if new_start < 1: + raise ValueError("New start line number must be >= 1") + if increment < 0: + raise ValueError("Increment must be positive") + + line_numbers = self.line_numbers() + if not line_numbers: + return # No program to renumber + + # Set defaults for old_start and old_end + if old_start is None: + old_start = line_numbers[0] + if old_end is None: + old_end = line_numbers[-1] + + # Find lines within the renumbering range + lines_to_renumber = [] + for line_num in line_numbers: + if old_start <= line_num <= old_end: + lines_to_renumber.append(line_num) + + if not lines_to_renumber: + return # No lines in range to renumber + + # Step 1: Create mapping of old line numbers to new line numbers + line_mapping = {} + new_line_num = new_start + + for old_line_num in lines_to_renumber: + line_mapping[old_line_num] = new_line_num + new_line_num += increment + + # Check for overflow (basic line numbers typically max at 65535) + if new_line_num - increment > 65535: + raise ValueError("Line numbers would exceed maximum value (65535)") + + # Check for conflicts with existing line numbers outside the range + for new_num in line_mapping.values(): + if new_num in line_numbers and new_num not in lines_to_renumber: + # Find which old line this conflicts with + for ln in line_numbers: + if ln == new_num and ln not in lines_to_renumber: + raise ValueError(f"New line number {new_num} conflicts with existing line {ln}") + + # Step 2: Update line number references in all program statements + updated_program = {} + updated_data = {} + + for line_num in line_numbers: + statement = self.__program[line_num] + + # Update line number references within the statement + updated_statement = self._update_line_references(statement, line_mapping) + + # Determine the new line number for this statement + if line_num in line_mapping: + new_line_num = line_mapping[line_num] + else: + new_line_num = line_num + + updated_program[new_line_num] = updated_statement + + # Handle DATA statements + if statement and statement[0].category == Token.DATA: + data_tokens = self.__data.getTokens(line_num) + if data_tokens: + updated_data[new_line_num] = data_tokens + + # Step 3: Replace the program with the updated version + self.__program = updated_program + + # Update DATA storage + self.__data.delete() + for line_num, data_tokens in updated_data.items(): + self.__data.addData(line_num, data_tokens) + + def _update_line_references(self, statement, line_mapping): + """Update line number references within a statement + + :param statement: List of tokens representing the statement + :param line_mapping: Dictionary mapping old line numbers to new ones + :return: Updated statement with new line number references + """ + if not statement: + return statement + + updated_statement = [] + i = 0 + + while i < len(statement): + token = statement[i] + + # Skip string literals and comments - they should not be modified + if token.category == Token.STRING: + updated_statement.append(token) + i += 1 + continue + + if token.category == Token.REM: + # Everything after REM is a comment, copy rest as-is + updated_statement.extend(statement[i:]) + break + + # Check for line number references in specific contexts + if self._is_line_number_reference(statement, i): + if token.category == Token.UNSIGNEDINT: + line_num = int(token.lexeme) + if line_num in line_mapping: + # Create new token with updated line number + new_token = BASICToken(token.column, token.category, str(line_mapping[line_num])) + updated_statement.append(new_token) + else: + updated_statement.append(token) + else: + updated_statement.append(token) + else: + updated_statement.append(token) + + i += 1 + + return updated_statement + + def _is_line_number_reference(self, statement, token_index): + """Determine if the token at the given index is a line number reference + + :param statement: List of tokens representing the statement + :param token_index: Index of the token to check + :return: True if this token is a line number reference + """ + if token_index >= len(statement): + return False + + token = statement[token_index] + if token.category != Token.UNSIGNEDINT: + return False + + # Check what comes before this number to determine context + if token_index == 0: + return False # First token is the line number itself, not a reference + + prev_token = statement[token_index - 1] + + # Direct line number references + if prev_token.category in [Token.GOTO, Token.GOSUB, Token.THEN, Token.RESTORE]: + return True + + # ON...GOTO and ON...GOSUB constructs + if prev_token.category == Token.COMMA: + # Look backward to find ON...GOTO or ON...GOSUB + for j in range(token_index - 2, -1, -1): + if statement[j].category == Token.ON: + # Check if this is followed by GOTO or GOSUB + for k in range(j + 1, token_index): + if statement[k].category in [Token.GOTO, Token.GOSUB]: + return True + break + elif statement[j].category not in [Token.UNSIGNEDINT, Token.COMMA, Token.NAME]: + break + + # Check for ON...GOTO/GOSUB patterns + if token_index >= 2: + # Look for pattern: ON GOTO/GOSUB + for j in range(token_index - 1, -1, -1): + if statement[j].category == Token.ON: + # Found ON, now look for GOTO or GOSUB before our number + for k in range(j + 1, token_index): + if statement[k].category in [Token.GOTO, Token.GOSUB]: + return True + break + + return False From 157a9aa315dd50386fe0b7a3f5659475b08081d5 Mon Sep 17 00:00:00 2001 From: Ken Perry Date: Tue, 25 Nov 2025 20:37:15 -0500 Subject: [PATCH 171/183] Added input, and read support for Arrays for the two issues --- README.md | 84 ++++++++++++++- basicparser.py | 283 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 283 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 2381608..87b0426 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,19 @@ $ python interpreter.py Although this started of as a personal project, it has been enhanced considerably by some other Github users. You can see them in the list of contributors! It's very much a group endeavour now. +## Recent Updates + +### Array INPUT and READ Support +The interpreter now supports reading data directly into array elements using both **INPUT** and **READ** statements: + +- **Array Elements as Targets**: Use syntax like `INPUT A(1), B(I+2)` and `READ N$(1), N$(2)` +- **Expression Indices**: Support for complex expressions in array indices like `A(I*2+1)` +- **Mixed Data Types**: Read numeric and string data into appropriate array types +- **Error Handling**: Proper SUBSCRIPT ERROR and OUT OF DATA error messages +- **Specification Compliant**: Follows classic BASIC behavior for all edge cases + +See the comprehensive test program `examples/array_input_read_tests.bas` for demonstrations of all functionality. + ## Operators A limited range of arithmetic expressions are provided. Addition and subtraction have the lowest precedence, @@ -311,8 +324,38 @@ Hello Another Line of Data > ``` -It is a limitation of this BASIC dialect that it is not possible to assign constants directly to array variables -within a **READ** statement, only simple variables. +#### Array Support in READ + +The **READ** statement now supports reading data directly into array elements, using expressions for array indices: + +``` +> 10 DIM A(5) +> 20 DIM N$(3) +> 30 DATA 10, 20, 30, "Hello", "World" +> 40 READ A(1), A(2), A(3), N$(1), N$(2) +> 50 PRINT A(1); A(2); A(3); N$(1); N$(2) +> RUN +102030HelloWorld +> +``` + +Array indices can be complex expressions including variables and arithmetic: + +``` +> 10 DIM B(10) +> 20 I = 2 +> 30 DATA 100, 200 +> 40 READ B(I*3), B(I+4) +> 50 PRINT "B(6)="; B(6) REM Shows 200 (second value overwrites first) +> RUN +B(6)=200 +> +``` + +Proper error handling is provided: +- **SUBSCRIPT ERROR**: When array indices are out of bounds or negative +- **OUT OF DATA**: When there are no more DATA items to read +- Index validation occurs BEFORE consuming DATA items ### Comments @@ -720,6 +763,41 @@ Num, Str, Num: 22, hello!, 33 > ``` +#### Array Support in INPUT + +The **INPUT** statement now supports inputting data directly into array elements: + +``` +> 10 DIM A(3) +> 20 DIM N$(2) +> 30 INPUT "Enter 3 numbers: "; A(1), A(2), A(3) +> 40 INPUT "Enter 2 names: "; N$(1), N$(2) +> 50 PRINT A(1); A(2); A(3); N$(1); N$(2) +> RUN +Enter 3 numbers: 5, 10, 15 +Enter 2 names: Alice, Bob +51015AliceBob +> +``` + +Array indices can use expressions with variables: + +``` +> 10 DIM B(10) +> 20 I = 5 +> 30 INPUT "Enter value for B(I): "; B(I) +> 40 PRINT "B(5)="; B(5) +> RUN +Enter value for B(I): 42 +B(5)=42 +> +``` + +Error handling includes: +- **SUBSCRIPT ERROR**: When array indices are out of bounds +- **"? REDO FROM START"**: When input types don't match array types +- Index validation occurs BEFORE prompting for input + A mismatch between the input value and input variable type will trigger an error, and the user will be asked to re-input the values again. @@ -927,6 +1005,8 @@ and expanded by Don Woods. * *Pneuma.bas* - A brief 21st century take on the venerable text adventure. +* *array_input_read_tests.bas* - A comprehensive test program that validates array INPUT and READ functionality, including expression indices, boundary conditions, error handling, and mixed data types. Demonstrates all the features of array element input and data reading. + ## Informal grammar definition **ABS**(*numerical-expression*) - Calculates the absolute value of the result of *numerical-expression* diff --git a/basicparser.py b/basicparser.py index 8a60689..40fa5cb 100644 --- a/basicparser.py +++ b/basicparser.py @@ -56,6 +56,9 @@ def __init__(self, dimensions, elem_type): raise SyntaxError("Fractional array size specified") dimensions[i] = int(dimensions[i]) + # Store original dimensions for bounds checking + self.original_dims = dimensions.copy() + # MSBASIC: Initialize to Zero # MSBASIC: Overdim by one, as some dialects are 1 based and expect # to use the last item at index = size @@ -596,19 +599,7 @@ def __arrayassignmentstmt(self, name): ' in line ' + str(self.__line_number)) # Assign to the specified array index - try: - if len(indexvars) == 1: - BASICarray.data[indexvars[0]] = right - - elif len(indexvars) == 2: - BASICarray.data[indexvars[0]][indexvars[1]] = right - - elif len(indexvars) == 3: - BASICarray.data[indexvars[0]][indexvars[1]][indexvars[2]] = right - - except IndexError: - raise IndexError('Array index out of range in line ' + - str(self.__line_number)) + self.__assign_array_val(BASICarray, indexvars, right) def __openstmt(self): """Parses an open statement, opens the indicated file and @@ -748,7 +739,7 @@ def __fseekstmt(self): def __inputstmt(self): """Parses an input statement, extracts the input from the user and places the values into the - symbol table + symbol table. Now supports array elements. """ self.__advance() # Advance past INPUT token @@ -781,61 +772,64 @@ def __inputstmt(self): prompt = self.__operand_stack.pop() self.__consume(Token.SEMICOLON) - # Acquire the comma separated input variables - variables = [] + # Parse the target variables (simple variables or array elements) + targets = [] if not self.__tokenindex >= len(self.__tokenlist): - if self.__token.category != Token.NAME: - raise ValueError('Expecting NAME in INPUT statement ' + - 'in line ' + str(self.__line_number)) - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable + # Parse first target + targets.append(self.__parse_variable_target()) while self.__token.category == Token.COMMA: self.__advance() # Advance past comma - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable + targets.append(self.__parse_variable_target()) valid_input = False while not valid_input: # Gather input from the user into the variables if fileIO: - inputvals = ((self.__file_handles[filenum].readline().replace("\n","")).replace("\r","")).split(',', (len(variables)-1)) + inputvals = ((self.__file_handles[filenum].readline().replace("\n","")).replace("\r","")).split(',', (len(targets)-1)) valid_input = True else: - inputvals = input(prompt).split(',', (len(variables)-1)) - - for variable in variables: - left = variable + inputvals = input(prompt).split(',', (len(targets)-1)) - try: - right = inputvals.pop(0) + try: + for target in targets: + try: + right = inputvals.pop(0) - if left.endswith('$'): - self.__symbol_table[left] = str(right) + if target['is_string']: + value = str(right) + else: + # Try to convert to numeric + try: + if '.' in right: + value = float(right) + else: + value = int(right) + except ValueError: + if not fileIO: + valid_input = False + print('Non-numeric input provided to a numeric variable - redo from start') + break + raise ValueError('Non-numeric input provided to a numeric variable ' + + 'in line ' + str(self.__line_number)) + + # Assign value to target (validates array indices if needed) + self.__assign_to_target(target, value) valid_input = True - elif not left.endswith('$'): - try: - if '.' in right: - self.__symbol_table[left] = float(right) - - else: - self.__symbol_table[left] = int(right) - - valid_input = True - - except ValueError: - if not fileIO: - valid_input = False - print('Non-numeric input provided to a numeric variable - redo from start') + except IndexError: + # No more input to process + if not fileIO: + valid_input = False + print('Not enough values input - redo from start') break + raise RuntimeError('Not enough input values in line ' + str(self.__line_number)) - except IndexError: - # No more input to process - if not fileIO: - valid_input = False - print('Not enough values input - redo from start') - break + except RuntimeError as e: + if 'SUBSCRIPT ERROR' in str(e): + raise e # Re-raise subscript errors immediately + else: + raise e def __restorestmt(self): @@ -851,50 +845,68 @@ def __datastmt(self): """Parses a DATA statement""" def __readstmt(self): - """Parses a READ statement.""" + """Parses a READ statement. Now supports array elements.""" self.__advance() # Advance past READ token - # Acquire the comma separated input variables - variables = [] + # Parse the target variables (simple variables or array elements) + targets = [] if not self.__tokenindex >= len(self.__tokenlist): - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable + # Parse first target + targets.append(self.__parse_variable_target()) while self.__token.category == Token.COMMA: self.__advance() # Advance past comma - variables.append(self.__token.lexeme) - self.__advance() # Advance past variable + targets.append(self.__parse_variable_target()) - # Gather input from the DATA statement into the variables - for variable in variables: + # Process each target + for target in targets: + # Validate array indices BEFORE reading data (per specification) + if target['type'] == 'array': + BASICarray = self.__symbol_table[target['array_name']] + self.__validate_array_indices(BASICarray, target['indices']) + + # Get data value + if len(self.__data_values) < 1: + try: + self.__data_values = self.__data.readData(self.__line_number) + except RuntimeError as e: + if "No DATA statements available" in str(e): + raise RuntimeError('? OUT OF DATA in line ' + str(self.__line_number)) + else: + raise e if len(self.__data_values) < 1: - self.__data_values = self.__data.readData(self.__line_number) + raise RuntimeError('? OUT OF DATA in line ' + str(self.__line_number)) - left = variable right = self.__data_values.pop(0) - if left.endswith('$'): - # Python inserts quotes around input data - if isinstance(right, int): - raise ValueError('Non-string input provided to a string variable ' + - 'in line ' + str(self.__line_number)) - + if target['is_string']: + # String target - accept any data type as string + if isinstance(right, (int, float)): + # Convert numeric data to string + value = str(right) else: - self.__symbol_table[left] = right - - elif not left.endswith('$'): + # Already a string + value = right + else: + # Numeric target - must be numeric data try: - numeric = float(right) - if int(numeric) == numeric: - numeric = int(numeric) - self.__symbol_table[left] = numeric - + if isinstance(right, str): + # Try to parse string as number + numeric = float(right) if '.' in right else int(right) + else: + numeric = float(right) + if int(numeric) == numeric: + numeric = int(numeric) + value = numeric except ValueError: raise ValueError('Non-numeric input provided to a numeric variable ' + 'in line ' + str(self.__line_number)) + # Assign value to target + self.__assign_to_target(target, value) + def __expr(self): """Parses a numerical expression consisting of two terms being added or subtracted, @@ -1066,9 +1078,7 @@ def __get_array_val(self, BASICarray, indexvars): :return: The value at the indexed position in the array """ - if BASICarray.dims != len(indexvars): - raise IndexError('Incorrect number of indices applied to array ' + - 'in line ' + str(self.__line_number)) + self.__validate_array_indices(BASICarray, indexvars) # Fetch the value from the array try: @@ -1082,11 +1092,120 @@ def __get_array_val(self, BASICarray, indexvars): arrayval = BASICarray.data[indexvars[0]][indexvars[1]][indexvars[2]] except IndexError: - raise IndexError('Array index out of range in line ' + - str(self.__line_number)) + raise RuntimeError('SUBSCRIPT ERROR in line ' + str(self.__line_number)) return arrayval + def __validate_array_indices(self, BASICarray, indexvars): + """Validates array indices and raises SUBSCRIPT ERROR if invalid + + :param BASICarray: The BASICArray to validate against + :param indexvars: List of index values to validate + :raises RuntimeError: If indices are out of bounds + """ + if BASICarray.dims != len(indexvars): + raise RuntimeError('SUBSCRIPT ERROR in line ' + str(self.__line_number)) + + for i, index in enumerate(indexvars): + # Convert float to int (truncate toward zero) + if isinstance(index, float): + index = int(index) + indexvars[i] = index + + # Check bounds - negative or greater than declared dimension + if index < 0 or index > BASICarray.original_dims[i]: + raise RuntimeError('SUBSCRIPT ERROR in line ' + str(self.__line_number)) + + def __assign_array_val(self, BASICarray, indexvars, value): + """Assigns a value to an array element after validating indices + + :param BASICarray: The BASICArray + :param indexvars: List of indices + :param value: Value to assign + """ + self.__validate_array_indices(BASICarray, indexvars) + + try: + if len(indexvars) == 1: + BASICarray.data[indexvars[0]] = value + elif len(indexvars) == 2: + BASICarray.data[indexvars[0]][indexvars[1]] = value + elif len(indexvars) == 3: + BASICarray.data[indexvars[0]][indexvars[1]][indexvars[2]] = value + except IndexError: + raise RuntimeError('SUBSCRIPT ERROR in line ' + str(self.__line_number)) + + def __parse_variable_target(self): + """Parses a variable target which can be a simple variable or array element + + :return: Dictionary with 'type' ('simple' or 'array'), 'name' (variable name), + 'indices' (list of index values for arrays), 'is_string' (boolean) + """ + if self.__token.category != Token.NAME: + raise SyntaxError('Expecting variable name in line ' + str(self.__line_number)) + + var_name = self.__token.lexeme + is_string = var_name.endswith('$') + self.__advance() + + # Check if this is an array element + if self.__token.category == Token.LEFTPAREN: + # Array element target + array_name = var_name + '_array' + + # Check if array exists + if array_name not in self.__symbol_table: + raise RuntimeError('Array not dimensioned in line ' + str(self.__line_number)) + + self.__advance() # Past LEFTPAREN + + # Parse index expressions + indexvars = [] + self.__expr() + indexvars.append(self.__operand_stack.pop()) + + while self.__token.category == Token.COMMA: + self.__advance() # Past comma + self.__expr() + indexvars.append(self.__operand_stack.pop()) + + self.__consume(Token.RIGHTPAREN) + + return { + 'type': 'array', + 'name': var_name, + 'array_name': array_name, + 'indices': indexvars, + 'is_string': is_string + } + else: + # Simple variable target + return { + 'type': 'simple', + 'name': var_name, + 'is_string': is_string + } + + def __assign_to_target(self, target, value): + """Assigns a value to a target (simple variable or array element) + + :param target: Target dictionary from __parse_variable_target + :param value: Value to assign + """ + # Type checking + if target['is_string'] and not isinstance(value, str): + raise ValueError('Non-string input provided to a string variable in line ' + + str(self.__line_number)) + elif not target['is_string'] and isinstance(value, str): + raise ValueError('Non-numeric input provided to a numeric variable in line ' + + str(self.__line_number)) + + if target['type'] == 'simple': + self.__symbol_table[target['name']] = value + else: # array + BASICarray = self.__symbol_table[target['array_name']] + self.__assign_array_val(BASICarray, target['indices'], value) + def __compoundstmt(self): """Parses compound statements, specifically if-then-else and From 354f464c529127a60ed27fedfe36eea417f44863 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 28 Nov 2025 17:34:57 +0100 Subject: [PATCH 172/183] Add a GitHub Action to run pytest --- flowsignal.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/flowsignal.py b/flowsignal.py index 214eb68..e01acf5 100644 --- a/flowsignal.py +++ b/flowsignal.py @@ -21,12 +21,14 @@ return address need to be added to the return stack. >>> flowsignal = FlowSignal(ftype=FlowSignal.RETURN) ->>> print(flowsignal.ftarget) --1 +>>> flowsignal.ftarget is None +True +>>> flowsignal.ftype +5 >>> flowsignal = FlowSignal(ftarget=100, ftype=FlowSignal.SIMPLE_JUMP) ->>> print(flowsignal.ftarget) +>>> flowsignal.ftarget 100 ->>> print(flowsignal.ftype) +>>> flowsignal.ftype 0 """ From d4a5c5e24726582d600482edc22ff5a94969422a Mon Sep 17 00:00:00 2001 From: richpl Date: Fri, 28 Nov 2025 17:23:16 +0000 Subject: [PATCH 173/183] Update basictoken.py Adjusted token ordering --- basictoken.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/basictoken.py b/basictoken.py index 85e5698..0f66bfd 100644 --- a/basictoken.py +++ b/basictoken.py @@ -119,9 +119,9 @@ class BASICToken: SEMICOLON = 86 # SEMICOLON LEFT = 87 # LEFT$ function RIGHT = 88 # RIGHT$ function - RENUMBER = 91 # RENUMBER command WHILE = 89 # WHILE keyword WEND = 90 # WEND keyword + RENUMBER = 91 # RENUMBER command # Displayable names for each token category catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN', @@ -140,8 +140,8 @@ class BASICToken: 'MAX', 'MIN', 'INSTR', 'AND', 'OR', 'NOT', 'PI', 'RNDINT', 'OPEN', 'HASH', 'CLOSE', 'FSEEK', 'APPEND', 'OUTPUT', 'RESTORE', 'RNDINT', 'TAB', 'SEMICOLON', - 'LEFT', 'RIGHT', 'RENUMBER', - 'WHILE', 'WEND'] + 'LEFT', 'RIGHT', + 'WHILE', 'WEND', 'RENUMBER'] smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN, '+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE, @@ -203,3 +203,4 @@ def pretty_print(self): def print_lexeme(self): print(self.lexeme, end=' ') + From 06cfa7b7993673f70e8a6004a05ffa2af8a37197 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 28 Nov 2025 17:34:57 +0100 Subject: [PATCH 174/183] Add a GitHub Action to run pytest --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c5096e7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,8 @@ +name: ci +on: [pull_request, push] +jobs: + pytest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - run: pipx run pytest --doctest-modules From ca5cb3fd4e1d7ee2a7135178a6711965863d1412 Mon Sep 17 00:00:00 2001 From: Ken Perry Date: Fri, 28 Nov 2025 23:22:35 -0500 Subject: [PATCH 175/183] Fixed the problem where negative numbers have space between '-' and number --- README.md | 41 ++++++++++++++++++++++++++++++++++++----- program.py | 24 +++++++++++++++++++++--- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a700220..9958d79 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ Program terminated ### Statement structure As per usual in old school BASIC, all program statements must be prefixed with a line number which indicates the order in which the -statements may be executed. A statement may be modified or +statements may be executed. There is no renumber command to allow all line numbers to be modified. A statement may be modified or replaced by re-entering a statement with the same line number: ``` @@ -389,6 +389,40 @@ Proper error handling is provided: - **OUT OF DATA**: When there are no more DATA items to read - Index validation occurs BEFORE consuming DATA items +#### Array Support in READ + +The **READ** statement now supports reading data directly into array elements, using expressions for array indices: + +``` +> 10 DIM A(5) +> 20 DIM N$(3) +> 30 DATA 10, 20, 30, "Hello", "World" +> 40 READ A(1), A(2), A(3), N$(1), N$(2) +> 50 PRINT A(1); A(2); A(3); N$(1); N$(2) +> RUN +102030HelloWorld +> +``` + +Array indices can be complex expressions including variables and arithmetic: + +``` +> 10 DIM B(10) +> 20 I = 2 +> 30 DATA 100, 200 +> 40 READ B(I*3), B(I+4) +> 50 PRINT "B(6)="; B(6) REM Shows 200 (second value overwrites first) +> RUN +B(6)=200 +> +``` + +Proper error handling is provided: +- **SUBSCRIPT ERROR**: When array indices are out of bounds or negative +- **OUT OF DATA**: When there are no more DATA items to read +- Index validation occurs BEFORE consuming DATA items + + ### Comments The **REM** statement is used to indicate a comment, and occupies an entire statement. It has no effect on execution: @@ -1230,11 +1264,8 @@ a subroutine call, or program termination. This paradigm of using the parser to object to make control flow decisions and to track execution, and a signalling mechanism to allow the parser to signal control flow changes to the Program object, is used consistently throughout the implementation. -## Open issues +## Python Basic feature -* It is not possible to renumber a program. This would require considerable extra functionality. -* Negative values are printed with a space (e.g. '- 5') in program listings because of tokenization. This does not affect functionality. -* User input values cannot be directly assigned to array variables in an **INPUT** or **READ** statement * Strings representing numbers (e.g. "10") can actually be assigned to numeric variables in **INPUT** and **READ** statements without an error, Python will silently convert them to integers. diff --git a/program.py b/program.py index 18bb71e..70b1d5e 100644 --- a/program.py +++ b/program.py @@ -175,13 +175,31 @@ def str_statement(self, line_number): statement = self.__program[line_number] if statement[0].category == Token.DATA: statement = self.__data.getTokens(line_number) - for token in statement: + + # Track operators and contexts where minus might be unary + operators_and_contexts = { + Token.PLUS, Token.MINUS, Token.TIMES, Token.DIVIDE, Token.MODULO, + Token.ASSIGNOP, Token.EQUAL, Token.NOTEQUAL, Token.GREATER, + Token.LESSER, Token.LESSEQUAL, Token.GREATEQUAL, + Token.LEFTPAREN, Token.COMMA, Token.AND, Token.OR + } + + for i, token in enumerate(statement): # Add in quotes for strings if token.category == Token.STRING: line_text += '"' + token.lexeme + '" ' - else: - line_text += token.lexeme + " " + # Check if this is a minus sign that should be treated as unary + if (token.category == Token.MINUS and + i + 1 < len(statement) and + statement[i + 1].category in {Token.UNSIGNEDINT, Token.UNSIGNEDFLOAT} and + (i == 0 or statement[i - 1].category in operators_and_contexts)): + # This is a unary minus, don't add space after it + line_text += token.lexeme + else: + # Normal token, add space after + line_text += token.lexeme + " " + line_text += "\n" return line_text From d806ef6f9bc2e5a8e0cb49aa9602d0fafcbaef9c Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 29 Nov 2025 16:46:52 +0000 Subject: [PATCH 176/183] Revert "Add a GitHub Action to run pytest" --- .github/workflows/ci.yml | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index c5096e7..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: ci -on: [pull_request, push] -jobs: - pytest: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - run: pipx run pytest --doctest-modules From 174df8a5a6fbeab90b579de153f8d37976e8da16 Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 29 Nov 2025 16:47:46 +0000 Subject: [PATCH 177/183] Revert "Add a GitHub Action to run pytest" --- flowsignal.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/flowsignal.py b/flowsignal.py index e01acf5..214eb68 100644 --- a/flowsignal.py +++ b/flowsignal.py @@ -21,14 +21,12 @@ return address need to be added to the return stack. >>> flowsignal = FlowSignal(ftype=FlowSignal.RETURN) ->>> flowsignal.ftarget is None -True ->>> flowsignal.ftype -5 +>>> print(flowsignal.ftarget) +-1 >>> flowsignal = FlowSignal(ftarget=100, ftype=FlowSignal.SIMPLE_JUMP) ->>> flowsignal.ftarget +>>> print(flowsignal.ftarget) 100 ->>> flowsignal.ftype +>>> print(flowsignal.ftype) 0 """ From 7173834d93d4f126b8d4ad495ba513766629575a Mon Sep 17 00:00:00 2001 From: richpl Date: Sat, 29 Nov 2025 17:26:43 +0000 Subject: [PATCH 178/183] Update flowsignal.py --- flowsignal.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flowsignal.py b/flowsignal.py index 214eb68..7c741bf 100644 --- a/flowsignal.py +++ b/flowsignal.py @@ -121,4 +121,5 @@ def __init__(self, ftarget=None, ftype=SIMPLE_JUMP, floop_var=None): self.ftype = ftype self.ftarget = ftarget - self.floop_var = floop_var \ No newline at end of file + + self.floop_var = floop_var From b8a4d6c445e3a704577dc3226cda1c6b8c5a8049 Mon Sep 17 00:00:00 2001 From: richpl Date: Sun, 30 Nov 2025 10:03:10 +0000 Subject: [PATCH 179/183] Fixed doctests --- flowsignal.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/flowsignal.py b/flowsignal.py index 7c741bf..65f7bae 100644 --- a/flowsignal.py +++ b/flowsignal.py @@ -21,12 +21,14 @@ return address need to be added to the return stack. >>> flowsignal = FlowSignal(ftype=FlowSignal.RETURN) ->>> print(flowsignal.ftarget) --1 +>>> flowsignal.ftarget is None +True +>>> flowsignal.ftype +5 >>> flowsignal = FlowSignal(ftarget=100, ftype=FlowSignal.SIMPLE_JUMP) ->>> print(flowsignal.ftarget) +>>> flowsignal.ftarget 100 ->>> print(flowsignal.ftype) +>>> flowsignal.ftype 0 """ @@ -123,3 +125,4 @@ def __init__(self, ftarget=None, ftype=SIMPLE_JUMP, floop_var=None): self.ftarget = ftarget self.floop_var = floop_var + From f308820bc8abe6cd4a5480c579b36fb6159cc03f Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 30 Nov 2025 13:30:24 +0100 Subject: [PATCH 180/183] README.md: Now there is a RENUMBER command Fixed in: * #85 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9958d79..3bf22e1 100644 --- a/README.md +++ b/README.md @@ -213,8 +213,7 @@ Program terminated ### Statement structure As per usual in old school BASIC, all program statements must be prefixed with a line number which indicates the order in which the -statements may be executed. There is no renumber command to allow all line numbers to be modified. A statement may be modified or -replaced by re-entering a statement with the same line number: +statements may be executed. A statement may be modified or replaced by re-entering a statement with the same line number: ``` > 10 LET I = 10 @@ -1272,3 +1271,4 @@ error, Python will silently convert them to integers. ## License PyBasic is made available under the GNU General Public License, version 3.0 or later (GPL-3.0-or-later). + From d526441f69d078d1af6a5b242c5642c7327f31b9 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 30 Nov 2025 22:04:37 -0500 Subject: [PATCH 181/183] make platform 'newlines' identification more robust --- basicparser.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/basicparser.py b/basicparser.py index 0e589c6..1120933 100644 --- a/basicparser.py +++ b/basicparser.py @@ -666,22 +666,30 @@ def __openstmt(self): if accessMode == "r+": # By checking the 'newlines' attribute the appropriate adjustment for # the file being opened can be determined. + + try: + # newline attribute is only set after a line is read + # manually identify newlines in case newlines attribute isn't supported + fileline = self.__file_handles[filenum].readline() + newlines = "" + for ichar in range(len(fileline)-1,-1,-1): + if fileline[ichar] not in ['\n','\r']: + break + newlines = fileline[ichar:] + except: + newlines = None + if hasattr(self.__file_handles[filenum],'newlines'): - try: - # newline attribute is only set after a line is read - self.__file_handles[filenum].readline() - except: - pass newlines = self.__file_handles[filenum].newlines - else: - newlines = None - + self.__file_handles[filenum].seek(0) filelen = 0 - if newlines != None: + + if newlines is not None: newlineAdj = len(newlines) - 1 else: - # If using version of Python that doesn't have newlines attribute + # If we wern't able to determine an appropriate value and + # using version of Python that doesn't have newlines attribute # use adjustment appropriate for Windows formatted files newlineAdj = 1 From 91bc5f7c7a3545ae5e7c5326a4a0bddf872c4339 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 29 Nov 2025 20:56:48 +0100 Subject: [PATCH 182/183] Whitespace fixes --- basicparser.py | 42 +++++++++++----------- interpreter.py | 12 +++---- lexer.py | 2 +- program.py | 98 +++++++++++++++++++++++++------------------------- 4 files changed, 77 insertions(+), 77 deletions(-) diff --git a/basicparser.py b/basicparser.py index 1120933..5d2399e 100644 --- a/basicparser.py +++ b/basicparser.py @@ -1052,7 +1052,7 @@ def __factor(self): else: #default int self.__operand_stack.append(0) - + self.__advance() elif self.__token.category == Token.LEFTPAREN: @@ -1106,33 +1106,33 @@ def __get_array_val(self, BASICarray, indexvars): def __validate_array_indices(self, BASICarray, indexvars): """Validates array indices and raises SUBSCRIPT ERROR if invalid - + :param BASICarray: The BASICArray to validate against :param indexvars: List of index values to validate :raises RuntimeError: If indices are out of bounds """ if BASICarray.dims != len(indexvars): raise RuntimeError('SUBSCRIPT ERROR in line ' + str(self.__line_number)) - + for i, index in enumerate(indexvars): # Convert float to int (truncate toward zero) if isinstance(index, float): index = int(index) indexvars[i] = index - + # Check bounds - negative or greater than declared dimension if index < 0 or index > BASICarray.original_dims[i]: raise RuntimeError('SUBSCRIPT ERROR in line ' + str(self.__line_number)) - + def __assign_array_val(self, BASICarray, indexvars, value): """Assigns a value to an array element after validating indices - + :param BASICarray: The BASICArray :param indexvars: List of indices :param value: Value to assign """ self.__validate_array_indices(BASICarray, indexvars) - + try: if len(indexvars) == 1: BASICarray.data[indexvars[0]] = value @@ -1145,40 +1145,40 @@ def __assign_array_val(self, BASICarray, indexvars, value): def __parse_variable_target(self): """Parses a variable target which can be a simple variable or array element - + :return: Dictionary with 'type' ('simple' or 'array'), 'name' (variable name), 'indices' (list of index values for arrays), 'is_string' (boolean) """ if self.__token.category != Token.NAME: raise SyntaxError('Expecting variable name in line ' + str(self.__line_number)) - + var_name = self.__token.lexeme is_string = var_name.endswith('$') self.__advance() - + # Check if this is an array element if self.__token.category == Token.LEFTPAREN: # Array element target array_name = var_name + '_array' - + # Check if array exists if array_name not in self.__symbol_table: raise RuntimeError('Array not dimensioned in line ' + str(self.__line_number)) - + self.__advance() # Past LEFTPAREN - + # Parse index expressions indexvars = [] self.__expr() indexvars.append(self.__operand_stack.pop()) - + while self.__token.category == Token.COMMA: self.__advance() # Past comma self.__expr() indexvars.append(self.__operand_stack.pop()) - + self.__consume(Token.RIGHTPAREN) - + return { 'type': 'array', 'name': var_name, @@ -1193,21 +1193,21 @@ def __parse_variable_target(self): 'name': var_name, 'is_string': is_string } - + def __assign_to_target(self, target, value): """Assigns a value to a target (simple variable or array element) - + :param target: Target dictionary from __parse_variable_target :param value: Value to assign """ # Type checking if target['is_string'] and not isinstance(value, str): - raise ValueError('Non-string input provided to a string variable in line ' + + raise ValueError('Non-string input provided to a string variable in line ' + str(self.__line_number)) elif not target['is_string'] and isinstance(value, str): - raise ValueError('Non-numeric input provided to a numeric variable in line ' + + raise ValueError('Non-numeric input provided to a numeric variable in line ' + str(self.__line_number)) - + if target['type'] == 'simple': self.__symbol_table[target['name']] = value else: # array diff --git a/interpreter.py b/interpreter.py index 1974b24..5e5ab9c 100644 --- a/interpreter.py +++ b/interpreter.py @@ -32,10 +32,10 @@ def main(): banner = (r""" - ._____________ ___________. ___ ___________ ______ + ._____________ ___________. ___ ___________ ______ | _ .__ \ / ___ _ \ / \ / | / | | |_) | \ \/ / | |_) | / ^ \ | (----`| | | ,----' - | ___/ \_ _/ | _ < / /_\ \ \ \ | | | | + | ___/ \_ _/ | _ < / /_\ \ \ \ | | | | | | | | | |_) \/ _____ \----) | | | | `----. | _| |__| |___________/ \_________/ |____________| """) @@ -127,7 +127,7 @@ def main(): # Parse comma-separated arguments, handling blank parameters args = [] current_arg = "" - + for i in range(1, len(tokenlist)): if tokenlist[i].category == Token.COMMA: # Process the accumulated argument @@ -138,20 +138,20 @@ def main(): current_arg = "" elif tokenlist[i].category == Token.UNSIGNEDINT: current_arg += tokenlist[i].lexeme - + # Process the final argument if any if current_arg.strip(): args.append(int(current_arg.strip())) elif len(tokenlist) > 1 and tokenlist[-1].category == Token.COMMA: args.append(None) # Trailing comma means blank parameter - + # Convert args list to proper parameters for renumber() # RENUMBER [newStart][,increment][,oldStart][,oldEnd] new_start = args[0] if len(args) > 0 and args[0] is not None else 10 increment = args[1] if len(args) > 1 and args[1] is not None else 10 old_start = args[2] if len(args) > 2 and args[2] is not None else None old_end = args[3] if len(args) > 3 and args[3] is not None else None - + program.renumber(new_start, increment, old_start, old_end) print("Program renumbered") except Exception as e: diff --git a/lexer.py b/lexer.py index 4e35680..47da604 100644 --- a/lexer.py +++ b/lexer.py @@ -199,4 +199,4 @@ def __get_next_char(self): if __name__ == "__main__": import doctest - doctest.testmod() \ No newline at end of file + doctest.testmod() diff --git a/program.py b/program.py index 70b1d5e..03642d1 100644 --- a/program.py +++ b/program.py @@ -151,7 +151,7 @@ def __init__(self): # return dictionary for loop returns self.__return_loop = {} - + # WHILE loop tracking stack - separate from GOSUB return stack # Each entry contains the line number of the WHILE statement self.__while_stack = [] @@ -175,23 +175,23 @@ def str_statement(self, line_number): statement = self.__program[line_number] if statement[0].category == Token.DATA: statement = self.__data.getTokens(line_number) - + # Track operators and contexts where minus might be unary operators_and_contexts = { Token.PLUS, Token.MINUS, Token.TIMES, Token.DIVIDE, Token.MODULO, - Token.ASSIGNOP, Token.EQUAL, Token.NOTEQUAL, Token.GREATER, + Token.ASSIGNOP, Token.EQUAL, Token.NOTEQUAL, Token.GREATER, Token.LESSER, Token.LESSEQUAL, Token.GREATEQUAL, Token.LEFTPAREN, Token.COMMA, Token.AND, Token.OR } - + for i, token in enumerate(statement): # Add in quotes for strings if token.category == Token.STRING: line_text += '"' + token.lexeme + '" ' else: # Check if this is a minus sign that should be treated as unary - if (token.category == Token.MINUS and - i + 1 < len(statement) and + if (token.category == Token.MINUS and + i + 1 < len(statement) and statement[i + 1].category in {Token.UNSIGNEDINT, Token.UNSIGNEDFLOAT} and (i == 0 or statement[i - 1].category in operators_and_contexts)): # This is a unary minus, don't add space after it @@ -199,7 +199,7 @@ def str_statement(self, line_number): else: # Normal token, add space after line_text += token.lexeme + " " - + line_text += "\n" return line_text @@ -318,7 +318,7 @@ def __validate_while_wend(self): """Validate that all WHILE statements have matching WEND statements""" while_stack = [] line_numbers = self.line_numbers() - + for line_num in line_numbers: statement = self.__program[line_num] if statement and len(statement) > 0: @@ -328,7 +328,7 @@ def __validate_while_wend(self): if not while_stack: raise RuntimeError(f"WEND without matching WHILE at line {line_num}") while_stack.pop() - + if while_stack: raise RuntimeError(f"WHILE without matching WEND at line {while_stack[0]}") @@ -344,7 +344,7 @@ def execute(self): self.__parser = BASICParser(self.__data) self.__data.restore(0) # reset data pointer - + # Validate WHILE-WEND pairs before execution self.__validate_while_wend() @@ -372,7 +372,7 @@ def execute(self): target_line = flowsignal.ftarget current_line = self.get_next_line_number() self.__clear_while_stack_on_goto(current_line, target_line) - + try: index = line_numbers.index(flowsignal.ftarget) @@ -602,13 +602,13 @@ def set_next_line_number(self, line_number): def renumber(self, new_start=10, increment=10, old_start=None, old_end=None): """Renumber the program according to BASIC RENUMBER command specification - + :param new_start: First line number assigned during renumbering (default: 10) - :param increment: Amount added to each successive line number (default: 10) + :param increment: Amount added to each successive line number (default: 10) :param old_start: Lowest line number to renumber (default: first line) :param old_end: Highest line number to renumber (default: last line) """ - + # Validate parameters if increment == 0: raise ValueError("Increment cannot be zero") @@ -616,38 +616,38 @@ def renumber(self, new_start=10, increment=10, old_start=None, old_end=None): raise ValueError("New start line number must be >= 1") if increment < 0: raise ValueError("Increment must be positive") - + line_numbers = self.line_numbers() if not line_numbers: return # No program to renumber - + # Set defaults for old_start and old_end if old_start is None: old_start = line_numbers[0] if old_end is None: old_end = line_numbers[-1] - + # Find lines within the renumbering range lines_to_renumber = [] for line_num in line_numbers: if old_start <= line_num <= old_end: lines_to_renumber.append(line_num) - + if not lines_to_renumber: return # No lines in range to renumber - + # Step 1: Create mapping of old line numbers to new line numbers line_mapping = {} new_line_num = new_start - + for old_line_num in lines_to_renumber: line_mapping[old_line_num] = new_line_num new_line_num += increment - + # Check for overflow (basic line numbers typically max at 65535) if new_line_num - increment > 65535: raise ValueError("Line numbers would exceed maximum value (65535)") - + # Check for conflicts with existing line numbers outside the range for new_num in line_mapping.values(): if new_num in line_numbers and new_num not in lines_to_renumber: @@ -655,66 +655,66 @@ def renumber(self, new_start=10, increment=10, old_start=None, old_end=None): for ln in line_numbers: if ln == new_num and ln not in lines_to_renumber: raise ValueError(f"New line number {new_num} conflicts with existing line {ln}") - + # Step 2: Update line number references in all program statements updated_program = {} updated_data = {} - + for line_num in line_numbers: statement = self.__program[line_num] - + # Update line number references within the statement updated_statement = self._update_line_references(statement, line_mapping) - + # Determine the new line number for this statement if line_num in line_mapping: new_line_num = line_mapping[line_num] else: new_line_num = line_num - + updated_program[new_line_num] = updated_statement - + # Handle DATA statements if statement and statement[0].category == Token.DATA: data_tokens = self.__data.getTokens(line_num) if data_tokens: updated_data[new_line_num] = data_tokens - + # Step 3: Replace the program with the updated version self.__program = updated_program - + # Update DATA storage self.__data.delete() for line_num, data_tokens in updated_data.items(): self.__data.addData(line_num, data_tokens) - + def _update_line_references(self, statement, line_mapping): """Update line number references within a statement - + :param statement: List of tokens representing the statement :param line_mapping: Dictionary mapping old line numbers to new ones :return: Updated statement with new line number references """ if not statement: return statement - + updated_statement = [] i = 0 - + while i < len(statement): token = statement[i] - + # Skip string literals and comments - they should not be modified if token.category == Token.STRING: updated_statement.append(token) i += 1 continue - + if token.category == Token.REM: # Everything after REM is a comment, copy rest as-is updated_statement.extend(statement[i:]) break - + # Check for line number references in specific contexts if self._is_line_number_reference(statement, i): if token.category == Token.UNSIGNEDINT: @@ -729,35 +729,35 @@ def _update_line_references(self, statement, line_mapping): updated_statement.append(token) else: updated_statement.append(token) - + i += 1 - + return updated_statement - + def _is_line_number_reference(self, statement, token_index): """Determine if the token at the given index is a line number reference - - :param statement: List of tokens representing the statement + + :param statement: List of tokens representing the statement :param token_index: Index of the token to check :return: True if this token is a line number reference """ if token_index >= len(statement): return False - + token = statement[token_index] if token.category != Token.UNSIGNEDINT: return False - + # Check what comes before this number to determine context if token_index == 0: return False # First token is the line number itself, not a reference - + prev_token = statement[token_index - 1] - + # Direct line number references if prev_token.category in [Token.GOTO, Token.GOSUB, Token.THEN, Token.RESTORE]: return True - + # ON...GOTO and ON...GOSUB constructs if prev_token.category == Token.COMMA: # Look backward to find ON...GOTO or ON...GOSUB @@ -770,7 +770,7 @@ def _is_line_number_reference(self, statement, token_index): break elif statement[j].category not in [Token.UNSIGNEDINT, Token.COMMA, Token.NAME]: break - + # Check for ON...GOTO/GOSUB patterns if token_index >= 2: # Look for pattern: ON GOTO/GOSUB @@ -781,5 +781,5 @@ def _is_line_number_reference(self, statement, token_index): if statement[k].category in [Token.GOTO, Token.GOSUB]: return True break - + return False From 73b2dba1504cbc7e7f7c74b6d0d3f12ff0abb5ad Mon Sep 17 00:00:00 2001 From: richpl Date: Thu, 12 Feb 2026 20:57:31 +0000 Subject: [PATCH 183/183] Generates the specified number of digits of Pi --- examples/pi_spigot.bas | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 examples/pi_spigot.bas diff --git a/examples/pi_spigot.bas b/examples/pi_spigot.bas new file mode 100644 index 0000000..a727b09 --- /dev/null +++ b/examples/pi_spigot.bas @@ -0,0 +1,22 @@ +100 REM PI SPIGOT ALGORITHM +110 INPUT "How many digits?: ";N +120 L=INT(10*N/3)+1 +130 DIM A(300) +140 FOR I=1 TO L +150 A(I)=2 +160 NEXT I +170 FOR J=1 TO N +180 Q=0 +190 FOR I=L TO 1 STEP -1 +200 X=10*A(I)+Q*I +210 D=2*I-1 +220 A(I)=X-D*INT(X/D) +230 Q=INT(X/D) +240 NEXT I +250 A(1)=Q-10*INT(Q/10) +260 PRINT STR$(INT(Q/10)); +270 IF J=1 THEN PRINT "."; +280 NEXT J +290 PRINT +300 END +