Files
advent-of-code-2021/05-hydrothermal-venture/code.scm
2021-12-06 14:21:53 +01:00

143 lines
4.0 KiB
Scheme

(import (srfi :43)) ; vector extensions
; returns numbers 0 to 9 from ascii character
(define (char->number c)
(- (char->integer c) 48)) ; 48 is ASCII's number zero
; reads a numnber from input
; also consumes the next non-number character in the file!
(define (get-number file)
(let loop [(n 0)]
(let [(c (get-char file))]
(if (char-numeric? c)
[loop (+ (char->number c) (* n 10))]
n))))
; reads pairs of coordinates
; returns a list of the form (((x1 y1) (x2 y2))...)
(define (read-lines file)
(let loop [(lines '())]
(if (eof-object? (peek-char file))
lines
[loop
(cons
(list
(list
(get-number file) ; x1
(get-number file)) ; y1
(list
(begin
(get-char file) ; consume -
(get-char file) ; consume >
(get-char file) ; consume space
(get-number file)) ; x2
(get-number file))) ; y2
lines)])))
; coordinate getters
(define (get-x1 coords) (car (car coords)))
(define (get-x2 coords) (car (cadr coords)))
(define (get-y1 coords) (cadr (car coords)))
(define (get-y2 coords) (cadr (cadr coords)))
(define (get-max-x coords) (max (get-x1 coords) (get-x2 coords)))
(define (get-max-y coords) (max (get-y1 coords) (get-y2 coords)))
; returns the largest value obtained while applying pick to each element of lst
(define (get-max lst pick)
(let loop [(m 0)
(lst lst)]
(if (null? lst)
m
[loop
(max m (pick (car lst)))
(cdr lst)])))
(define (sign value)
(cond
[(= 0 value) 0]
[(< 0 value) 1]
[else -1]))
; image get/setters
(define (make-image width height)
(list width
height
(make-vector (* width height) 0)))
(define (get-width image) (car image))
(define (get-height image) (cadr image))
(define (get-pixels image) (caddr image))
(define (get-offset image x y) (+ x (* y (get-width image))))
(define (get-pixel image x y)
(vector-ref (get-pixels image) (get-offset image x y)))
(define (set-pixel! image x y value)
(vector-set! (get-pixels image) (get-offset image x y) value))
; rasterize a line from given coordinates
(define (additively-rasterize-line! image coords allow-diagonal)
(let* [(x1 (get-x1 coords))
(y1 (get-y1 coords))
(x2 (get-x2 coords))
(y2 (get-y2 coords))
(dx (sign (- x2 x1)))
(dy (sign (- y2 y1)))]
(when (or allow-diagonal
(and (= dx 0) (not (= dy 0)))
(and (= dy 0) (not (= dx 0))))
[let loop! [(x x1) (y y1)]
(set-pixel! image x y (+ 1 (get-pixel image x y)))
(unless (and (= x x2) (= y y2))
[loop! (+ x dx) (+ y dy)])])))
; rasterize all given lines to an image
; each pixel represents the number of lines touching it
; diagonals must be 45° if allowed
(define (additively-rasterize lines allow-diagonals)
(let* [(width (+ 1 (get-max lines get-max-x)))
(height (+ 1 (get-max lines get-max-y)))
(image (make-image width height))]
(let loop! [(lines lines)]
(when (not (null? lines))
(additively-rasterize-line! image (car lines) allow-diagonals)
(loop! (cdr lines))))
image))
; count pixels with value strictly higher than 1
(define (count-crossings image)
(vector-fold
(lambda (index state pixel)
(if (> pixel 1)
[+ state 1]
state))
0
(get-pixels image)))
; output ASCII pgm formatted image with max 3 shades of gray to given file
(define (output-pgm image filename)
(when (file-exists? filename)
(delete-file filename))
(call-with-output-file
filename
(lambda (file)
(fprintf file "P2~%#~%~a ~a~%3~%"
(get-width image)
(get-height image))
(vector-for-each
(lambda (index pixel)
(fprintf file "~a " pixel)
(when (= 0 (modulo index (get-width image)))
(newline file)))
(get-pixels image)))))
(call-with-input-file
"input"
(lambda (file)
(let [(lines (read-lines file))]
(let [(image (additively-rasterize lines #f))]
(output-pgm image "part1.pgm")
(printf "part 1:~% dangerous areas = ~a~%"
(count-crossings image)))
(let [(image (additively-rasterize lines #t))]
(output-pgm image "part2.pgm")
(printf "part 2:~% dangerous areas = ~a~%"
(count-crossings image))))))