; your code goes here
(defparameter *width* 12)
(defparameter *height* 8)
(defparameter *elements* '(:none :wall :start :goal :passed))
(defparameter *around* '((1 0) (-1 0) (0 1) (0 -1)))
(defun gen-field (data)
(labels ((index (x y)
(+ x (* y *width*))))
(let ((field (make-array (list *width* *height*))))
(dotimes (y *height*)
(dotimes (x *width*)
(let ((elt (aref data (index x y))))
(setf (aref field x y)
(cond ((eq elt 'S) :start)
((eq elt 'G) :goal)
((= elt 0) :none)
((= elt 1) :wall))))))
field)))
(defun init-field ()
(gen-field #(0 0 0 1 0 0 1 0 0 0 0 0
0 0 0 1 0 0 1 0 G 0 0 0
0 0 0 1 0 0 1 0 0 0 0 0
0 S 0 0 0 0 1 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0 0 0
0 0 0 1 0 0 1 1 1 1 0 1
0 0 0 1 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0)))
(defun find-element (field elt)
(dotimes (y *height*)
(dotimes (x *width*)
(when (eq (aref field x y) elt)
(return-from find-element (list x y))))))
(defun find-start (field)
(find-element field :start))
(defun find-goal (field)
(find-element field :goal))
(defun draw-field (field)
(dotimes (y *height*)
(dotimes (x *width*)
(princ (case (aref field x y)
(:none "_ ")
(:wall "# ")
(:start "S ")
(:goal "G ")
(:passed "@ ")
)))
(terpri)))
(defun range-p (x y)
(and (<= 0 x (1- *width*))
(<= 0 y (1- *height*))))
(defun distance (start goal)
(round
(sqrt
(+ (expt (- (first goal) (first start)) 2)
(expt (- (second goal) (second start)) 2)))))
(defun collect-around (fn pos)
(let (around)
(dolist (a *around*)
(let ((x (+ (first pos) (first a)))
(y (+ (second pos) (second a))))
(when (range-p x y)
(let ((it (funcall fn x y)))
(when it
(push it around))))))
around))
(defparameter *attrs* '(:none :open :close))
(defstruct a*
attr
cost
pos
parent)
(defparameter *astar-field* (make-hash-table :test #'equal))
(defun add (attr cost pos parent)
(multiple-value-bind (val win)
(gethash pos *astar-field*)
(if win
val
(setf (gethash pos *astar-field*)
(make-a* :attr attr :cost cost :pos pos :parent parent)))))
(defun make-around (field goal pos cost)
(sort (collect-around
(lambda (x y)
(unless (eq (aref field x y) :wall)
(let* ((hcost (distance (list x y) goal))
(elt (add :open (+ cost hcost) (list x y) pos)))
(unless (eq (a*-attr elt) :close)
elt))))
pos)
(lambda (a b)
(< (a*-cost a) (a*-cost b)))))
(defun a*-recur (field goal pos cost)
(if (equal pos goal)
(throw 'found t)
(let ((around (make-around field goal pos (1+ cost))))
(setf (a*-attr (gethash pos *astar-field*)) :close)
(dolist (elt around)
(a*-recur field goal (a*-pos elt) (a*-cost elt))))))
(defun a*-backtrace (field start goal)
(labels ((rec (a*)
(let ((pos (a*-pos a*)))
(unless (equal pos start)
(destructuring-bind (x y) pos
(setf (aref field x y) :passed)
(rec (gethash (a*-parent a*) *astar-field*)))))))
(rec (gethash goal *astar-field*))
field))
(defun a*-start (field start goal)
(setf *astar-field* (make-hash-table :test #'equal))
(add :close 0 start nil)
(catch 'found
(a*-recur field goal start 0))
(a*-backtrace field start goal))
(defun test ()
(let* ((field (init-field))
(start (find-start field))
(goal (find-goal field)))
(draw-field (a*-start field start goal))))
(test)