123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- (ql:quickload "alexandria")
- (defun extremum (vector pred &key (key #'identity) (start 0) (end (length vector)))
- (let ((extremum-index)
- (extremum)
- (extremum-keyed))
- (loop for i upfrom start below end
- do (let* ((e (aref vector i))
- (x (funcall key e)))
- (when (or (null extremum-index)
- (funcall pred x extremum-keyed))
- (setq extremum-index i
- extremum e
- extremum-keyed x))))
- (values extremum extremum-index extremum-keyed)))
- (defun less (a b)
- "Like #'< but ignores nils.
- Works only with two parameters."
- (if (and a b)
- (< a b)
- (if a a)))
- (defclass nimmt-player ()
- ((name :accessor name :initarg name)
- (hand :accessor hand :initarg hand :initform nil)
- (score :accessor score :initform 0)))
- (defclass human-player (nimmt-player)
- ((name :initform "domonkos")))
- (defmethod pick-row ((object human-player))
- (1- (query-player-for-row)))
- (defmethod pick-card ((object human-player))
- (let ((selection))
- (format t "Pick a card!~%")
- (setf selection (query-player-for-card (hand object)))
- (setf (hand object) (remove selection (hand object)))
- selection))
- (defclass random-bot (nimmt-player)
- ((name :initform "random-bot")))
- (defmethod pick-card ((object random-bot))
- (let ((rand (random (length (hand object)))))
- (elt (hand object) rand)))
- (defmethod pick-row ((object random-bot))
- (let ((selection (random (length board))))
- ;(format t "Randomly selecting row: ~s~%" selection)
- selection))
- (defclass always-smallest-bot (nimmt-player)
- ((name :initform "always-smallest-bot")))
- (defmethod pick-row ((object always-smallest-bot))
- "Select row with the smallest penality."
- (extremum (coerce (alexandria::iota (length board)) 'vector) #'< :key #'row-penality))
- (defmethod pick-card ((object always-smallest-bot))
- "Select the card that is the closest to the ones on the board."
- (multiple-value-bind (card index diff)
- (extremum (coerce (remove-if (lambda (e) (> e my-card)) (map 'list #'first board)) 'vector)
- #'<
- :key (lambda (e) (- my-card e)))
- (if diff
- card
- (first (hand object)))))
- (defmethod punish-player (object nimmt-player score)
- (incf (score object) score))
- (defun penality (n)
- (cond ((= n 55) 7)
- ((zerop (mod n 11)) 5)
- ((zerop (mod n 10)) 3)
- ((zerop (mod n 5)) 2)
- (1)))
- (defvar cards-per-turn 10)
- (defparameter board (make-array 4 :initial-element nil))
- (defparameter deck (alexandria::iota 104 :start 1))
- (defparameter ai-count 1)
- (defparameter players nil)
- (defparameter cards-in-this-turn nil)
- (defun deck-init ()
- "Returns a shuffled set of 104 cards."
- (setf deck (alexandria::shuffle (alexandria::iota 104 :start 1))))
- (defun draw (&optional (n 1))
- (loop while deck
- repeat n
- collect (pop deck)))
- (defun players-init ()
- (mapc (lambda (player)
- (setf (hand player) (sort (draw cards-per-turn) #'<)))
- players))
- (defun reset-player-scores ()
- (mapc (lambda (player)
- (setf (score player) 0))
- players))
- (defun board-init ()
- (setf board (map 'vector
- (lambda (e) (setf e (draw)))
- (make-array 4 :initial-element nil))))
- (defun game-init ()
- (deck-init)
- (board-init)
- (sort-board)
- (print-board))
- (defun row-penality (row-num)
- (reduce #'+ (mapcar #'penality (aref board row-num)))) ; TODO: apply?
- (defun card-distance-from (card)
- (let ((anchor card))
- (lambda (other-card)
- (let ((diff (- anchor (first other-card))))
- (if (plusp diff) diff)))))
- (defun select-row (card)
- (let ((measure (card-distance-from card)))
- (extremum board #'less :key measure)))
- (defun place-card (card player)
- (multiple-value-bind (row row-index distance) (select-row card)
- (declare (ignore row))
- (if distance ; we have a row with a smaller number -> push
- (if (check-row-length row-index)
- (push card (aref board row-index))
- (progn (punish-player player 'score (row-penality row-index))
- (empty-row row-index card)))
- (let ((selected-row (pick-row player)))
- (punish-player player 'score (row-penality selected-row))
- (empty-row selected-row card)))))
- (defun check-row-length (row-index)
- (> 5 (length (aref board row-index))))
- (defun query-player-for-row ()
- (print-board)
- (format t "Select a row (1-4): ")
- (loop for num = (read)
- when (and (integerp num)
- (<= 1 num 4))
- return num
- do (format t "Couldn't parse, retry:~%")))
- (defun print-board ()
- (loop for i below (length board)
- do (format t "~d: penality: ~d, row: (~{~4d~})~%" (1+ i) (row-penality i) (aref board i))))
- (defun query-player-for-card (hand)
- (format t "Select a card from hand: ~s" (sort hand #'<))
- (loop for selection = (read)
- when (and (integerp selection)
- (member selection hand))
- return selection
- do (format t "Couldn't parse, retry:~%")))
- (defun sort-board ()
- (sort board #'< :key #'first))
- (defun empty-row (row new-card)
- ;(format t "Emptying row...~%")
- (setf (aref board row) (list new-card)))
- ;(sort-board)) ; TODO: temporarly disabled
- (defun query-player-number (message &rest parameters)
- (format t message parameters)
- (loop
- for input = (read)
- when (numberp input)
- return input
- do (format t "That's not a number")))
- ;(defun start-new-game ()
- ; (setf ai-count (query-player-number "How many AI players you want to play against?~%"))
- ; (setf player-count (1+ ai-count))
- ; (game-init :player-count player-count)
- ; (play-game))
- (defun select-cards ()
- (loop for player in players
- for card = (pick-card player)
- do (progn
- (setf (hand player) (remove card (hand player)))
- (push (cons card player) cards-in-this-turn)))) ; TODO: collect
- (defun place-cards (cards)
- (loop for (card . player) in (sort cards #'< :key #'first)
- do (progn
- (format t "c ~s p ~s~%" card (name player))
- (place-card card player))))
- (defun print-results ()
- (format t "Results:~%")
- (mapc (lambda (p) (format t "~13a: ~s~%" (name p) (score p)))
- (sort players #'< :key #'score)))
- (defun play-game ()
- (loop
- ;repeat cards-per-turn
- repeat 1
- do (progn
- (setf cards-in-this-turn nil)
- (select-cards)
- ;(format t "cards: ~s~%" cards-in-this-turn)
- (place-cards cards-in-this-turn)
- (print-board)
- ))
- ;(print-results)
- )
|