Gauche で経路探索A*(エースター)

経路探索アルゴリズムA*(エースター)を参考にGauche で書いてみました。

(use srfi-1)  ;; List library
(use srfi-43) ;; ベクタライブラリ 
(use gauche.interactive)
(use slib)
(require 'trace)

(define maze-string
  (string-append "**************************\n" 
                 "*S* *                    *\n" 
                 "* * *  *  *************  *\n" 
                 "* *   *    ************  *\n" 
                 "*    *                   *\n" 
                 "************** ***********\n" 
                 "*                        *\n" 
                 "** ***********************\n" 
                 "*      *              G  *\n" 
                 "*  *      *********** *  *\n" 
                 "*    *        ******* *  *\n" 
                 "*       *                *\n" 
                 "**************************"))
 
(define map-vect 
  (list->vector 
    (map list->vector 
      (map string->list (string-split maze-string "\n")))))

;; =====================================
;; ノードクラス
;; =====================================
(define-class <node> () 
  ((c      :init-value #\null :init-keyword :c      :accessor char)
   (x      :init-value 0      :init-keyword :x      :accessor x-of)
   (y      :init-value 0      :init-keyword :y      :accessor y-of)
   (f-star :init-value 0      :init-keyword :f-star :accessor f-star)
   (parent :init-value #f     :init-keyword :parent :accessor parent)))

  (define-method wall?  ((nd <node>))       (eq?  (char nd) #\*))
  (define-method gall?  ((nd <node>))       (eq?  (char nd) #\G))
  (define-method start? ((nd <node>))       (eq?  (char nd) #\S))
  (define-method x-set! ((nd <node>) pos)       (set! (x-of nd) pos))
  (define-method fstar-set!  ((nd <node>) value) (set! (f-star nd) value))
  (define-method parent-set! ((nd <node>) p)     (set! (parent nd) p))
  (define-method char-set!   ((nd <node>) c)     (set! (char nd)   c))

  (define-method cost   ((nd <node>) destination)         ; 縦の距離と横の距離を加算
    (+ (abs (- (x-of nd)(x-of destination)))
       (abs (- (y-of nd)(y-of destination)))))
  (define-method g-star ((nd <node>))                    ; スタート -> n  推定値 f*(n)
    (- (f-star *start*) (h-star nd)))
  (define-method h-star ((nd <node>)) (cost nd *gall*))   ; n -> ゴールまでの 推定値 h*(n)
  (define-method schar  ((nd <node>)) (string-append
                                        " " (string (char nd))))
  (define-method string-fstar ((nd <node>)) 
                              (if (wall? nd) " ." 
                                             (format #f "~2d" (f-star nd))))

  (define-method show   ((nd <node>))
    (format #f "[~s x:~d y:~d f:~d p:~s]"
      (string (char nd)) (x-of nd) (y-of nd)(f-star nd)
      (let ((p (parent nd)))
        (if (eq? #f p)
          "<>"
          (format #f "<x:~d y:~d>" (x-of p) (y-of p))))))

  ;; f_dash = g_star(m) + h_star(m) + cost(n,m)
  (define-method f-dash   ((nd <node>) destination)
    (+ (g-star destination) (h-star destination) (cost nd destination)))
;; -->ノードクラス

(define (show-node ls) (string-join (map show ls)))

(define (make-node char x y f-star parent)
        (make <node> :c char :x x :y y :f-star f-star :parent parent))

;; 文字の入った2次元ベクタから、 node オブジェクトのベクタマップを作って返す。
(define (node-vect map-v)
  (vector-map 
    (lambda(y vect) ;; y 座標 行 
      (vector-map 
        (lambda(x char)(make-node char x y 0 #f))
        vect))  
    map-v))

;; マップベクタ
(define *vmap*         (node-vect map-vect))
(define (get-node x y) (vector-ref (vector-ref *vmap* y) x))
(define *mp-height*    (vector-length *vmap*))
(define *mp-width*     (vector-length (vector-ref *vmap* 0)))

;; スタート、ゴール位置
(define *start* #f)
(define *gall*  #f)
(vector-for-each 
  (lambda(y vect) 
    (vector-for-each
      (lambda(x node) 
        (if (start? node)(set! *start* node)) 
        (if (gall? node) (set! *gall*  node))) 
        vect))
  *vmap*)

(define openlist  '()) ; 計算中のノード
(define closelist '()) ; 計算済みのノード

;; 枠外・壁でなければノードのリストを返す。枠外・壁は'()
(define (adjacent chk-pred x y)
  (let* ((node (get-node x y)))
    (if (and (chk-pred x y) (not (wall? node)))
      (cons node '())
       '())))

;; 縦横に隣接しているノードのリストを返す
(define (adjacent_nodes x y)
  (let* ((out1 (adjacent (lambda(x y)(>= y 0))           x (- y 1)))  ; 上
         (out2 (adjacent (lambda(x y)(<  y *mp-height*)) x (+ y 1) )) ; 下
         (out3 (adjacent (lambda(x y)(>= x 0))           (- x 1) y))  ; 左
         (out4 (adjacent (lambda(x y)(< x *mp-width*))   (+ x 1) y))  ; 右
         (out  (append out1 out2 out3 out4))) 
       ;;(print
       ;;   (string-append
       ;;     "x:" (number->string x)" y:" (number->string y) (show-node out)))
    out))

;; ノードにprocを摘要したマップを表示
(define (show-map proc)
  (let ((lmap (map vector->list (vector->list *vmap*))))
    (map 
      (lambda(ls)
        (print (string-join 
                 (map (lambda(x)(proc x)) ls))))
       lmap)))

;; 経路更新 ・f-star を f-dashに置き換え、親を更新
(define (fstar-parent-set! fdash node next-node)
  (begin 
    (fstar-set!  next-node fdash)
    (parent-set! next-node node)))

(define (update-if node fdash next-node)
  (if (< fdash (f-star next-node))
    (fstar-parent-set! fdash node next-node)))

;; リストに追加
(define (push-elem ls elem) (append ls (cons elem '())))

;; n に隣接している全てのノード(ここでは隣接ノードを m とおく)に対して以下の操作を行う
(define (exec-next-node node adjoining-ls openlist closelist)
  (begin
      ;;(print "node :" (show node) "\n"
      ;;       "next :" (string-join (map show adjoining-ls)) "\n"
      ;;       "open :" (string-join (map show openlist)) "\n"
      ;;       "close:" (string-join (map show closelist)))
       ;;
      ;;(print "-hit enter key-")(let ((char (read-char))) #f)
      ;;(sys-nanosleep 300000000);wait for 0.3 sec

  (if (> (length adjoining-ls) 0)  ; 隣接ノード
    (let* ((next-node         (car adjoining-ls))
           (cdr-adjoining     (cdr adjoining-ls))
           (fdash             (f-dash node next-node)))
      (cond
        ((find (lambda(x)(equal? x next-node)) openlist)
          (begin
            (update-if node fdash next-node) ;f-star、親を更新
            (exec-next-node node cdr-adjoining openlist closelist)))
        ((find (lambda(x)(equal? x next-node)) closelist)
          (begin
            (if (< fdash (f-star next-node))
              (begin
                (fstar-parent-set! fdash node next-node)
                (exec-next-node node cdr-adjoining
                  (push-elem  openlist next-node)
                  (remove (lambda(x)(eq? x next-node)) closelist)))
              (exec-next-node node cdr-adjoining openlist closelist))))
        (else 
          (begin
            (fstar-parent-set! fdash node next-node)
            (exec-next-node node cdr-adjoining
              (push-elem  openlist next-node) closelist)))))
    (cons openlist (cons closelist '())))))

;; 経路にマーク
(define (write_route node)
  (if (start? node) 
    #f
    (begin
      ;; (print (show node))
      (unless (gall? node )(char-set! node #\$))
      (write_route (parent node)))))

;; 経路探索
(define (search_route openlist closelist)
  (call/cc
    (lambda (return)
      (if (> (length openlist) 0)
        (let* ((sorted-openlist (sort openlist
                                  (lambda (x y) (< (f-star x) (f-star y)))))
               (min-f-star      (car sorted-openlist)))       ; Openリスト中 f-star 最小
          (let ((ret (exec-next-node
                       min-f-star 
                       (adjacent_nodes (x-of min-f-star)(y-of min-f-star)) ; 隣接ノード
                       (cdr sorted-openlist)
                       (if (gall? min-f-star)
                         (begin 
                           ;; (show-map string-fstar)
                           (write_route *gall*) ;; 親をたどってマーク
                           (show-map schar)
                           (return "gall"))
                         (push-elem  closelist min-f-star)))))
            (search_route (car ret) (car(cdr ret)))) )
        (return "gall")))))

;; スタート S の最短経路コスト推定値 f*(S)
;; g*(S) = 0 (スタートから n)
;; f*(S) = g*(S) + h*(S) ---> f*(S) = h*(S)
(fstar-set! *start* (h-star *start*))

;;(trace search_route)
(search_route
  (append openlist (list *start*))
  closelist)