RCRPG/Clojure: Difference between revisions

From Rosetta Code
Content added Content deleted
(clojure code)
 
(refactoring)
Line 29:
"name" "name-room"} #"\|"))
 
(def *base-valid-actions* #{:drop-item :take-item :inventory :move :alias-command :name-room :dig :help :equip :look})
 
(defn valid-actions
[aliases]
(into #{}
(concat
(map keyword (keys aliases))
*base-valid-actions*)))
 
;; room = (x y z [on-the-ground])
Line 79 ⟶ 86:
 
(defn in?
"returns true if k is in collsequence"
[coll k]
(some #{k} coll))
Line 93 ⟶ 100:
[(coord-at (world :current-room) direction) (hash-set (rand-nth [:gold :sledge :ladder]))])
 
(defn if-valid-direction
"fn if direction is valid, otherwise prints an error message, else fnelse"
[direction fn fnelse]
(if-not (in? *directions* direction))
(do
(println "Where?!")
fnelse)
fn))
 
;; ** describing rooms ** ;;
 
;; find adjacent exits
(defn find-exits
"returns a list of directions with the exit directions for room, eg. (:north :east)"
Line 147 ⟶ 149:
(let [room-content (room-ground room)]
(if (not-empty room-content)
(str " On the ground you can see: " (describe-items room-content) "."))))
(str
" On the ground you can see: " (describe-items room-content) "."))))
 
(defn describe
Line 156 ⟶ 157:
(str " (" r ")")
nil)]
(println (str "You are at " (join " " (room-position room)) room-name "." (describe-ground room)
(join " " (describeroom-exitsposition room world)))))
room-name "."
(describe-ground room)
(describe-exits room world))))
 
(defn perform-action
[world action args]
(let [result (apply (resolve (symbol action)) (conj args world ))]
(cond
(string? result) (do (println result) world)
(vector? result) (do (println (first result)) (second result) ))))
 
;; ** actions ** ;;
 
(defn look [world]
"describes the room the player is in"
(describe (current-room world) world))
world)
 
(defn dig
Line 169 ⟶ 180:
([world direction]
(let [dir (keyword direction)]
(if- (valid-direction dir)
(if (exit? (current-room world) world dir)
(do"There is already a room!"
(println "There is already a room!")
world)
(if (= (world :equipped) :sledge)
[(dostr "You dig a new room " direction "ward.")
(printlnassoc (strworld "You:maze dig(merge a(world new:maze) (new-room-at "world direction "ward."dir)))]
"You need (assocto worldequip :mazea (mergesledge (worldin :maze)order (new-room-atto worlddig dir))the wall!"))
(do"Where?!")))
(println[world] "YouWhere needdo toyou equip a sledge in orderwant to dig the wall!?"))
world)))
world)))
([world]
(println "Where do you want to dig?")
world))
 
(defn move
Line 191 ⟶ 195:
(let [dir (keyword direction)
target-coord (coord-at (room-position (current-room world)) dir)]
(if- (valid-direction dir)
(if (exit? (current-room world) world dir)
(if (and (= dir :up) (not ((room-ground (current-room world)) :ladder)))
"You cannot go up if there's no ladder in the room."
(do
(println "You cannot go up if there's no ladder in the room.")
world)
(let [updated-world (assoc world :current-room target-coord)]
[(describe (current-room updated-world) updated-world)
updated-world]))
(do"There's no exit in that direction!")
"Where?!")))
(println "There's no exit in that direction!")
([world] "Where do you want to worldgo?"))
world)))
([world]
(println "Where do you want to go?")
world))
 
(defn equip
Line 213 ⟶ 211:
(let [i (keyword item)]
(if (in-inv? world i)
(do["Equipped!"
(printlnassoc world :equipped "Equipped!"i)])
"You haven't (assocsuch worldan :equipped iitem"))
([world] "What do you want to equip?"))
(do
(println "You haven't such an item")
world))))
([world]
(println "What do you want to equip?")
world))
 
