Consider a random testing approach. Throw the random tester at the main function, Think of 'streams' of input that the main function can absorb, and generate random streams .
On Jun 3, 2013, at 5:14 PM, Sean Kanaley wrote: > Just kidding there are more parse bugs, but I'm doing my best to fix them. > The consolation is that the other implementations are either far longer, > incorrect, or both. Still... I shall post it shortly and hope for the best. > > > On Mon, Jun 3, 2013 at 4:26 PM, Sean Kanaley <skana...@gmail.com> wrote: > Thank you both for the replies. I have incorporated all of your suggestions. > The thin board wrapper over vector2 proved useful to quickly switch > everything over to arrays. The program should no longer crash due to user > input aside from any kind of internal buffer issues that may or may not be > possible, which now exceeds the spec of allowing malformed input (though > bounds checking was still lacking). > > I also found a few important bugs in the process, so forgive me but I shall > repost the code in its entirety. The critical changes are the macro to > process adjacent positions, since clearing recursively seems to require not > using diagonals but summing the number of adjacent mines obviously does; > try-clear! wasn't returning the proper values to stop recursion; assume! > should be toggleable (not actually in the spec though); and some other minor > things. > > It's surprising how difficult a seemingly easy task like cloning minesweeper > can be. Ultimately this took several hours for what I thought would be 30-60 > minutes. > > Also I couldn't find a way to make a mutable-array immutable with anything > resembling mutable-array->array so I left the board as mutable. > > #lang racket > (require math) > ;board uses arrays directly, but maintaining an abstraction is nice > (define (board-ref b row col) (array-ref b (vector row col))) > (define (board-rows b) (vector-ref (array-shape b) 0)) > (define (board-cols b) (vector-ref (array-shape b) 1)) > > (define (on-board? b row col) > (and (<= 0 row (sub1 (board-rows b))) > (<= 0 col (sub1 (board-cols b))))) > (define (board->lists b) (array->list* b)) > > ;run on adjacent board positions > (define-syntax (for-adj stx) > (syntax-case stx () > [(_ b (r row) (c col) diag? body ...) > (with-syntax ([is (if (syntax->datum #'diag?) #''(0 0 1 1 1 -1 -1 -1) > #''(0 0 1 -1))] > [js (if (syntax->datum #'diag?) #''(1 -1 0 -1 1 0 -1 1) > #''(1 -1 0 0))]) > #'(for ([i is] [j js]) > > (let ([r (+ row i)] > [c (+ col j)]) > (when (on-board? b r c) > body ...))))])) > > ;mark is either hidden, assume-mine, or clear > ;n is int equal to # adj mines or -1 for mine > (struct pos ([mark #:mutable] n) #:transparent) > > (define (mine? p) (= (pos-n p) -1)) > ;hidden0? is needed because only spaces with no mines in them and no mines > adjacent > ;to them are cleared recursively > > (define (hidden0? p) > (and (symbol=? (pos-mark p) 'hidden) > (zero? (pos-n p)))) > (define (show-pos p) > (match-let ([(pos m n) p]) > (case m > [(hidden) "."] > [(assume-mine) "?"] > [(clear) (if (zero? n) " " (number->string n))] > [else (error "illegal mark" m)]))) > ;put "|" around positions > (define (show-board b) > (for ([row (board->lists b)]) > (displayln (format "|~a|" (string-join (map show-pos row) "|"))))) > > ;winning = every position is either cleared or a hidden mine > (define (win? b) > (for*/and ([r (range 0 (board-rows b))] > [c (range 0 (board-cols b))]) > (let ([p (board-ref b r c)]) > (or (symbol=? (pos-mark p) 'clear) > (mine? p))))) > > (define (init-board rows cols) > (let ([chance (+ (/ (random) 10) 0.1)] > ;empty board > [b (array->mutable-array (build-array (vector rows cols) > (λ (x) (pos 'hidden 0))))]) > > ;loop whole board > (for* ([row (range 0 rows)] > [col (range 0 cols)]) > (when (< (random) chance) > ;put a mine > (array-set! b (vector row col) (pos 'hidden -1)) > > ;increment adjacent mine counts unless that adjacent position is a > mine > (for-adj b (r row) (c col) #t > > (let ([p (board-ref b r c)]) > (unless (mine? p) > (array-set! b (vector r c) (pos 'hidden (add1 (pos-n > p))))))))) > b)) > > ;only clear position if it's not a mine > ;only continue recursing when it's a hidden0? > (define (try-clear! p) > (cond [(mine? p) #f] > [(hidden0? p) (set-pos-mark! p 'clear) #t] > [else (set-pos-mark! p 'clear) #f])) > > > ;the following player move functions return boolean where #f = lose, #t = > still going > ;assuming can never directly lose ((void) == #t from the set!) > ;make sure to not allow overwriting an already cleared position > (define (toggle-assume! b row col) > > (let ([p (board-ref b row col)]) > (set-pos-mark! p (case (pos-mark p) > [(assume-mine) 'hidden] > [(hidden) 'assume-mine] > [(clear) 'clear] > [else (error "invalid mark" (pos-mark p))])))) > > > ;clearing loses when the chosen position is a mine > ;void = #t as far as if works, so no need to return #t > (define (clear! b row col) > (let ([p (board-ref b row col)]) > (and (not (mine? p)) > ;not a mine, so recursively check adjacents, and maintain list of > visited positions > ;to avoid infinite loops > (let ([seen '()]) > ;clear the chosen position first, only continuing if it's a 0 > (when (try-clear! p) > > (let clear-adj ([row row] [col col]) > (for-adj b (r row) (c col) #f > > ;make sure its not seen > (when (and (not (member (list r c) seen)) > (try-clear! (board-ref b r c))) > ;it was cleared, so loop after saving this position > as being seen > (set! seen (cons (list r c) seen)) > (clear-adj r c))))))))) > > (define (parse-and-do-move! b s) > (match (string-split s) > [(list type row col) > (let ([row (string->number row)] > > [col (string->number col)]) > (if (on-board? b row col) > (case type > [("?") (toggle-assume! b row col)] > > [("!") (clear! b row col)] > [else (parse-and-do-move! b (read-line))]) > (parse-and-do-move! b (read-line))))] > [else (parse-and-do-move! b (read-line))])) > (define (run) > (displayln (string-append "--- Enter one of:\n" > "--- \"! <row> <col>\" to clear at (row,col), > or\n" > "--- \"? <row> <col>\" to flag a possible mine at > (row,col).\n")) > > (let ([b (init-board 8 8)]) > (let run () > (show-board b) > (display "enter move: ") > (if (parse-and-do-move! b (read-line)) > (if (win? b) (displayln "CLEAR!") (run)) > (displayln "BOOM!"))))) > > ____________________ > Racket Users list: > http://lists.racket-lang.org/users
____________________ Racket Users list: http://lists.racket-lang.org/users