001  (ns cc.journeyman.the-great-game.gossip.news-items
002    "Using news items (propositions) to transfer knowledge between gossip agents.
003     
004     ## Status
005     
006     What is here is essentially working. It's not, however, working with the 
007     rich data objects which will be needed, and it's not yet nearly efficient 
008     enough, but it allows knowledge to propagate through the world procedurally,
009     at a rate limited by the speed of movement of the gossip agents.
010  
011     ## Discussion
012     
013     The ideas here are based on the essay [The spread of knowledge in a large
014     game world](The-spread-of-knowledge-in-a-large-game-world.html), q.v.; 
015     they've advanced a little beyond that and will doubtless
016     advance further in the course of writing and debugging this namespace.
017  
018     A news item is a map with the keys:
019   
020     * `date` - the date on which the reported event is claimed to have happened;
021     * `nth-hand` - the number of agents the news item has passed through;
022     * `verb` - what it is that happened (key into `news-topics`);
023  
024     plus other keys taken from the `keys` value associated with the verb in
025     `news-topics`.
026     
027     ## Notes:
028     
029     *TODO*   
030     This namespace at present considers the `:knowledge` of a gossip to be a flat
031     list of propositions, each of which must be checked every time any new
032     proposition is offered. This is woefully inefficient. "
033    (:require [cc.journeyman.the-great-game.world.location :refer [distance-between]]
034              [cc.journeyman.the-great-game.time :refer [game-time]]
035              [cc.journeyman.the-great-game.utils :refer [inc-or-one truthy?]]
036              [taoensso.timbre :as l]))
037  
038  (def news-topics
039    "Topics of interest to gossip agents. Topics are keyed in this map by
040    their `verbs`. The `keys` associated with each topic are the extra pieces
041    of information required to give context to a gossip item. Generally:
042  
043    * `actor` is the id of the character who it is reported performed the
044    action;
045    * `other` is the id of the character on whom it is reported the action
046    was performed;
047    * `location` is the place at which the action was performed;
048    * `object` is an object (or possibly list of objects?) relevant to the
049    action;
050    * `price` is special to buy/sell, but of significant interest to merchants.
051  
052    ## Characters
053  
054    *TODO* but note that at most all the receiver can learn about a character
055    from a news item is what the giver knows about that character, degraded by
056    what the receiver finds interesting about them. If we just pass the id here,
057    then either the receiver knows everything in the database about the
058    character, or else the receiver knows nothing at all about the character.
059    Neither is desirable. Further thought needed.
060  
061    By implication, the character values passed should include *all* the
062    information the giver knows about the character; that can then be degraded
063    as the receiver stores only that segment which the receiver finds
064    interesting.
065  
066    ## Locations
067  
068    A 'location' value is a list comprising at most the x/y coordinate location
069    and the ids of the settlement and region (possibly hierarchically) that contain
070    the location. If the x/y is not local to the home of the receiving agent, they
071    won't remember it and won't pass it on; if any of the ids are not interesting
072    So location information will degrade progressively as the item is passed along.
073  
074    It is assumed that the `:home` of a character is a location in this sense.
075  
076    ## Inferences
077  
078    If an agent learns that Adam has married Betty, they can infer that Betty has
079    married Adam; if they learn that Charles killed Dorothy, that Dorothy has died.
080    I'm not convinced that my representation of inferences here is ideal."
081    {;; A significant attack is interesting whether or not it leads to deaths
082     :attack {:verb :attack :keys [:actor :other :location]}
083      ;; Deaths of characters may be interesting
084     :die {:verb :die :keys [:actor :location]}
085      ;; Deliberate killings are interesting.
086     :kill {:verb :kill :keys [:actor :other :location]
087            :inferences [{:verb :die :actor :other :other :nil}]}
088      ;; Marriages may be interesting
089     :marry {:verb :marry :keys [:actor :other :location]
090             :inferences [{:verb :marry :actor :other :other :actor}]}
091      ;; The end of ongoing open conflict between to characters may be interesting
092     :peace {:verb :peace :keys [:actor :other :location]
093             :inferences [{:verb :peace :actor :other :other :actor}]}
094      ;; Things related to the plot are interesting, but will require special
095      ;; handling. Extra keys may be required by particular plot events.
096     :plot {:verb :plot :keys [:actor :other :object :location]}
097      ;; Rapes are interesting.
098     :rape {:verb :rape :keys [:actor :other :location]
099             ;; Should you also infer from rape that actor is male and adult?
100            :inferences [{:verb :attack}
101                         {:verb :sex}
102                         {:verb :sex :actor :other :other :actor}]}
103      ;; Merchants, especially, are interested in prices in other markets
104     :sell {:verb :sell :keys [:actor :other :object :location :quantity :price]}
105      ;; Sex can juicy gossip, although not normally if the participants are in an
106      ;; established sexual relationship.
107     :sex {:verb :sex :keys [:actor :other :location]
108           :inferences [{:verb :sex :actor :other :other :actor}]}
109      ;; Thefts are interesting.
110     :steal {:verb :steal :keys [:actor :other :object :location]}
111      ;; The succession of rulers is interesting; of respected craftsmen,
112      ;; potentially also interesting.
113     :succession {:verb :succession :keys [:actor :other :location :rank]}
114      ;; The start of ongoing open conflict between two characters may be interesting.
115     :war {:verb :war :keys [:actor :other :location]
116           :inferences [{:verb :war :actor :other :other :actor}]}})
117  
118  
119  (def all-known-verbs 
120    "All verbs currently known to the gossip system."
121    (set (keys news-topics)))
122  
123  
124  (defn interest-in-character
125    "Integer representation of how interesting this `character` is to this
126    `gossip`.
127    *TODO:* this assumes that characters are passed as keywords, but, as
128    documented above, they probably have to be maps, to allow for degradation."
129    [gossip character]
130    (count
131     (concat
132      ;; TODO: we ought also check the relationships of the gossip.
133      ;; Are relationships just propositions in the knowledge base?
134      (filter #(= (:actor %) character) (:knowledge gossip))
135      (filter #(= (:other %) character) (:knowledge gossip)))))
136  
137  
138  (defn interesting-character?
139    "Boolean representation of whether this `character` is interesting to this
140    `gossip`."
141    [gossip character]
142    (> (interest-in-character gossip character) 0))
143  
144  (defn interest-in-location
145    "Integer representation of how interesting this `location` is to this
146    `gossip`."
147    [gossip location]
148    (cond
149      (and (map? location) (number? (:x location)) (number? (:y location)))
150      (if-let [home (:home gossip)]
151        (let [d (distance-between location home)
152              i (if
153                 (zero? d) 1
154                 (/ 10000 d))
155              ;; 10000 at metre scale is 10km; interest should
156              ;;fall off with distance from home, but possibly on a log scale
157              ]
158          (if (>= i 1) i 0))
159        0)
160      (coll? location)
161      (reduce
162       +
163       (map
164        #(interest-in-location gossip %)
165        location))
166      :else
167      (count
168       (filter
169        #(some (fn [x] (= x location)) (:location %))
170        (cons {:location (:home gossip)} (:knowledge gossip))))))
171  
172  ;; (distance-between {:x 25 :y 37} {:x 25 :y 37})
173  ;; (interest-in-location {:home [{0, 0} :test-home] :knowledge []} [:test-home])
174  
175  (defn interesting-location?
176    "True if the location of this news `item` is interesting to this `gossip`."
177    [gossip location]
178    (> (interest-in-location gossip location) 0))
179  
180  (defn interesting-object?
181    [gossip object]
182    ;; TODO: Not yet (really) implemented
183    true)
184  
185  (defn interesting-topic?
186    [gossip topic]
187    ;; TODO: Not yet (really) implemented
188    true)
189  
190  (defn interesting-verb?
191    "Is this `verb` interesting to this `gossip`?"
192    [gossip verb]
193    (let [vs (:interesting-verbs gossip)]
194      (truthy?
195       (if (set? vs)
196         (vs verb)
197         false))))
198  
199  ;; (interesting-verb? {:interesting-verbs #{:kill :sell}} :sell)
200  
201  (defn compatible-value?
202    "True if `known-value` is the same as `new-value`, or, for each key present
203     in `new-value`, has the same value for that key. 
204     
205     The rationale here is that if `new-value` contains new or different 
206     information, it's worth learning; otherwise, not."
207    [new-value known-value]
208    (or
209     (= new-value known-value)
210     ;; TODO: some handwaving here about being a slightly better descriptor --
211     ;; having more keys than might 
212     (when (and (map? new-value) (map? known-value))
213       (every? true? (map #(= (new-value %) (known-value %))
214                          (keys new-value))))))
215  
216  (defn compatible-item?
217    "True if `new-item` is identical with, or less specific than, `known-item`.
218     
219     If we already know 'Bad Joe killed Sweet Daisy', there's no point in 
220     learning that 'someone killed Sweet Daisy', but there is point in learning
221     'someone killed Sweet Daisy _with poison_'."
222    [new-item known-item]
223    (truthy?
224     (reduce
225      #(and %1 %2)
226      (map #(if
227             (known-item %) ;; if known-item has this key
228              (compatible-value? (new-item %) (known-item %))
229              true)
230           (remove #{:nth-hand :confidence :learned-from} (keys new-item))))))
231  
232  (defn known-item?
233    "True if this news `item` is already known to this `gossip`.
234     
235     This means that the `gossip` already knows an item which identifiably has
236     the same _or more specific_ values for all the keys of this `item` except
237     `:nth-hand`, `:confidence` and `:learned-from`."
238    [gossip item]
239    (truthy?
240     (reduce
241      #(or %1 %2)
242      false
243      (filter true? (map #(compatible-item? item %) (:knowledge gossip))))))
244  
245  (defn interesting-item?
246    "True if anything about this news `item` is interesting to this `gossip`."
247    [gossip item]
248    (and (not (known-item? gossip item))
249         (interesting-verb? gossip item) ;; news is only interesting if the topic is.
250         (or
251          (interesting-character? gossip (:actor item))
252          (interesting-character? gossip (:other item))
253          (interesting-location? gossip (:location item))
254          (interesting-object? gossip (:object item))
255          (interesting-topic? gossip (:verb item)))))
256  
257  (defn infer
258    "Infer a new knowledge item from this `item`, following this `rule`."
259    [item rule]
260  ;;  (l/info "Applying rule '" rule "' to item '" item "'")
261    (reduce merge
262            item
263            (cons
264             {:verb (:verb rule)
265              :nth-hand (inc-or-one (:nth-hand item))}
266             (map (fn [k] {k (item (rule k))})
267                  (remove
268                   #{:verb :nth-hand}
269                   (keys rule))))))
270  
271  (declare learn-news-item)
272  
273  (defn make-all-inferences
274    "Return a set of knowledge entries that can be inferred from this news
275    `item`."
276    [item]
277    (set
278      (map
279       #(infer item %)
280       (:inferences (news-topics (:verb item))))))
281  
282  (defn degrade-character
283    "Return a character specification like this `character`, but comprising
284    only those properties this `gossip` is interested in."
285    [gossip character]
286    ;; TODO: Not yet (really) implemented
287    character)
288  
289  (defn degrade-location
290    "Return a location specification like this `location`, but comprising
291    only those elements this `gossip` is interested in. If none, return
292    `nil`."
293    [gossip location]
294    (let [l (when
295             (coll? location)
296              (filter
297               #(when (interesting-location? gossip %) %)
298               location))]
299      (when-not (empty? l) l)))
300  
301  (defn degrade-news-item
302    [gossip item]
303    (assoc
304     item
305     :nth-hand (inc-or-one (:nth-hand item))
306     :time-stamp (if
307                  (number? (:time-stamp item))
308                   (:time-stamp item)
309                   (game-time))
310     :location (degrade-location gossip (:location item))
311     :actor (degrade-character gossip (:actor item))
312     :other (degrade-character gossip (:other item))
313                    ;; TODO: do something to degrade confidence in the item,
314                    ;; probably as a function of the provider's confidence in
315                    ;; the item and the gossip's trust in the provider
316     ))
317  
318  ;; (degrade-news-item {:home [{:x 25 :y 37} :auchencairn :scotland]}
319  ;;                   {:verb :marry :actor :adam :other :belinda :location [{:x 25 :y 37} :auchencairn :scotland]})
320  
321  (defn learn-news-item
322    "Return a gossip like this `gossip`, which has learned this news `item` if
323    it is of interest to them."
324    ([gossip item]
325     (learn-news-item gossip item true))
326    ([gossip item follow-inferences?]
327     (if
328      (interesting-item? gossip item)
329       (let [item' (degrade-news-item gossip item)
330             g (assoc
331                gossip
332                :knowledge
333                (cons
334                 item'
335                 (:knowledge gossip)))]
336         (if follow-inferences?
337           (assoc
338            g
339            :knowledge
340            (concat (:knowledge g) (make-all-inferences item')))
341           g)))
342     gossip))
343  
344  
345