Excellent.  Yeah, I was thinking I was probably going to too much trouble 
to get the "nil" value.  And the (derive) solution is very nice.

Thanks very much!

On Monday, April 2, 2018 at 11:30:53 AM UTC-7, Mikhail Gusarov wrote:
>
> Hello Will.
>
> You can simplify it further:
>
> 1. Define a multimethod always dispatching by room id.
> 2. Create a :default implementation. It will be called for non-fancy rooms.
> 3. Create an implementation for :fancy-room. It will be preferred over 
> :default for it.
>
> If you ever have a group of rooms with a similar fancy description, use a 
> hierarchy
>
> (derive :fancy-room1 :fancy-group)
> (derive :fancy-room2 :fancy-group)
>
> and create an implementation for :fancy-group.
>
> Regards, Mikhail.
>
> On Mon, 2 Apr 2018, at 19:39, Will Duquette wrote:
>
> Spent the weekend pondering all of this, and here's the way I think I want 
> to do it.
>
> 1. The world-state is stored in an atom, and updated much as Gary Johnson 
> suggests.
>
> 2. I define a multi-method, (describe-room [room world-state]), that is 
> responsible for computing the current description of the room: what it 
> looks like, what items are in it, whatever the player can currently see or 
> has just noticed.
>
> 3. The multi-method's dispatch function looks for the :description.  
>
> 3a. If it's a string, the dispatch function returns nil; and I get the 
> default implementation: it describes the room in the simplest possible way.
>
> 3b. If the :description is undefined or nil, the dispatch function returns 
> the room's id, e.g., :fancy-room.  The :fancy-room definition includes an 
> implementation of the multi-method that's specific to the :fancy-room.  I 
> wouldn't expect to use this approach all that often, but it should be 
> flexible enough to do anything that comes up.
>
> 3c. If I find that I have a number of standard flavors for how to describe 
> a room, I can define the dispatch function accordingly and add additional 
> implementations.
>
> There are other ways to skin this cat; but this one seems to give me 
> maximum simplicity for the normal case, maximum flexibility for special 
> cases, and it lets me keep all of the logic related to a single room in one 
> place in the code.
>
> Comments?
>
> 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 clo...@googlegroups.com <javascript:>
> Note that posts from new members are moderated - please be patient with 
> your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com <javascript:>
> 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+u...@googlegroups.com <javascript:>.
> For more options, visit https://groups.google.com/d/optout.
>
>
>

-- 
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