Good afternoon.
I'm teaching myself Common Lisp and have run into an issue with a specific line in my code.
This is a two room adventure with a fight in the arena - the whole goal of this was to write the prompt command one time, therefore I had to set up a way for Common Lisp to look at a line of text and determine which was the room and which was the direction to go in.
I decided to code it as:
(defparameter *world* '(room direction))
then seperate the room and direction like this:
(defparameter *direction* (cdr *world*))
and
(defparameter *room* (car *world*)
Common lisp seems to not understand
(defparameter *direction* (cdr *world*))
and tells me :
Unhandled SB-C::INPUT-ERROR-IN-LOAD in thread #<SB-THREAD:THREAD "main thread" RUNNING
{10010B0523}>:
READ error during LOAD:
end of file on #<SB-SYS:FD-STREAM for "file C:\\Users\\micha\\common_lisp\\arena.lisp" {10022F4C23}>
(in form starting at line: 169, column: 41, file-position: 4438)
Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {10010B0523}>
0: (SB-DEBUG::DEBUGGER-DISABLED-HOOK #<SB-C::INPUT-ERROR-IN-LOAD {10024CEF73}> #<unused argument> :QUIT T)
1: (SB-DEBUG::RUN-HOOK *INVOKE-DEBUGGER-HOOK* #<SB-C::INPUT-ERROR-IN-LOAD {10024CEF73}>)
2: (INVOKE-DEBUGGER #<SB-C::INPUT-ERROR-IN-LOAD {10024CEF73}>)
3: (ERROR #<SB-C::INPUT-ERROR-IN-LOAD {10024CEF73}>)
4: (SB-C:COMPILER-ERROR SB-C::INPUT-ERROR-IN-LOAD :CONDITION #<END-OF-FILE {10024CEF43}> :POSITION 4438 :LINE/COL NIL :STREAM #<SB-SYS:FD-STREAM for "file C:\\Users\\micha\\common_lisp\\arena.lisp" {10022F4C23}>)
5: (SB-C::%DO-FORMS-FROM-INFO #<CLOSURE (LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-INT:LOAD-AS-SOURCE) {10022FBE3B}> #<SB-C::SOURCE-INFO {10022FBDF3}> SB-C::INPUT-ERROR-IN-LOAD)
6: (SB-INT:LOAD-AS-SOURCE #<SB-SYS:FD-STREAM for "file C:\\Users\\micha\\common_lisp\\arena.lisp" {10022F4C23}> :VERBOSE NIL :PRINT NIL :CONTEXT "loading")
7: ((FLET SB-FASL::THUNK :IN LOAD))
8: (SB-FASL::CALL-WITH-LOAD-BINDINGS #<CLOSURE (FLET SB-FASL::THUNK :IN LOAD) {96F2EB}> #<SB-SYS:FD-STREAM for "file C:\\Users\\micha\\common_lisp\\arena.lisp" {10022F4C23}>)
9: ((FLET SB-FASL::LOAD-STREAM :IN LOAD) #<SB-SYS:FD-STREAM for "file C:\\Users\\micha\\common_lisp\\arena.lisp" {10022F4C23}> NIL)
10: (LOAD #<SB-SYS:FD-STREAM for "file C:\\Users\\micha\\common_lisp\\arena.lisp" {10022F4C23}> :VERBOSE NIL :PRINT NIL :IF-DOES-NOT-EXIST T :EXTERNAL-FORMAT :DEFAULT)
11: ((FLET SB-IMPL::LOAD-SCRIPT :IN SB-IMPL::PROCESS-SCRIPT) #<SB-SYS:FD-STREAM for "file C:\\Users\\micha\\common_lisp\\arena.lisp" {10022F4C23}>)
12: ((FLET SB-UNIX::BODY :IN SB-IMPL::PROCESS-SCRIPT))
13: ((FLET "WITHOUT-INTERRUPTS-BODY-2" :IN SB-IMPL::PROCESS-SCRIPT))
14: (SB-IMPL::PROCESS-SCRIPT "arena.lisp")
15: (SB-IMPL::TOPLEVEL-INIT)
16: ((FLET SB-UNIX::BODY :IN SAVE-LISP-AND-DIE))
17: ((FLET "WITHOUT-INTERRUPTS-BODY-7" :IN SAVE-LISP-AND-DIE))
18: ((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))
19: ("foreign function: #x43270B")
20: ("foreign function: #x403F08")
I tried this out in the repl by loading the script and in the repl, entering
*world*
gives me the expected result of:
(ROOM DIRECTION)
entering
(car *world*)
gives me the expected result of:
ROOM
(cdr *room*)
gives me the expected result of:
(DIRECTION)
Not really sure why it errors out, and yes, I will show the code. I will ask that you not outright tell me the answer, just nudge me in the right direction,please
Here's the code itself:
;; Area - an arena battle in Common Lisp
;;
;; There will be two directions, north to the arena and south away from it
;; The player won't be allowed to leave the area.
;; The player and the gladiator's stats will be randomly rolled
;; The combat will be randomly rolled as well
;; The prompt will be coded one time only too.
;; The following variables will be used:
;;
;; Player
;; gladiator
;; world - which will be a combination of the following two variables:
;; room
;; direction
;;
;; NextRoom
;;
;;phealth (randomly rolled)
;; ghealth (randomly rolled)
;; pstrength (randomly rolled)
;; gstrength (randomly rolled)
;; pdexterity (randomly rolled)
;; gdexterity (randomly rolled)
;; description - the room description
;; path - the exits available
;; prompt
;;
;;
;; First set up the global variables
;; set a random state for the dice throw
(setf *random-state* (make-random-state t))
;; This is you , the player
(defun *player* () )
;; This is your computer generated opponent
(defun *gladiator* () )
;; This defines the world as it exists within this program
(defun *world* () )
;; The particular room you're in
(defun *room* () )
;; This is the exits that are available.
;; We have to use a word other than "exit" because it's a reserved word
;; in Common Lisp and would throw an error if we try to define it
;; to something other than what it's set up for.
(defun *direction* () )
;; This is used to tell the computer to move ahead to the next room.
;; We could POTENTIALLY make room = room, but that's really confusing
;; We want to make this code as clear as is possible
;; Camel case isn't an absolute necessity, however when using a two-word
;; definition, it makes it easier to remember. REMEMBER, to the computer
;; NextRoom isn't the same is nextroom, Nextroom or nextRoom!
(defun *NextRoom* () )
;; This is your health
(defun *phealth* ()
)
;; This is your opponent's health
(defun *ghealth* () )
;; This is your strength
(defun *pstrength* () )
;; This is your opponents strength
(defun *gstrength* () )
;; This is your dexterity
(defun *pdexterity* () )
;; This is your opponents dexterity
(defun *gdexterity* () )
;; This is the prompt - where you enter your all of your commands
(defun *prompt* () )
;; We'll be using dice to set up players stats, the gladiator stats and it will
;; add a bit of randomness to the combat
(defun *dice* ()
(+ 1 (random 200 )))
;; now set up *dice1* for the d20 rolls
(defun *dice1* ()
(+ 1 (random 20 )))
;; Here we'll roll the stats for our player and our gladiator
;; both will be randomized. We'll be rolling for health
;; strength and dexterity.
;; A d200 will be used to health
;; A d20 will be used for both strength and dexterity
;; Now setup the dice roll that will be used for combat
(defun *dice_combat* ()
(+ 1 (random 20)))
;; players health first
(let ( *phealth* *dice*))
;; now we set the gladiator's health
(let ( *ghealth* *dice*))
;; now we roll the players strength
(let ( *pstrength* *dice1*))
;; now we roll the gladiator's strength
(let ( *gstrength* *dice1*))
;; now we set up the player's dexterity
(let ( *pdexterity* *dice1*))
;; now we set up to gladitor's dexterity
(let ( *gdexterity* *dice1*))
;; now we define difference - this will be used to gauge strike effectiveness
;; and damage between the player and the gladiator
(defparameter *difference* () )
#| now we setup the "world" function which consists of
two sub - functions, room and direction.
We're going to run them together, for example
(arena south) is going to be our way of stating that
an arena is south of us. We'll be using cadr to seperate each
item so they can be parsed seperately and understood to be
room and direction. Again, we can't use "exit" because Common Lisp
already uses this |#
#| now we establish what we want "world" to be, This will hold our room name
as well as our direction for that room. We can use this to check that the direction we
want to go in is correct, and it can be used to guide our player to the correct room
as well |#
(defparameter *world* '(room direction))
;;; Now, isolate the second parameter in world, and make that our direction
(defparameter *direction* (cdr *world*))
;;; Now, make the room name the first word in the paramter world
(defparameter *room* (car *world*)
#| Now we set up the prompt - this will loop throughout the program unless we exit
it. Dying in this game automatically exits us, so we don't have to look for that,
this only needs to look for exit. We'll set it up to look for inputs as well, and unless
an acceptable input is entered we'll return to this prompt. |#
(defun *prompt*
(let ((choice 1)) ; whatever choice is , let it equal "1" for right now, it's a pointer
(format t " ~% > ") ; Let our prompt print a carriage return then display " > "
(let ((response (read-line))) ; now we tell Common Lisp to read the input
#| Here we need to point the prompt at the world and verify if we're
given a valid choice , if we are, we can go to the next room, otherwise
we'll print an error message, then return to the prompt |#
(if t (*direction*)
(= *room* *NextRoom*)
(format t "You bang your head against the wall!" (*prompt*))))))
#| Now we set up the combat loop. It's random, based on throws of the
dice - HOWEVER, the players Dexterity will be judged against the
gladiators dexterity, who ever has the highest dexterity, the difference will
be removed from the strike, lessening the effect of the strike. The difference
will also be removed from any damage, lessening the damage as well.
When the player enters the room, the gladiator strikes first.
A dice is rolled for the gladiator. If he meets or exceeds the players dexterity
(pdexterity) he hits the player. If it's less, than he misses. If he hits, the
amount of damage he does will be the difference between the player's
health (*phealth*) and the amount over his dexterity, for example , if the
players health is 18 and the gladiator rolls a 20, he will have hit for 2
points as 20 - 18 = 2. If the gladiator hits (based on the dice roll) and
the amount is a negative number or a 0, the blow WOULD have hit, had the
player not dodged it. The same rules hold true for the gladiator! |#
#| Here we set up the functions that computes how much damage you have.
there will be one for the player and one for the gladiator. It will allow up
to keep track of the current health and subtract hit points away when struck |#
(defun p_health_counter ()
(( - *dice_combat* *pdexterity ( - *phealth))))
(defun g_health_counter ()
(( - *dice_combat* *gdexterity ( - *ghealth))))
;; Here we define the player's death - this occurs if the player's health goes to zero or below
(defun *pdeath* ()
(format t "~%Unfortunately for you, the gladiator has no mercy on you and he beheads you ~%~%~% **** You have died! **** ~%~%")) ; prints the player death string and stops as there's no where else to go.
;; Here we define the gladiators death
(defun *gdeath* ()
(format t "~% You offer the gladiator mercy, he responds by spitting in your face. You slit his throat. ~%~%~% **** The Gladiator has died ! **** ~%~%~% You spin around and with unerring aim, throw your sword INTO the Emporer and impale him! ~%~%~% *** The Emperor is dead! Long live the Emperor *** ~%~%~%. You take the Emperor\'s place and rule with justice and mercy and die a beloved emperor a century later. %~%~"))
;; You kill both the gladiator and the evil emperor that placed you there to begin with a rule justly!
(defun p_hit() ; The player's hit. I found it easier to play the hits and misses away from the combat loop.
(format t "~%The gladiator hits you for %A points!" (*p_health_counter*))
(defun p_miss() ; this time the gladiator misses, you take no combat damage
(format t "~% You dodge out of the way - just in the nick of time !"))
(defun g_hit () ;You hit the gladiator this time
(format t "~% You hit the gladiator for %A points!" ( *g_health_counter*)))
(defun g_miss() ;You miss the gladiator and do no damage
(format t "~% The gladiator dodges out of the way and laughs at your failed attempt to hit him!"))
(defun *combat* ()
(loop while (or ( <= 0 *phealth*) (<= 0 *ghealth*)) ; if either that player or the gladiators health is zero or lower, the game ends with their death, otherwise, keep fighting
(*dice_combat*) ; roll *dice_combat* to see if the swing is a hit or miss
#| The gladiator swings first, his roll must meet or exceed
the player's dexterity in order to actually hit the player |#
(if ( >= *dice_combat* *pdexterity*
(p_hit ( = *phealth* *p_health_counter*)) ;remove hp
(p_miss ( - *phealth* 0 )))) ; missed , no hp removed
#| Now the player swings, same rules as before |#
(if ( >= *dice_combat* *gdexterity*
(g_hit ( = *gheath* *g_health_counter*)) ;remove hp
(g_miss ( - *ghealth* 0)))))) ;player missed, no hp removed
#| Now we set up the rooms, it's going to be two rooms, the syntax for this command
was already set up in the function on line 165, "(defparameter *world*)". It will
have the room followed by the direction, for example (forest south). That means
that if the player types "south", he'll go to the forest. The rooms have to be coded
in reverse, so the first room we'll code will be the last room we enter. In this case
it's going to be the arena, the second room will be the training room where our
player will be allowed to only go south. |#
;; First we create the training room
(defun training_room
(format t " ~%~% .......So, you got plastered at the emperor's banquet \(and to be quite honest, your head still aches from too much wine \), you insulted the Emperor by wearing a mask with an immense nose on it and pretending you were him. The Emperor roared in anger and sentenced you to die in the arena, for his amusement. The door to the arena stands open to the south and I'm afraid it/'s the only way you can go, the door to the north is locked and you have no chance of opening that door whatsoever ~%")
(defun *world* arena south))
;; Now we create the arena - in this room, the gladiator will fight you automatically
(defun arena
(format t "~%~% The Arena of Quendor rises before you. It was in this very arena that the famous gladiator Dimwit Flathead slayed the fabled slavering Grue. THIS time, it's YOU facing Dimwit Flathead the XIV. He towers over you and looks like he can break you in half with his fingers. He holds a longsword, as do you. In astonishing speed he wields his sword and swings.........~%")
(*combat*)) ; this starts the combat automatically.
;; last thing to do is to tell Common Lisp what function to run first - that
;; will be the training room.
(training_room)
Thank you for your assistance!