(defn drop-item
Line 234 ⟶ 227:
equipped-item (world :equipped)]
(if (= i :all)
(do["Everything dropped!"
(println "Everythingassoc dropped!")world
(assoc world
:equipped nil
:inventory #{}
:maze (update-room (clojure.set/union current-ground current-inventory))))]
(doif (in-inv? world i)
(if["Item (in-inv? world i)dropped!"
(doassoc world
:equipped (printlnif "Item(= equipped-item i) nil dropped!"equipped-item)
:inventory (assocdisj worldcurrent-inventory i)
:equippedmaze (ifupdate-room (=conj equippedcurrent-itemground i) nil equipped-item))]
"You haven't such an :inventory (disj current-inventory iitem"))))
([world] "What do you want to drop?"))
:maze (update-room (conj current-ground i))))
(do
(println "You haven't such an item")
world))))))
([world]
(println "What do you want to drop?")
world))
 
(defn take-item
Line 266 ⟶ 252:
equipped-item (world :equipped)]
(if (= i :all)
(do["Everything taken!"
(println "Everythingassoc taken!")world
:inventory (clojure.set/union current-inventory current-ground)
(assoc world
:inventorymaze (clojure.set/union currentupdate-inventoryroom current-ground#{}))]
(if (in? :maze (updatecurrent-roomground #{}))i)
(do ["Item taken!"
(if (in? current-groundassoc i)world
:inventory (doconj current-inventory i)
:maze (printlnupdate-room "Item(disj taken!"current-ground i)))]
"There is not such (associtem worldon the ground!"))))
([world] "What do you want to pick up?"))
:inventory (current-inventory i)
:maze (update-room (disj current-ground i))))
(do
(println "There is not such item on the ground!")
world))))))
([world]
(println "What do you want to pick up?")
world))
 
(defn inventory
Line 289 ⟶ 268:
[world]
(let [inv (world :inventory)]
(println (if (empty? inv)
"You are not carrying anything"
(str "You are carrying: " (describe-items inv)))))
world)
 
(defn equip
Line 298 ⟶ 276:
(let [i (keyword item)]
(if (in-inv? world i)
["Item equipped." (assoc world :equipped i)]
(do
"You haven't (println "Itemsuch equipped.item")))
([world] "What do you want to equip?"))
(assoc world :equipped i))
(do
(println "You haven't such item")
world))))
([world]
(do
(println "What do you want to equip?")
world)))
 
(defn alias-command
Line 314 ⟶ 285:
(let [command (join " " commands)
current-aliases (world :aliases)]
[(str "Alias created for the command " command) (assoc world :aliases (assoc current-aliases alias command))]))
(do
(println (str[world] "Alias created for the command what?" command))
([world alias] (str "Alias '" alias "' to what?")))
(assoc world :aliases (assoc current-aliases alias command)))))
([world]
(do
(println "Alias what?")
world))
([world alias]
(do
(println (str "Alias '" alias "' to what?"))
world)))
 
(defn name-room
Line 332 ⟶ 295:
current-named-room (world :named-rooms)
current-location (room-position (current-room world))]
["Done!" (assoc world :named-rooms (assoc current-named-room current-location a))]))
(do
([world] "What name?"))
(println "Done!")
 
(assoc world :named-rooms (assoc current-named-room current-location a)))))
(defn help
([world]
"prints an help message"
(do
[world]
(println "What name?")
(str
world)))
"Welcome to the dungeon!\n"
"You need a sledge to dig rooms and ladders to go upwards.\n"
"Valid commands are: directions (north, south...), dig, take, drop, equip, inventory and look.\n"
"Additionally you can tag rooms with the 'name' command and alias commands with 'alias'.\n"
"Have fun!\n"))
 
