1

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!

KoshVorlon
  • 144
  • 8
  • 3
    Note also that your code doesn't use the typical naming conventions. Earmuffs (asterisks around the name: `*foo*`) should only be used for special variables (i.e. ones defined with `defparameter` or `defvar`), not function names. Lisp programmers also use dashes instead of camel/snake case (`*NextRoom*` -> `next-room`, `*dice_combat*` -> `dice-combat`). Even if you for some reason decide to deviate from that, you should at least pick just one convention to use. – jkiiski Apr 20 '21 at 05:46

1 Answers1

5

Your code

(defparameter *room* (car  *world*)

is missing the closing paren, and this is what the error is actually telling you: the form did not end before EOF.

Note that the code you sent has incorrect let syntax. Remember, parens in lisp and scheme are meaningful.

sds
  • 58,617
  • 29
  • 161
  • 278
  • Thanks for the pointer on the let statement. Yeah, I figured it out, what I have in there won't work. The whole idea was to construct a statement saying "Let the value of *phealth* equal the random dice roll coded in *dice*". It looks like that type of statement with a "let" function doesn't work. I got it to work with a defparameter statement so I'll try that instead. Yeah, that missing parentheses was a dumb error too! Thank you again! – KoshVorlon Apr 19 '21 at 19:48