/games/rpswar/main.rkt

http://github.com/get-bonus/get-bonus · Racket · 195 lines · 179 code · 16 blank · 0 comment · 38 complexity · 2cfef2543043a1b00ae11e20d58a7e7c MD5 · raw file

  1. #lang racket/base
  2. (require racket/runtime-path
  3. racket/match
  4. racket/list
  5. gb/gui/os
  6. gb/graphics/main
  7. gb/data/mvector
  8. gb/input/keyboard
  9. gb/input/controller
  10. gb/lib/math
  11. gb/lib/random
  12. gb/data/psn
  13. gb/meta
  14. gb/meta-help
  15. gb/sys/menu
  16. math/base
  17. (prefix-in cd: gb/physics/cd-narrow)
  18. "fst.rkt"
  19. "graph.rkt")
  20. (struct a-match (match-number ai-spec) #:transparent)
  21. (struct start a-match () #:transparent)
  22. (struct round a-match (round-number wins ai-state) #:transparent)
  23. (struct user-input round () #:transparent)
  24. (struct computer-input round (user-input) #:transparent)
  25. (struct resolve round (user-input computer-input) #:transparent)
  26. (struct resolved round (user-input computer-input outcome) #:transparent)
  27. (struct end round () #:transparent)
  28. (define input '(r p s))
  29. (define output input)
  30. (define rps-outcome
  31. (match-lambda*
  32. ['(r s) 'user]
  33. ['(s r) 'computer]
  34. ['(r p) 'computer]
  35. ['(p r) 'user]
  36. ['(p s) 'computer]
  37. ['(s p) 'user]
  38. [_ 'draw]))
  39. (define rps->string
  40. (match-lambda
  41. ['r "Rock"]
  42. ['p "Paper"]
  43. ['s "Scissors"]))
  44. (define graphs (make-weak-hash))
  45. (define (fst-graph* fst)
  46. (hash-ref! graphs fst (λ () (fst-graph fst))))
  47. (define (state->menu return w)
  48. (define same w)
  49. (define (string next text status [auto #f])
  50. (menu:option
  51. text
  52. (λ ()
  53. (list* (menu:status status)
  54. (menu:action (λ () (return next)))
  55. (if auto
  56. (list (menu:auto 'next (λ () (return next))))
  57. empty)))))
  58. (define (next-menu next)
  59. (menu:list 'nextm (list (string next "Next " "Advance to next message." 'next))))
  60. (match w
  61. [(start match# ai)
  62. (define next (user-input match# ai 1 0 (fst-start ai)))
  63. (list
  64. (menu:info (list (format "Match: ~a" match#)
  65. (format "Fight!")))
  66. (next-menu next))]
  67. [(user-input match# ai round# wins state)
  68. (define (next ui)
  69. (computer-input match# ai round# wins state ui))
  70. (list
  71. (menu:info (list (format "Match: ~a" match#)
  72. (format "Round: ~a" round#)
  73. (format "Ratio: ~a/~a" wins round#)
  74. ""
  75. (format-fst ai state)
  76. ""
  77. (format "What will you throw down?")))
  78. (menu:list 'rps (for/list ([ui (in-list input)])
  79. (string (next ui)
  80. (rps->string ui)
  81. (format "Throw down a ~a"
  82. (rps->string ui))))))]
  83. [(computer-input match# ai round# wins state ui)
  84. (define next
  85. (resolve match# ai round# wins state ui (fst-output ai state)))
  86. (list
  87. (menu:info (list (format "Match: ~a" match#)
  88. (format "Round: ~a" round#)
  89. (format "Ratio: ~a/~a" wins round#)
  90. (format "You threw down ~a" (rps->string ui))))
  91. (next-menu next))]
  92. [(resolve match# ai round# wins state ui ci)
  93. (define next
  94. (resolved match# ai round# wins state ui ci
  95. (rps-outcome ui ci)))
  96. (list
  97. (menu:info (list (format "Match: ~a" match#)
  98. (format "Round: ~a" round#)
  99. (format "Ratio: ~a/~a" wins round#)
  100. (format "You threw down ~a" (rps->string ui))
  101. (format "The AI threw down ~a" (rps->string ci))))
  102. (next-menu next))]
  103. [(resolved match# ai round# wins state ui ci outcome)
  104. (define total-wins
  105. (if (eq? 'user outcome)
  106. (add1 wins)
  107. wins))
  108. (define next
  109. (if (or (= round# (* 2 (fst-states ai)))
  110. (and (> round# 3)
  111. (or (> (/ total-wins round#)
  112. 1/2)
  113. (> (/ (- round# total-wins) round#)
  114. 1/2))))
  115. (end match# ai round# total-wins state)
  116. (let ()
  117. (define next-state
  118. (fst-next ai state ui))
  119. (user-input match# ai
  120. (if (eq? outcome 'draw)
  121. round#
  122. (add1 round#))
  123. total-wins next-state))))
  124. (list
  125. (menu:info (list (format "Match: ~a" match#)
  126. (format "Round: ~a" round#)
  127. (format "Ratio: ~a/~a" wins round#)
  128. (match outcome
  129. ['user (format "You won the round!")]
  130. ['computer (format "The AI won the round!")]
  131. ['draw (format "Draw")])))
  132. (next-menu next))]
  133. [(end match# _ round# wins _)
  134. (list
  135. (menu:info (list (format "Match: ~a" match#)
  136. (format "Round: ~a" round#)
  137. (format "Ratio: ~a/~a" wins round#)
  138. (format "The match is over.")))
  139. (next-menu same))]))
  140. (define (repeat-n n f a)
  141. (if (zero? n)
  142. a
  143. (repeat-n (sub1 n) f (f a))))
  144. (define (game-start final-ai)
  145. (big-bang/os
  146. crt-width crt-height (psn (/ crt-width 2.) (/ crt-height 2.0))
  147. #:sound-scale (/ crt-width 2.)
  148. (λ (env)
  149. (let loop ([s (start 1 final-ai)])
  150. (define ns
  151. (let/ec return
  152. (render-menu (state->menu return s))))
  153. (when (end? ns)
  154. (win-write
  155. 'done?
  156. #t
  157. 'return
  158. (/ (round-wins ns)
  159. (round-round-number ns))))
  160. (loop ns))))
  161. 0.0)
  162. (define (generate-ai)
  163. (define start-fst (random-one-state-fst input output))
  164. (define final-ai
  165. (let loop ([ai start-fst])
  166. (define next-ai (repeat-n (random-integer 1 10) mutate-fst ai))
  167. (if (zero? (random 2))
  168. next-ai
  169. (loop next-ai))))
  170. final-ai)
  171. (define fst-spec
  172. (fst/e input output))
  173. (define game
  174. (game-info 'rpswar "RPS War"
  175. (list "Play Rock-Paper-Scissors against an algorithmic opponent. The opponent does not choose its play randomly, but uses a strategy fixed at the beginning of the round. Try to reverse engineer the strategy to win!")
  176. 2
  177. (godel-generate fst-spec generate-ai)
  178. (godel-start fst-spec game-start)))
  179. (provide game)