;; ** user input ** ;;
Line 354 ⟶ 322:
command (translate (first sanitized-input) (world :aliases))
i (concat command (rest sanitized-input))
[action (first& args] i)
argscurrent-valid-actions (restworld i:aliases)]
(cond
(= (first i) "exit") nil
(contains? *(valid-actions* current-valid-actions) (keyword action)) (try
(applytry (resolveperform-action (symbolworld action)) (conj args world ))
(catch IllegalArgumentException e (println "Invalid arguments") world))
:else (do (println "What do you mean?") world)))
(do
(println "Hm?!")
world)))
 
(defn help
"prints an help message"
[world]
(do
(println "Welcome to the dungeon!")
(println "You need a sledge to dig rooms and ladders to go upwards.")
(println "Valid commands are: directions (north, south...), dig, take, drop, equip, inventory and look.")
(println "Additionally you can tag rooms with the 'name' command and alias commands with 'alias'." )
(println "Have fun!")
world))
 
;; main loop

Revision as of 22:17, 15 October 2011

RCRPG/Clojure is part of RCRPG. You may find other members of RCRPG at Category:RCRPG.

Clojure version of RCRPG. The code can also be checked out and contributed to on github .

Code

<lang lisp> (ns rosettacode.rcrpg.clojure) (use '[clojure.string :only (join split)])

(defn split-keys

 "returns map m (with string keys) creating multiple entries for keys separated by regex splitter."
 [m splitter]
 (apply hash-map (flatten
   (map #(let [splitted (split (first %) splitter)]
           (if (> (count splitted) 1)
               (map vector splitted (repeat (second %)))
               %)) m))))

