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