nonogram (picross) made during my trip home added by Kooda on Wed Apr 17 00:40:56 2019

#!/bin/sh
#|
exec csi -s $0 $@
|#

;; Usage: nonogram [WIDTH HEIGHT]

;; Controls (qwerty):
;; - arrow keys: move around
;; - f: mark square as lit
;; - d: mark square as not lit
;; - a: reset square
;; - q or return: quit and check answer

;; You need an UTF-8 enabled terminal
;; and a font that provides these characters:
;; - ■
;; - □
;; - ☒

;; Zoom support is recommended
;; The checker doesn’t work when there are multiple solutions

(import
  scheme
  (chicken base)
  (chicken process-context)
  (chicken random)
  (srfi 1)
  ansi-escape-sequences
  stty
  utf8
  vector-lib)

(set-pseudo-random-seed! (random-bytes))

(define-record grid width height data)

(define (grid-ref g x y)
  (vector-ref (grid-data g)
              (+ x (* y (grid-width g)))))

(define (grid-set! g x y val)
  (vector-set! (grid-data g)
               (+ x (* y (grid-width g)))
               val))

(define (empty-grid width height)
  (make-grid width height
             (make-vector (* width height) #f)))

(define (random-grid width height)
  (make-grid width height
             (random-vector (* width height))))

(define (random-vector size)
  (vector-unfold (lambda (_) (zero? (pseudo-random-integer 2)))
                 size))

(define (compute-hints limit ref)
  (remove!
    zero?
    (let lp ((i 0) (prev 0))
      (cond ((= i limit) (list prev))
            ((ref i) (lp (add1 i) (add1 prev)))
            (else (cons prev (lp (add1 i) 0)))))))

(define (compute-hints/lines g)
  (let lp ((j 0))
    (if (= j (grid-height g))
        '()
        (cons (compute-hints (grid-width g) (lambda (i) (grid-ref g i j)))
              (lp (add1 j))))))

(define (compute-hints/columns g)
  (let lp ((i 0))
    (if (= i (grid-width g))
        '()
        (cons (compute-hints (grid-height g) (lambda (j) (grid-ref g i j)))
              (lp (add1 i))))))

(define (biggest-list l)
  (fold (lambda (l m) (max m (length l))) 0 l))

(define (display-grid g line-hints column-hints)
  (for-each display-hints column-hints)
  (let lpi ((i 0)
            (line-hints line-hints))
    (unless (= i (grid-height g))
      (let lpj ((j 0))
        (unless (= j (grid-width g))
          (display (case (grid-ref g j i)
                     ((#t) #\■)
                     ((#f) #\□)
                     ((x)  #\☒)))
          (display #\space)
          (lpj (add1 j))))
      (display-hints (car line-hints))
      (lpi (add1 i)
           (cdr line-hints)))))


(define (display-hints hs)
  (for-each
    (lambda (h) (display (if (zero? h) #\space h)) (display #\space))
    hs)
  (newline))

(define (normalize-hints hs m)
  (map (lambda (h)
         (append (make-list (- m (length h)) 0) h))
    hs))

(define (rotate-list l inner-len)
  (let lp ((i 0))
    (if (= i inner-len)
        '()
        (cons (map (lambda (il) (list-ref il i)) l)
              (lp (add1 i))))))

#;(define (play-loop g line-hints column-hints)
  (newline)
  (display-grid g line-hints column-hints)
  (let ((action (read)))
    (case action
      ((done) g)
      ((y) (grid-set! g (read) (read) #t)
           (play-loop g line-hints column-hints))
      ((n) (grid-set! g (read) (read) 'x)
           (play-loop g line-hints column-hints))
      ((e) (grid-set! g (read) (read) #f)
           (play-loop g line-hints column-hints))
      (else (error "unknown command")))))

(define (remove-xs! grid)
  (let ((v (grid-data grid)))
    (vector-for-each (lambda (i val)
                       (when (eqv? val 'x)
                         (vector-set! v i #f)))
                     v)))

(define width 10)
(define height 10)

(when (= (length (command-line-arguments)) 2)
  (set! width (string->number (car (command-line-arguments))))
  (set! height (string->number (cadr (command-line-arguments)))))

(define g (random-grid width height))
(define line-hints (compute-hints/lines g))
(define col-hints (compute-hints/columns g))
(define max-line (biggest-list line-hints))
(define max-col (biggest-list col-hints))
(set! col-hints (rotate-list (normalize-hints col-hints max-col)
                             max-col))

(define play-grid (empty-grid (grid-width g) (grid-height g)))

#;(play-loop play-grid line-hints col-hints)


(define (input)
  (let ((char (read-char)))
    (or (and (or (eqv? char #\e)
                 (eqv? char #\f))
             'enable)
        (and (or (eqv? char #\i)
                 (eqv? char #\d))
             'disable)
        (and (eqv? char #\a)
             'reset)
        (and (or (eqv? char #\q)
                 (eqv? char #\return))
             'quit)
        (and (eqv? char #\escape)
             (eqv? (read-char) #\[)
             (case (read-char)
               ((#\A) 'up)
               ((#\B) 'down)
               ((#\C) 'right)
               ((#\D) 'left)
               (else #f)))
        )))

(define x 0)
(define y 0)

(define (clamp x low hi)
  (min (max x low) hi))

(with-stty '((not icanon) (not echo))
  (lambda ()
    (display "\x1b[?1049h")
    (define *exit* #f)
    (let lp ()
      (display (cursor-position 1 1))
      (display (erase-display))
      (display-grid play-grid line-hints col-hints)
      (display (cursor-position (+ y 1 max-col) (+ (* x 2) 1)))
      (case (input)
        ((up) (set! y (clamp (- y 1) 0 (- height 1))))
        ((down) (set! y (clamp (+ y 1) 0 (- height 1))))
        ((right) (set! x (clamp (+ x 1) 0 (- width 1))))
        ((left) (set! x (clamp (- x 1) 0 (- width 1))))
        ((enable) (grid-set! play-grid x y #t))
        ((disable) (grid-set! play-grid x y 'x))
        ((reset) (grid-set! play-grid x y #f))
        ((quit) (set! *exit* #t)))
      (unless *exit* (lp)))))

(display "\x1b[?1049l")

(remove-xs! play-grid)
(if (equal? play-grid g) ;; TODO make that work when multiple solutions
    (print "Nice!")
    (print "Nope :("))
(display-grid g line-hints col-hints)