Forth Warrior is a game of programming, stabbing and low cunning. Your Forth code controls the actions of a valiant adventurer as she plunges ever deeper into a mysterious dungeon. Gather precious gems, defeat slime creatures and watch your step! Your program must fit in 8 kilobytes and is scored based on how many cycles it takes to execute, how many actions are taken, how many gems are collected and how many slimes are defeated. Once you've made it through all the levels, try to optimize your code and hone your strategy to maximize your score!
This game is intended to give intermediate Forth enthusiasts a fun, well-defined task to cut their teeth on. While there are many differences in gameplay, I consider it to be a spiritual successor to Ruby Warrior. When Forth Warrior boots up, you're given a command prompt which allows you to interactively edit and test Forth code. If you've never programmed in Forth before, read a few chapters of Leo Brodie's canonical Starting Forth. For a faster-paced and somewhat less whimsical guide, take a look at A Beginner's Guide to Forth. As noted below, the dialect of Forth used in this game varies a bit from that used in these references, but the bulk of the language is the same.
To build Forth Warrior from source, you'll need the Java SDK and Apache Ant. Download the Mako repository, build the "Maker" compiler and use it to compile and run Forth Warrior itself:
git clone https://github.com/JohnEarnest/Mako.git
cd Mako
ant
./maker games/Warrior2/Warrior2.fs --run
Maker can also be used to output a ROM which can be baked into a standalone JAR, for easier distribution:
./maker games/Warrior2/Warrior2.fs tools/Standalone/Data
cd tools/Standalone
ant
java -jar Mako.jar
To begin a game, use the begin command. It reads the name of a word, so begin example will start the game using the word example as an entrypoint. An individual level may be tested independently by using the test command, which works like begin but additionally takes a level number from the stack. Thus, 2 test example will start on level 2 and use the word example as an entrypoint.
The words command provides a listing of all currently defined words. To find out more information about a particular word, use help followed by the name of a word. For example, help +. An asterisk after the stack effect indicates an immediate word.
If you create a file called warrior.fs in the same directory as the game, the load command can be used to execute all the code in this file- this makes it easy to work on your Warrior in your favorite text editor. If you aren't starting the game "cold" every time you make some changes, you may want to use forget to clear out old definitions from your dictionary before issuing load again. You can also specify a filename with load, which is useful if you're tinkering with several AIs- for example load dummy.fs.
While the game is running, any debugging output will be shown a line at a time at the bottom of the screen, with brief pauses between lines. This means if you wish to print less than 40 characters at a time you should be using typeln or terminating your output with cr, the command for printing a carriage return. Control+C will immediately halt the program and return to the Forth prompt.







opened with a key.

An extremely simple brain that can make it past a few obstacles:
: dummy ( -- )
E loop
dup look
dup STAIRS = swap FLOOR = or if
dup walk
else
1 + 4 mod
then
again
;
A more sophisticated brain which handles many types of obstacles and attempts to traverse mazes by following the left wall:
create actions
' walk , # floor
0 , # wall
' walk , # stairs
' open , # door
' take , # key
' take , # gem
' attack , # slime
' walk , # spikes
: act ( dir -- dir f )
dup 4 mod dup look
dup WALL = if drop drop 0 exit then
actions + @ exec -1
;
: try ( dir -- dir' )
1 - act if exit then
1 + act if exit then
1 + act if exit then
;
: lefthand ( -- )
E loop try again
;
Forth Warrior provides a very small Forth kernel with only the essential primitives. Most of these words will seem familiar if you've programmed in Forth before. The following is a summary of major deviations from a common ANS-style Forth.
-
Word names become available immediately after
:. Thus, recursive procedures can simply refer to themselves. Mutual recursion is best accomplished by using vectored words and'. -
'is state-smart. If used in interpreting mode, it will push an xt onto the stack. Otherwise it will compile a literal into the current definition. -
#is a single-line comment, rather than\. -
String constants have special-cased syntax for convenience. When compiling, simply enclose a string in double quotes and the address to the head of the resulting null-terminated string will be compiled as a literal. All string-oriented routines use C-style null-terminated strings. Thus, a hello world program can simply be
: hello "Hello, World!" typeln ; -
Loop constructs are a little different from usual. This dialect provides a word
loopto begin a loop, and this can be matched withagain,untilorwhileto loop unconditionally, until a flag is true or while a flag is true, respectively.breakexits the innermost loop.
To demonstrate, here's a simple counted loop written three different ways:
: A 0 loop dup . 1 + dup 10 > until ;
: B 0 loop dup . 1 + dup 11 < while ;
: C 0 loop dup . 1 + dup 10 > if break then again ;
Each will print out:
0 1 2 3 4 5 6 7 8 9 10
Many common words that are not included in the kernel can be synthesized in terms of the primitives available. For example, here's how we might define allot:
: allot loop 0 , 1 - dup while drop ; ( length -- )
# used like:
create buffer 45 allot
Direction Constants:
N(0)E(1)S(2)W(3)
Type Constants:
FLOOR(0) Passable ground terrain.WALL(1) Impassible.STAIRS(2) The exit to a given level.DOOR(3) Locked, impassible barrier. Use a key toopenit.KEY(4) Key for opening doors.takethem to collect them.GEM(5) Worth points and heals damage.takethem to collect them.SLIME(6) Deadly enemy.attackthem to kill them and don't step on them.SPIKES(7) These hurt to walk on. Avoid it when possible.
Queries:
health( -- n ) The number of hearts the player has.level( -- n ) The current floor of the dungeon.gems( -- n ) How many gems the player has collected.keys( -- n ) How many keys the player is carrying.listen( -- n ) How many enemies are still on this level.look( dir -- type ) View the type of thing/tile in an adjacent space. (These actions do not advance time in the game world)
Commands:
wait( -- ) Do nothing for a game tick.walk( dir -- ) Move to an adjacent tile.attack( dir -- ) Attack any enemies in an adjacent tile.take( dir -- ) Pick up an item in an adjacent tile.open( dir -- ) Unlock an adjacent door.
Misc:
fast( -- ) Display game animations more quickly.slow( -- ) Display game animations at normal speed. (default)help( -- ) Given a word name, print a brief explanation of what it does.load( -- ) Reload the source file.
