Created
December 27, 2009 16:50
-
-
Save sma/264323 to your computer and use it in GitHub Desktop.
A simple BASIC interpreter that can run the old HAMURABI game
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from __future__ import division | |
from math import trunc | |
from random import random | |
import re | |
TOKENS = re.compile(r'(?<=REM).*|\.?\d+|\w+\$?|[():;=+\-*/]|<[=>]?|>=?|"[^"]*"') | |
class Basic(object): | |
def __init__(self, filename): | |
self.tokens = [] | |
self.lines = {} | |
for line in open(filename): | |
tokens = TOKENS.findall(line) | |
self.lines[tokens[0]] = len(self.tokens) | |
self.tokens.extend(tokens[1:]) | |
self.tokens.append(":") | |
self.index = 0 | |
self.vars = {} | |
self.stack = [] | |
def next(self): | |
token = self.tokens[self.index] | |
self.index += 1 | |
return token | |
def at(self, token): | |
if self.next() == token: | |
return True | |
self.index -= 1 | |
return False | |
def expect(self, token): | |
if not self.at(token): | |
raise SyntaxError, "expected %s, but found %s" % (token, self.next()) | |
def goto(self, line): | |
self.index = self.lines[line] | |
def run(self): | |
while not self.at("END"): | |
token = self.next() | |
getattr(self, "do_" + token, lambda:self.do_LET(token))() | |
def do_LET(self, token): | |
if self.at("="): | |
self.vars[token] = self.expression() | |
self.expect(":") | |
else: | |
raise SyntaxError, "unknown command %s" % token | |
def do_PRINT(self): | |
def nstr(n): | |
return int(n) if isinstance(n, float) and trunc(n) == n else n | |
if not self.at(":"): | |
print nstr(self.expression()), | |
while self.at(";"): | |
if self.at(":"): | |
return | |
print nstr(self.expression()), | |
self.expect(":") | |
def do_IF(self): | |
cond = self.condition() | |
self.expect("THEN") | |
line = self.next() | |
self.expect(":") | |
if cond: | |
self.goto(line) | |
def do_INPUT(self): | |
name = self.next() | |
self.expect(":") | |
self.vars[name] = float(raw_input("? ")) | |
def do_GOSUB(self): | |
line = self.next() | |
self.expect(":") | |
self.stack.append(self.index) | |
self.goto(line) | |
def do_RETURN(self): | |
self.expect(":") | |
self.index = self.stack.pop() | |
def do_GOTO(self): | |
line = self.next() | |
self.expect(":") | |
self.goto(line) | |
def do_REM(self): | |
self.next() | |
self.expect(":") | |
def do_FOR(self): | |
name = self.next() | |
self.expect("=") | |
self.vars[name] = self.expression() | |
self.expect("TO") | |
end = self.expression() | |
self.expect(":") | |
self.stack.append((name, end, self.index)) | |
def do_NEXT(self): | |
if not self.at(":"): | |
self.next() | |
self.expect(":") | |
name, end, index = self.stack[-1] | |
self.vars[name] += 1 | |
if self.vars[name] >= end: | |
self.stack.pop() | |
else: | |
self.index = index | |
def condition(self): | |
left = self.expression() | |
op = self.next() | |
right = self.expression() | |
if op == "=": | |
return left == right | |
if op == "<": | |
return left < right | |
if op == "<=": | |
return left <= right | |
if op == ">": | |
return left > right | |
if op == ">=": | |
return left >= right | |
if op == "<>": | |
return left != right | |
raise SyntaxError, "unknown comparision operator %s" % op | |
def expression(self): | |
left = self.term() | |
while True: | |
if self.at("+"): | |
left += self.term() | |
elif self.at("-"): | |
left -= self.term() | |
else: | |
break | |
return left | |
def term(self): | |
left = self.factor() | |
while True: | |
if self.at("*"): | |
left *= self.factor() | |
elif self.at("/"): | |
left /= self.factor() | |
else: | |
break | |
return left | |
def factor(self): | |
token = self.next() | |
if token == "TAB": | |
return " " * int(self.expression()) | |
if token == "INT": | |
return trunc(self.expression()) | |
if token == "RND": | |
self.expression() | |
return random() | |
if token == "CHR$": | |
return chr(int(self.expression())) | |
if token == "(": | |
v = self.expression() | |
self.expect(")") | |
return v | |
if token.isdigit() or token[0] == ".": | |
return float(token) | |
if token.isalnum(): | |
return self.vars[token] | |
if token[0] == '"': | |
return token[1:-1] | |
raise SyntaxError, "can't handle %s" % token | |
basic = Basic("hammurabi.bas") | |
basic.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 PRINT TAB(32);"HAMURABI" | |
20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" | |
30 PRINT:PRINT:PRINT | |
80 PRINT "TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA" | |
90 PRINT "FOR A TEN-YEAR TERM OF OFFICE.":PRINT | |
95 D1=0: P1=0 | |
100 Z=0: P=95:S=2800: H=3000: E=H-S | |
110 Y=3: A=H/Y: I=5: Q=1 | |
210 D=0 | |
215 PRINT:PRINT:PRINT "HAMURABI: I BEG TO REPORT TO YOU,": Z=Z+1 | |
217 PRINT "IN YEAR";Z;",";D;"PEOPLE STARVED,";I;"CAME TO THE CITY," | |
218 P=P+I | |
227 IF Q>0 THEN 230 | |
228 P=INT(P/2) | |
229 PRINT "A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED." | |
230 PRINT "POPULATION IS NOW";P | |
232 PRINT "THE CITY NOW OWNS ";A;"ACRES." | |
235 PRINT "YOU HARVESTED";Y;"BUSHELS PER ACRE." | |
250 PRINT "THE RATS ATE";E;"BUSHELS." | |
260 PRINT "YOU NOW HAVE ";S;"BUSHELS IN STORE.": PRINT | |
270 IF Z=11 THEN 860 | |
310 C=INT(10*RND(1)): Y=C+17 | |
312 PRINT "LAND IS TRADING AT";Y;"BUSHELS PER ACRE." | |
320 PRINT "HOW MANY ACRES DO YOU WISH TO BUY"; | |
321 INPUT Q: IF Q<0 THEN 850 | |
322 IF Y*Q<=S THEN 330 | |
323 GOSUB 710 | |
324 GOTO 320 | |
330 IF Q=0 THEN 340 | |
331 A=A+Q: S=S-Y*Q: C=0 | |
334 GOTO 400 | |
340 PRINT "HOW MANY ACRES DO YOU WISH TO SELL"; | |
341 INPUT Q: IF Q<0 THEN 850 | |
342 IF Q<A THEN 350 | |
343 GOSUB 720 | |
344 GOTO 340 | |
350 A=A-Q: S=S+Y*Q: C=0 | |
400 PRINT | |
410 PRINT "HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE"; | |
411 INPUT Q | |
412 IF Q<0 THEN 850 | |
418 REM *** TRYING TO USE MORE GRAIN THAN IS IN SILOS? | |
420 IF Q<=S THEN 430 | |
421 GOSUB 710 | |
422 GOTO 410 | |
430 S=S-Q: C=1: PRINT | |
440 PRINT "HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED"; | |
441 INPUT D: IF D=0 THEN 511 | |
442 IF D<0 THEN 850 | |
444 REM *** TRYING TO PLANT MORE ACRES THAN YOU OWN? | |
445 IF D<=A THEN 450 | |
446 GOSUB 720 | |
447 GOTO 440 | |
449 REM *** ENOUGH GRAIN FOR SEED? | |
450 IF INT(D/2)<=S THEN 455 | |
452 GOSUB 710 | |
453 GOTO 440 | |
454 REM *** ENOUGH PEOPLE TO TEND THE CROPS? | |
455 IF D<10*P THEN 510 | |
460 PRINT "BUT YOU HAVE ONLY";P;"PEOPLE TO TEND THE FIELDS! NOW THEN," | |
470 GOTO 440 | |
510 S=S-INT(D/2) | |
511 GOSUB 800 | |
512 REM *** A BOUNTIFUL HARVEST! | |
515 Y=C: H=D*Y: E=0 | |
521 GOSUB 800 | |
522 IF INT(C/2)<>C/2 THEN 530 | |
523 REM *** RATS ARE RUNNING WILD!! | |
525 E=INT(S/C) | |
530 S=S-E+H | |
531 GOSUB 800 | |
532 REM *** LET'S HAVE SOME BABIES | |
533 I=INT(C*(20*A+S)/P/100+1) | |
539 REM *** HOW MANY PEOPLE HAD FULL TUMMIES? | |
540 C=INT(Q/20) | |
541 REM *** HORROS, A 15% CHANCE OF PLAGUE | |
542 Q=INT(10*(2*RND(1)-.3)) | |
550 IF P<C THEN 210 | |
551 REM *** STARVE ENOUGH FOR IMPEACHMENT? | |
552 D=P-C: IF D>.45*P THEN 560 | |
553 P1=((Z-1)*P1+D*100/P)/Z | |
555 P=C: D1=D1+D: GOTO 215 | |
560 PRINT: PRINT "YOU STARVED";D;"PEOPLE IN ONE YEAR!!!" | |
565 PRINT "DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY" | |
566 PRINT "BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE" | |
567 PRINT "ALSO BEEN DECLARED NATIONAL FINK!!!!": GOTO 990 | |
710 PRINT "HAMURABI: THINK AGAIN. YOU HAVE ONLY" | |
711 PRINT S;"BUSHELS OF GRAIN. NOW THEN," | |
712 RETURN | |
720 PRINT "HAMURABI: THINK AGAIN. YOU OWN ONLY";A;"ACRES. NOW THEN," | |
730 RETURN | |
800 C=INT(RND(1)*5)+1 | |
801 RETURN | |
850 PRINT: PRINT "HAMURABI: I CANNOT DO WHAT YOU WISH." | |
855 PRINT "GET YOURSELF ANOTHER STEWARD!!!!!" | |
857 GOTO 990 | |
860 PRINT "IN YOUR 10-YEAR TERM OF OFFICE,";P1;"PERCENT OF THE" | |
862 PRINT "POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF" | |
865 PRINT D1;"PEOPLE DIED!!": L=A/P | |
870 PRINT "YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH" | |
875 PRINT L;"ACRES PER PERSON.": PRINT | |
880 IF P1>33 THEN 565 | |
885 IF L<7 THEN 565 | |
890 IF P1>10 THEN 940 | |
892 IF L<9 THEN 940 | |
895 IF P1>3 THEN 960 | |
896 IF L<10 THEN 960 | |
900 PRINT "A FANTASTIC PERFORMANCE!!! CHARLEMANGE, DISRAELI, AND" | |
905 PRINT "JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!":GOTO 990 | |
940 PRINT "YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVAN IV." | |
945 PRINT "THE PEOPLE (REMIANING) FIND YOU AN UNPLEASANT RULER, AND," | |
950 PRINT "FRANKLY, HATE YOUR GUTS!!":GOTO 990 | |
960 PRINT "YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT" | |
965 PRINT "REALLY WASN'T TOO BAD AT ALL. ";INT(P*.8*RND(1));"PEOPLE" | |
970 PRINT "WOULD DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR" | |
975 PRINT "TRIVIAL PROBLEMS." | |
990 PRINT: FOR N=1 TO 10: PRINT CHR$(7);: NEXT N | |
995 PRINT "SO LONG FOR NOW.": PRINT | |
999 END |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment