Hunt the Wumpus - or - Call Me Sentimental
[1/12] from: greggirwin::mindspring::com at: 22-Oct-2001 23:02
REBOL[
Title: "Hunt the Wumpus"
Date: 22-Oct-2001
Author: "Gregg Irwin"
EMail: [greggirwin--acm--org]
Comment: {
I found an old version, in C (by Eric Raymond), which was adapted
from
an even older version, in BASIC (by Magnus Olsson), that had been
found
in a SIMTEL archive. It's got lineage.
As of 22-Oct-2001, it appears to work, though I haven't traced the
arrow path'ing logic yet nor made it smart. Arrows can go pretty
much
anywhere you tell them and, yes, you can shoot yourself as in the
original.
I tweaked a few strings, but the instructions are almost unscathed.
Ahhh, it takes me back to my Dad's HP85 with the built-in 5 inch
monochrome screen, tape drive, and thermal printer...
}
]
; Setup
; initialize arrow count
; randomly place bats, pits, wumpus, and player
; make a working copy of the starting locations.
; Check-for-hazards
; Each Turn
; ask player to move or shoot
; if shoot
; check for hit on self and wumpus
; move wumpus
; if move
; check-for-hazards
; until player dead, wumpus dead, or out of arrows
arrow-count: 0
; indexes into location block
player: 1
wumpus: 2
pit-1: 3
pit-2: 4
bats-1: 5
bats-2: 6
protect [player wumpus pit-1 pit-2 bats-1 bats-2]
loc: []
save: []
winner: none
finished: false
; The cave is a dodecahedron. Each block is a room containing the
; number for the rooms it is connected to.
cave: [
[2 5 8] [1 3 10] [2 4 12] [3 5 14] [1 4 6]
[5 7 15] [6 8 17] [1 7 9] [8 10 18] [2 9 11]
[10 12 19] [3 11 13] [12 14 20] [4 13 15] [6 14 16]
[15 17 20] [7 16 18] [9 17 19] [11 18 20] [13 16 19]
]
random-room: does [random 20]
random-direction: does [random 3]
get-num: func [prompt] [
if error? try [return to-integer ask prompt] [
return 0
]
]
print-instructions: does [
print {
WELCOME TO 'HUNT THE WUMPUS' (love that all caps retro look)
THE WUMPUS LIVES IN A CAVE OF 20 ROOMS. EACH ROOM HAS 3 TUNNELS LEADING TO
OTHER ROOMS.
HAZARDS:
BOTTOMLESS PITS - TWO ROOMS HAVE BOTTOMLESS PITS IN THEM. IF YOU GO THERE,
YOU FALL INTO THE PIT (YOU LOSE!)
SUPER BATS - TWO OTHER ROOMS HAVE SUPER BATS. IF YOU GO THERE, A BAT GRABS
YOU AND TAKES YOU TO SOME OTHER ROOM AT RANDOM. (WHICH MAY BE
TROUBLESOME)
WUMPUS: THE WUMPUS IS NOT BOTHERED BY HAZARDS (HE HAS SUCKER FEET AND IS TOO
BIG FOR A BAT TO LIFT). USUALLY HE IS ASLEEP. TWO THINGS WAKE HIM UP:
YOU
SHOOTING AN ARROW OR YOU ENTERING HIS ROOM. IF THE WUMPUS WAKES HE MOVES
ONE ROOM (75% chance) OR STAYS STILL (25% chance). AFTER THAT, IF HE IS
WHERE YOU ARE, HE EATS YOU UP AND YOU LOSE!
YOU: EACH TURN YOU MAY MOVE OR SHOOT A CROOKED ARROW.
MOVING: YOU CAN MOVE ONE ROOM (THROUGH ONE TUNNEL).
ARROWS: YOU HAVE 5 ARROWS. YOU LOSE WHEN YOU RUN OUT.
EACH ARROW CAN GO FROM 1 TO 5 ROOMS. YOU AIM BY TELLING THE COMPUTER
THE ROOMS YOU WANT THE ARROW TO GO TO. IF THE ARROW CAN'T GO THAT
WAY
(I.E. NO TUNNEL) IT MOVES AT RANDOM TO THE NEXT ROOM.
* IF THE ARROW HITS THE WUMPUS, YOU WIN.
* IF THE ARROW HITS YOU, YOU LOSE.
WARNINGS: WHEN YOU ARE ONE ROOM AWAY FROM A WUMPUS OR HAZARD, THE COMPUTER
SAYS:
WUMPUS: 'YOU SMELL A WUMPUS'
BAT : 'YOU HEAR BATS'
PIT : 'YOU FEEL A DRAFT'
}
ask "Press <Enter> when you're ready to start the game"
]
check-for-hazards: func [/local i] [
print ""
print ["You are in room " loc/:player]
print ["Tunnels lead to " pick cave loc/:player]
; Look at each of the 3 rooms around the player to see if the contain
; the wumpus, a pit, or bats.
for i 1 3 1 [
room: pick pick cave loc/:player i
if (room = loc/:wumpus) [print " You smell a wumpus!"]
if any [(room = loc/:pit-1) (room = loc/:pit-2)] [print " You feel a
draft!"]
if any [(room = loc/:bats-1) (room = loc/:bats-2)] [print " You hear
bats!"]
]
print ""
]
move-or-shoot?: func [/local cmd] [
cmd: ask "Shoot or move (S/M)? "
; The default case handles bad inputs and prompts the user again.
switch/default cmd [
"S" ['shoot]
"M" ['move]
][move-or-shoot?]
]
move-arrow: func [room path /local i rnd-rm] [
;print reduce ["move arrow" room tab path]
if not tail? path [
either find cave/:room first path [
; next item in path is valid
check-for-arrow-hit first path
if finished [return]
; Recursive call
move-arrow first path next path
][
; pick a random direction
i: random-direction
check-for-arrow-hit rnd-rm: cave/:room/:i
if finished [return]
; Recursive call
move-arrow rnd-rm next path
]
]
]
shoot: func [/local path] [
path: to-block ask "Enter 1 to 5 room numbers for the arrow's path: "
move-arrow loc/:player path
if not finished [print "Your arrow missed"]
move-wumpus
; See if the Wumpus moved onto the player
if finished [return]
arrow-count: arrow-count - 1
if (arrow-count <= 0) [
print reduce [newline "You ran out of arrows..."]
winner: 'wumpus
finished: true
return
]
check-for-hazards
]
check-for-arrow-hit: func [room] [
if (room = loc/:wumpus) [
print reduce [newline "You got the Wumpus!"]
winner: 'player
finished: true
return
]
if (room = loc/:player) [
print reduce [newline "You shot yourself!"]
winner: 'arrow
finished: true
return
]
]
; 75% chance that it will move
wumpus-moves?: does [either (random 4) < 4 [true][false]]
move-wumpus: does [
if wumpus-moves? [loc/2: pick (pick cave loc/:wumpus) random-direction]
if (loc/:wumpus = loc/:player) [
winner: 'wumpus
finished: true
]
]
move-player: func [/bat-move new-loc /local new-player-loc] [
either bat-move [
loc/1: new-loc
][
new-player-loc: get-num "To which room? "
; Call recursively, then bail, if bad input
if any [(new-player-loc < 1) (new-player-loc > 20)] [
move-player
return
]
; Call recursively, then bail, if illegal move
if not find pick cave loc/:player new-player-loc [
print "You can't move there, unless you plan to dig a new
tunnel."
move-player
return
]
; It was a legal move, so update the player's location.
change at loc player new-player-loc
]
if (loc/:player = loc/:wumpus) [
print "Yikes! You bumped the Wumpus!"
move-wumpus
; See if the Wumpus moved onto the player
if finished [return]
]
if any [(loc/:player = loc/:pit-1) (loc/:player = loc/:pit-2) ] [
print reduce [newline "Aaiiiiieeeee....(you fell in a pit)"]
winner: 'pit
finished: true
return
]
if any [(loc/:player = loc/:bats-1) (loc/:player = loc/:bats-2) ] [
print "ZAP! Super-Bat snatch! Elsewhereville for you!"
move-player/bat-move random-room
return
]
check-for-hazards
]
; This is ugly, but it works for now and avoids setup collisions.
; Don't really need items for this, but I'd like to improve it and then I
might.
init-locations: func [/local items avail-rooms result offset] [
random/seed now/time/precise
items: [player wumpus pit-1 pit-2 bats-1 bats-2]
avail-rooms: copy [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]
result: make block! length? items
repeat i length? items [
; Too dense for one line?
;append result pick avail-rooms offset: random length? avail-rooms
offset: random/secure length? avail-rooms
append result pick avail-rooms offset
remove at avail-rooms offset
]
return result
]
setup: func [/same-as-last] [
; Initialize arrow count.
arrow-count: 5
; If we haven't setup before, or they want the same setup,
; there's no need to initialize.
if any [(not same-as-last) (start-loc = none)] [
; Randomly place bats, pits, wumpus, and player
start-loc: init-locations
]
; Make a working copy of the starting locations.
loc: copy start-loc
]
do-game: func [/same-as-last] [
either same-as-last [
setup/same-as-last
][
setup
]
check-for-hazards
while [not finished] [
either move-or-shoot? = 'shoot [shoot][move-player]
]
print switch winner [
wumpus ["<SNARF> The Wumpus got you!!!"]
player ["<BLARP> The Wumpus will get you next time!"]
pit ["<SPLAT> Better luck next time"]
arrow ["<GLURG> You really should be more careful"]
]
print ""
]
start: does [
if (ask "Would you like instructions (Y/N)? ") = "Y" [
print-instructions
]
play: "Y"
same-setup: "N"
while [play = "Y"] [
either same-setup = "N" [
do-game
][
do-game/same-as-last
]
play: ask "Play again (Y/N)? "
if play = "Y" [
winner: none
finished: false
same-setup: ask "Same setup (Y/N)? "
]
]
]
start
[2/12] from: ammonjohnson:y:ahoo at: 23-Oct-2001 0:50
wanna send it to me? I get invalid string crap.
Enjoy!!
Ammon
[3/12] from: gchiu:compkarori at: 23-Oct-2001 19:34
Hi Gregg,
Just in case you didn't know, you can use 'has as a
shorthand for functions with local variables but no
parameters
myfunc: has [ v1 v2 .. ] [
]
--
Graham Chiu
[4/12] from: matt:fitzgerald:bigpond at: 23-Oct-2001 16:04
That's because it has line feeds thrown in at random points (line wrap on at
column 80 perhaps?)!! hehe.. I just made my way through and fixed it up..
kind of neat little game!
-Matt
[5/12] from: gchiu:compkarori at: 23-Oct-2001 19:48
On Tue, 23 Oct 2001 00:50:47 -0700
"Ammon Johnson" <[ammonjohnson--yahoo--com]> wrote:
> wanna send it to me? I get invalid string crap.
>
I got the wumpus in 3 moves :)
Just make sure that the print commands don't split across
lines.
--
Graham Chiu
[6/12] from: carl:cybercraft at: 23-Oct-2001 19:15
On 23-Oct-01, Gregg Irwin wrote:
a HUNT THE WUMPUS game!
But I shot it in the second game.
BTW, you use 'save as a word, which isn't the best of REBOL words to
commandeer...
You're sentimental. (:
--
Carl Read
[7/12] from: ammonjohnson:yah:oo at: 23-Oct-2001 1:27
Oh, Duh! Some people need help to think!
Enjoy!!
Ammon
[8/12] from: chaz:innocent at: 23-Oct-2001 7:53
> Ahhh, it takes me back to my Dad's HP85 with the built-in 5 inch
> monochrome screen, tape drive, and thermal printer...
Those were the days
An IBM 'Personal' Computer with an Eighty TWO eighty-six processor? What
are you thinking? You'll never USE that much power! A FORTY MEGABYTE
Harddrive? That enough storage for you, your kids, and your grandchildren!
You want a color monitor? Why not get amber? That's all the color you
really need.
[9/12] from: greggirwin:mindspring at: 23-Oct-2001 10:15
Hi Ammon, et al
<< wanna send it to me? I get invalid string crap. >>
Garrrrrhhh! Line wrapping. Sorry about that.
--Gregg
[10/12] from: greggirwin:mindspring at: 23-Oct-2001 10:15
Hi Graham,
<< Just in case you didn't know, you can use 'has as a
shorthand for functions with local variables but no
parameters >>
Yeah, I actually know about it but I'm still trying to decide which REBOL
words I like and where it's clearer to be consistent even if it means a
little more typing. E.g. is it confusing, especially to newcomers, to have
func, function, has, and does mixed throughout the code? 'has is also adds
the fact that you don't need to use /local with it. On one hand I like
reducing clutter but, on the other, we have to maintain clarity of intent
and easy scanning of code. I'm still learning so my style may be
inconsistent for some time yet.
Thanks though! There's a lot I don't know so always feel free to point out
improvements I can make.
--Gregg
[11/12] from: greggirwin:mindspring at: 23-Oct-2001 10:15
Hi Carl,
<< BTW, you use 'save as a word, which isn't the best of REBOL words to
commandeer...>>
DOH! Thanks very much. It was actually a left-over and not used. Even worse.
:)
--Gregg
[12/12] from: media:quazart at: 23-Oct-2001 12:40
Ok since you guys are at it... ;-)
Remember the vic-20 or the tandy TRS-80 color computer !? those were the
most fun computers to program... (it might help that I was around 10 years
old at the time ;-)
yep them old tape drives... I remember being all exited when I finally got
myself the computer-controled tape drive! imagine that... how much
technology do you need... the computer can start and stop the (basically a
Hi Fidelity audio) tape recorder, so you don't need to run around the table
to do it yourself... (you still had to rewind it by hand though...I always
found that odd). Computers where so much fun then... hardly anything being
more standard than the fact that the BASIC language having numbers for its
lines... (If I remember correctly, BASIC is the actuall only thing ever
programmed (in part) by Bill Gates himself... everything else ... well you
all know the rest...)
I remember when optimizing code meant to shorten the variable names, because
you needed the few extra 20-100 bytes to finish your code... because there
was not enough of RAM in the system to fit the actual source of the
application within the computer's ram... shorter names also meant
drastically faster code on some platforms... I even remember building
myself a codec which converted string and array data into pictures (using
equivalents of peeks and pokes!) so that I could use the graphics RAM of my
TRS-80 for data. That way I could have more than the (shared) code/data RAM
limit (If I didn't use the graphics rasters, of course!).
ah, well, I have to get back to reality and to REBOL... (I'm currently
building a multi-track timeline for process control.... its quite a challege
trying to weed out View's weird event handling problems)
-Maxim