Aha!  How about this, to cut the Gordian knot:

   1. The fancy room's :description isn't necessarily a simple string.  It 
   can be a vector of specs, where each spec is a text snippet or a pair 
   containing a predicate function and a text snippet.
   2. The (describe) function takes two arguments, the global state and the 
   room record.
   3. It steps through the specs, including only those snippets whose 
   predicate is true.  The predicates, of course, are predicates on the global 
   state.

The room is then a simple object; and the describe method remains purely 
functional.

On Thursday, March 29, 2018 at 3:45:02 PM UTC-7, Will Duquette wrote:
>
> I'm an experienced programmer, but a Clojure newbie; as a beginner 
> project, I'm looking into how one would idiomatically write a text 
> adventure of sorts in Clojure.  I'm less interested in producing a playable 
> game than I am in learning how to do such a thing in a proper functional 
> style.
>
> Suppose in this game I have a room whose description changes based on a 
> global flag.  For example, there's something in the Fancy Room that you 
> won't notice until you've reached the major plot point.
>
> The world map is (for the sake of argument) a hash-map whose keys are the 
> room IDs and whose values are room records, where each record is a hash-map.
>
> (def world {:fancy-room {:name "Fancy Room" :description "This is a fancy 
> room." ...}})
>
> I'm aware that I could use a (defstruct) or (defrecord); I'm keeping it 
> simple for now.  Then, the flags are saved in a ref; the intent is that 
> mutable set is segregated, so that it can more easily be written to a save 
> file.
>
> ;; Global set of flags
> (def flags (ref #{})
>
> (defn flag-set [flag]
>    (dosync (alter flags conj flag))
>
> ;; When the major plot point is reached
> (flag-set :major-plot-point-reached)
>  
> Normally, to describe a room you just return its :description.
>
> (defn describe [room] (:description (world get room)))
>
> But for the :fancy-room, the returned description depends on the global 
> flag, and it will be specific to :fancy-room.  I could add this logic 
> directly to the (describe) function's body, but that would be ugly.  What 
> I'd like to do is attach a lambda to the :fancy-room in some way.  The 
> (describe) function looks for a :describer, and if it's there it calls it; 
> and if not it just returns the :description:
>
> (defn describe [entity]
>     (if (:describer entity) 
>       ((:describer entity) entity)
>       (:description entity)))
>
> *Question 1*: this works, but it looks ugly to me; I figure there's a 
> better, more idiomatic way to do this kind of thing that's probably obvious 
> to anyone with any real experience.  Multimethods, maybe?  Define a Room 
> protocol, then let most rooms be NormalRoom records, but let :fancy-room be 
> a FancyRoom record?
>
> *Question 2*: Whatever code actually computes the description, it will 
> need access to the :major-plot-point-reached flag.  What's the cleanest way 
> to give the description code access to the flags ref?  It could simply 
> access "@flags" directly:
>
> (if (:major-plot-point-reached @flags) 
>   "This is a fancy room.  Hey, that light sconce looks movable!"
>   "This is a fancy room.")
>
> But that doesn't seem properly functional.  Would it be better to pass the 
> game state into each method?
>
> (defn describe [entity state]
>   (if (:describer entity)
>      ((:describer entity) entity state)
>      (:description entity)))
>
> Any ideas?
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to