core.rkt 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. #lang racket
  2. ;; start with some basic utilities
  3. (define (d n)
  4. (random 1 (+ n 1)))
  5. ;; the logic for judging the 'outcome' of an Ironsworn roll or progress track
  6. (define (judge-outcome attempt challenge-1 challenge-2)
  7. (let [(outcome
  8. (cond [(and (> attempt challenge-1) (> attempt challenge-2))
  9. 'success]
  10. [(or (> attempt challenge-1) (> attempt challenge-2))
  11. 'partial]
  12. [else 'failure]))]
  13. `(,outcome ,attempt (,challenge-1 ,challenge-2))))
  14. ;; a typical Ironsworn roll:
  15. (define (roll-plus-mod m)
  16. (let [
  17. ;; roll two challenge dice
  18. (challenge-1 (d 10))
  19. (challenge-2 (d 10))
  20. ;; roll an attempt die
  21. (attempt (+ (d 6) m))]
  22. ;; judge the outome
  23. (judge-outcome attempt challenge-1 challenge-2)))
  24. ;; this just keeps us honest that this is playable with dice
  25. (define *allowed-table-sizes* '(2 4 6 8 10 12 20 36 50 100))
  26. ;; this is used in define-oracle to actually process the information, and ought to
  27. ;; return a zero-argument function that will roll on the table
  28. ;; TODO: also allow generating print structures from the same data!
  29. (define (oracle table)
  30. (cond [(andmap valid-oracle-result? table)
  31. (if (member (length table) *allowed-table-sizes*)
  32. (lambda () (car (shuffle table)))
  33. (error 'oracle "Length ~a is not a die-size-compatible length" (length table)))]
  34. [(andmap valid-table-member? table)
  35. (let [(max (max-table-size table))]
  36. (lambda () (table-index (d max) table)))]
  37. [else (error 'oracle "Unexpected argument: ~a" table)]))
  38. ;; given a range-based table, find the highest value to roll
  39. (define (max-table-size table)
  40. (let [(final (last table))]
  41. (if (= (length final) 3) (cadr final) (car final))))
  42. ;; true if the `thing` is a symbol or string
  43. (define (valid-oracle-result? thing)
  44. (or (symbol? thing) (string? thing)))
  45. ;; true if the thing is a list of the form (num num string) or (num string)
  46. (define (valid-table-member? member)
  47. (and (list? member)
  48. (or (and (= (length member) 3)
  49. (number? (car member))
  50. (number? (cadr member))
  51. (valid-oracle-result? (caddr member)))
  52. (and (= (length member) 2)
  53. (number? (car member))
  54. (valid-oracle-result? (cadr member))))))
  55. ;; look up a number and find it in the table. (this assumes the table is sorted and
  56. ;; includes all possible values covered!
  57. ;; TODO: make this more resilient to bad input
  58. (define (table-index idx table)
  59. (if (null? table)
  60. (error 'table-index "Unexpected empty table")
  61. (let [(head (car table))]
  62. (cond [(and (= (length head) 3)
  63. (>= idx (car head))
  64. (<= idx (cadr head)))
  65. (caddr head)]
  66. [(and (= (length head) 2)
  67. (= idx (car head)))
  68. (cadr head)]
  69. [else (table-index idx (cdr table))]))))
  70. (define-syntax-rule [define-oracle name . table]
  71. (define name (oracle (quote table))))
  72. (define (asset name table)
  73. (define (mk-feature feature)
  74. (list (car feature) (smart-string-append (cdr feature))))
  75. (let [(stuff (map mk-feature table))]
  76. `(asset ,name ,stuff)))
  77. (define (smart-string-append list)
  78. (define (ensure-leading-space str)
  79. (if (char-whitespace? (string-ref str 0))
  80. str
  81. (string-append " " str)))
  82. (apply string-append (cons (car list) (map ensure-leading-space (cdr list)))))
  83. (define-syntax-rule [define-asset-path name . table]
  84. (define name (asset (quote name) (quote table))))
  85. (provide judge-outcome roll-plus-mod oracle define-oracle define-asset-path)