First, we define the Goban: a board is a square matrix of points. A point is an location on the Goban. A point knows the points that it is adjacent to:
(defclass Point () ((r :documentation "row" :accessor r :initarg r) (c :documentation "column" :accessor c :initarg c) (index :documentation "location on the board" :accessor index :initarg index) (adjacent :documentation "the 2, 3, or 4 points that I touch." :accessor adjacent )))
(defclass Board () ((size :documentation "size of board: 9, 13, or 19, usually" :accessor Size :initarg Size) (points :documentation "a 1-d array of the points on this board" :accessor Points :initarg Points) (all-points :documentation "a list of all points on this board" :accessor All-Points :initarg All-Points)))
Creating a new Goban is easy: we make the points array, populate it in row-column order, make the points list, then set the adjacency information for each point:
(defun make-Board (sz) (let ((b (make-instance 'Board 'Size sz 'Points (make-array (* sz sz))))) (loop for r from 0 to (1- sz) do (loop for c from 0 to (1- sz) do (let ((index (+ (* r sz) c))) (setf (aref (Points b) index) (make-instance 'Point 'r r 'c c 'index index))))) (setf (all-points b) (coerce (Points b) 'list)) (loop for p across (Points b) do (setf (adjacent p) (adjacency-list b p))) b))
Finally, we write the stuff that computes the adjacency info for points. I like the valid and b-at methods, but adjacency-list seems a bit stilted...
(defmethod valid ((b Board) r c) (let ((Size (Size b))) (and (>= r 0) (>= c 0) (< r Size) (< c Size))))
(defmethod b-at ((b Board) r c) (aref (Points b) (+ (* r (Size b)) c)))
(defmethod adjacency-list ((b Board) (p Point)) (let ((r (r p)) (c (c p))) (mapcan (lambda (row col) (if (valid b row col) (list (b-at b row col)))) (list (1+ r) (1- r) r r ) (list c c (1+ c) (1- c)))))