checker-position.lisp 4.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. ;;;; checker-position.lisp
  2. (in-package #:gamao-impl)
  3. #|
  4. CHECKER-POSITION
  5. A persistent data structure representing the checker position. API:
  6. type checker-position
  7. make-checker-position => checker-position
  8. update-checker-position checker-position location player checkers => checker-position
  9. checker-position-checkers-at checker-position location player => checkers, owner
  10. |#
  11. (defstruct (checker-position (:constructor %make-checker-position (&optional (n 0)))
  12. (:conc-name %checker-position-))
  13. "A persistent representation of a checker position. The points are identified by numbers 1, 2, ..., 24; :BAR represents the bar and :OFF represents borne off checkers.
  14. Creation: MAKE-CHECKER-POSITION
  15. Updating: UPDATE-CHECKER-POSITION
  16. Querying: CHECKER-POSITION-CHECKERS-AT
  17. Rerooting for efficiency: COMMIT-CHECKER-POSITION
  18. "
  19. n)
  20. ;; TODO specify initial position (?)
  21. (defun make-checker-position ()
  22. "Return a fresh CHECKER-POSITION having zero checkers everywhere."
  23. (%make-checker-position 0))
  24. (defun %checker-position-point-checker-byte (point)
  25. (byte 4 (1+ (* 5 (1- point)))))
  26. (defun %checker-position-сolour-checker-byte (point)
  27. (* 5 (1- point)))
  28. (defun %checker-position-encode-location (location player)
  29. (case location
  30. (:off (case player
  31. (:black (byte 4 128))
  32. (:white (byte 4 132))))
  33. (:bar (case player
  34. (:black (byte 4 120))
  35. (:white (byte 4 124))))
  36. (otherwise (%checker-position-point-checker-byte location))))
  37. (defun %checker-position-decode-player-on-point (point checker-position)
  38. (if (logbitp (%checker-position-сolour-checker-byte point) (%checker-position-n checker-position))
  39. :black
  40. :white))
  41. (defun checker-position-checkers-at (checker-position location player)
  42. "Return the number of checkers at LOCATION of CHECKER-POSITION and their owner.
  43. LOCATION: :BAR, :OFF, or a point represented by an integer between 1 and 24
  44. PLAYER: :BLACK, :WHITE or NIL
  45. If LOCATION is :BAR or :OFF, PLAYER must be non-null.
  46. If LOCATION is a point, and PLAYER is not null, return the number of checkers of specified player on the point.
  47. If LOCATION is a point, and PLAYER is null, return the number of checkers of any player on the point.
  48. If PLAYER is not null, it is returned as the second value. Otherwise, it is the player occupying the point or NIL if no one occupies it."
  49. (let ((decoded-checkers (ldb (%checker-position-encode-location location player) (%checker-position-n checker-position))))
  50. (cond ((zerop decoded-checkers) (values 0 player))
  51. ((member location '(:off :bar)) (values decoded-checkers player))
  52. (t (if (or (null player) (eq player (%checker-position-decode-player-on-point location checker-position)))
  53. (values decoded-checkers (%checker-position-decode-player-on-point location checker-position))
  54. (values 0 player))))))
  55. (defun update-checker-position (checker-position location player checkers)
  56. "Return a new checker position differing from CHECKER-POSITION in that PLAYER has CHECKERS checkers on LOCATION."
  57. (let ((encoded-checkers-value (dpb checkers (%checker-position-encode-location location player) (%checker-position-n checker-position))))
  58. (%make-checker-position (case location
  59. ((or :bar :off) encoded-checkers-value)
  60. (otherwise (ecase player
  61. (:black (dpb 1 (byte 1 (%checker-position-сolour-checker-byte location)) encoded-checkers-value))
  62. (:white (dpb 0 (byte 1 (%checker-position-сolour-checker-byte location)) encoded-checkers-value))))))))
  63. ;; Does not perform any checks! The caller must ensure that it's OK to actually move the checker.
  64. (defun checker-position-move-checker (checker-position player from to)
  65. (let* ((checkers-at-start (checker-position-checkers-at checker-position from player))
  66. (checkers-at-finish (checker-position-checkers-at checker-position to player))
  67. (checker-removed (update-checker-position checker-position from player (1- checkers-at-start)))
  68. (checker-put (update-checker-position checker-removed to player (1+ checkers-at-finish))))
  69. checker-put))