Starting to get The spread of knowledge in a large game world working

This commit is contained in:
Simon Brooke 2020-04-12 16:19:11 +01:00
parent 58589d2da4
commit 691b6b2959
No known key found for this signature in database
GPG key ID: A7A4F18D1D4DF987
4 changed files with 264 additions and 2 deletions

View file

@ -5,6 +5,7 @@
:output-path "docs/codox" :output-path "docs/codox"
:source-uri "https://github.com/simon-brooke/the-great-game/blob/master/{filepath}#L{line}"} :source-uri "https://github.com/simon-brooke/the-great-game/blob/master/{filepath}#L{line}"}
:dependencies [[org.clojure/clojure "1.8.0"] :dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/math.numeric-tower "0.0.4"]
[environ "1.1.0"] [environ "1.1.0"]
[com.taoensso/timbre "4.10.0"]] [com.taoensso/timbre "4.10.0"]]
:description "Prototype code towards the great game I've been writing about for ten years, and know I will never finish." :description "Prototype code towards the great game I've been writing about for ten years, and know I will never finish."

View file

@ -1,5 +1,5 @@
(ns the-great-game.gossip.gossip (ns the-great-game.gossip.gossip
"Interchange of news events between agents agents" "Interchange of news events between gossip agents"
(:require [the-great-game.utils :refer [deep-merge]])) (:require [the-great-game.utils :refer [deep-merge]]))
;; Note that habitual travellers are all gossip agents; specifically, at this ;; Note that habitual travellers are all gossip agents; specifically, at this
@ -43,7 +43,7 @@
(defn move-gossip (defn move-gossip
"Return a world like this `world` but with this `gossip` moved to this "Return a world like this `world` but with this `gossip` moved to this
`new-location`. Many gossips are essentially shadow-records of agents of `new-location`. Many gossips are essentially shadow-records of agents of
other types, and the movement if the gossip should be controlled by the other types, and the movement of the gossip should be controlled by the
run function of the type of the record they shadow. The [[#run]] function run function of the type of the record they shadow. The [[#run]] function
below does NOT call this function." below does NOT call this function."
[gossip world new-location] [gossip world new-location]

View file

@ -0,0 +1,251 @@
(ns the-great-game.gossip.news-items
"Categories of news events interesting to gossip agents"
(:require [clojure.math.numeric-tower :refer [expt sqrt]]))
;; The ideas here are based on the essay 'The spread of knowledge in a large
;; game world', q.v.; they've advanced a little beyond that and will doubtless
;; advance further in the course of writing and debugging this namespace.
;; A news item is a map with the keys:
;;
;; * `date` - the date on which the reported event happened;
;; * `nth-hand` - the number of agents the news item has passed through;
;; * `verb` - what it is that happened (key into `news-topics`);
;;
;; plus other keys taken from the `keys` value associated with the verb in
;; `news-topics`
(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
of information required to give context to a gossip item. Generally:
* `actor` is the id of the character who performed the action;
* `other` is the id of the character on whom the action was performed;
* `location` is the place at which the action was performed;
* `object` is an object (or possibly list of objects?) relevant to the action;
* `price` is special to buy/sell, but of significant interest to merchants.
#### Notes:
##### Locations:
A 'location' value is a list comprising at most the x/y coordinate location
and the ids of the settlement and region (possibly hierarchically) that contain
the location. If the x/y is not local to the home of the receiving agent, they
won't remember it and won't pass it on; if any of the ids are not interesting
So location information will degrade progressively as the item is passed along.
It is assumed that the `:home` of a character is a location in this sense.
##### Inferences:
If an agent learns that Adam has married Betty, they can infer that Betty has
married Adam; if they learn that Charles killed Dorothy, that Dorothy has died.
I'm not convinced that my representation of inferences here is ideal.
"
{ ;; A significant attack is interesting whether or not it leads to deaths
:attack {:verb :attack :keys [:actor :other :location]}
;; Deaths of characters may be interesting
:die {:verb :attack :keys [:actor :location]}
;; Deliberate killings are interesting.
:kill {:verb :kill :keys [:actor :other :location]
:inferences [{:verb :die :actor :other :other :nil}]}
;; Marriages may be interesting
:marry {:verb :marry :keys [:actor :other :location]
:inferences [{:verb :marry :actor :other :other :actor}]}
;; The end of ongoing open conflict between to characters may be interesting
:peace {:verb :peace :keys [:actor :other :location]
:inferences [{:verb :peace :actor :other :other :actor}]}
;; Things related to the plot are interesting, but will require special
;; handling. Extra keys may be required by particular plot events.
:plot {:verb :plot :keys [:actor :other :object :location]}
;; Rapes are interesting.
:rape {:verb :rape :keys [:actor :other :location]
:inferences [{:verb :attack}
{:verb :sex}
{:verb :sex :actor :other :other :actor}]}
;; Merchants, especially, are interested in prices in other markets
:sell {:verb :sell :keys [:actor :other :object :location :price]}
;; Sex can juicy gossip, although not normally if the participants are in an
;; established sexual relationship.
:sex {:verb :sex :keys [:actor :other :location]
:inferences [{:verb :sex :actor :other :other :actor}]}
;; Thefts are interesting
:steal {:verb :steal :keys [:actor :other :object :location]}
;; The succession of rulers is interesting; of respected craftsmen,
;; potentially also interesting.
:succession {:verb :succession :keys [:actor :other :location :rank]}
;; The start of ongoing open conflict between to characters may be interesting
:war {:verb :war :keys [:actor :other :location]
:inferences [{:verb :war :actor :other :other :actor}]}
})
(defn interest-in-character
"Integer representation of how interesting this `character` is to this
`gossip`."
[gossip character]
(count
(concat
(filter #(= (:actor % character)) (:knowledge gossip))
(filter #(= (:other % character)) (:knowledge gossip)))))
(defn interesting-character?
"Boolean representation of whether this `character` is interesting to this
`gossip`."
[gossip character]
(> (interest-in-character gossip character) 0))
(defn get-coords
"Return the coordinates in the game world of `location`, which may be
1. A coordinate pair in the format {:x 5 :y 32};
2. A location, as discussed above;
3. Any other gameworld object, having a `:location` property whose value
is one of the above."
[location]
(cond
(empty? location) nil
(map? location)
(cond
(and (number? (:x location)) (number? (:y location)))
location
(:location location)
(:location location))
:else
(get-coords (first (remove keyword? location)))))
;; (get-coords {:x 5 :y 7})
;; (get-coords [{:x -4 :y 55} :auchencairn :galloway :scotland])
(defn distance-between
[location-1 location-2]
(let [c1 (get-coords location-1)
c2 (get-coords location-2)]
(if
(and c1 c2)
(sqrt (+ (expt (- (:x c1) (:x c2)) 2) (expt (- (:y c1) (:y c2)) 2))))))
;; (distance-between {:x 5 :y 5} {:x 2 :y 2})
;; (distance-between {:x 5 :y 5} {:x 2 :y 5})
;; (distance-between {:x 5 :y 5} [{:x -4 :y 55} :auchencairn :galloway :scotland])
;; (distance-between {:x 5 :y 5} [:auchencairn :galloway :scotland])
(defn interest-in-location
"Integer representation of how interesting this `location` is to this
`gossip`."
[gossip location]
(cond
(coll? location)
(reduce
+
(map
#(interest-in-location gossip %)
location))
(and (map? location) (:x location) (:y location))
(if-let [home (:home gossip)]
(let [d (distance-between location home)
i (/ 10000 d) ;; 10000 at metre scale is 10km; interest should
;;fall of with distance from home, but possibly on a log scale
]
(if (i > 1) i 0)
i))
:else
(count
(filter
#(some (fn [x] (= x location)) (:location %))
(:knowledge gossip)))))
;; (interest-in-location
;; {:knowledge [{:verb :steal
;; :actor :albert
;; :other :belinda
;; :object :foo
;; :location [{:x 35 :y 23} :auchencairn :galloway]}]}
;; :galloway)
;; (interest-in-location
;; {:knowledge [{:verb :steal
;; :actor :albert
;; :other :belinda
;; :object :foo
;; :location [{:x 35 :y 23} :auchencairn :galloway]}]}
;; [:galloway :scotland])
;; (interest-in-location
;; {:knowledge [{:verb :steal
;; :actor :albert
;; :other :belinda
;; :object :foo
;; :location [{:x 35 :y 23} :auchencairn :galloway]}]}
;; :dumfries)
;; (interest-in-location
;; {:home {:x 35 :y 23}}
;; {:x 35 :y 24})
(defn interesting-location?
"True if the location of this news `item` is interesting to this `gossip`."
[gossip item]
(> (interest-in-location gossip (:location item)) 1))
(defn interesting-item?
"True if anything about this news `item` is interesting to this `gossip`."
[gossip item]
(or
(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))))
(defn infer
"Infer a new knowledge item from this `item`, following this `rule`"
[item rule]
(reduce merge
item
(cons
{:verb (:verb rule)}
(map (fn [k] {k (apply (k rule) (list item))})
(remove
#(= % :verb)
(keys rule))))))
;; (infer {:verb :marry :actor :adam :other :belinda}
;; {:verb :marry :actor :other :other :actor})
;; (infer {:verb :rape :actor :adam :other :belinda}
;; {:verb :attack})
;; (infer {:verb :rape :actor :adam :other :belinda}
;; {:verb :sex :actor :other :other :actor})
(defn learn-news-item
"Return a gossip like this `gossip`, which has learned this news `item` if
it is of interest to them."
([gossip item]
(learn-news-item gossip item false))
([gossip item follow-inferences?]
(if
(interesting-item? gossip item)
(let [g (assoc gossip :knowledge
(cons
(assoc
item
:nth-hand (if
(number? (:nth-hand item))
(inc (:nth-hand item))
1)
;; ought to degrate the location
;; ought to maybe-degrade characters we're not yet interested in
)
;; ought not to add knowledge items we already have, except
;; to replace if new item is of increased specificity
(:knowledge gossip)))]
(if follow-inferences?
(reduce
merge
g
(map
#(learn-news-item gossip (infer item %) false)
(:inferences (news-topics (:verb item))))))))))

View file

@ -0,0 +1,10 @@
(ns the-great-game.world.location
"Functions dealing with location in the world.")
;; A 'location' value is a list comprising at most the x/y coordinate location
;; and the ids of the settlement and region (possibly hierarchically) that contain
;; the location. If the x/y is not local to the home of the receiving agent, they
;; won't remember it and won't pass it on; if any of the ids are not interesting
;; So location information will degrade progressively as the item is passed along.
;; It is assumed that the `:home` of a character is a location in this sense.