main.scm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. (import
  2. (except (rnrs base) let-values map error vector-map)
  3. (only (guile)
  4. lambda* λ
  5. current-output-port
  6. simple-format
  7. command-line)
  8. ;; SRFIs
  9. ;; srfi-1 list and alist procs
  10. (srfi srfi-1)
  11. ;; srfi-8 for receive form
  12. (srfi srfi-8)
  13. ;; srfi-9 for structs
  14. (srfi srfi-9)
  15. (srfi srfi-9 gnu)
  16. ;; srfi-69 for hash tables
  17. (srfi srfi-69)
  18. ;; srfi-43 for vector procs
  19. (srfi srfi-43)
  20. ;; Libraries
  21. (json)
  22. ;; custom, not here implemented, libraries
  23. (fslib)
  24. ;; custom modules
  25. (lib math-procs)
  26. (lib alist-procs)
  27. (lib list-procs)
  28. (lib vector-procs)
  29. (lib fileio)
  30. (lib filesystem)
  31. (lib random)
  32. (lib user-input-output)
  33. (lib string-procs)
  34. (lib set)
  35. (prefix (lib logger) log:)
  36. (config)
  37. (model)
  38. (data-abstraction)
  39. (effect))
  40. ;; =====
  41. ;; EVENT
  42. ;; =====
  43. (define calculate-event-probability
  44. (λ (event general-config)
  45. (let ([base-prob (alist-ref event "probability")]
  46. [prob-multiplier (alist-nested-refs general-config '("difficulty"))]
  47. [min-prob (alist-nested-refs general-config '("min-probability"))]
  48. [max-prob (alist-nested-refs general-config '("max-probability"))])
  49. (limit-by-interval (* base-prob prob-multiplier)
  50. min-prob
  51. max-prob))))
  52. (define event-happening?
  53. (lambda* (event general-config #:key (random-state #f))
  54. (log:debug
  55. (string-format "Checking if event ~a is happening ..." (alist-ref event "name")))
  56. (try-for-probability
  57. (calculate-event-probability event general-config)
  58. #:random-state random-state)))
  59. (define handle-effects
  60. (λ (effects traveler-state transportation-configs effect-procs)
  61. "Handle effects in the order, in which they are
  62. specified in the story parameters."
  63. (vector-fold
  64. (λ (ind acc-event-outcome effect)
  65. ;; Get the appropriate effect procedure and update
  66. ;; the traveler state using it. An effect procedure
  67. ;; gets the traveler's state and the story
  68. ;; parameters, in case it needs them for any reason.
  69. (log:debug (string-format "processing effect: ~a" effect))
  70. (log:debug (string-format "current event-outcome: ~a" acc-event-outcome))
  71. ((hash-table-ref effect-procs effect)
  72. acc-event-outcome traveler-state transportation-configs))
  73. (make-event-outcome)
  74. effects)))
  75. (define describe-event-to-player
  76. (λ (event)
  77. (confirm-info-message (string-format "EVENT name: ~a" (alist-ref event "name")))
  78. (confirm-info-message (string-format "EVENT description: ~a" (choose-random-element (alist-ref event "explanations"))))
  79. (confirm-info-message (string-format "EVENT effects: ~a" (alist-ref event "default-effects")))))
  80. (define handle-event-actions
  81. (λ (event traveler-state transportation-configs effect-procs)
  82. (log:debug (string-format "handling event ~a" (alist-ref event "name")))
  83. (let ([actions (alist-ref event "actions")])
  84. ;; Ask the user for a decision, which will result in
  85. ;; an event outcome.
  86. (log:debug (string-format "event has the following actions: ~a" actions))
  87. (ask-user-for-decision-with-continuations
  88. "How do you want to react?"
  89. (map number->string (range 1 (+ (vector-length actions) 1) #:end-inclusive #t))
  90. (append
  91. (vector->list
  92. (vector-map (λ (ind action) (alist-ref action "label")) actions))
  93. ;; add "other" case for people to specify their own action
  94. '("other"))
  95. (append
  96. (vector->list
  97. (vector-map (λ (ind action)
  98. ;; Return a procedure, which will
  99. ;; result in an event outcome when
  100. ;; applied. The event outcome depends
  101. ;; on the effects of the event.
  102. (log:debug "vector map from actions to effect handling lambdas")
  103. (λ ()
  104. (confirm-info-message (alist-ref action "description"))
  105. (handle-effects (alist-ref action "effects")
  106. traveler-state
  107. transportation-configs
  108. effect-procs)))
  109. actions))
  110. ;; add custom action query and handling for "other"
  111. (list (λ ()
  112. (log:debug "will query for custom action")
  113. (let ([custom-action (query-user-for-custom-action)])
  114. (handle-effects (alist-ref custom-action "effects")
  115. traveler-state
  116. transportation-configs
  117. effect-procs)))))))))
  118. (define calculate-event-outcome
  119. (λ (event traveler-state transportations-config effect-procs)
  120. "Calculate an event outcome from the event action's
  121. effects or the fallback effects of the event itself."
  122. (let ([event-description (choose-random-element (alist-ref event "explanations"))]
  123. [event-effects (alist-ref event "default-effects")]
  124. [event-actions (alist-ref event "actions")])
  125. ;; If the event has actions to choose from, then let
  126. ;; the player choose and process the effects of their
  127. ;; choice of action. Otherwise use the fallback
  128. ;; effects stored in the event, if there are any.
  129. (cond
  130. [(vector-empty? event-actions)
  131. (cond
  132. [(null? event-effects)
  133. (log:debug "The event has no effects, returning default event outcome.")
  134. (make-event-outcome)]
  135. [else
  136. (log:debug "event has effects itself, calculating event outcome")
  137. (handle-effects event-effects
  138. traveler-state
  139. transportations-config
  140. effect-procs)])]
  141. [else
  142. (log:debug "event has actions, will handle event actions")
  143. (handle-event-actions event
  144. traveler-state
  145. transportations-config
  146. effect-procs)]))))
  147. (define process-event
  148. (λ (event
  149. events
  150. event-groups
  151. traveler-state
  152. transportations-config
  153. effect-procs
  154. ;; continuations
  155. next-event next-event-group next-step)
  156. "Process an event. Processing an event has an event
  157. outcome, according to which the traveler state is updated
  158. and the journey continues."
  159. (describe-event-to-player event)
  160. ;; Calculate the event outcome and then act according
  161. ;; to it.
  162. (let ([event-outcome
  163. (calculate-event-outcome
  164. event traveler-state transportations-config effect-procs)])
  165. ;; Update route and numerical values of the traveler
  166. ;; state upfront and then handle the specifics of
  167. ;; the event outcome.
  168. (let ([updated-traveler-state
  169. (set-fields traveler-state
  170. ((traveler-route)
  171. ;; If a route is returned in the
  172. ;; event outcome, use that
  173. ;; route, otherwise keep the one
  174. ;; in the traveler state.
  175. (or (event-outcome-updated-route event-outcome)
  176. (traveler-route traveler-state)))
  177. ((journey-delay) (event-outcome-additional-delay event-outcome))
  178. ((additional-costs) (event-outcome-additional-costs event-outcome)))])
  179. (log:debug (string-format "updated traveler state: ~a" updated-traveler-state))
  180. (cond
  181. [(event-outcome-route-changed event-outcome)
  182. (log:debug "event outcome has an updated route")
  183. (log:debug "going to next step of the journey")
  184. (next-step updated-traveler-state)]
  185. [(event-outcome-disable-event-group event-outcome)
  186. (log:debug "event outcome has disabled other events of the same event group")
  187. (log:debug "checking next event group")
  188. (next-event-group (rest event-groups) updated-traveler-state)]
  189. [(event-outcome-disable-events event-outcome)
  190. (log:debug "event outcome has disabled other events for this step of the journey")
  191. (log:debug "going to next step of the journey")
  192. (next-step updated-traveler-state)]
  193. [else
  194. (log:debug "going to next event")
  195. (next-event (rest events) updated-traveler-state)])))))
  196. (define travel
  197. (lambda* (init-traveler-state
  198. general-config
  199. transportations-config
  200. effect-procs
  201. #:key
  202. (random-state #f)
  203. (random-int-gen (make-random-integer-generator)))
  204. "Calculate the cost for one traveler travelling to the
  205. destination of their journey."
  206. ;; Iterate through the route, which consists of steps of the journey.
  207. (let next-step ([traveler-state init-traveler-state])
  208. (cond
  209. ;; If there are no more steps, the traveler has arrived at their
  210. ;; destination. Return the additional costs.
  211. [(null? (traveler-route traveler-state))
  212. (confirm-info-message (string-format "Congratulations ~a, you reached your destination!" (traveler-name traveler-state)))
  213. traveler-state]
  214. ;; Otherwise continue with the current step of the journey.
  215. [else
  216. (let* ([step (first (traveler-route traveler-state))]
  217. [from (alist-ref step "from")]
  218. [to (alist-ref step "to")]
  219. [transportation (alist-ref step "transportation")]
  220. [event-groups (get:event-groups transportations-config transportation)])
  221. (log:debug (string-format "event groups for this step are: ~a" event-groups))
  222. (confirm-info-message (string-format "~a you are currently in/at: ~a" (traveler-name traveler-state) from))
  223. (confirm-info-message (string-format "~a you are taking the ~a from ~a to ~a" (traveler-name traveler-state) transportation from to))
  224. ;; Iterate through the event groups.
  225. (let next-event-group ([remaining-event-groups event-groups]
  226. [traveler-state traveler-state])
  227. (cond
  228. ;; If there are no event groups remaining to handle, continue
  229. ;; with the next route part.
  230. [(null? remaining-event-groups)
  231. (log:debug "no more event groups in this step")
  232. (next-step
  233. (set-traveler-route traveler-state
  234. (rest (traveler-route traveler-state))))]
  235. [else
  236. (log:debug (string-format "Looking at events in event group ~s now ..." (first (first remaining-event-groups))))
  237. ;; Iterate through the events of the event group and break out,
  238. ;; if required.
  239. (let next-event ([remaining-events
  240. (get:event-group/events (first remaining-event-groups))]
  241. [current-traveler-state traveler-state])
  242. ;; Handle the events in the group of events,
  243. ;; if there are any.
  244. (cond
  245. ;; If there are no events left to handle, continue with the
  246. ;; next event group.
  247. [(null? remaining-events)
  248. (log:debug "no more events in this event group")
  249. (next-event-group (rest remaining-event-groups) current-traveler-state)]
  250. [else
  251. ;; If there are events remaining, deal with the events.
  252. (let ([event (first remaining-events)])
  253. ;; Check if an event is happening and
  254. ;; process the event and its effects, if
  255. ;; it does happen.
  256. (cond
  257. [(event-happening? event general-config #:random-state random-state)
  258. (log:debug (string-format "event ~a is happening" (alist-ref event "name")))
  259. ;; If the event does happen, handle
  260. ;; the event. Display the event
  261. ;; explanation.
  262. (process-event event
  263. remaining-events
  264. remaining-event-groups
  265. current-traveler-state
  266. transportations-config
  267. effect-procs
  268. ;; continuation procedures -- hand those
  269. ;; in, so that the return value does not
  270. ;; have to be processed
  271. next-event
  272. next-event-group
  273. next-step)]
  274. [else
  275. ;; If the event does not happen recur
  276. ;; with 0 additional cost.
  277. (next-event (rest remaining-events) current-traveler-state)]))]))])))]))))
  278. (define process-traveler
  279. (lambda* (traveler general-config json-doc-story-params effect-procs
  280. #:key
  281. (random-state #f)
  282. (random-int-gen (make-random-integer-generator)))
  283. "Process a single traveler."
  284. (confirm-info-message (string-format "~a this is the start of your journey." (first traveler)))
  285. (let* ([planned-costs
  286. (ask-user-for-number
  287. (string-format "~a, how much does the transportation to your destination cost?"
  288. (first traveler))
  289. (λ (num) #t))]
  290. [init-state
  291. (make-traveler-state (first traveler)
  292. (get:traveler/route traveler)
  293. planned-costs
  294. #:journey-delay 0
  295. #:additional-costs 0)])
  296. ;; TODO: think about return value of travel procedure
  297. (travel init-state
  298. general-config
  299. json-doc-story-params
  300. effect-procs
  301. #:random-state random-state
  302. #:random-int-gen random-int-gen))))
  303. ;; ====
  304. ;; MAIN
  305. ;; ====
  306. (define main
  307. (λ ()
  308. (receive (transportations-config players-config general-config)
  309. (get-configuration)
  310. #;(log:debug
  311. (string-format "TRANSPORTATIONS-CONFIG: ~a" transportations-config))
  312. (let ([initial-random-state (seed->random-state 12345)]
  313. [random-int-gen (make-random-integer-generator #:seed 12345)])
  314. (let ([effect-procs
  315. (alist->hash-table
  316. `(("delay" . ,journey-delay-effect)
  317. ("additional-costs" . ,additional-costs-effect)
  318. ("unusable" . ,unusable-effect)
  319. ("route-change" . ,route-change-effect)
  320. ("disable-other-events" . ,disable-other-events-effect)))])
  321. (map
  322. (λ (player-config)
  323. (process-traveler player-config
  324. general-config
  325. transportations-config
  326. effect-procs
  327. #:random-state initial-random-state
  328. #:random-int-gen random-int-gen))
  329. players-config))))))
  330. (log:info (string-format "result: ~a" (main)))