如何操作由列表构成的复杂数据结构?
How to manipulate complex data structures made of lists?
我正在尝试展示 3D Connect 4 棋盘游戏:
例如,我有以下列表结构列表:
(
(
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
)
(1 1 1 1 1 1 1 1 1 1 1 1 10 10 10 10 10 10 10 10 10 10 10 10)
)
每一个NIL代表其中的一个位置。例如,如果我将两块(一块黑色和一块白色)放在第一个位置,它看起来像这样:
(
(
((B W) NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
)
(1 1 1 1 1 1 1 1 1 1 1 1 10 10 10 10 10 10 10 10 10 10 10 10)
)
这意味着W将是底部的那个。我还需要将它们相互比较,以便程序在找到获胜者时说出。
如何在每个位置加棋子?我将如何比较它们,因为它们是具有 NIL 值的列表?
原则上,如果您有一个列表的列表,您可以使用嵌套的 nth
调用来到达您想要检查的位置。在这种特定情况下,nth
的线性时间性质可能并不可怕,但我可能会使用 4x4 列表数组或 4x4x4 数组,尽管在那种情况下你最终需要跟踪 "the next position" 自己,即使这会简化 "check for win condition" 逻辑。
以及如何使用 push
和 nth
改变列表列表的列表的示例(我编辑了 s 显示,使 "arrayness" 更容易看到):
* *s*
((NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL))
* (push 'w (nth 0 (nth 0 *s*)))
(W)
* *s*
(((W) NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL))
无论您选择使用何种实现,最好定义一个接口来操作您的对象。在下面,我定义了 make-board
、push-token
和 pop-token
函数。您还可以定义其他访问器函数,例如获取坐标 (x y z) 处的值。
然后,您只需通过此接口操作您的数据,这样您的代码就可读性强且易于维护。我正在使用向量的二维矩阵,其中内部向量由于它们的填充指针而用作堆栈(有关详细信息,请参见 MAKE-ARRAY
)。
板class和代币类型
(defclass board ()
((matrix :reader board-matrix :initarg :matrix)
(size :reader board-size :initarg :size)))
(deftype token-type () '(member white black))
构造器
(defun make-board (size)
(let ((board
(make-array (list size size))))
(dotimes (i (array-total-size board))
(setf (row-major-aref board i)
(make-array size
:element-type 'symbol
:fill-pointer 0)))
(make-instance 'board :matrix board :size size)))
自定义打印机
(defmethod print-object ((b board) stream)
(print-unreadable-object (b stream :type t)
(let ((matrix (board-matrix b))
(size (board-size b)))
(dotimes (row size)
(fresh-line)
(dotimes (col size)
(let* ((stack (aref matrix row col)))
(dotimes (z size)
(princ (case (aref stack z)
(white #\w)
(black #\b)
(t #\.))
stream)))
(princ #\space stream))))))
在 (x,y) 处推动
(defun push-token (board x y token)
(check-type token token-type)
(vector-push token (aref (board-matrix board) y x)))
从 (x,y) 弹出
(defun pop-token (board x y)
(ignore-errors
(let ((stack (aref (board-matrix board) y x)))
(prog1 (vector-pop stack)
;; we reset the previous top-most place to NIL because we
;; want to allow the access of any cell in the 3D
;; board. The fill-pointer is just here to track the
;; position of the highest token.
(setf (aref stack (fill-pointer stack)) nil)))))
测试
(let ((board (make-board 4)))
(flet ((@ (&rest args) (print board)))
(print board)
(@ (push-token board 1 2 'white))
(@ (push-token board 1 2 'black))
(@ (push-token board 1 2 'white))
(@ (push-token board 1 2 'black))
(@ (push-token board 1 2 'black))
(@ (push-token board 0 3 'white))
(@ (pop-token board 1 2))
(@ (pop-token board 1 2))))
输出
#<BOARD
.... .... .... ....
.... .... .... ....
.... .... .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... w... .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wb.. .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wbw. .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wbwb .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wbwb .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wbwb .... ....
w... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wbw. .... ....
w... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wb.. .... ....
w... .... .... .... >
我正在尝试展示 3D Connect 4 棋盘游戏:
例如,我有以下列表结构列表:
(
(
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
)
(1 1 1 1 1 1 1 1 1 1 1 1 10 10 10 10 10 10 10 10 10 10 10 10)
)
每一个NIL代表其中的一个位置。例如,如果我将两块(一块黑色和一块白色)放在第一个位置,它看起来像这样:
(
(
((B W) NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
)
(1 1 1 1 1 1 1 1 1 1 1 1 10 10 10 10 10 10 10 10 10 10 10 10)
)
这意味着W将是底部的那个。我还需要将它们相互比较,以便程序在找到获胜者时说出。
如何在每个位置加棋子?我将如何比较它们,因为它们是具有 NIL 值的列表?
原则上,如果您有一个列表的列表,您可以使用嵌套的 nth
调用来到达您想要检查的位置。在这种特定情况下,nth
的线性时间性质可能并不可怕,但我可能会使用 4x4 列表数组或 4x4x4 数组,尽管在那种情况下你最终需要跟踪 "the next position" 自己,即使这会简化 "check for win condition" 逻辑。
以及如何使用 push
和 nth
改变列表列表的列表的示例(我编辑了 s 显示,使 "arrayness" 更容易看到):
* *s*
((NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL))
* (push 'w (nth 0 (nth 0 *s*)))
(W)
* *s*
(((W) NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL)
(NIL NIL NIL NIL))
无论您选择使用何种实现,最好定义一个接口来操作您的对象。在下面,我定义了 make-board
、push-token
和 pop-token
函数。您还可以定义其他访问器函数,例如获取坐标 (x y z) 处的值。
然后,您只需通过此接口操作您的数据,这样您的代码就可读性强且易于维护。我正在使用向量的二维矩阵,其中内部向量由于它们的填充指针而用作堆栈(有关详细信息,请参见 MAKE-ARRAY
)。
板class和代币类型
(defclass board ()
((matrix :reader board-matrix :initarg :matrix)
(size :reader board-size :initarg :size)))
(deftype token-type () '(member white black))
构造器
(defun make-board (size)
(let ((board
(make-array (list size size))))
(dotimes (i (array-total-size board))
(setf (row-major-aref board i)
(make-array size
:element-type 'symbol
:fill-pointer 0)))
(make-instance 'board :matrix board :size size)))
自定义打印机
(defmethod print-object ((b board) stream)
(print-unreadable-object (b stream :type t)
(let ((matrix (board-matrix b))
(size (board-size b)))
(dotimes (row size)
(fresh-line)
(dotimes (col size)
(let* ((stack (aref matrix row col)))
(dotimes (z size)
(princ (case (aref stack z)
(white #\w)
(black #\b)
(t #\.))
stream)))
(princ #\space stream))))))
在 (x,y) 处推动
(defun push-token (board x y token)
(check-type token token-type)
(vector-push token (aref (board-matrix board) y x)))
从 (x,y) 弹出
(defun pop-token (board x y)
(ignore-errors
(let ((stack (aref (board-matrix board) y x)))
(prog1 (vector-pop stack)
;; we reset the previous top-most place to NIL because we
;; want to allow the access of any cell in the 3D
;; board. The fill-pointer is just here to track the
;; position of the highest token.
(setf (aref stack (fill-pointer stack)) nil)))))
测试
(let ((board (make-board 4)))
(flet ((@ (&rest args) (print board)))
(print board)
(@ (push-token board 1 2 'white))
(@ (push-token board 1 2 'black))
(@ (push-token board 1 2 'white))
(@ (push-token board 1 2 'black))
(@ (push-token board 1 2 'black))
(@ (push-token board 0 3 'white))
(@ (pop-token board 1 2))
(@ (pop-token board 1 2))))
输出
#<BOARD
.... .... .... ....
.... .... .... ....
.... .... .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... w... .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wb.. .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wbw. .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wbwb .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wbwb .... ....
.... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wbwb .... ....
w... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wbw. .... ....
w... .... .... .... >
#<BOARD
.... .... .... ....
.... .... .... ....
.... wb.. .... ....
w... .... .... .... >