nimmt.lisp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. (ql:quickload "alexandria")
  2. (defun extremum (vector pred &key (key #'identity) (start 0) (end (length vector)))
  3. (let ((extremum-index)
  4. (extremum)
  5. (extremum-keyed))
  6. (loop for i upfrom start below end
  7. do (let* ((e (aref vector i))
  8. (x (funcall key e)))
  9. (when (or (null extremum-index)
  10. (funcall pred x extremum-keyed))
  11. (setq extremum-index i
  12. extremum e
  13. extremum-keyed x))))
  14. (values extremum extremum-index extremum-keyed)))
  15. (defun less (a b)
  16. "Like #'< but ignores nils.
  17. Works only with two parameters."
  18. (if (and a b)
  19. (< a b)
  20. (if a a)))
  21. (defclass nimmt-player ()
  22. ((name :accessor name :initarg name)
  23. (hand :accessor hand :initarg hand :initform nil)
  24. (score :accessor score :initform 0)))
  25. (defclass human-player (nimmt-player)
  26. ((name :initform "domonkos")))
  27. (defmethod pick-row ((object human-player))
  28. (1- (query-player-for-row)))
  29. (defmethod pick-card ((object human-player))
  30. (let ((selection))
  31. (format t "Pick a card!~%")
  32. (setf selection (query-player-for-card (hand object)))
  33. (setf (hand object) (remove selection (hand object)))
  34. selection))
  35. (defclass random-bot (nimmt-player)
  36. ((name :initform "random-bot")))
  37. (defmethod pick-card ((object random-bot))
  38. (let ((rand (random (length (hand object)))))
  39. (elt (hand object) rand)))
  40. (defmethod pick-row ((object random-bot))
  41. (let ((selection (random (length board))))
  42. ;(format t "Randomly selecting row: ~s~%" selection)
  43. selection))
  44. (defclass always-smallest-bot (nimmt-player)
  45. ((name :initform "always-smallest-bot")))
  46. (defmethod pick-row ((object always-smallest-bot))
  47. "Select row with the smallest penality."
  48. (extremum (coerce (alexandria::iota (length board)) 'vector) #'< :key #'row-penality))
  49. (defmethod pick-card ((object always-smallest-bot))
  50. "Select the card that is the closest to the ones on the board."
  51. (multiple-value-bind (card index diff)
  52. (extremum (coerce (remove-if (lambda (e) (> e my-card)) (map 'list #'first board)) 'vector)
  53. #'<
  54. :key (lambda (e) (- my-card e)))
  55. (if diff
  56. card
  57. (first (hand object)))))
  58. (defmethod punish-player (object nimmt-player score)
  59. (incf (score object) score))
  60. (defun penality (n)
  61. (cond ((= n 55) 7)
  62. ((zerop (mod n 11)) 5)
  63. ((zerop (mod n 10)) 3)
  64. ((zerop (mod n 5)) 2)
  65. (1)))
  66. (defvar cards-per-turn 10)
  67. (defparameter board (make-array 4 :initial-element nil))
  68. (defparameter deck (alexandria::iota 104 :start 1))
  69. (defparameter ai-count 1)
  70. (defparameter players nil)
  71. (defparameter cards-in-this-turn nil)
  72. (defun deck-init ()
  73. "Returns a shuffled set of 104 cards."
  74. (setf deck (alexandria::shuffle (alexandria::iota 104 :start 1))))
  75. (defun draw (&optional (n 1))
  76. (loop while deck
  77. repeat n
  78. collect (pop deck)))
  79. (defun players-init ()
  80. (mapc (lambda (player)
  81. (setf (hand player) (sort (draw cards-per-turn) #'<)))
  82. players))
  83. (defun reset-player-scores ()
  84. (mapc (lambda (player)
  85. (setf (score player) 0))
  86. players))
  87. (defun board-init ()
  88. (setf board (map 'vector
  89. (lambda (e) (setf e (draw)))
  90. (make-array 4 :initial-element nil))))
  91. (defun game-init ()
  92. (deck-init)
  93. (board-init)
  94. (sort-board)
  95. (print-board))
  96. (defun row-penality (row-num)
  97. (reduce #'+ (mapcar #'penality (aref board row-num)))) ; TODO: apply?
  98. (defun card-distance-from (card)
  99. (let ((anchor card))
  100. (lambda (other-card)
  101. (let ((diff (- anchor (first other-card))))
  102. (if (plusp diff) diff)))))
  103. (defun select-row (card)
  104. (let ((measure (card-distance-from card)))
  105. (extremum board #'less :key measure)))
  106. (defun place-card (card player)
  107. (multiple-value-bind (row row-index distance) (select-row card)
  108. (declare (ignore row))
  109. (if distance ; we have a row with a smaller number -> push
  110. (if (check-row-length row-index)
  111. (push card (aref board row-index))
  112. (progn (punish-player player 'score (row-penality row-index))
  113. (empty-row row-index card)))
  114. (let ((selected-row (pick-row player)))
  115. (punish-player player 'score (row-penality selected-row))
  116. (empty-row selected-row card)))))
  117. (defun check-row-length (row-index)
  118. (> 5 (length (aref board row-index))))
  119. (defun query-player-for-row ()
  120. (print-board)
  121. (format t "Select a row (1-4): ")
  122. (loop for num = (read)
  123. when (and (integerp num)
  124. (<= 1 num 4))
  125. return num
  126. do (format t "Couldn't parse, retry:~%")))
  127. (defun print-board ()
  128. (loop for i below (length board)
  129. do (format t "~d: penality: ~d, row: (~{~4d~})~%" (1+ i) (row-penality i) (aref board i))))
  130. (defun query-player-for-card (hand)
  131. (format t "Select a card from hand: ~s" (sort hand #'<))
  132. (loop for selection = (read)
  133. when (and (integerp selection)
  134. (member selection hand))
  135. return selection
  136. do (format t "Couldn't parse, retry:~%")))
  137. (defun sort-board ()
  138. (sort board #'< :key #'first))
  139. (defun empty-row (row new-card)
  140. ;(format t "Emptying row...~%")
  141. (setf (aref board row) (list new-card)))
  142. ;(sort-board)) ; TODO: temporarly disabled
  143. (defun query-player-number (message &rest parameters)
  144. (format t message parameters)
  145. (loop
  146. for input = (read)
  147. when (numberp input)
  148. return input
  149. do (format t "That's not a number")))
  150. ;(defun start-new-game ()
  151. ; (setf ai-count (query-player-number "How many AI players you want to play against?~%"))
  152. ; (setf player-count (1+ ai-count))
  153. ; (game-init :player-count player-count)
  154. ; (play-game))
  155. (defun select-cards ()
  156. (loop for player in players
  157. for card = (pick-card player)
  158. do (progn
  159. (setf (hand player) (remove card (hand player)))
  160. (push (cons card player) cards-in-this-turn)))) ; TODO: collect
  161. (defun place-cards (cards)
  162. (loop for (card . player) in (sort cards #'< :key #'first)
  163. do (progn
  164. (format t "c ~s p ~s~%" card (name player))
  165. (place-card card player))))
  166. (defun print-results ()
  167. (format t "Results:~%")
  168. (mapc (lambda (p) (format t "~13a: ~s~%" (name p) (score p)))
  169. (sort players #'< :key #'score)))
  170. (defun play-game ()
  171. (loop
  172. ;repeat cards-per-turn
  173. repeat 1
  174. do (progn
  175. (setf cards-in-this-turn nil)
  176. (select-cards)
  177. ;(format t "cards: ~s~%" cards-in-this-turn)
  178. (place-cards cards-in-this-turn)
  179. (print-board)
  180. ))
  181. ;(print-results)
  182. )