Add files via upload

This commit is contained in:
Peter Norvig 2018-02-06 23:31:10 -08:00 committed by GitHub
parent 3634892b01
commit e5eaa45a96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -14,7 +14,9 @@
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"program = '''\n",
@ -59,18 +61,20 @@
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import re \n",
"\n",
"tokenize = re.compile(\n",
" r'''\\d* \\.? \\d+ (?: E -? \\d+)? | # number \n",
" SIN|COS|TAN|ATN|EXP|ABS|LOG|SQR|RND|INT|FN[A-Z]| # functions (including user-defined FNA-FNZ)\n",
" LET|READ|DATA|PRINT|GOTO|IF|FOR|NEXT|END|STOP | # keywords\n",
" DEF|GOSUB|RETURN|DIM|REM|TO|THEN|STEP | # more keywords\n",
" [A-Z]\\d? | # variable names (letter optionally followed by a digit)\n",
" \".*? \" | # labels (strings in double quotes)\n",
"tokenize = re.compile(r'''\n",
" \\d* \\.? \\d+ (?: E -? \\d+)? | # number \n",
" SIN|COS|TAN|ATN|EXP|ABS|LOG|SQR|RND|INT|FN[A-Z]| # functions\n",
" LET|READ|DATA|PRINT|GOTO|IF|FOR|NEXT|END | # keywords\n",
" DEF|GOSUB|RETURN|DIM|REM|TO|THEN|STEP|STOP | # keywords\n",
" [A-Z]\\d? | # variable names (letter + optional digit)\n",
" \".*?\" | # labels (strings in double quotes)\n",
" <>|>=|<= | # multi-character relational operators\n",
" \\S # any non-space single character ''', \n",
" re.VERBOSE).findall"
@ -175,14 +179,17 @@
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"tokens = [] # Global variable to hold a list of tokens\n",
"\n",
"def tokenizer(line):\n",
" \"Return a list of the tokens on this line, handling spaces properly, and upper-casing.\"\n",
" return tokenize(remove_spaces(line).upper())\n",
" line = ''.join(tokenize(line)) # Remove whitespace\n",
" return tokenize(line.upper())\n",
"\n",
"def peek(): \n",
" \"Return the first token in the global `tokens`, or None if we are at the end of the line.\"\n",
@ -197,7 +204,7 @@
" \n",
"def remove_spaces(line): \n",
" \"Remove white space from line, except space inside double quotes.\"\n",
" return ''.join(tokenize(line))\n",
" return \n",
"\n",
"def lines(text): \n",
" \"A list of the non-empty lines in a text.\"\n",
@ -240,9 +247,6 @@
" assert (tokenizer('100IFX1+123.4+E1-12.3E4 <> 1.2E-34*-12E34+1+\"HI\" THEN99') ==\n",
" ['100', 'IF', 'X1', '+', '123.4', '+', 'E1', '-', '12.3E4', '<>', \n",
" '1.2E-34', '*', '-', '12E34', '+', '1', '+', '\"HI\"', 'THEN', '99'])\n",
" assert remove_spaces('10 GO TO 99') == '10GOTO99'\n",
" assert (remove_spaces('100 PRINT \"HELLO WORLD\", SIN(X) ^ 2')\n",
" == '100PRINT\"HELLO WORLD\",SIN(X)^2')\n",
" assert lines('one line') == ['one line']\n",
" assert lines(program) == [\n",
" '10 REM POWER TABLE',\n",
@ -364,7 +368,7 @@
"- **`DIM`** `<list of variable>`\n",
"- **`REM`** `<any string of characters whatsoever>`\n",
" \n",
"The notation `<variable>` means any variable and `<list of variable>` means zero or more variables, separated by commas. `[STEP <expression>]` means that the literal string `\"STEP\"`, followed by an expression, is optional. \n",
"The notation `<variable>` means any variable and `<list of variable>` means zero or more variables, separated by commas. `[`**`STEP`** `<expression>]` means that the literal string `\"STEP\"` followed by an expression is optional. \n",
"\n",
"Rather than use one of the many [language parsing frameworks](https://wiki.python.org/moin/LanguageParsing), I will show how to build a parser from scratch. First I'll translate the grammar above into Python. Not character-for-character (because it would take a lot of work to get Python to understand how to handle those characters), but almost word-for-word (because I can envision a straightforward way to get Python to handle the following format):"
]
@ -391,7 +395,8 @@
" 'GOSUB': [linenumber],\n",
" 'RETURN': [],\n",
" 'DIM': [list_of(variable)], \n",
" 'REM': [anycharacters]\n",
" 'REM': [anycharacters],\n",
" 'A': []\n",
" }"
]
},
@ -462,9 +467,9 @@
"metadata": {},
"outputs": [],
"source": [
"def linenumber(): return (int(pop()) if peek().isnumeric() else fail('missing line number'))\n",
"def number(): return (-1 if pop('-') else +1) * float(pop()) # Optional minus sign\n",
"def step(): return (expression() if pop('STEP') else 1) # 1 is the default step\n",
"def linenumber(): return (int(pop()) if peek().isnumeric() else fail('missing line number'))\n",
"def relational(): return pop(is_relational) or fail('expected a relational operator')\n",
"def varname(): return pop(is_varname) or fail('expected a variable name')\n",
"def funcname(): return pop(is_funcname) or fail('expected a function name')\n",
@ -506,7 +511,9 @@
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def variable(): \n",
@ -562,7 +569,7 @@
},
"outputs": [],
"source": [
"def parse(program): return sorted(map(parse_line, lines(program)))\n",
"def parse(program): return sorted(parse_line(line) for line in lines(program))\n",
"\n",
"def parse_line(line): global tokens; tokens = tokenizer(line); return statement()"
]
@ -577,7 +584,9 @@
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def parse_line(line):\n",
@ -648,7 +657,7 @@
"* Optionally, `\";\"` can be used instead of `\",\"`.\n",
"* Optionally, the `\",\"` or `\";\"` can be omitted&mdash;we can have a label immediately followed by an expression.\n",
"\n",
"The effect of a comma is to advance the output to the next column that is a multiple of 15 (and to a new line if this goes past column 75). The effect of a semicolon is similar, but works in multiples of 3, not 15. (Note that column numbering starts at 0, not 1.) Normally, at the end of a `PRINT` statement we advance to a new line, but this is not done if the statement ends in `\",\"` or `\";\"`. Here are some examples:\n",
"The effect of a comma is to advance the output to the next column that is a multiple of 15 (and to a new line if this goes past column 100). The effect of a semicolon is similar, but works in multiples of 3, not 15. (Note that column numbering starts at 0, not 1.) Normally, at the end of a `PRINT` statement we advance to a new line, but this is not done if the statement ends in `\",\"` or `\";\"`. Here are some examples:\n",
"\n",
"* `10 PRINT X, Y`\n",
"<br>Prints the value of `X` in column 0 and `Y` in column 15. Advances to new line.\n",
@ -711,7 +720,9 @@
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def expression(prec=1): \n",
@ -733,7 +744,7 @@
" return Funcall(pop(), primary())\n",
" elif pop('-'): # '-X' => Funcall('NEG', 'X')\n",
" return Funcall('NEG', primary())\n",
" elif pop('('): # '(X + 1)' => Opcall('X', '+', 1)\n",
" elif pop('('): # '(X)' => 'X'\n",
" exp = expression()\n",
" pop(')') or fail('expected \")\" to end expression')\n",
" return exp\n",
@ -741,7 +752,7 @@
" return fail('unknown expression')\n",
"\n",
"def precedence(op): \n",
" return (3 if op == '^' else 2 if op in ('*', '/') else 1 if op in ('+', '-') else 0)\n",
" return (3 if op == '^' else 2 if op in ('*', '/', '%') else 1 if op in ('+', '-') else 0)\n",
"\n",
"def associativity(op): \n",
" return (0 if op == '^' else 1)"
@ -897,7 +908,9 @@
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def execute(stmts): \n",
@ -958,7 +971,9 @@
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import math\n",
@ -973,7 +988,7 @@
" 'SIN': math.sin, 'COS': math.cos, 'TAN': math.tan, 'ATN': math.atan, \n",
" 'ABS': abs, 'EXP': math.exp, 'LOG': math.log, 'SQR': math.sqrt, 'INT': int,\n",
" '>': op.gt, '<': op.lt, '=': op.eq, '>=': op.ge, '<=': op.le, '<>': op.ne, \n",
" '^': pow, '+': op.add, '-': op.sub, '*': op.mul, '/': op.truediv, \n",
" '^': pow, '+': op.add, '-': op.sub, '*': op.mul, '/': op.truediv, '%': op.mod,\n",
" 'RND': lambda _: random.random(), 'NEG': op.neg}\n",
" data = deque() # A queue of numbers that READ can read from\n",
" for (_, typ, args) in stmts:\n",
@ -1052,7 +1067,9 @@
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def basic_print(items): \n",
@ -1066,11 +1083,11 @@
" newline()\n",
" \n",
"def print_string(s): \n",
" \"Print a string, keeping track of column, and advancing to newline if at or beyond column 75.\"\n",
" \"Print a string, keeping track of column, and advancing to newline if at or beyond column 100.\"\n",
" global column\n",
" print(s, end='')\n",
" column += len(s)\n",
" if column >= 75: newline()\n",
" if column >= 100: newline()\n",
" \n",
"def pad(width): \n",
" \"Pad out to the column that is the next multiple of width.\"\n",
@ -1284,9 +1301,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"1 2 3 4 5 \n",
"6 7 8 9 10 \n",
"11 12 "
"1 2 3 4 5 6 7 \n",
"8 9 10 11 12 "
]
}
],
@ -1330,14 +1346,12 @@
"N = 5 I^N:\n",
"1 32 243 1024 3125 \n",
"\n",
"\n",
"N = 6 I^N:\n",
"1 64 729 4096 15625 \n",
"46656 \n",
"1 64 729 4096 15625 46656 \n",
"\n",
"N = 7 I^N:\n",
"1 128 2187 16384 78125 \n",
"279936 823543 \n",
"1 128 2187 16384 78125 279936 823543 \n",
"\n",
"\n"
]
}
@ -1372,10 +1386,9 @@
"name": "stdout",
"output_type": "stream",
"text": [
"1e+06 941192 884736 830584 778688 729000 681472 636056 592704 \n",
"551368 512000 474552 438976 405224 373248 343000 314432 287496 \n",
"262144 238328 216000 195112 175616 157464 140608 125000 110592 \n",
"97336 85184 74088 64000 54872 46656 39304 32768 27000 21952 17576 13824 10648 \n",
"1e+06 941192 884736 830584 778688 729000 681472 636056 592704 551368 512000 474552 \n",
"438976 405224 373248 343000 314432 287496 262144 238328 216000 195112 175616 157464 \n",
"140608 125000 110592 97336 85184 74088 64000 54872 46656 39304 32768 27000 21952 17576 13824 10648 \n",
"8000 5832 4096 2744 1728 1000 512 216 64 8 0 "
]
}
@ -1454,27 +1467,27 @@
"data": {
"text/plain": [
"defaultdict(float,\n",
" {('P', (1.0,)): 1.25,\n",
" ('S', (3.0, 5.0)): 33.0,\n",
" ('S', (3.0, 4.0)): 16.0,\n",
" ('S', (1.0, 5.0)): 42.0,\n",
" ('S', (2.0, 1.0)): 10.0,\n",
" ('P', (3.0,)): 2.5,\n",
" ('S', (1.0, 4.0)): 29.0,\n",
" ('S', (1.0, 2.0)): 20.0,\n",
" ('S', (3.0, 1.0)): 35.0,\n",
" 'I': 3.0,\n",
" ('S', (2.0, 4.0)): 21.0,\n",
" ('P', (2.0,)): 4.3,\n",
" ('S', (2.0, 5.0)): 8.0,\n",
" ('S', (1.0, 1.0)): 40.0,\n",
" ('S', (3.0, 3.0)): 29.0,\n",
" {('S', (2.0, 1.0)): 10.0,\n",
" 'J': 5.0,\n",
" ('S', (1.0, 1.0)): 40.0,\n",
" ('S', (3.0, 5.0)): 33.0,\n",
" ('P', (2.0,)): 4.3,\n",
" ('S', (3.0, 2.0)): 47.0,\n",
" ('S', (2.0, 5.0)): 8.0,\n",
" 'I': 3.0,\n",
" ('P', (1.0,)): 1.25,\n",
" ('S', (1.0, 4.0)): 29.0,\n",
" ('S', (2.0, 4.0)): 21.0,\n",
" ('S', (3.0, 1.0)): 35.0,\n",
" ('S', (3.0, 4.0)): 16.0,\n",
" 'S': 169.4,\n",
" ('S', (2.0, 2.0)): 16.0,\n",
" ('S', (1.0, 2.0)): 20.0,\n",
" ('S', (1.0, 3.0)): 37.0,\n",
" ('S', (2.0, 3.0)): 3.0,\n",
" ('S', (1.0, 3.0)): 37.0})"
" ('S', (2.0, 2.0)): 16.0,\n",
" ('S', (1.0, 5.0)): 42.0,\n",
" ('P', (3.0,)): 2.5,\n",
" ('S', (3.0, 3.0)): 29.0})"
]
},
"execution_count": 35,
@ -1495,10 +1508,9 @@
"name": "stdout",
"output_type": "stream",
"text": [
"7 2 8 4 9 1 9 8 0 8 3 0 3 4 5 6 4 2 1 6 9 9 9 7 5 \n",
"8 4 1 5 9 9 7 5 5 7 2 0 3 2 1 2 1 8 3 9 9 3 0 0 2 \n",
"2 8 9 7 5 8 9 0 2 7 8 6 7 4 9 9 4 2 5 4 0 9 6 5 3 \n",
"3 3 5 1 7 1 6 1 0 7 3 5 8 1 9 0 4 7 1 7 4 5 1 7 6 \n"
"3 1 1 0 2 4 4 5 1 1 8 5 5 0 1 0 4 9 4 6 2 7 7 9 4 4 1 5 6 7 3 6 4 1 \n",
"1 2 0 8 7 2 5 4 1 2 1 5 1 0 2 5 7 8 2 9 3 4 7 5 9 6 8 6 9 4 9 1 6 6 \n",
"1 6 9 2 8 1 4 8 4 7 2 2 6 0 0 5 6 4 9 5 8 7 1 3 8 8 1 9 4 2 8 3 "
]
}
],
@ -1655,54 +1667,61 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Error in line '10 X = 1' at 'X = 1': unknown statement type\n",
"Error in line '20 GO TO JAIL' at 'J A I L': missing line number\n",
"Error in line '30 FOR I = 1 ' at '': expected 'TO'\n",
"Error in line '40 IF X > 0 & X < 10 GOTO 999' at '& X < 10 GOTO 999': expected 'THEN'\n",
"Error in line '50 LET Z = (Z + 1' at '': expected \")\" to end expression\n",
"Error in line '60 PRINT \"OH CANADA\", EH?' at '?': unknown expression\n",
"Error in line '70 LET Z = +3' at '+ 3': unknown expression\n",
"Error in line '80 LET X = Y ** 2' at '* 2': unknown expression\n",
"Error in line '90 LET A(I = 1' at '= 1': expected \")\" to close subscript\n",
"Error in line '100 IF A = 0 THEN 900 + 99' at '+ 99': extra tokens at end of line\n",
"Error in line '110 NEXT A(I)' at '( I )': extra tokens at end of line\n",
"Error in line '120 DEF F(X) = X ^ 2 + 1' at 'F ( X ) = X ^ 2 + 1': expected a function name\n",
"Error in line '130 IF X != 0 THEN 999' at '! = 0 THEN 999': expected a relational operator\n",
"Error in line '140 DEF FNS(X + 2*P1) = SIN(X)' at '+ 2 * P1 ) = SIN ( X )': expected ')'\n",
"Error in line '150 DEF FNY(M, B) = M * X + B' at ', B ) = M * X + B': expected ')'\n",
"Error in line '160 LET 3 = X' at '3 = X': expected a variable name\n",
"Error in line '170 LET SIN = 7 * DEADLY' at 'SIN = 7 * D E A D L Y': expected a variable name\n",
"Error in line '180 LET X = A-1(I)' at '( I )': extra tokens at end of line\n",
"Error in line 'STOP' at 'STOP': missing line number\n",
"Error in line '200 STOP IT, ALREADY' at 'I T , A L READ Y': extra tokens at end of line\n",
"PROGRAM STILL EXECUTES: 2 + 2 = 4 \n"
"Error in line '1 X = 1' at 'X = 1': unknown statement type\n",
"Error in line '2 GO TO JAIL' at 'J A I L': missing line number\n",
"Error in line '3 FOR I = 1 ' at '': expected 'TO'\n",
"Error in line '4 IF X > 0 & X < 10 GOTO 999' at '& X < 10 GOTO 999': expected 'THEN'\n",
"Error in line '5 LET Z = (Z + 1' at '': expected \")\" to end expression\n",
"Error in line '6 PRINT \"OH CANADA\", EH?' at '?': unknown expression\n",
"Error in line '7 LET Z = +3' at '+ 3': unknown expression\n",
"Error in line '8 LET X = Y ** 2' at '* 2': unknown expression\n",
"Error in line '9 LET A(I = 1' at '= 1': expected \")\" to close subscript\n",
"Error in line '10 IF A = 0 THEN 900 + 99' at '+ 99': extra tokens at end of line\n",
"Error in line '11 NEXT A(I)' at '( I )': extra tokens at end of line\n",
"Error in line '12 DEF F(X) = X ^ 2 + 1' at 'F ( X ) = X ^ 2 + 1': expected a function name\n",
"Error in line '13 IF X != 0 THEN 999' at '! = 0 THEN 999': expected a relational operator\n",
"Error in line '14 DEF FNS(X + 2*P1) = SIN(X)' at '+ 2 * P1 ) = SIN ( X )': expected ')'\n",
"Error in line '15 DEF FNY(M, B) = M * X + B' at ', B ) = M * X + B': expected ')'\n",
"Error in line '16 LET 3 = X' at '3 = X': expected a variable name\n",
"Error in line '17 LET SIN = 7 * DEADLY' at 'SIN = 7 * D E A D L Y': expected a variable name\n",
"Error in line '18 LET X = A-1(I)' at '( I )': extra tokens at end of line\n",
"Error in line '19 FOR SCORE + 7' at 'C O R E + 7': expected '='\n",
"Error in line '20 STOP IN NAME(LOVE)' at 'I N N A M E ( L O V E )': extra tokens at end of line\n",
"Error in line '85 ENDURANCE.' at 'U R A N C E .': extra tokens at end of line\n",
"ADD 2 + 2 = 4 \n"
]
}
],
"source": [
"run('''\n",
"10 X = 1\n",
"20 GO TO JAIL\n",
"30 FOR I = 1 \n",
"40 IF X > 0 & X < 10 GOTO 999\n",
"50 LET Z = (Z + 1\n",
"60 PRINT \"OH CANADA\", EH?\n",
"70 LET Z = +3\n",
"80 LET X = Y ** 2\n",
"90 LET A(I = 1\n",
"100 IF A = 0 THEN 900 + 99\n",
"110 NEXT A(I)\n",
"120 DEF F(X) = X ^ 2 + 1\n",
"130 IF X != 0 THEN 999\n",
"140 DEF FNS(X + 2*P1) = SIN(X)\n",
"150 DEF FNY(M, B) = M * X + B\n",
"160 LET 3 = X\n",
"170 LET SIN = 7 * DEADLY\n",
"180 LET X = A-1(I)\n",
"STOP\n",
"200 STOP IT, ALREADY\n",
"998 PRINT \"PROGRAM STILL EXECUTES: 2 + 2 = \" 2 + 2\n",
"999 END\n",
"1 X = 1\n",
"2 GO TO JAIL\n",
"3 FOR I = 1 \n",
"4 IF X > 0 & X < 10 GOTO 999\n",
"5 LET Z = (Z + 1\n",
"6 PRINT \"OH CANADA\", EH?\n",
"7 LET Z = +3\n",
"8 LET X = Y ** 2\n",
"9 LET A(I = 1\n",
"10 IF A = 0 THEN 900 + 99\n",
"11 NEXT A(I)\n",
"12 DEF F(X) = X ^ 2 + 1\n",
"13 IF X != 0 THEN 999\n",
"14 DEF FNS(X + 2*P1) = SIN(X)\n",
"15 DEF FNY(M, B) = M * X + B\n",
"16 LET 3 = X\n",
"17 LET SIN = 7 * DEADLY\n",
"18 LET X = A-1(I)\n",
"19 FOR SCORE + 7\n",
"20 STOP IN NAME(LOVE)\n",
"80 REMARKABLY, THE INTERPRETER\n",
"81 REMEDIES THE ERRORS, AND THE PROPGRAM\n",
"82 REMAINS AN EXECUTABLE ENTITY, UN-\n",
"83 REMITTENTLY RUNNING, WITH NO\n",
"84 REMORSE OR REGRETS, AND WITH GREAT\n",
"85 ENDURANCE.\n",
"98 PRINT \"ADD 2 + 2 = \" 2 + 2\n",
"99 END\n",
"''')"
]
},
@ -1710,10 +1729,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Final Program\n",
"# Longer Program: Life\n",
"\n",
"Now for a final, longer example, Conway's Game of [Life](https://en.wikipedia.org/wiki/Conway's_Game_of_Life),\n",
"which shows that BASIC is capable of handling a non-trivial problem&mdash;but I wouldn't want to rely on it for anything much bigger. This should give us some added confidence in the validity of the interpreter, but I would say the interpreter needs more work before I would trust it. I hope you found working through the interpreter infortative, and maybe you have ideas for how to improve it, or to develop an interpreter for another language."
"Now for a final, slightly more complicated example: Conway's Game of [Life](https://en.wikipedia.org/wiki/Conway's_Game_of_Life). "
]
},
{
@ -1725,7 +1743,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
" GENERATION 0 \n",
"GEN 0 \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
@ -1736,7 +1754,7 @@
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
" GENERATION 1 \n",
"GEN 1 \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
@ -1747,7 +1765,7 @@
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
" GENERATION 2 \n",
"GEN 2 \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
@ -1758,7 +1776,7 @@
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
" GENERATION 3 \n",
"GEN 3 \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
@ -1769,7 +1787,7 @@
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
" GENERATION 4 \n",
"GEN 4 \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
@ -1780,7 +1798,7 @@
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
" GENERATION 5 \n",
"GEN 5 \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . O O . . . . . \n",
@ -1791,7 +1809,7 @@
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
" GENERATION 6 \n",
"GEN 6 \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . O O O . . . . \n",
@ -1802,7 +1820,7 @@
". . . . . . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
" GENERATION 7 \n",
"GEN 7 \n",
". . . . . . . . . . \n",
". . . . O . . . . . \n",
". . . O . O . . . . \n",
@ -1813,7 +1831,7 @@
". . . . O . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
" GENERATION 8 \n",
"GEN 8 \n",
". . . . . . . . . . \n",
". . . . O . . . . . \n",
". . . O O O . . . . \n",
@ -1824,7 +1842,7 @@
". . . . O . . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
" GENERATION 9 \n",
"GEN 9 \n",
". . . . . . . . . . \n",
". . . O O O . . . . \n",
". . O . . . O . . . \n",
@ -1835,7 +1853,7 @@
". . . O O O . . . . \n",
". . . . . . . . . . \n",
". . . . . . . . . . \n",
" GENERATION 10 \n",
"GEN 10 \n",
". . . . O . . . . . \n",
". . . O O O . . . . \n",
". . O O O O O . . . \n",
@ -1852,15 +1870,17 @@
"source": [
"run('''\n",
"100 REM CONWAY'S GAME OF LIFE\n",
"102 REM G IS NUMBER OF GENERATIONS, M IS MATRIX SIZE (M X M)\n",
"104 REM A(X, Y) IS 1 IFF CELL AT (X, Y) IS LIVE\n",
"106 REM L(SELF_ALIVE, NEIGHBORS_ALIVE) IS 1 IFF CELL WITH THOSE COUNTS SHOULD LIVE ON\n",
"110 LET G = 10\n",
"120 LET M = 10\n",
"125 READ A(3,4), A(3,5), A(3,6), A(6,5), A(6,6), A(7,5), A(7,6)\n",
"130 DATA 1, 1, 1, 1, 1, 1, 1\n",
"140 READ L(0, 3), L(1, 3), L(1, 2)\n",
"145 DATA 1, 1, 1\n",
"\n",
"102 REM G IS NUMBER OF GENERATIONS, \n",
"104 REM M IS MATRIX SIZE (M X M)\n",
"106 REM L(SELF, NEIGHBORS_ALIVE) IS 1 IFF CELL WITH THOSE COUNTS LIVES\n",
"108 REM A(X, Y) IS 1 IFF CELL AT (X, Y) IS LIVE\n",
"110 REM B(X, Y) GETS THE NEXT GENERATION\n",
"\n",
"120 READ G, M, L(0,3), L(1,3), L(1,2)\n",
"121 DATA 10, 10, 1, 1, 1\n",
"130 READ A(3,4), A(3,5), A(3,6), A(6,5), A(6,6), A(7,5), A(7,6)\n",
"131 DATA 1, 1, 1, 1, 1, 1, 1\n",
"\n",
"150 REM MAIN LOOP: PRINT, THEN REPEAT G TIMES: UPDATE / COPY / PRINT\n",
"155 LET I = 0\n",
@ -1872,7 +1892,7 @@
"210 NEXT I\n",
"220 STOP\n",
"\n",
"300 REM SUBROUTINE: UPDATE B = NEXT_GENERATION(A)\n",
"300 REM ========== UPDATE B = NEXT_GEN(A)\n",
"310 FOR Y = 1 TO M\n",
"320 FOR X = 1 TO M\n",
"325 LET N = A(X-1,Y)+A(X+1,Y)+A(X,Y-1)+A(X,Y+1)+A(X-1,Y-1)+A(X+1,Y+1)+A(X-1,Y+1)+A(X+1,Y-1)\n",
@ -1881,7 +1901,7 @@
"350 NEXT Y\n",
"360 RETURN\n",
"\n",
"500 REM SUBROUTINE: COPY A = B\n",
"500 REM ========== COPY A = B\n",
"510 FOR Y = 1 TO M\n",
"520 FOR X = 1 TO M\n",
"530 LET A(X, Y) = B(X, Y)\n",
@ -1889,14 +1909,14 @@
"550 NEXT Y\n",
"560 RETURN\n",
"\n",
"700 REM SUBROUTINE: PRINT A\n",
"705 PRINT \" GENERATION \" I\n",
"700 REM ========== PRINT A\n",
"705 PRINT \"GEN \" I\n",
"710 FOR Y = 1 TO M\n",
"720 FOR X = 1 TO M\n",
"730 IF A(X, Y) = 0 THEN 750\n",
"740 PRINT \"O\";\n",
"750 IF A(X, Y) = 1 THEN 770\n",
"760 PRINT \".\";\n",
"730 IF A(X, Y) = 1 THEN 760\n",
"740 PRINT \".\";\n",
"750 GOTO 770\n",
"760 PRINT \"O\";\n",
"770 NEXT X\n",
"780 PRINT\n",
"790 NEXT Y\n",
@ -1907,13 +1927,144 @@
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
"cell_type": "markdown",
"metadata": {},
"source": [
"Actually, this was an assignment in my high school BASIC class. (We used a [slightly different](https://www.grc.com/pdp-8/docs/os8_basic_reference.pdf) version of BASIC.) Back then, output was on rolls of paper, and I thought it was wasteful to print only one generation per line. So I arranged to print multiple generations on the same line, storing them until it was time to print them out. But BASIC doesn't have three-dimensional arrays, so I needed to store several generations worth of data in one `A(X, Y)` value. Today, I know that that could be done by allocating one bit for each generation, but back then I don't think I knew about binary representation, so I stored one generation in each decimal digit. That means I no longer need two matrixes, `A` and `B`; instead, the current generation will always be the value in the one's place, the previous generation in the ten's place, and the one before that in the hundred's place. (Also, I admit I cheated: I added the mod operatoir, `%`, which did not appear in early versions of BASIC, just because it was useful for this program.)"
]
},
"outputs": [],
"source": []
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"GEN 0 GEN 1 GEN 2 \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . O . . . . . . . | . . . . . . . . . . | . . O . . . . . . . | \n",
". . O . . O O . . . | . O O O . O O . . . | . . O . O O O . . . | \n",
". . O . . O O . . . | . . . . . O O . . . | . . O . O O O . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
"GEN 3 GEN 4 GEN 5 \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . O O . . . . . | \n",
". . . O . O . . . . | . . O O O O . . . . | . . O . O O . . . . | \n",
". O O . O . O . . . | . . O . O . O . . . | . . O . . . O . . . | \n",
". . . . O . O . . . | . . . O O . O . . . | . . . O O . O . . . | \n",
". . . . . O . . . . | . . . . . O . . . . | . . . . O O . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
"GEN 6 GEN 7 GEN 8 \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . . . . . . . . . | . . . . O . . . . . | . . . . O . . . . . | \n",
". . . O O O . . . . | . . . O . O . . . . | . . . O O O . . . . | \n",
". . O . O O . . . . | . . O . . . O . . . | . . O O . O O . . . | \n",
". . O . . . O . . . | . . O . . . O . . . | . O O O . O O O . . | \n",
". . . O O . O . . . | . . O . . . O . . . | . . O O . O O . . . | \n",
". . . O O O . . . . | . . . O . O . . . . | . . . O O O . . . . | \n",
". . . . . . . . . . | . . . . O . . . . . | . . . . O . . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n",
"GEN 9 GEN 10 GEN 11 \n",
". . . . . . . . . . | . . . . O . . . . . | . . . O O O . . . . | \n",
". . . O O O . . . . | . . . O O O . . . . | . . O . . . O . . . | \n",
". . O . . . O . . . | . . O O O O O . . . | . O . . . . . O . . | \n",
". O . . . . . O . . | . O O . . . O O . . | O . . . O . . . O . | \n",
". O . . . . . O . . | O O O . . . O O O . | O . . O . O . . O . | \n",
". O . . . . . O . . | . O O . . . O O . . | O . . . O . . . O . | \n",
". . O . . . O . . . | . . O O O O O . . . | . O . . . . . O . . | \n",
". . . O O O . . . . | . . . O O O . . . . | . . O . . . O . . . | \n",
". . . . . . . . . . | . . . . O . . . . . | . . . O O O . . . . | \n",
". . . . . . . . . . | . . . . . . . . . . | . . . . . . . . . . | \n"
]
}
],
"source": [
"run('''\n",
"100 REM CONWAY'S GAME OF LIFE\n",
"\n",
"102 REM G IS NUMBER OF GENERATIONS, \n",
"104 REM M IS MATRIX SIZE (M X M)\n",
"106 REM L(SELF, NEIGHBORS_ALIVE) IS 1 IFF CELL WITH THOSE COUNTS LIVES\n",
"108 REM A(X, Y) STORES THE HISTORY OF THE CELL AT (X, Y); 1 MEANS LIVE,\n",
"110 REM BUT WE STORE SEVERAL GENERATIONS: A(X, Y) = 100 MEANS THE CELL\n",
"112 REM IS DEAD IN THE CURRENT AND PREVIOUS GENERATION (00), BUT LIVE IN THE\n",
"114 REM GENERATION BEFORE THAT (1). WE STORE MULTIPLE GENERATIONS SO THAT\n",
"116 REM WE CAN PRINT THEM OUT ON ONE LINE, SAVING SPACE/PAPER.\n",
"\n",
"120 READ G, M, L(0,3), L(1,3), L(1,2)\n",
"122 DATA 11, 10, 1, 1, 1\n",
"124 READ A(3,4), A(3,5), A(3,6), A(6,5), A(6,6), A(7,5), A(7,6)\n",
"126 DATA 1, 1, 1, 1, 1, 1, 1\n",
"\n",
"130 REM FNA(N) = THE PREVIOUS GENERATION'S VALUE\n",
"132 DEF FNA(N) = INT(N / 10) % 10\n",
"\n",
"134 REM FNC(N) = THE GENERATION IN COLUMN C; FNC(123) = C FOR EACH C IN 1..3\n",
"136 DEF FNC(N) = FNA(N / (10 ^ (2 - C)))\n",
"\n",
"150 REM MAIN LOOP: DO 3 UPDATES (2 FIRST TIME), THEN PRINT AND SHIFT\n",
"160 FOR I = 1 TO G\n",
"170 GOSUB 300\n",
"175 IF I % 3 <> 2 THEN 200\n",
"180 GOSUB 700\n",
"190 GOSUB 800\n",
"200 NEXT I\n",
"210 STOP\n",
"\n",
"300 REM ========== UPDATE A: SHIFT OLD GENS LEFT; ADD IN NEW GEN\n",
"310 FOR Y = 1 TO M\n",
"320 FOR X = 1 TO M \n",
"330 LET A(X, Y) = 10 * A(X, Y)\n",
"340 NEXT X\n",
"350 NEXT Y\n",
"360 FOR Y = 1 TO M\n",
"370 FOR X = 1 TO M \n",
"380 LET N1 = FNA(A(X+1,Y-1)) + FNA(A(X+1,Y)) + FNA(A(X+1,Y+1)) + FNA(A(X,Y-1))\n",
"390 LET N2 = FNA(A(X-1,Y-1)) + FNA(A(X-1,Y)) + FNA(A(X-1,Y+1)) + FNA(A(X,Y+1))\n",
"400 LET S = FNA(A(X, Y))\n",
"410 LET A(X, Y) = A(X, Y) + L(S, N1 + N2)\n",
"420 NEXT X\n",
"430 NEXT Y\n",
"440 RETURN\n",
"\n",
"700 REM ========== PRINT A (3 GENERATIONS ACROSS THE PAGE)\n",
"705 PRINT \"GEN \" I-2, \" \", \" GEN \" I-1, \" \", \" GEN \" I\n",
"710 FOR Y = 1 TO M\n",
"715 FOR C = 1 TO 3\n",
"720 FOR X = 1 TO M\n",
"730 IF FNC(A(X, Y)) = 1 THEN 760\n",
"740 PRINT \".\";\n",
"750 GOTO 770\n",
"760 PRINT \"O\";\n",
"770 NEXT X\n",
"775 PRINT \"|\";\n",
"777 NEXT C\n",
"780 PRINT\n",
"790 NEXT Y\n",
"795 RETURN\n",
"\n",
"800 REM ========== FORGET ALL BUT THE MOST RECENT GENERATION IN A\n",
"810 FOR Y = 1 TO M\n",
"820 FOR X = 1 TO M\n",
"830 LET A(X, Y) = A(X, Y) % 10\n",
"840 NEXT X\n",
"850 NEXT Y\n",
"860 RETURN\n",
"\n",
"999 END \n",
"''')"
]
}
],
"metadata": {