(def *translation-map*

 (split-keys {"drop" "drop-item"
              "get|take" "take-item"
              "i|inv" "inventory"
              "n|north" "move north"
              "w|west" "move west"
              "s|south" "move south"
              "e|east" "move east"
              "u|up" "move up"
              "d|down" "move down"
              "alias" "alias-command"
              "name" "name-room"} #"\|"))

(def *base-valid-actions* #{:drop-item :take-item :inventory :move :alias-command :name-room :dig :help :equip :look})

(defn valid-actions

 [aliases]
 (into #{}
   (concat
     (map keyword (keys aliases))
     *base-valid-actions*)))
room = (x y z [on-the-ground])

(def *world-state* {:maze {[0 0 0] #{:sledge}

                          [1 1 5] #{:lots-of-gold}}
                   :inventory #{}
                   :current-room [0 0 0]
                   :equipped nil
                   :aliases *translation-map*
                   :named-rooms {[0 0 0] "the starting room"
                                 [1 1 5] "the prince room"}})

(def *directions* [:north :west :south :east :up :down])

(def *coords*

 (zipmap *directions*  [[0  1  0 ]
                        [-1 0  0 ]
                        [0 -1  0 ]
                        [1  0  0 ]
                        [0  0  1 ]
                        [0  0  -1]]))

(defn translate

 "if source is a key of target, returns the value as a sequence (split by spaces), else returns source."
 [source target]
 (split (if-let [t (get target source)] t source) #" "))
** utilities ** ;;

(defn coord-at

 "given a coordinate (eg. [1 1 1]) and a direction (eg. :north) returns the coordinate for the direction (eg. [1 2 1])"
 [current direction]
 (vec (map + (direction *coords*) current)))

(defn current-room

 "returns the room the player is in, eg. [[1 2 3] #{:gold}]"
 [world]
 [(:current-room world) ((world :maze) (world :current-room ))])

(defn room-position

 "returns the coordinate for room (eg. [1 2 1])"
 [room]
 (first room))

(defn room-ground

 "returns the content of room (eg. #{gold})"
 [room]
 (second room))

(defn in?

 "returns true if k is in sequence"
 [coll k]
 (some #{k} coll))

(defn in-inv?

 "returns true if the player has item in her inventory"
 [world item]
 (in? (world :inventory) item))

(defn new-room-at

 "returns a new room relative to the player's current position, towards direction"
 [world direction]
 [(coord-at (world :current-room) direction) (hash-set (rand-nth [:gold :sledge :ladder]))])

(defn valid-direction

 "fn if direction is valid, otherwise prints an error message, else fnelse"
 [direction]
 (in? *directions* direction))
** describing rooms ** ;;

(defn find-exits

 "returns a list of directions with the exit directions for room, eg. (:north :east)"
 [room world]
 (let [calc-neighbour #(map + (room-position room) (second %))
       maze (world :maze)]
   (->> *coords* (filter #(contains? maze (calc-neighbour %))) (map key))))

(defn exit?

 "returns true if room has an exit towards direction"
 [room world direction]
 (in? (find-exits room world) direction))

(defn describe-exits

 "returns string describing the exits in room"
 [room world]
 (let [exits-to-string (fn [exits]
         (cond
         (= (count exits) 1) (str " There is an exit " (name (first exits)) "ward")
         (not-empty exits) (str " There are exits at " (->> exits (map name) (join ", ")) ".")
         :else ""))]
   (exits-to-string (find-exits room world))))

(defn describe-items

 "returns a description of items"
 [items]
 (let [i (reduce #(conj %1
           (case %2
             :sledge "a sledge"
             :gold "some gold coins"
             :ladder "a ladder lying down"
             :lots-of-gold "LOTS of gold!"))
           [] items)]
    (str
      (join ", " (drop-last 2 i))
      (when (> (count i) 2) ", ")
      (join " and " (take-last 2 i)))))

(defn describe-ground

 "returns string describing the exits in room"
 [room]
 (let [room-content (room-ground room)]
 (if  (not-empty room-content)
    (str " On the ground you can see: " (describe-items room-content) "."))))

(defn describe

 "prints a description of room"
 [room world]
 (let [room-name (if-let [r ((world :named-rooms) (room-position room))]
         (str " (" r ")")
         nil)]
   (str "You are at "
     (join " "  (room-position room))
     room-name "."
     (describe-ground room)
     (describe-exits room world))))

(defn perform-action

 [world action args]
 (let [result (apply (resolve (symbol action)) (conj args world ))]
   (cond
     (string? result) (do (println result) world)
     (vector? result) (do (println (first result)) (second result) ))))
** actions ** ;;

(defn look [world]

 "describes the room the player is in"
 (describe (current-room world) world))

(defn dig

 "digs a new room towards direction if there's not a room already and the player has a sledge"
 ([world direction]
   (let [dir (keyword direction)]
     (if (valid-direction dir)
       (if (exit? (current-room world) world dir)
         "There is already a room!"
         (if (= (world :equipped) :sledge)
           [(str "You dig a new room " direction "ward.")
             (assoc world :maze (merge (world :maze) (new-room-at world dir)))]
           "You need to equip a sledge in order to dig the wall!"))
       "Where?!")))
 ([world] "Where do you want to dig?"))

(defn move

 "moves the player to an adjacent room and describes it"
 ([world direction]
   (let [dir (keyword direction)
         target-coord (coord-at (room-position (current-room world)) dir)]
     (if (valid-direction dir)
       (if (exit? (current-room world) world dir)
         (if (and (= dir :up) (not ((room-ground (current-room world)) :ladder)))
           "You cannot go up if there's no ladder in the room."
           (let [updated-world (assoc world :current-room target-coord)]
             [(describe (current-room updated-world) updated-world)
              updated-world]))
         "There's no exit in that direction!")
       "Where?!")))
 ([world] "Where do you want to go?"))

(defn equip

 "equips an item if specified and if the player has it in her inventory"
 ([world item]
   (let [i (keyword item)]
     (if (in-inv? world i)
       ["Equipped!"
        (assoc world :equipped i)])
       "You haven't such an item"))
 ([world] "What do you want to equip?"))

(defn drop-item

 "drops an item in the inventory or all of them leaving it in the room"
 ([world item]
   (let [i (keyword item)
         current-position (get world :current-room)
         current-ground (room-ground (current-room world))
         current-maze (world :maze)
         current-inventory (world :inventory)
         update-room (partial assoc current-maze current-position)
         equipped-item (world :equipped)]
     (if (= i :all)
       ["Everything dropped!"
        (assoc world
           :equipped nil
           :inventory #{}
           :maze (update-room (clojure.set/union current-ground current-inventory)))]
       (if (in-inv? world i)
         ["Item dropped!"
          (assoc world
            :equipped (if (= equipped-item i) nil equipped-item)
            :inventory (disj current-inventory i)
            :maze (update-room (conj current-ground i)))]
         "You haven't such an item"))))
 ([world] "What do you want to drop?"))

(defn take-item

 "picks up an item from the ground or all of them putting them into the inventory"
 ([world item]
   (let [i (keyword item)
         current-position (get world :current-room)
         current-ground (room-ground (current-room world))
         current-maze (:maze world)
         current-inventory (world :inventory)
         update-room (partial assoc current-maze current-position)
         equipped-item (world :equipped)]
     (if (= i :all)
       ["Everything taken!"
        (assoc world
          :inventory (clojure.set/union current-inventory current-ground)
          :maze (update-room #{}))]
       (if (in? current-ground i)
         ["Item taken!"
          (assoc world
            :inventory (conj current-inventory i)
            :maze (update-room (disj current-ground i)))]
         "There is not such item on the ground!"))))
 ([world] "What do you want to pick up?"))

(defn inventory

 "prints what the player is carrying"
 [world]
 (let [inv (world :inventory)]
   (if (empty? inv)
      "You are not carrying anything"
      (str "You are carrying: " (describe-items inv)))))

(defn equip

 ([world item]
   (let [i (keyword item)]
     (if (in-inv? world i)
       ["Item equipped." (assoc world :equipped i)]
       "You haven't such item")))
 ([world] "What do you want to equip?"))

(defn alias-command

 "aliases command to alias"
 ([world alias & commands]
   (let [command (join " " commands)
         current-aliases (world :aliases)]
     [(str "Alias created for the command " command) (assoc world :aliases (assoc current-aliases alias command))]))
 ([world] "Alias what?")
 ([world alias] (str "Alias '" alias "' to what?")))

(defn name-room

 "tags the current location with alias"
 ([world & alias]
   (let [a (join " " alias)
         current-named-room (world :named-rooms)
         current-location (room-position (current-room world))]
     ["Done!" (assoc world :named-rooms (assoc current-named-room current-location a))]))
 ([world]  "What name?"))

(defn help

 "prints an help message"
 [world]
 (str
   "Welcome to the dungeon!\n"
   "You need a sledge to dig rooms and ladders to go upwards.\n"
   "Valid commands are: directions (north, south...), dig, take, drop, equip, inventory and look.\n"
   "Additionally you can tag rooms with the 'name' command and alias commands with 'alias'.\n"
   "Have fun!\n"))
** user input ** ;;

(defn sanitize-input

 "lowercases input, splits it into words and trims the tokens"
 [input]
 (remove empty? (-> input .toLowerCase .trim (split #" "))))

(defn parse-input

 "interprets user input. returns an updated world state if the game has to continue, nil if the user inputs 'exit'"
 [input world]
 (if (not-empty input)
   (let [sanitized-input (sanitize-input input)
         command (translate (first sanitized-input) (world :aliases))
         i (concat command (rest sanitized-input))
         [action & args] i
         current-valid-actions (world :aliases)]
     (cond
       (= (first i) "exit") nil
       (contains? (valid-actions current-valid-actions) (keyword action))
         (try (perform-action world action args)
         (catch IllegalArgumentException e (println "Invalid arguments") world))
       :else (do (println "What do you mean?") world)))
   (do
     (println "Hm?!")
     world)))
main loop

(defn run

 "the main game loop"
 []
 (do
   (println "Welcome to the dungeon!\nGrab the sledge and make your way to room 1,1,5 for a non-existant prize!")
   (loop [input (read-line)
          world *world-state*]
     (if-let [w (parse-input input world)]
             (recur (read-line) w)
             (println "See you next time!")))))

(run) </lang>