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