On Tue, Jan 08, 2008 at 11:25:18AM -0800, Brad Beyenhof wrote:
On Jan 8, 2008 11:05 AM, Barry Gershenfeld <[EMAIL PROTECTED]> wrote:
real    0m37.930s
user    0m0.000s
sys     0m0.000s
[snip]
(Okay peanut gallery, this is where you post /your/ time...)

I didn't use any gcc options when compiling, and it's running on my
Ubuntu 7.10 server (a Linode VPS). I changed my "blank" character in
the source code to an underscore so I wouldn't have to quote the
arguments:

$ time ./a.out 2468135_7 12345678_ > sli.txt

real    0m4.711s
user    0m3.070s
sys     0m0.190s

Ok, my turn.  The code, in Common Lisp, is attached below.  Running this
interactively with SBCL on a 2.4Ghz Intel Core 2 DUO.

For the given example

   (time (solve '(2 4 6 8 1 3 5 nil 7)))
Evaluation took:
  0.628 seconds of real time
  0.600038 seconds of user run time
  0.028002 seconds of system run time
  [Run times include 0.188 seconds GC run time.]
  0 calls to %EVAL
  0 page faults and
  83,066,512 bytes consed.

I may have to look at the other solutions, since this thing is
significantly faster than either solution posted so far.  I didn't try any
optimization.

I spent probably 8 hours last night learning enough common lisp (I know
scheme), and then about 5 hours this evening writing this.

Interestingly, the full space search

  (time (solve '(1 2 3 4 5 6 8 7 nil)))

is 0.828 seconds.  (It doesn't print anything if there is no solution).
It runs quite a bit slower if you print out the boards during the run, or
even time the printing of the solution.

To be honest, I'm completely surprised by these results.  I give this
language and at least this implementation a lot of credit.

So, any ideas on what I should write next?  This was a perfect problem for
learning this language.

Dave

----------------------------------------------------------------------
;;; Brute-force solution to the 9-square (or 15-square or whatever)
;;; problem.

;; Dimensions of board to solve.
(defconstant width 3 "Width of board in squares")
(defconstant height 3 "Height of board in squares")
(defconstant full-size (* width height) "Total number of board squares")

(defun valid-x-p (x)
  (and (>= x 0)
       (< x width)))
(defun valid-y-p (y)
  (and (>= y 0)
       (< y height)))

;; A given board is represented as a simple vector with the squares
;; given as integers starting at 1.  The empty square has the value
;; NIL.

(defconstant solved-board
  (let ((nums (loop for i from 1 to full-size
                    collect (and (< i full-size) i))))
    (make-array full-size :initial-contents nums))
  "The final solved board")

(defun show-board (board)
  "Show the board in a readable format"
  (loop for y from 0 to (1- height)
        do (loop for x from 0 to (1- width)
                 for pos from (* width y)
                 do (format t " [EMAIL PROTECTED]" (or (elt board pos) "_")))
        do (format t "~%")))

(defun nless (a b)
  "Numeric sorting, except that 'nil' is larger than other numbers"
  (< (or a full-size) (or b full-size)))

;; Given a board as a list, convert to vector format, and make sure it
;; is valid.
(defun make-board (items)
  (let* ((board (make-array full-size :initial-contents items))
         (sboard (copy-seq board)))
    (unless (equalp (sort sboard #'nless) solved-board)
      (error "Board does not have proper numbers"))
    board))

;; Given a particular board, compute a unique bignum defining the
;; possible position.  Treats the empty space as value full-size, and
;; simply computes the number base (1+ full-size).
(defun compute-cookie (board)
  (loop for item across board
        for power = 1 then (* power 10)
        sum (* power (or item full-size))))

;; Pre-compute all possible moves from a given position.
(defun one-move (x y)
  "Compute the valid moves from the given X and Y"
  (loop for (dx dy) in '((-1 0) (1 0) (0 -1) (0 1))
        nconc (let ((nx (+ x dx))
                    (ny (+ y dy)))
                (and (valid-x-p nx) (valid-y-p ny)
                     (list (+ (* width ny) nx))))))
(defconstant all-moves-list
  (loop for y from 0 to (1- height)
        nconc (loop for x from 0 to (1- width)
                    for pos from (* width y)
                    collect (one-move x y)))
  "All valid moves, as a single array")
(defconstant all-moves (make-array full-size :initial-contents all-moves-list))

;; Given a particular board, exchange the specified two pieces,
;; non-destructively (copies the board first).
(defun exchange (board a b)
  (let ((board2 (copy-seq board)))
    (setf (elt board2 a) (elt board b))
    (setf (elt board2 b) (elt board a))
    board2))

;; Given a particular board, return a list of the boards that are
;; reachable from this board.
(defun find-reachable (board)
  (loop with my-pos = (position nil board)
        for pos in (elt all-moves my-pos)
        collect (exchange board my-pos pos)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; A simple FIFO.  New nodes are inserted at the tail, and removed
;;; from the head.  The HEAD is the head of the list, and the TAIL is
;;; the cons cell of the last member of the list.
(defstruct fifo
  (head nil)
  (tail nil))

(defun fifo-empty-p (f)
  (not (fifo-head f)))

(defun fifo-append (f item)
  "Append the given ITEM to the end of the fifo F"
  (let ((tmp (list item)))
    (if (fifo-tail f)
      (setf (rest (fifo-tail f)) tmp)
      (setf (fifo-head f) tmp))
    (setf (fifo-tail f) tmp))
  f)

(defun fifo-append-many (f items)
  "Append a list of ITEMS to the end of fifo F"
  (loop for item in items
        do (fifo-append f item))
  f)

;; TODO: Return multiple values for more information.
(defun fifo-take (f)
  "Remove an item from the fifo, returning NIL if empty."
  (if (fifo-head f)
    (let ((item (first (fifo-head f))))
      (setf (fifo-head f) (rest (fifo-head f)))
      (unless (fifo-head f)
        (setf (fifo-tail f) nil))
      item)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Wrap it all up, doing a breadth-first search for a solution.
;;; Takes INITIAL-ITEMS as a list of the initial board.
(defun solve (initial-items)
  (do* ((start (make-board initial-items))
        (todo (fifo-append (make-fifo) (list start (list start))))
        (seen (let ((hash (make-hash-table :test 'equalp)))
                (setf (gethash (compute-cookie start) hash) t)
                hash)))
    ((fifo-empty-p todo) nil)

    (let* ((entry (fifo-take todo))
           (cur (first entry))
           (seq (second entry)))
      ;(format t "Trying: ~A~%" seq)
      ;(show-board cur)
      (when (equalp cur solved-board)
        ;(format t "Found solution!!!: ~A~%" seq)
        (return-from solve (reverse seq)))
      (loop for move in (find-reachable cur)
            do (unless (gethash (compute-cookie move) seen)
                 (setf (gethash (compute-cookie move) seen) t)
                 (fifo-append todo (list move (cons move seq))))))))

(defun show-solve (initial-items)
  (let*
        ((solution (time (solve initial-items))))
    (when solution
      (format t "Solution in ~A moves:~%" (1- (length solution)))
      (loop for move in solution
            for count from 0
            do (format t "~%Move #~A:~%" count)
            do (show-board move)))))

(defvar sample-board (make-board '(5 4 3 2 1 nil 8 6 7)))

--
[email protected]
http://www.kernel-panic.org/cgi-bin/mailman/listinfo/kplug-lpsg

Reply via email to