How to handle messy relations with the world state is definitely something that could be better understood. Most models for game state I'm familiar with are highly object-oriented. [1] Figuring out how to approach it in Clojure, from a functional perspective seems to be an interesting challenge that not enough people are talking publicly about.
I think, at least for the current NaNoGenMo project, I've got a workable method for representing the part of world state I'm interested in in a functional way. I'm trying a quality-based narrative driven by a rudimentary rules-based system, loosely based on Elan Ruskin's approach to dialog scripting [2] and Fallen London [3]. It's still passing a lot of data into the functions that are deciding what to do next, but not the entire thing. There may be a better approach, but this one has the virtue of a current working prototype. (I should probably look into something more robust than my duct-taped naive ad-hoc rule-system, but: prototype). Presently, I'm passing [map-of-qualities map-of-rules] to the rule-system. (Having it spit out an event stream sounds like a great way to handle the output.) The catch, of course, being that sometimes I want the rule to be selected randomly, and sometimes the output has a random variation. Say there are several valid, equally weighted, rules, and we want to pick between them at random, but keep it deterministically repeatable. My current thought is to pass a seed in (possibly as part of the map of qualities/states) and use it when doing the conflict resolution. Or the rules could have a seed value that gets hashed with the general seed value each cycle. Still requires updating the seed state somehow each cycle (and careful hashing) but at least that keeps the processing within each cycle deterministic and independent of each others states. I'm definitely open to suggestions on other ways to approach this entire issue. - Isaac [1] Though now that I think about it, I recall that NetHack's codebase is highly procedural. Really messy, complex C code though. [2] http://www.gdcvault.com/play/1015317/AI-driven-Dynamic-Dialog-through [3] http://fallenlondon.storynexus.com/ On Tuesday, October 28, 2014 4:09:42 PM UTC-4, James Reeves wrote: > > Hi Isaac > > It sounds to me that your source of pseudo-randomness is an inherent part > of your world state, as without it you're unable to calculate the next > state. > > You mention the problem of having to thread your PRNG through your > functions, but I think the problem is broader than that. For instance, you > may decide on a combat engine that takes in a source of randomness and two > creatures as its arguments, only to decide at a later date that you also > want the terrain to affect the battle. Usually we want to restrict what > data a function cares about in order to make its effects more predictable, > but because a game is modelling an analogue of the real world, any part of > the current game state could potentially have an effect upon the outcome. > > I must admit I've yet to settle on an entirely satisfactory solution. > You'd prefer to be able to restrain what a function cares about, but you > also don't want to thread an argument through a dozen functions every time > you want the function at the bottom to affect something new. > > The most straightforward solution is to thread the game state through your > code as the first argument. It does solve the problem, but feels somewhat > unsatisfactory. > > There might be a solution in lenses, zippers, or some similar structure. > Instead of passing through the state bare, we pass through a view upon the > state. How best to do this in Clojure is, I think, still an area of > research. > > Alternatively you could think of it in terms of an event stream. You do as > much as you can functionally, then return an event, or action, which > results in a state change. This is similar to the "with gloves" approach > that the IO monad uses. > > I apologise for not providing any concrete answers, but this has been > something I've been thinking about as well. You can quite easily narrow the > scope of data a function has access to, but it's very hard to widen it > again. > > - James > > > On 28 October 2014 19:08, Isaac Karth <isaac...@gmail.com <javascript:>> > wrote: > >> I've been working on some projects (roguelikes, story-generators) that >> often have a reason to have a random outcome for things like procedurally >> generating a map. Ideally, they would be deterministically psuduorandom, so >> that I can generate the same results from the same seed. The part I'm >> having trouble with is figuring out what method to use to implement this in >> a functional way. >> >> The naive approach that first occurred to me was to pass a PRNG and seed >> value into each function. That would certainly keep the functions >> referentially transparent. But it would also require either rewriting a >> bunch of functions that don't directly use the randomness themselves or >> adding the RNG as part of the map that's already being passed. >> >> Plus, the seed should probably vary (deterministically) between calls to >> the subfunctions. It's not too useful if all the rooms on a map have the >> same type because they all pulled their die roll from the same-nth result >> of the exact same seed. Maybe the seed could be created from the contents >> of the vector or a UUID of a map object or something? >> >> The other suggestion I've run across is to rebind something like >> clojure.data.generators/*rnd* for each high-level procedural generation >> call. I think this requires the generation to be single threaded and >> otherwise deterministic. At the moment I don't feel like I know enough >> about how things work under the hood to say if this a good idea or not. >> >> I've poked around at the bigml.sampling and clojure.data.generators >> libraries, which look useful for dealing with some of this, but I haven't >> come across anything that directly addresses what kind of architecture to >> use for deterministic randomness. This may be my inexperience. I feel like >> I may be a bit too close to the problem, and I can't quite see what the >> idiomatic answer is. Re-read SICP? Implement some kind of monad? Just bite >> the bullet and pass the RNG+seed? Find the secret pure functional library >> everyone is using for repeatable stochastic simulations? >> >> Does anyone have any advice on this? Or prior experience with approaches >> that work? What are some best practices for approaching deterministic >> simulations in an idiomatic, functional way? >> >> -- >> 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.