main.scm 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  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 karma general-config)
  45. (let ([base-prob (alist-ref event "probability")]
  46. [event-karma-probability-modifier (alist-ref event "karma-probability-modifier")]
  47. [prob-multiplier (alist-nested-refs general-config '("difficulty"))]
  48. [min-prob (alist-nested-refs general-config '("min-probability"))]
  49. [max-prob (alist-nested-refs general-config '("max-probability"))])
  50. (let ([additional-probability-because-of-karma
  51. (* (/ 1 100) karma event-karma-probability-modifier)])
  52. (log:debug
  53. (string-format "additional-probability-because-of-karma: ~a"
  54. additional-probability-because-of-karma))
  55. (log:debug
  56. (string-format "probability without karma effect: ~a"
  57. (* base-prob prob-multiplier)))
  58. (log:debug
  59. (string-format "probability with karma effect: ~a"
  60. (+ additional-probability-because-of-karma
  61. (* base-prob prob-multiplier))))
  62. (limit-by-interval (+ additional-probability-because-of-karma
  63. (* base-prob prob-multiplier))
  64. min-prob
  65. max-prob)))))
  66. (define event-happening?
  67. (lambda* (event traveler-state general-config #:key (random-state #f))
  68. (log:debug
  69. (string-format "Checking if event ~a is happening ..." (alist-ref event "name")))
  70. (let ([event-probability
  71. (calculate-event-probability event
  72. (karma traveler-state)
  73. general-config)])
  74. (log:debug
  75. (string-format "Event has probability of ~a to happen." event-probability))
  76. (try-for-probability event-probability #:random-state random-state))))
  77. (define handle-effects
  78. (λ (effects traveler-state transportation-configs effect-procs)
  79. "Handle effects in the order, in which they are
  80. specified in the story parameters."
  81. (vector-fold
  82. (λ (ind acc-event-outcome effect)
  83. ;; Get the appropriate effect procedure and update
  84. ;; the traveler state using it. An effect procedure
  85. ;; gets the traveler's state and the story
  86. ;; parameters, in case it needs them for any reason.
  87. (log:debug (string-format "processing effect: ~a" effect))
  88. (log:debug (string-format "current event-outcome: ~a" acc-event-outcome))
  89. ((hash-table-ref effect-procs effect)
  90. acc-event-outcome traveler-state transportation-configs))
  91. (make-event-outcome)
  92. effects)))
  93. (define describe-event-to-player
  94. (λ (event)
  95. (confirm-info-message (string-format "EVENT name: ~a" (alist-ref event "name")))
  96. (confirm-info-message (string-format "EVENT description: ~a" (choose-random-element (alist-ref event "explanations"))))
  97. (confirm-info-message (string-format "EVENT effects: ~a" (alist-ref event "default-effects")))))
  98. (define handle-event-actions
  99. (λ (event traveler-state transportation-configs effect-procs)
  100. (log:debug (string-format "handling event ~a" (alist-ref event "name")))
  101. (let ([actions (alist-ref event "actions")])
  102. ;; Ask the user for a decision, which will result in
  103. ;; an event outcome.
  104. (log:debug (string-format "event has the following actions: ~a" actions))
  105. (ask-user-for-decision-with-continuations
  106. "How do you want to react?"
  107. (map number->string (range 1 (+ (vector-length actions) 1) #:end-inclusive #t))
  108. (append
  109. (vector->list
  110. (vector-map (λ (ind action) (alist-ref action "label")) actions))
  111. ;; add "other" case for people to specify their own action
  112. '("other"))
  113. (append
  114. (vector->list
  115. (vector-map (λ (ind action)
  116. ;; Return a procedure, which will
  117. ;; result in an event outcome when
  118. ;; applied. The event outcome depends
  119. ;; on the effects of the event.
  120. (log:debug "vector map from actions to effect handling lambdas")
  121. (λ ()
  122. (confirm-info-message (alist-ref action "description"))
  123. ;; Set the event outcome karma to
  124. ;; the karma of the chosen action.
  125. (set-event-outcome-karma
  126. (handle-effects (alist-ref action "effects")
  127. traveler-state
  128. transportation-configs
  129. effect-procs)
  130. (alist-ref action "karma"
  131. #:default (λ ()
  132. (log:warning
  133. (string-format "could not find field \"karma\" on action ~a"
  134. action))
  135. 0.00)))))
  136. actions))
  137. ;; add custom action query and handling for "other"
  138. (list (λ ()
  139. (log:debug "will query for custom action")
  140. (let ([custom-action (query-user-for-custom-action)])
  141. ;; Set the event outcome karma to the
  142. ;; karma of the chosen action.
  143. (set-event-outcome-karma
  144. (handle-effects (alist-ref custom-action "effects")
  145. traveler-state
  146. transportation-configs
  147. effect-procs)
  148. (alist-ref custom-action "karma"
  149. #:default (λ ()
  150. (log:warning
  151. (string-format "could not find field \"karma\" on action ~a"
  152. custom-action))
  153. 0.00)))))))))))
  154. (define calculate-event-outcome
  155. (λ (event traveler-state transportations-config effect-procs)
  156. "Calculate an event outcome from the event action's
  157. effects or the fallback effects of the event itself."
  158. (let ([event-description (choose-random-element (alist-ref event "explanations"))]
  159. [event-effects (alist-ref event "default-effects")]
  160. [event-actions (alist-ref event "actions")])
  161. ;; If the event has actions to choose from, then let
  162. ;; the player choose and process the effects of their
  163. ;; choice of action. Otherwise use the fallback
  164. ;; effects stored in the event, if there are any.
  165. (cond
  166. [(vector-empty? event-actions)
  167. (cond
  168. [(null? event-effects)
  169. (log:debug "The event has no effects, returning default event outcome.")
  170. (make-event-outcome)]
  171. [else
  172. (log:debug "event has effects itself, calculating event outcome")
  173. (handle-effects event-effects
  174. traveler-state
  175. transportations-config
  176. effect-procs)])]
  177. [else
  178. (log:debug "event has actions, will handle event actions")
  179. (handle-event-actions event
  180. traveler-state
  181. transportations-config
  182. effect-procs)]))))
  183. (define calculate-adjusted-traveler-karma
  184. (λ (event traveler-state)
  185. (let* ([traveler-karma (karma traveler-state)]
  186. [karma-probability-modifier
  187. (alist-ref event "karma-probability-modifier"
  188. #:default (λ ()
  189. (log:warning
  190. (string-format "could not find key karma-probability-modifier in event ~a"
  191. event))
  192. 0))]
  193. [event-is-positive (> karma-probability-modifier 0)]
  194. [event-is-negative (< karma-probability-modifier 0)])
  195. ;; If the karma of the traveler is positive and the event is positive
  196. (cond
  197. ;; If the traveler has positive karma and a positive
  198. ;; event happens, the traveler profitted from
  199. ;; karma. They used up good karma, so we move
  200. ;; positive karma closer to 0.
  201. [(and event-is-positive (> traveler-karma 0))
  202. (max (- traveler-karma
  203. (/ (abs karma-probability-modifier)
  204. 2))
  205. 0)]
  206. ;; If the traveler has negative karma and a negative
  207. ;; event happens, the traveler got punished by
  208. ;; karma. They paid, so we move negative karma closer
  209. ;; to 0.
  210. [(and event-is-negative (< traveler-karma 0))
  211. (min (+ traveler-karma
  212. (/ (abs karma-probability-modifier)
  213. 2))
  214. 0)]
  215. ;; If the event is neutral, the traveler karma is
  216. ;; neutral or traveler karma and event do not agree
  217. ;; on being either both positive or both negative,
  218. ;; then justice was not served. In this case do not
  219. ;; adjust traveler karma.
  220. [else 0]))))
  221. (define process-event
  222. (λ (event
  223. events
  224. event-groups
  225. traveler-state
  226. transportations-config
  227. effect-procs
  228. ;; continuations
  229. next-event next-event-group next-step)
  230. "Process an event. Processing an event has an event
  231. outcome, according to which the traveler state is updated
  232. and the journey continues."
  233. (describe-event-to-player event)
  234. (let ([traveler-state
  235. (set-fields traveler-state
  236. [(event-count) (+ (event-count traveler-state) 1)]
  237. [(karma) (calculate-adjusted-traveler-karma event traveler-state)])])
  238. ;; Calculate the event outcome and then act according
  239. ;; to it.
  240. (let ([event-outcome
  241. (calculate-event-outcome
  242. event traveler-state transportations-config effect-procs)])
  243. ;; Update route and numerical values of the traveler
  244. ;; state upfront and then handle the specifics of
  245. ;; the event outcome.
  246. (let ([updated-traveler-state
  247. (set-fields traveler-state
  248. [(traveler-route)
  249. ;; If a route is returned in the
  250. ;; event outcome, use that
  251. ;; route, otherwise keep the one
  252. ;; in the traveler state.
  253. (or (event-outcome-updated-route event-outcome)
  254. (traveler-route traveler-state))]
  255. [(journey-delay)
  256. (+ (event-outcome-additional-delay event-outcome)
  257. (journey-delay traveler-state))]
  258. [(additional-costs)
  259. (+ (event-outcome-additional-costs event-outcome)
  260. (additional-costs traveler-state))]
  261. [(karma)
  262. (+ (event-outcome-karma event-outcome)
  263. (karma traveler-state))])])
  264. (log:debug (string-format "updated traveler state: ~a" updated-traveler-state))
  265. (cond
  266. [(event-outcome-route-changed event-outcome)
  267. (log:debug "event outcome has an updated route")
  268. (log:debug "going to next step of the journey")
  269. (next-step updated-traveler-state)]
  270. [(event-outcome-disable-event-group event-outcome)
  271. (log:debug "event outcome has disabled other events of the same event group")
  272. (log:debug "checking next event group")
  273. (next-event-group (rest event-groups) updated-traveler-state)]
  274. [(event-outcome-disable-events event-outcome)
  275. (log:debug "event outcome has disabled other events for this step of the journey")
  276. (log:debug "going to next step of the journey")
  277. (next-step updated-traveler-state)]
  278. [else
  279. (log:debug "going to next event")
  280. (next-event (rest events) updated-traveler-state)]))))))
  281. (define travel
  282. (lambda* (init-traveler-state
  283. general-config
  284. transportations-config
  285. effect-procs
  286. #:key
  287. (random-state #f)
  288. (random-int-gen (make-random-integer-generator)))
  289. "Calculate the cost for one traveler travelling to the
  290. destination of their journey."
  291. ;; Iterate through the route, which consists of steps of the journey.
  292. (let next-step ([traveler-state init-traveler-state])
  293. (cond
  294. ;; If there are no more steps, the traveler has arrived at their
  295. ;; destination. Return the additional costs.
  296. [(null? (traveler-route traveler-state))
  297. (confirm-info-message (string-format "Congratulations ~a, you reached your destination!" (traveler-name traveler-state)))
  298. traveler-state]
  299. ;; Otherwise continue with the current step of the journey.
  300. [else
  301. (let* ([step (first (traveler-route traveler-state))]
  302. [from (alist-ref step "from")]
  303. [to (alist-ref step "to")]
  304. [transportation (alist-ref step "transportation")]
  305. [event-groups (get:event-groups transportations-config transportation)])
  306. ;; (log:debug (string-format "event groups for this step are: ~a" event-groups))
  307. (confirm-info-message (string-format "~a you are currently in/at: ~a" (traveler-name traveler-state) from))
  308. (confirm-info-message (string-format "~a you are taking the ~a from ~a to ~a" (traveler-name traveler-state) transportation from to))
  309. ;; Iterate through the event groups.
  310. (let next-event-group ([remaining-event-groups event-groups]
  311. [traveler-state traveler-state])
  312. (cond
  313. ;; If there are no event groups remaining to handle, continue
  314. ;; with the next route part.
  315. [(null? remaining-event-groups)
  316. (log:debug "no more event groups in this step")
  317. (next-step
  318. (set-traveler-route traveler-state
  319. (rest (traveler-route traveler-state))))]
  320. [else
  321. (log:debug (string-format "Looking at events in event group ~s now ..." (first (first remaining-event-groups))))
  322. ;; Iterate through the events of the event group and break out,
  323. ;; if required.
  324. (let next-event ([remaining-events
  325. (get:event-group/events (first remaining-event-groups))]
  326. [current-traveler-state traveler-state])
  327. ;; Handle the events in the group of events,
  328. ;; if there are any.
  329. (cond
  330. ;; If there are no events left to handle, continue with the
  331. ;; next event group.
  332. [(null? remaining-events)
  333. (log:debug "no more events in this event group")
  334. (next-event-group (rest remaining-event-groups) current-traveler-state)]
  335. [else
  336. ;; If there are events remaining, deal with the events.
  337. (let ([event (first remaining-events)])
  338. ;; Check if an event is happening and
  339. ;; process the event and its effects, if
  340. ;; it does happen.
  341. (cond
  342. [(event-happening? event current-traveler-state general-config #:random-state random-state)
  343. (log:debug (string-format "event ~a is happening" (alist-ref event "name")))
  344. ;; If the event does happen, handle
  345. ;; the event. Display the event
  346. ;; explanation.
  347. (process-event event
  348. remaining-events
  349. remaining-event-groups
  350. current-traveler-state
  351. transportations-config
  352. effect-procs
  353. ;; continuation procedures -- hand those
  354. ;; in, so that the return value does not
  355. ;; have to be processed
  356. next-event
  357. next-event-group
  358. next-step)]
  359. [else
  360. ;; If the event does not happen recur
  361. ;; with 0 additional cost.
  362. (next-event (rest remaining-events) current-traveler-state)]))]))])))]))))
  363. (define process-traveler
  364. (lambda* (traveler general-config json-doc-story-params effect-procs
  365. #:key
  366. (random-state #f)
  367. (random-int-gen (make-random-integer-generator)))
  368. "Process a single traveler."
  369. (confirm-info-message (string-format "~a this is the start of your journey." (first traveler)))
  370. (let* ([planned-costs
  371. (ask-user-for-number
  372. (string-format "~a, how much does the transportation to your destination cost?"
  373. (first traveler))
  374. (λ (num) #t))]
  375. [init-state
  376. (make-traveler-state (first traveler)
  377. (get:traveler/route traveler)
  378. planned-costs)])
  379. ;; TODO: think about return value of travel procedure
  380. (travel init-state
  381. general-config
  382. json-doc-story-params
  383. effect-procs
  384. #:random-state random-state
  385. #:random-int-gen random-int-gen))))
  386. ;; ====
  387. ;; MAIN
  388. ;; ====
  389. (define main
  390. (λ ()
  391. (receive (transportations-config players-config general-config)
  392. (get-configuration)
  393. #;(log:debug
  394. (string-format "TRANSPORTATIONS-CONFIG: ~a" transportations-config))
  395. (let ([initial-random-state (seed->random-state 12345)]
  396. [random-int-gen (make-random-integer-generator #:seed 12345)])
  397. (let ([effect-procs
  398. (alist->hash-table
  399. `(("delay" . ,journey-delay-effect)
  400. ("additional-costs" . ,additional-costs-effect)
  401. ("unusable" . ,unusable-effect)
  402. ("route-change" . ,route-change-effect)
  403. ("disable-other-events" . ,disable-other-events-effect)))])
  404. (map
  405. (λ (player-config)
  406. (process-traveler player-config
  407. general-config
  408. transportations-config
  409. effect-procs
  410. #:random-state initial-random-state
  411. #:random-int-gen random-int-gen))
  412. players-config))))))
  413. (log:info (string-format "result: ~a" (main)))