board.lisp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. (in-package #:board)
  2. #|
  3. This is a low-level implementation of the backgammon board. It supports basic operations but knows nothing of the rules of the game.
  4. The board is not intended to be destructed.
  5. The board has 24 points, the bar, and the bear-off zone. We call them places.
  6. Implementation
  7. The board is implemented as an alist. The car of each entry is either one of
  8. the integers 1, ..., 24 used to identify a point, or one of the symbols BAR,
  9. OFF. The cdr depends on the type of the place. If no association corresponds
  10. to a place, it is assumed to have no checkers.
  11. The integer ID of a point is its number from white's viewpoint.
  12. |#
  13. (deftype point-id () '(integer 1 24))
  14. (deftype place () '(or point-id (member bar off)))
  15. (defun pointp (place)
  16. "Given a place ID, return true if it is a point."
  17. (check-type place place)
  18. (not (member place '(bar off))))
  19. (defun point-id (point-number player)
  20. "Return the point ID of PLAYER's POINT."
  21. (check-type point-number game:point)
  22. (check-type player game:player)
  23. (ecase player
  24. (:white point-number)
  25. (:black (- 25 point-number))))
  26. (defun place (place-name player)
  27. "Return the place ID of PLAYER's place, where the place is a point number, BAR, or OFF."
  28. (case place-name
  29. ((bar off) place-name)
  30. (otherwise (point-id place-name player ))))
  31. (defun point-number (point-id player)
  32. "Return PLAYER's point number corresponding to the ID POINT-ID."
  33. (check-type player game:player)
  34. (check-type point-id point-id)
  35. (ecase player
  36. (:white point-id)
  37. (:black (- 25 point-id))))
  38. (deftype board () t)
  39. (deftype checker-count () '(integer 0 15))
  40. (defconstant bar 'bar)
  41. (defconstant off 'off)
  42. #|
  43. There are three kinds of associations in the alist representing a board: a
  44. point, where a point ID is followed by the number of checkers on it and their
  45. colour; the bar, where the symbol BAR is followed by the number of white &
  46. black checkers; and the bear-off zone, where the symbol OFF is followed by the
  47. number of white & black checkers. We define these kinds of associations as
  48. list-based structures.
  49. |#
  50. (defstruct (board-point (:type list))
  51. "The list-based structure representing a point state: the point ID, the number of checkers on it, and if any, the owner of the checkers."
  52. (id 0 :type point-id)
  53. (checkers 0 :type checker-count)
  54. (whose nil :type (or null game:player)))
  55. (defstruct (bar (:type list) :named)
  56. "The list-based structure representing the number of white & black checkers on the bar."
  57. (white 0 :type checker-count)
  58. (black 0 :type checker-count))
  59. (defstruct (off (:type list) :named)
  60. "The list-based structure representing the number of white & black checkers in the bear-off zone"
  61. (white 0 :type checker-count)
  62. (black 0 :type checker-count))
  63. #|
  64. The following functions retrieve information about the board.
  65. |#
  66. (defun checkers-on-point (point-id board)
  67. "Given a point, return the number of checkers on it and their colour."
  68. (check-type point-id point-id)
  69. (check-type board board)
  70. (let ((p (assoc point-id board)))
  71. (if (null p)
  72. (values 0 nil)
  73. (values (board-point-checkers p)
  74. (board-point-whose p)))))
  75. (defun checkers-on-bar (player board)
  76. "Return the number of PLAYER's checkers on the bar."
  77. (check-type player game:player)
  78. (check-type board board)
  79. (let ((b (assoc 'bar board)))
  80. (if (null b)
  81. 0
  82. (ecase player
  83. (:white (bar-white b))
  84. (:black (bar-black b))))))
  85. (defun checkers-off (player board)
  86. "Return the number of checkers PLAYER has borne off."
  87. (check-type player game:player)
  88. (check-type board board)
  89. (let ((borne-off (assoc 'off board)))
  90. (if (null borne-off)
  91. 0
  92. (ecase player
  93. (:white (off-white borne-off))
  94. (:black (off-black borne-off))))))
  95. (defun set-checkers-on-point (point-id n player board)
  96. "Return the board differing from BOARD in that the POINT-ID point has N PLAYER's checkers."
  97. (check-type n checker-count)
  98. (check-type player game:player)
  99. (check-type point-id point-id)
  100. (check-type board board)
  101. (cons (make-board-point :id point-id
  102. :checkers n
  103. :whose (if (zerop n) nil player))
  104. board))
  105. (defun set-checkers-on-bar (n player board)
  106. "Return the board differing from BOARD in that PLAYER has N checkers on the bar."
  107. (check-type n checker-count)
  108. (check-type player game:player)
  109. (check-type board board)
  110. (let* ((white (checkers-on-bar :white board))
  111. (black (checkers-on-bar :black board)))
  112. (cons (make-bar :white (ecase player
  113. (:white n)
  114. (:black white))
  115. :black (ecase player
  116. (:white black)
  117. (:black n)))
  118. board)))
  119. (defun set-checkers-off (n player board)
  120. "Return the board differing from BOARD in that PLAYER has borne off N checkers."
  121. (check-type n checker-count)
  122. (check-type player game:player)
  123. (check-type board board)
  124. (let* ((white (checkers-off :white board))
  125. (black (checkers-off :black board)))
  126. (cons (make-off :white (ecase player
  127. (:white n)
  128. (:black white))
  129. :black (ecase player
  130. (:white black)
  131. (:black n)))
  132. board)))
  133. (defun has-checkers-on-p (place player board)
  134. (check-type place place)
  135. (check-type player game:player)
  136. (check-type board board)
  137. (case place
  138. (bar (plusp (checkers-on-bar player board)))
  139. (off (plusp (checkers-off player board)))
  140. (t (multiple-value-bind (checkers whose) (checkers-on-point place board)
  141. (and (eql whose player)
  142. (plusp checkers))))))
  143. (defun highest (player board)
  144. (loop for p from 24 downto 1
  145. when (has-checkers-on-p (point-id p player) player board)
  146. do (return p)
  147. finally (return 0)))
  148. (defun loss (player board)
  149. (cond ((has-checkers-on-p off player board) 1)
  150. ((or (has-checkers-on-p bar player board)
  151. (>= (highest player board) 19))
  152. 3)
  153. (t 2)))
  154. (defun move-checker (from to player board)
  155. "Returns a new bord with the checker of the player moved from FROM to TO. If there are opponent's checkers on TO, they go to the bar. The validity of the move is NOT checked."
  156. (let* ((remove-checker (case from
  157. (bar (set-checkers-on-bar (1- (checkers-on-bar player board))
  158. player
  159. board))
  160. (otherwise (set-checkers-on-point from
  161. (1- (checkers-on-point from board))
  162. player
  163. board))))
  164. (put-checker (case to
  165. (off (set-checkers-off (1+ (checkers-off player board))
  166. player
  167. remove-checker))
  168. (otherwise (multiple-value-bind (checkers-on-destination whose) (checkers-on-point to board)
  169. (if (or (eql whose player) (null whose))
  170. (set-checkers-on-point to
  171. (1+ checkers-on-destination)
  172. player
  173. remove-checker)
  174. (let ((opponent (game:opponent player)))
  175. (set-checkers-on-point to
  176. 1
  177. player
  178. (set-checkers-on-bar (+ (checkers-on-bar opponent board)
  179. checkers-on-destination)
  180. opponent
  181. remove-checker)))))))))
  182. put-checker))
  183. (defun make-empty-board ()
  184. '())
  185. (defparameter *initial-arrangement* '((24 2)
  186. (13 5)
  187. (8 3)
  188. (6 5)))
  189. (defun make-initial-board ()
  190. (list* (make-bar)
  191. (make-off)
  192. (loop for point from 1 to 24
  193. collect (loop for player in '(:white :black)
  194. for n = (second (assoc (point-number point player) *initial-arrangement*))
  195. when n do (return (make-board-point :id point :checkers n :whose player))
  196. finally (return (make-board-point :id point :checkers 0))))))