Added ideas about scheduling daily behaviour.
All tests currently pass, but that's at least partly because a lot of the new code doesn't yet have tests.
This commit is contained in:
parent
1c4cb01af5
commit
81ceaec950
10 changed files with 347 additions and 37 deletions
|
|
@ -4,17 +4,21 @@
|
|||
(:require [cc.journeyman.the-great-game.objects.game-object :refer [ProtoObject]]
|
||||
[cc.journeyman.the-great-game.objects.container :refer [ProtoContainer]]))
|
||||
|
||||
;;; hierarchy of needs probably gets implemented here
|
||||
;;; I'm probably going to want to defprotocol stuff, to define the hierarchy
|
||||
;;; of things in the gameworld; either that or drop to Java, wich I'd rather not do.
|
||||
;;; hierarchy of needs probably gets implemented here
|
||||
;;; I'm probably going to want to defprotocol stuff, to define the hierarchy
|
||||
;;; of things in the gameworld; either that or drop to Java, wich I'd rather not do.
|
||||
|
||||
;;; attitudes - liking/disliking, attraction/repulsion, amity/hostility, trust/fear
|
||||
;;; also need to live at this layer, even though dynamic change in attitudes belongs
|
||||
;;; in the character layer.
|
||||
|
||||
(defprotocol ProtoAgent
|
||||
"An object which can act in the world"
|
||||
(act
|
||||
[actor world circle]
|
||||
"Allow `actor` to do something in this `world`, in the context of this
|
||||
"Allow `actor` to do something in this `world`, in the context of this
|
||||
`circle`; return the new state of the actor if something was done, `nil`
|
||||
if nothing was done. Circle is expected to be one of
|
||||
if nothing was done. `Circle` is expected to be one of
|
||||
|
||||
* `:active` - actors within visual/audible range of the player
|
||||
character;
|
||||
|
|
@ -22,24 +26,86 @@
|
|||
to it that they may enter the active circle within a short period;
|
||||
* `:background` - actors who are active in the background in order to
|
||||
handle trade, news, et cetera;
|
||||
* `other` - actors who are not members of any other circle, although
|
||||
I'm not clear whether it would ever be appropriate to invoke an
|
||||
`act` method on them.
|
||||
* `:other` - actors who are not members of any other circle.
|
||||
|
||||
The `act` method *must not* have side effects; it must *only* return a
|
||||
new state. If the actor's intention is to seek to change the state of
|
||||
something else in the game world, it must add a representation of that
|
||||
intention to the sequence which will be returned by its
|
||||
`pending-intentions` method.")
|
||||
(hungry? [actor world circle] "True if this actor is hungry and has no
|
||||
immediate access to food.")
|
||||
(pending-intentions
|
||||
[actor]
|
||||
"Returns a sequence of effects an actor intends, as a consequence of
|
||||
acting. The encoding of these is not yet defined."))
|
||||
acting.")
|
||||
(pending-scheduled-action? [actor world circle]
|
||||
"True if there is a plan in this `actor`'s
|
||||
schedule which should be activated now.
|
||||
NOTE THAT plans in the `daily` schedule are
|
||||
NOT activated when in circles `:background`
|
||||
or `:other`")
|
||||
(plan-fight-or-flight [actor world circle]
|
||||
"Return a plan to resolve any active threat to this
|
||||
`actor` in this `world`.")
|
||||
(plan-find-food [actor workd circle]
|
||||
"Return a plan to find this `actor` food in this `world`.")
|
||||
(plan-find-rest [actor workd circle]
|
||||
"Return a plan to find this `actor` a safe place to rest, or
|
||||
if in one, to actually rest, in this `world`.")
|
||||
(plan-goal [actor world circle] "Return a plan to advance this `actor`
|
||||
towards their personal objective, in this
|
||||
world, or `nil` for default actors with no
|
||||
objective.")
|
||||
(plan-scheduled-action [actor workd circle]
|
||||
"Return a plan taken from the schedule of this actor
|
||||
for the current date and time, if any, else `nil`.")
|
||||
(schedule [actor] "Return a map of scheduled actions for this `actor`.
|
||||
TODO: work out the detailed format!")
|
||||
(threatened? [actor world circle] "True if this `actor` is threatened in this
|
||||
`world`.")
|
||||
(tired? [actor world circle] "True if this `actor` needs rest."))
|
||||
|
||||
(defrecord Agent
|
||||
;; "A default agent."
|
||||
[name craft home culture]
|
||||
[name craft home culture]
|
||||
ProtoObject
|
||||
ProtoContainer
|
||||
ProtoAgent
|
||||
)
|
||||
|
||||
(act
|
||||
“Return a world like this `world `except that this `actor `has acted in it.
|
||||
‘Circle’ indicates which activation circle the actor is in.
|
||||
|
||||
Note that this implies that a `plan `is a function of three arguments, an
|
||||
actor, a world. and a circle.”
|
||||
[actor world circle]
|
||||
(let [urgent (case circle
|
||||
:other (cond
|
||||
(pending-scheduled-action? actor world circle)
|
||||
(plan-scheduled-action actor world circle)
|
||||
(empty? (:plans actor))
|
||||
(plan-goal actor world circle))
|
||||
:background (cond
|
||||
(threatened? actor world circle)
|
||||
(plan-fight-or-flight actor world circle)
|
||||
(pending-scheduled-action? actor world circle)
|
||||
(plan-scheduled-action actor world circle)
|
||||
(empty? (:plans actor))
|
||||
(plan-goal actor world circle))
|
||||
;; else
|
||||
(cond
|
||||
(threatened? actor world circle)
|
||||
(plan-fight-or-flight actor world circle)
|
||||
(hungry? actor world circle)
|
||||
(plan-find-food actor world circle)
|
||||
(tired? actor world circle)
|
||||
(plan-find-rest actor world circle)
|
||||
(pending-scheduled-action? actor world circle)
|
||||
(plan-scheduled-action actor world circle)
|
||||
(empty? (:plans actor))
|
||||
(plan-goal actor world circle)))
|
||||
a’ (if urgent
|
||||
(assoc actor :plans (cons urgent (:plans actor)))
|
||||
actor)]
|
||||
(eval ((first (:plans a’)) a’ world)))))
|
||||
|
|
|
|||
63
src/cc/journeyman/the_great_game/agent/schedule.clj
Normal file
63
src/cc/journeyman/the_great_game/agent/schedule.clj
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
(ns cc.journeyman.the-great-game.agent.schedule
|
||||
"Schedules of plans for actors in the game, in order that they may have
|
||||
daily and seasonal patterns of behaviour.")
|
||||
|
||||
;; TODO: I don't have a good handle yet on when a new scheduled task can
|
||||
;; interrupt an existing scheduled task. It's highly undesirable that
|
||||
;; uncompleted scheduled tasks should be left on the queue. The simplest
|
||||
;; solution is to arrange the schedule such that, under notmal circumstances,
|
||||
;; no scheduled task will interrupt another. But if a scheduled task is
|
||||
;; interrupted by an attack, say, or a conversation, and then continued,
|
||||
;; there's a chance of overrunning the start of the next.
|
||||
;;
|
||||
;; Perhaps I need to give scheduled tasks the equivalent of a watchdog timer,
|
||||
;; but that makes them much more sophisticated objects than I wanted them to
|
||||
;; be.
|
||||
|
||||
;; NOTE: this assumes that a world contains a key `:time` whose values are
|
||||
;; a map with at least the keys
|
||||
;; 1. `:day`, whose value is an integer representing the current day of the
|
||||
;; year, and
|
||||
;; 2. `minute`, whose value is an integer representing the current minute of
|
||||
;; the day.
|
||||
;; it probably also includes a `:year`, but that isn't needed here.
|
||||
|
||||
;; (def default-human-schedule
|
||||
;; "A sample schedule for a human actor. This assumes that each of:
|
||||
;; 1. `find-food`;
|
||||
;; 2. `goto-market`;
|
||||
;; 3. `help-with-harvest`;
|
||||
;; 3. `perform-craft`
|
||||
;; 4. `sleep-until-dawn`
|
||||
;; Are plans, which is to say, functions of three arguments, an `actor`,
|
||||
;; a `world` and a `circle`."
|
||||
;; {:annual {32 {:daily {1020 (fn [a w c] (attend-festival a w c :imbolc))}}
|
||||
;; 122 {:daily {1020 (fn [a w c] (attend-festival a w c :bealtaine))}}
|
||||
;; 210 {:daily {480 help-with-harvest}}
|
||||
;; 211 {:daily {480 help-with-harvest}}
|
||||
;; 212 {:daily {480 help-with-harvest}}
|
||||
;; 213 {:daily {480 help-with-harvest}}
|
||||
;; 214 {:daily {480 help-with-harvest
|
||||
;; 1020 (fn [a w c](attend-festival a w c :lughnasadh))}}
|
||||
;; 306 {:daily {1020 (fn [a w c] (attend-festival a w c :samhain))}}}
|
||||
;; :daily {420 find-food
|
||||
;; 480 (fn [actor world circle]
|
||||
;; (case circle
|
||||
;; (:other :background) nil
|
||||
;; ;; else
|
||||
;; (if (has-craft-supplies? actor world circle)
|
||||
;; (goto-market actor world circle)
|
||||
;; (perform-craft actor world circle))))
|
||||
;; 720 find-food
|
||||
;; 780 perform-craft
|
||||
;; 1020 find-food
|
||||
;; 1320 sleep-until-dawn}})
|
||||
|
||||
(defn plan-scheduled-action [actor world circle]
|
||||
"Return the scheduled plan for the current time in this `world` from the
|
||||
schedule of this `actor`, provided the `actor is in an appropriate `circle`"
|
||||
(case circle
|
||||
(:active :pending) (let [s (:schedule actor)
|
||||
d (or (:daily (-> s :annual (-> world :time :day)))
|
||||
(:daily s))]
|
||||
(when d (d (-> world :time :minute))))))
|
||||
9
src/cc/journeyman/the_great_game/character/character.clj
Normal file
9
src/cc/journeyman/the_great_game/character/character.clj
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
(ns cc.journeyman.the-great-game.character.character
|
||||
"A character that can talk; either human or dragon (although very probably
|
||||
we won't do talking dragons until really well into this process). All
|
||||
characters have the news-passing abilities of a gossip, but we use `gossip`
|
||||
to mean a special character who is part of the news-passing network."
|
||||
(:require [cc.journeyman.the-great-game.gossip.gossip :refer [dialogue]]
|
||||
[cc.journeyman.the-great-game.agent.agent :refer [Agent]]))
|
||||
|
||||
|
||||
|
|
@ -36,6 +36,8 @@
|
|||
[cc.journeyman.the-great-game.utils :refer [inc-or-one truthy?]]
|
||||
[taoensso.timbre :as l]))
|
||||
|
||||
(declare interesting-location?)
|
||||
|
||||
(def news-topics
|
||||
"Topics of interest to gossip agents. Topics are keyed in this map by
|
||||
their `verbs`. The `keys` associated with each topic are the extra pieces
|
||||
|
|
@ -116,7 +118,7 @@
|
|||
:war {:verb :war :keys [:actor :other :location]
|
||||
:inferences [{:verb :war :actor :other :other :actor}]}})
|
||||
|
||||
(def all-known-verbs
|
||||
(def all-known-verbs
|
||||
"All verbs currently known to the gossip system."
|
||||
(set (keys news-topics)))
|
||||
|
||||
|
|
@ -131,7 +133,9 @@
|
|||
;; TODO: we ought also check the relationships of the gossip.
|
||||
;; Are relationships just propositions in the knowledge base?
|
||||
(filter #(= (:actor %) character) (:knowledge gossip))
|
||||
(filter #(= (:other %) character) (:knowledge gossip)))))
|
||||
(filter #(= (:other %) character) (:knowledge gossip))
|
||||
(when (interesting-location? gossip (:home character))
|
||||
(list true)))))
|
||||
|
||||
(defn interesting-character?
|
||||
"Boolean representation of whether this `character` is interesting to this
|
||||
|
|
@ -180,19 +184,13 @@
|
|||
;; TODO: Not yet (really) implemented
|
||||
true)
|
||||
|
||||
(defn interesting-topic?
|
||||
[gossip topic]
|
||||
;; TODO: Not yet (really) implemented
|
||||
true)
|
||||
|
||||
(defn interesting-verb?
|
||||
"Is this `verb` interesting to this `gossip`?"
|
||||
[gossip verb]
|
||||
(let [vs (:interesting-verbs gossip)]
|
||||
(truthy?
|
||||
(if (set? vs)
|
||||
(vs verb)
|
||||
false))))
|
||||
(when (set? vs)
|
||||
(vs verb)))))
|
||||
|
||||
;; (interesting-verb? {:interesting-verbs #{:kill :sell}} :sell)
|
||||
|
||||
|
|
@ -249,8 +247,7 @@
|
|||
(interesting-character? gossip (:actor item))
|
||||
(interesting-character? gossip (:other item))
|
||||
(interesting-location? gossip (:location item))
|
||||
(interesting-object? gossip (:object item))
|
||||
(interesting-topic? gossip (:verb item)))))
|
||||
(interesting-object? gossip (:object item)))))
|
||||
|
||||
(defn infer
|
||||
"Infer a new knowledge item from this `item`, following this `rule`."
|
||||
|
|
@ -273,9 +270,9 @@
|
|||
`item`."
|
||||
[item]
|
||||
(set
|
||||
(map
|
||||
#(infer item %)
|
||||
(:inferences (news-topics (:verb item))))))
|
||||
(map
|
||||
#(infer item %)
|
||||
(:inferences (news-topics (:verb item))))))
|
||||
|
||||
(defn degrade-character
|
||||
"Return a character specification like this `character`, but comprising
|
||||
|
|
@ -330,8 +327,8 @@
|
|||
:knowledge
|
||||
(set
|
||||
(cons
|
||||
item'
|
||||
(:knowledge gossip))))]
|
||||
item'
|
||||
(:knowledge gossip))))]
|
||||
(if follow-inferences?
|
||||
(assoc
|
||||
g
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue