Today is the Fifth Plough of the Plough
Implemented almost the whole of the Myth of the God Incarnate calendar
This commit is contained in:
parent
bd76e93568
commit
7e7a55c8ec
39 changed files with 2085 additions and 167 deletions
|
|
@ -2,3 +2,6 @@
|
|||
"Anything in the game world with agency")
|
||||
|
||||
;; 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
(ns the-great-game.gossip.gossip
|
||||
"Interchange of news events between gossip agents"
|
||||
(:require [the-great-game.utils :refer [deep-merge]]))
|
||||
(:require [the-great-game.utils :refer [deep-merge]]
|
||||
[the-great-game.gossip.news-items :refer [learn-news-item]]))
|
||||
|
||||
;; Note that habitual travellers are all gossip agents; specifically, at this
|
||||
;; stage, that means merchants. When merchants are moved we also need to
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
(ns the-great-game.gossip.news-items
|
||||
"Categories of news events interesting to gossip agents"
|
||||
(:require [clojure.math.numeric-tower :refer [expt sqrt]]))
|
||||
(:require [the-great-game.world.location :refer [distance-between]]
|
||||
[the-great-game.time :refer [now]]))
|
||||
|
||||
;; 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
|
||||
|
|
@ -20,14 +21,26 @@
|
|||
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;
|
||||
* `actor` is the id of the character who it is reported performed the
|
||||
action;
|
||||
* `other` is the id of the character on whom it is reported 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;
|
||||
* `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:
|
||||
|
||||
##### Characters:
|
||||
|
||||
*TODO* but note that at most all the receiver can learn about a character
|
||||
from a news item is what the giver knows about that character, degraded by
|
||||
what the receiver finds interesting about them. If we just pass the id here,
|
||||
then either the receiver knows everything in the database about the
|
||||
character, or else the receiver knows nothing at all about the character.
|
||||
Neither is desirable. Further thought needed.
|
||||
|
||||
##### Locations:
|
||||
|
||||
A 'location' value is a list comprising at most the x/y coordinate location
|
||||
|
|
@ -62,6 +75,7 @@
|
|||
:plot {:verb :plot :keys [:actor :other :object :location]}
|
||||
;; Rapes are interesting.
|
||||
:rape {:verb :rape :keys [:actor :other :location]
|
||||
;; Should you also infer from rape that actor is male and adult?
|
||||
:inferences [{:verb :attack}
|
||||
{:verb :sex}
|
||||
{:verb :sex :actor :other :other :actor}]}
|
||||
|
|
@ -84,7 +98,9 @@
|
|||
|
||||
(defn interest-in-character
|
||||
"Integer representation of how interesting this `character` is to this
|
||||
`gossip`."
|
||||
`gossip`.
|
||||
*TODO:* this assumes that characters are passed as keywords, but, as
|
||||
documented above, they probably have to be maps, to allow for degradation."
|
||||
[gossip character]
|
||||
(count
|
||||
(concat
|
||||
|
|
@ -97,99 +113,46 @@
|
|||
[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
|
||||
(and (map? location) (number? (:x location)) (number? (: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))
|
||||
0)
|
||||
(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-object?
|
||||
[gossip object]
|
||||
;; TODO: Not yet (really) implemented
|
||||
true)
|
||||
|
||||
(defn interesting-topic?
|
||||
[gossip topic]
|
||||
;; TODO: Not yet (really) implemented
|
||||
true)
|
||||
|
||||
(defn interesting-item?
|
||||
"True if anything about this news `item` is interesting to this `gossip`."
|
||||
[gossip item]
|
||||
|
|
@ -212,18 +175,44 @@
|
|||
#(= % :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})
|
||||
(declare learn-news-item)
|
||||
|
||||
(defn make-all-inferences
|
||||
"Return a list of knowledge entries inferred from this news `item` by this
|
||||
`gossip`."
|
||||
[item]
|
||||
(set
|
||||
(reduce
|
||||
concat
|
||||
(map
|
||||
#(:knowledge (learn-news-item {} (infer item %) false))
|
||||
(:inferences (news-topics (:verb item)))))))
|
||||
|
||||
(defn degrade-character
|
||||
"Return a character specification like this `character`, but comprising
|
||||
only those properties this `gossip` is interested in."
|
||||
[gossip character]
|
||||
;; TODO: Not yet (really) implemented
|
||||
character)
|
||||
|
||||
(defn degrade-location
|
||||
"Return a location specification like this `location`, but comprising
|
||||
only those elements this `gossip` is interested in. If none, return
|
||||
`nil`."
|
||||
[gossip location]
|
||||
(let [l (if
|
||||
(coll? location)
|
||||
(filter
|
||||
#(when (interesting-location? gossip %) %)
|
||||
location))]
|
||||
(when-not (empty? l) l)))
|
||||
|
||||
(defn learn-news-item
|
||||
"Return a gossip like this `gossip`, which has learned this news `item` if
|
||||
it is of interest to them."
|
||||
;; TODO: Not yet implemented
|
||||
([gossip item]
|
||||
(learn-news-item gossip item false))
|
||||
(learn-news-item gossip item true))
|
||||
([gossip item follow-inferences?]
|
||||
(if
|
||||
(interesting-item? gossip item)
|
||||
|
|
@ -235,17 +224,21 @@
|
|||
(number? (:nth-hand item))
|
||||
(inc (:nth-hand item))
|
||||
1)
|
||||
;; ought to degrate the location
|
||||
:date (if (number? (:date item)) (:date item) (now))
|
||||
:location (degrade-location gossip (:location item))
|
||||
;; ought to degratde 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
|
||||
(assoc
|
||||
g
|
||||
(map
|
||||
#(learn-news-item gossip (infer item %) false)
|
||||
(:inferences (news-topics (:verb item))))))))))
|
||||
:knowledge
|
||||
(concat (:knowledge g) (make-all-inferences item)))
|
||||
g))
|
||||
gossip)))
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
144
src/the_great_game/time.clj
Normal file
144
src/the_great_game/time.clj
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
(ns the-great-game.time
|
||||
(:require [clojure.string :as s]))
|
||||
|
||||
(def game-start-time
|
||||
"The start time of this run."
|
||||
(System/currentTimeMillis))
|
||||
|
||||
(def ^:const game-day-length
|
||||
"The Java clock advances in milliseconds, which is fine.
|
||||
But we need game-days to be shorter than real world days.
|
||||
A Witcher 3 game day is 1 hour 36 minutes, or 96 minutes, which is
|
||||
presumably researched. Round it up to 100 minutes for easier
|
||||
calculation."
|
||||
(* 100 ;; minutes per game day
|
||||
60 ;; seconds per minute
|
||||
1000)) ;; milliseconds per second
|
||||
|
||||
(defn now
|
||||
"For now, we'll use Java timestamp for time; ultimately, we need a
|
||||
concept of game-time which allows us to drive day/night cycle, seasons,
|
||||
et cetera, but what matters about time is that it is a value which
|
||||
increases."
|
||||
[]
|
||||
(System/currentTimeMillis))
|
||||
|
||||
(def ^:const canonical-ordering-of-houses
|
||||
"The canonical ordering of religious houses."
|
||||
[:eye
|
||||
:foot
|
||||
:nose
|
||||
:hand
|
||||
:ear
|
||||
:mouth
|
||||
:stomach
|
||||
:furrow
|
||||
:plough])
|
||||
|
||||
(def ^:const days-of-week
|
||||
"The eight-day week of the game world. This differs from the canonical
|
||||
ordering of houses in that it omits the eye."
|
||||
(rest canonical-ordering-of-houses))
|
||||
|
||||
(def ^:const days-in-week
|
||||
"This world has an eight day week."
|
||||
(count days-of-week))
|
||||
|
||||
(def ^:const seasons-of-year
|
||||
"The ordering of seasons in the year is different from the canonical
|
||||
ordering of the houses, for reasons of the agricultural cycle."
|
||||
[:foot
|
||||
:nose
|
||||
:hand
|
||||
:ear
|
||||
:mouth
|
||||
:stomach
|
||||
:plough
|
||||
:furrow
|
||||
:eye])
|
||||
|
||||
(def ^:const seasons-in-year
|
||||
"Nine seasons in a year, one for each house (although the order is
|
||||
different."
|
||||
(count seasons-of-year))
|
||||
|
||||
(def ^:const weeks-of-season
|
||||
"To fit nine seasons of eight day weeks into 365 days, each must be of
|
||||
five weeks."
|
||||
[:first :second :third :fourth :fifth])
|
||||
|
||||
(def ^:const weeks-in-season
|
||||
"To fit nine seasons of eight day weeks into 365 days, each must be of
|
||||
five weeks."
|
||||
(count weeks-of-season))
|
||||
|
||||
(def ^:const days-in-season
|
||||
(* weeks-in-season days-in-week))
|
||||
|
||||
(defn game-time
|
||||
"With no arguments, the current game time. If a Java `timestamp` value is
|
||||
passed (as a `long`), the game time represented by that value."
|
||||
([] (game-time (now)))
|
||||
([timestamp]
|
||||
(- timestamp game-start-time)))
|
||||
|
||||
(defmacro day-of-year
|
||||
"The day of the year represented by this `game-time`, ignoring leap years."
|
||||
[game-time]
|
||||
`(mod (long (/ ~game-time game-day-length)) 365))
|
||||
|
||||
(def waiting-day?
|
||||
"Does this `game-time` represent a waiting day?"
|
||||
(memoize
|
||||
;; we're likely to call this several times in quick succession on the
|
||||
;; same timestamp
|
||||
(fn [game-time]
|
||||
(>=
|
||||
(day-of-year game-time)
|
||||
(* seasons-in-year weeks-in-season days-in-week)))))
|
||||
|
||||
(defn day
|
||||
"Day of the eight-day week represented by this `game-time`."
|
||||
[game-time]
|
||||
(let [day-of-week (mod (day-of-year game-time) days-in-week)]
|
||||
(if (waiting-day? game-time)
|
||||
(nth weeks-of-season day-of-week)
|
||||
(nth days-of-week day-of-week))))
|
||||
|
||||
(defn week
|
||||
"Week of season represented by this `game-time`."
|
||||
[game-time]
|
||||
(let [day-of-season (mod (day-of-year game-time) days-in-season)
|
||||
week (/ day-of-season days-in-week)]
|
||||
(if (waiting-day? game-time)
|
||||
:waiting
|
||||
(nth weeks-of-season week))))
|
||||
|
||||
(defn season
|
||||
[game-time]
|
||||
(let [season (int (/ (day-of-year game-time) days-in-season))]
|
||||
(if (waiting-day? game-time)
|
||||
:waiting
|
||||
(nth seasons-of-year season))))
|
||||
|
||||
(defn date-string
|
||||
"Return a correctly formatted date for this `game-time` in the calendar of
|
||||
the Great Place."
|
||||
[game-time]
|
||||
(s/join
|
||||
" "
|
||||
(if
|
||||
(waiting-day? game-time)
|
||||
[(s/capitalize
|
||||
(name
|
||||
(nth
|
||||
weeks-of-season
|
||||
(mod (day-of-year game-time) days-in-week))))
|
||||
"waiting day"]
|
||||
[(s/capitalize (name (week game-time)))
|
||||
(s/capitalize (name (day game-time)))
|
||||
"of the"
|
||||
(s/capitalize (name (season game-time)))])))
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
(ns the-great-game.world.location
|
||||
"Functions dealing with location in the world.")
|
||||
"Functions dealing with location in the world."
|
||||
(:require [clojure.math.numeric-tower :refer [expt sqrt]]))
|
||||
|
||||
;; 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
|
||||
|
|
@ -8,3 +9,29 @@
|
|||
;; 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.
|
||||
|
||||
(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)))))
|
||||
|
||||
(defn distance-between
|
||||
[location-1 location-2]
|
||||
(let [c1 (get-coords location-1)
|
||||
c2 (get-coords location-2)]
|
||||
(when
|
||||
(and c1 c2)
|
||||
(sqrt (+ (expt (- (:x c1) (:x c2)) 2) (expt (- (:y c1) (:y c2)) 2))))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue