迷路

queueのはずがqueue-pop!、queue-push!を使っていて失敗。
いつの間にかdfsになっていた。
確かに問題があらかじめ解っていたら30分くらいで書けるのかもしれない。
「使い慣れた言語があること」を評価基準にしているのかもしれない。

おもしろいと思ったこと

  • 異同順序をhash-tableから取ってくるときに、unfoldが使える。
  • listに変更を加える際には、"(ref (car (list-tail )) )"
  • 範囲外に出てしまう座標を見つけるのにand-let*はかなり便利

code

(use util.queue)
(use util.match)
(use srfi-1)

(define (read-maze file)
  (call-with-input-file file
    (lambda (in) (map string->list (port->string-list in)))))

(define (find-start&goal maze)
  (define (search elt)
    (let/cc return
      (fold (lambda (line y)
	      (cond ((list-index (pa$ char-ci=? elt) line)
		     => (lambda (x) (return (list y x))))
		    (else (+ y 1))))
	    0 maze)))
  (values (search #\S) (search #\G)))

(define (next-move-point maze p)
  (match-let1 (y x) p
    (let1 seq
	(list (list y (+ x 1))
	      (list (- y 1) x)
	      (list (+ y 1) x)
	      (list y (- x 1)))
      (filter (lambda (pt) 
		(match-let1 (y x) pt
		  (and-let* ((line (list-ref maze y #f))
			     (c (list-ref line x #f)))
		    (not (char-ci=? #\* c)))))
	      seq))))

(define (bfs maze start goal)
  (let ((hist (make-hash-table 'equal?))
	(queue (make-queue)))
    (define (next p)
      (for-each (lambda (nextp)
		  (unless (hash-table-exists? hist nextp)
		    (enqueue! queue nextp)
		    (hash-table-put! hist nextp p)))
		(next-move-point maze p))
	(dequeue! queue))
    (define (end-action p)
      (unfold (cut equal? <> start)
	      identity
	      (cut hash-table-get hist <>)
	      (hash-table-get hist p)))
    (let loop ((p (next start)))
      (if (equal? p goal) (end-action p) (loop (next p))))))

(define (maze-set! maze p c)
  (match-let1 (y x) p
    (set! (car (list-tail (ref maze y) x)) c)))

(define (maze-show maze)
  (for-each (compose print (cut apply string <>))
		maze))

(define (main args)
  (let* ((file (cadr args))
	 (maze (read-maze file)))
    (receive (start goal) (find-start&goal maze)
      (rlet1 history (bfs maze start goal)
	(for-each (cut maze-set! maze <> #\$) history)
	(maze-show maze))))
  0)