ペントミノ パズル(明治ミルクチョコパズル)をGaucheで解く(1時間で解けるようになった)

Karetta|Cパズルプログラミング-再帰編|ペントミノを参考にアルゴリズムを改良した。

  • ピースXを左上1/4に制限
  • 近傍を予備的にチェック
  • 使用済みピースを調べない(これは前回もそうだった)

ちゃんと2339通りが出力される。1時間(PenR E2140 Mem 2GB)で。
この限界を越えるには、solveの再帰を末尾再帰にするしかあるまい。
どう書く.orgに投稿しようかな。

(use srfi-1)
(use srfi-43)

(define count 0)
(define board (make-vector 60 #f))

(define (check-board-multiple5-space?)
  (define (fill start)
    (define l (list start))
    (let propagete! ((x start))
      (let ((m (remove! (lambda (p)
			  (or (not p) (vector-ref board p) (memq p l)))
			(receive (row col) (quotient&remainder x 10)
			  (list (and (> row 0) (- x 10))
				(and (< row 5) (+ x 10))
				(and (> col 0) (- x 1))
				(and (< col 9) (+ x 1)))))))
	(unless (zero? (length m))
	  (set! l (append! m l))
	  (for-each propagete! m))))
    l)
  (let ((i (vector-index not board)))
    (or (not i)
	(zero? (remainder (length (fill i)) 5)))))

(define (plot mino index v)
  (for-each (lambda (xy)
	      (vector-set! board (+ index (* (car xy) 10) (cdr xy)) v))
	    mino))

(define (solve remain-kind)
  (let1 i (vector-index not board)
    (if i
      (receive (row col) (quotient&remainder i 10)
	(for-each (lambda (kind)
		    (for-each (lambda (mino)
				(when (every (lambda (xy)
					       (let ((r (+ (car xy) row))
						     (c (+ (cdr xy) col)))
						 (and (>= r 0) (< r 6) (>= c 0) (< c 10)
						      (not (vector-ref board (+ (* r 10) c))))))
					     mino)
				  (plot mino i kind)
				  (when (check-board-multiple5-space?)
				    (solve (delete kind remain-kind)))
				  (plot mino i #f)))
			      (vector-ref pentominos kind)))
		  remain-kind))
      ;; found answer
      (print-board))))

(define (main args)
  (for-each (lambda (i)
	      (plot (car (vector-ref pentominos 0)) i 0)
	      (solve (iota 11 1))
	      (plot (car (vector-ref pentominos 0)) i #f))
	    '(2 3 4 11 12 13 14)))

(define (print-board)
  (print "Answer No." (inc! count))
  (vector-for-each (lambda (i x)
		     (if x
		       (format #t "~X" x)
		       (display '*))
		     (when (= 9 (remainder i 10)) (newline)))
		   board)
  (newline))

(define (to-data mino)
  (let ((offset (string-scan (car mino) "#")))
    (append-map (lambda (line row)
		  (filter-map (lambda (x col)
				(if (char=? x #\space)
				  #f
				  (cons row (- col offset))))
			      (string->list line) (iota 5)))
		mino (iota 5))))

(define pentominos
  (list->vector
   (map (cut map to-data <>)
	'(((" #"
	    "###"
	    " #"))
	  
	  ((" #"
	    "###"
	    "  #")
	   
	   (" ##"
	    "##"
	    " #")
	   
	   ("#"
	    "###"
	    " #")
	   
	   (" #"
	    " ##"
	    "##")
	   
	   (" #"
	    "###"
	    "#")
	   
	   (" #"
	    "##"
	    " ##")
	   
	   ("  #"
	    "###"
	    " #")
	   
	   ("##"
	    " ##"
	    " #"))
	  
	  (("####"
	    "#")
	   
	   ("#"
	    "#"
	    "#"
	    "##")
	   
	   ("   #"
	    "####")
	   
	   ("##"
	    " #"
	    " #"
	    " #")
	   
	   ("####"
	    "   #")
	   
	   ("##"
	    "#"
	    "#"
	    "#")
	   
	   ("#"
	    "####")
	   
	   (" #"
	    " #"
	    " #"
	    "##"))
	  
	  ((" #"
	    "##"
	    "#"
	    "#")
	   
	   ("##"
	    " ###")
	   
	   (" #"
	    " #"
	    "##"
	    "#")

	   ("###"
	    "  ##")
	   
	   ("#"
	    "##"
	    " #"
	    " #")
	   
	   (" ###"
	    "##")

	   ("#"
	    "#"
	    "##"
	    " #")
	   
	   ("  ##"
	    "###"))
	  
	  (("##"
	    "##"
	    "#")
	   
	   ("##"
	    "###")
	   
	   (" #"
	    "##"
	    "##")
	   
	   ("###"
	    " ##")
	   
	   ("##"
	    "##"
	    " #")
	   
	   ("###"
	    "##")

	   ("#"
	    "##"
	    "##")

	   (" ##"
	    "###"))

	  ((" #"
	    "##"
	    " #"
	    " #")
	   
	   ("####"
	    " #")
	   
	   ("  #"
	    "####")
	   
	   ("#"
	    "##"
	    "#"
	    "#")

	   (" #"
	    "####")

	   ("#"
	    "#"
	    "##"
	    "#")

	   (" #"
	    " #"
	    "##"
	    " #")
	   
	   ("####"
	    "  #"))
	  
	  (("##"
	    " #"
	    " ##")
	   
	   ("  #"
	    "###"
	    "#")
	   
	   ("#"
	    "###"
	    "  #")
	   
	   (" ##"
	    " #"
	    "##"))
	  
	  (("#"
	    "###"
	    "#")
	   
	   (" #"
	    " #"
	    "###")
	   
	   ("  #"
	    "###"
	    "  #")
	   
	   ("###"
	    " #"
	    " #"))
	  
	  (("# #"
	    "###")
	   
	   ("##"
	    " #"
	    "##")
	   
	   ("###"
	    "# #")
	   
	   ("##"
	    "#"
	    "##"))
	  
	  (("#"
	    "#"
	    "###")
	   
	   ("  #"
	    "  #"
	    "###")
	   
	   ("###"
	    "  #"
	    "  #")
	   
	   ("###"
	    "#"
	    "#"))
	  
	  (("#"
	    "##"
	    " ##")
	   
	   ("  #"
	    " ##"
	    "##")
	   
	   ("##"
	    " ##"
	    "  #")
	   
	   (" ##"
	    "##"
	    "#"))
	  
	  (("#"
	    "#"
	    "#"
	    "#"
	    "#")
	   
	   ("#####"))))))