The weather has turned horrible again in Berlin, and this means I can finally spend more time inside with other projects. Yay for cold, wet drizzles!
When I've had a bit of time, I've been continuing my Forth education with small programs. One of those finally gave me the push to understand two things I had read in
Thinking Forth, but not fully grasped until this. It's a ridiculously primitve adventure game with nine rooms you can only walk through:
Code:
\ The Very Very Small Forth Mini-Adventure
\ Scot W. Stevenson <scot.stevenson@gmail.com>
\ This version: 26. Oct 2014
\ Uses NOS for X and TOS for Y
3 constant SIZE \ 3x3 playing field
SIZE dup * 1- constant TARGET# \ exit Room 8 for nine rooms
SIZE 1- constant WALL \ for zero-based indexing
create Descriptions
s" The word START is written on the floor" , , \ Room 0
s" Frogs sit on the stones of this little room" , ,
s" This corner is very dirty and smells like cat pee" , ,
s" Somebody wrote 'Elbereth' in the dust here" , , \ Room 3
s" A single shaft of light streams from the ceiling" , ,
s" There is a big bolder in your way" , ,
s" This corner is amazingly clean" , , \ Room 6
s" There is a poster of Buffy the Vampire Slayer on the wall here" , ,
s" There is a big door marked EXIT here" , , \ Room 8 (the exit)
create Roomtypes \ 1 blocked, 0 free
0 , 0 , 0 ,
0 , 0 , 1 ,
0 , 0 , 0 ,
: room# ( x y -- u ) SIZE * + ; \ index of given room for arrays
: examine ( x y -- x y ) \ does not consume arguments; system uses LOOK
2dup room# 2* cells Descriptions +
dup @ swap 1 cells + @ swap \ get ( addr u ) for string
cr type space ;
: won? ( x y -- f ) room# TARGET# = ;
: blocked? ( x y -- f ) room# cells Roomtypes + @ 1 = ;
: step ( x y x y -- x y ) 2swap 2drop ; \ system uses MOVE
: guide ( x y x y -- x y )
2dup won? if cr ." You've won!" step examine else
2dup blocked? if cr ." Can't go there: " examine 2drop else
step then then ;
: west ( x y -- x y ) over 1- 0 max over guide ;
: east ( x y -- x y ) over 1+ WALL min over guide ;
: north ( x y -- x y ) over >r dup 1+ WALL min r> swap guide ;
: south ( x y -- x y ) over >r dup 1- 0 max r> swap guide ;
: adventure ( -- x y )
0 dup \ start in Room 0
cr
." Welcome to the Very Very Small Forth Mini-Adventure" cr
." Find your way to the far corner with commands 'examine'" cr
." 'west', 'east', 'north', and 'south'."
space examine ;
adventure
(For gforth 0.7.0:
gforth and then
include adventure.fs) So my insights were, duh:
1. Use the stack, not variables. First versions used x and y as variables, because, well, you need variables, right? Been using them since BASIC days. Except everything had x and y in them, and that seemed stupid. Now TOS is y and NOS is x. Amazing how that cleans up the code.
2. Use the Forth interpreter itself. I spent a whole day wondering how to write a good CLI, something like the one that is available in the Python library. Except that Forth comes with a perfectly good interpreter, of course. So now Forth itself includes an adventure game, which I'm sure EMACS has built in somehow, but takes a bit of getting used to. As an added bonus, debugging is suddenly amazingly simple, because you have all the tools right there. Wondering if you left stuff on the stack? Just type .S in the middle of the game.
This isn't quite done -- I think I need a 2DROP somewhere after you've reached the exit to clean up. And I'm not sure if I should just declare a VOCABULARY named adventure and use "look" instead of examine, which is system command in gforth. Any suggestions on the best practices for that?