Refactored IntCode to internally use a hashmap instead of a vector.
This commit is contained in:
parent
9a4afc2dd3
commit
10c0b5bf28
32
lib.rkt
32
lib.rkt
|
@ -12,7 +12,7 @@
|
|||
|
||||
make-vector-grid
|
||||
vectors->lists
|
||||
hash->vector
|
||||
hash->vectors
|
||||
show-list-grid
|
||||
show-vector-grid
|
||||
show-hash-grid
|
||||
|
@ -38,7 +38,9 @@
|
|||
list->queue
|
||||
|
||||
vector-ref*
|
||||
vector-set!*)
|
||||
vector-set!*
|
||||
hash->vector
|
||||
vector->hash)
|
||||
|
||||
|
||||
;; Function helpers ;;
|
||||
|
@ -60,7 +62,7 @@
|
|||
[path (string-append "../input/" filename ".txt")])
|
||||
(read-lines path)))
|
||||
|
||||
;; show-solution : any/c -> any/c -> void
|
||||
;; show-solution : a -> b -> void
|
||||
;; Print part1 and part2 on separate lines.
|
||||
(define (show-solution part1 part2)
|
||||
(printf "Part 1: ~a\nPart 2: ~a\n" part1 part2))
|
||||
|
@ -80,10 +82,10 @@
|
|||
(define (vectors->lists vector-grid)
|
||||
(map vector->list (vector->list vector-grid)))
|
||||
|
||||
;; hash->vector : hash-grid -> number -> vector-grid
|
||||
;; hash->vectors : hash-grid -> number -> vector-grid
|
||||
;; Where the position is not in the hash-grid,
|
||||
;; the vector-grid takes on the default value.
|
||||
(define (hash->vector hash-grid [default 0])
|
||||
(define (hash->vectors hash-grid [default 0])
|
||||
(let* ([keys (hash-keys hash-grid)]
|
||||
[xs (map car keys)]
|
||||
[ys (map cdr keys)]
|
||||
|
@ -113,7 +115,7 @@
|
|||
|
||||
;; show-hash-grid : (hashof (value => char)) -> hash-grid -> number -> void
|
||||
(define (show-hash-grid char-hash hash-grid [default 0])
|
||||
(show-vector-grid char-hash (hash->vector hash-grid default)))
|
||||
(show-vector-grid char-hash (hash->vectors hash-grid default)))
|
||||
|
||||
|
||||
;; Number helpers ;;
|
||||
|
@ -189,7 +191,7 @@
|
|||
(cons (f v (first lst)) lst))
|
||||
(list init) lst)))
|
||||
|
||||
;; list-ref* : (listof any) -> number -> any -> any
|
||||
;; list-ref* : (listof a) -> number -> a -> a
|
||||
;; Same as list-ref, except a default value is provided
|
||||
;; if the index is beyond the length of the list.
|
||||
(define (list-ref* lst pos failure-result)
|
||||
|
@ -224,7 +226,7 @@
|
|||
[lists (map (λ (lst) (take lst min-len)) lists)])
|
||||
(apply map list lists)))
|
||||
|
||||
;; list->queue : (listof any) -> (queueof any)
|
||||
;; list->queue : (listof a) -> (queueof a)
|
||||
;; Creates a queue and adds elements of list in order
|
||||
(define (list->queue lst)
|
||||
(let ([Q (make-queue)])
|
||||
|
@ -251,4 +253,16 @@
|
|||
(let ([new-vec (make-vector (max (vector-length vec) (add1 pos)))])
|
||||
(vector-copy! new-vec 0 vec)
|
||||
(vector-set! new-vec pos v)
|
||||
new-vec))
|
||||
new-vec))
|
||||
|
||||
;; hash->vector : (hashof (number => a)) -> (vectorof a)
|
||||
;; Convert an intmap into a mutable vector
|
||||
(define (hash->vector hash [default 0])
|
||||
(let ([length (add1 (apply max (hash-keys hash)))])
|
||||
(build-vector length (λ (i) (hash-ref hash i default)))))
|
||||
|
||||
;; vector->hash : (vectorof a) -> (hashof (number => a))
|
||||
;; Convert a vector into an immutable intmap
|
||||
(define (vector->hash vec)
|
||||
(let ([kvs (map cons (range (vector-length vec)) (vector->list vec))])
|
||||
(make-immutable-hash kvs)))
|
|
@ -3,17 +3,17 @@
|
|||
(require racket/vector
|
||||
"../lib.rkt")
|
||||
|
||||
(define (vector-ref vec pos)
|
||||
(vector-ref* vec pos 0))
|
||||
|
||||
;; string->program : string -> (listof number)
|
||||
;; string->program : string -> (vectorof number)
|
||||
;; A program is a list of numbers,
|
||||
;; which are sequences of instructions and parameters.
|
||||
(define (string->program str)
|
||||
(list->vector (map string->number (string-split str ","))))
|
||||
|
||||
(define (hash-ref* hash key)
|
||||
(hash-ref hash key 0))
|
||||
|
||||
(define (program? p)
|
||||
(vectorof number?))
|
||||
hash?)
|
||||
|
||||
;; state =
|
||||
;; | out number (() -> state)
|
||||
|
@ -38,21 +38,21 @@
|
|||
[in (resume) (resume input)]
|
||||
[else (error "resume-with-input: Unexpected program state.")]))
|
||||
|
||||
;; resume-with-io : state -> (listof number) -> (listof number)
|
||||
;; resume-with-io : state -> (listof number) -> (listof number) -> (listof number)
|
||||
;; Run the program, providing input as needed, and collecting output.
|
||||
(define (resume-with-io st inputs)
|
||||
(define (resume-with-io st inputs [outputs '()])
|
||||
(type-case state st
|
||||
[in (resume)
|
||||
(resume-with-io (resume (car inputs)) (cdr inputs))]
|
||||
(resume-with-io (resume (car inputs)) (cdr inputs) outputs)]
|
||||
[out (value resume)
|
||||
(cons value (resume-with-io (resume) inputs))]
|
||||
[halt (program) '()]))
|
||||
(resume-with-io (resume) inputs (cons value outputs))]
|
||||
[halt (program) (reverse outputs)]))
|
||||
|
||||
;; halt-with-program : state -> program
|
||||
;; halt-with-program : state -> (vectorof number)
|
||||
;; Return program state of halted execution.
|
||||
(define (halt-with-program st)
|
||||
(type-case state st
|
||||
[halt (program) program]
|
||||
[halt (program) (hash->vector program)]
|
||||
[else (error "halt-with-program: Unexpected program state.")]))
|
||||
|
||||
;; exec* : program -> number -> number -> state
|
||||
|
@ -74,7 +74,7 @@
|
|||
;; If the mode is 2, the value at pointer is an address to be offset by base.
|
||||
;; Note that leading zeroes in the encoded instruction are omitted.
|
||||
(define (exec* program #:ptr [pointer 0] #:base [base 0])
|
||||
(define instruction (vector-ref program pointer))
|
||||
(define instruction (hash-ref* program pointer))
|
||||
(define opcode (remainder instruction 100))
|
||||
(define next-pointer
|
||||
(match opcode
|
||||
|
@ -84,9 +84,9 @@
|
|||
[99 (+ pointer 1)]))
|
||||
(define (get-location index mode)
|
||||
(match mode
|
||||
[0 (vector-ref program (+ pointer index))]
|
||||
[0 (hash-ref* program (+ pointer index))]
|
||||
[1 (+ pointer index)]
|
||||
[2 (+ (vector-ref program (+ pointer index)) base)]))
|
||||
[2 (+ (hash-ref* program (+ pointer index)) base)]))
|
||||
(let* ([mode1 (remainder (quotient instruction 100) 10)]
|
||||
[mode2 (remainder (quotient instruction 1000) 10)]
|
||||
[mode3 (remainder (quotient instruction 10000) 10)]
|
||||
|
@ -95,19 +95,19 @@
|
|||
[l2 (λ () (get-location 2 mode2))]
|
||||
[l3 (λ () (get-location 3 mode3))]
|
||||
;; v* : call to read values from program
|
||||
[v1 (λ () (vector-ref program (l1)))]
|
||||
[v2 (λ () (vector-ref program (l2)))]
|
||||
[v3 (λ () (vector-ref program (l3)))])
|
||||
[v1 (λ () (hash-ref* program (l1)))]
|
||||
[v2 (λ () (hash-ref* program (l2)))]
|
||||
[v3 (λ () (hash-ref* program (l3)))])
|
||||
(match opcode
|
||||
[(or 1 2)
|
||||
(let* ([arith (match opcode [1 +] [2 *])]
|
||||
[value (arith (v1) (v2))]
|
||||
[program (vector-set!* program (l3) value)])
|
||||
[program (hash-set program (l3) value)])
|
||||
(exec* program #:ptr next-pointer #:base base))]
|
||||
[3
|
||||
(let* ([resume
|
||||
(λ (input)
|
||||
(let ([program (vector-set!* program (l1) input)])
|
||||
(let ([program (hash-set program (l1) input)])
|
||||
(exec* program #:ptr next-pointer #:base base)))])
|
||||
(in resume))]
|
||||
[4
|
||||
|
@ -122,7 +122,7 @@
|
|||
[(or 7 8)
|
||||
(let* ([lt-eq (match opcode [7 <] [8 =])]
|
||||
[value (if (lt-eq (v1) (v2)) 1 0)]
|
||||
[program (vector-set!* program (l3) value)])
|
||||
[program (hash-set program (l3) value)])
|
||||
(exec* program #:ptr next-pointer #:base base))]
|
||||
[9
|
||||
(let ([base (+ base (v1))])
|
||||
|
@ -130,6 +130,9 @@
|
|||
[99
|
||||
(halt program)])))
|
||||
|
||||
;; Just so we always run the program on a fresh copy
|
||||
;; The external interface accepts a vector,
|
||||
;; while the internals use an immutable hashmap
|
||||
;; for functional purity and performance
|
||||
(define (exec program #:ptr [pointer 0] #:base [base 0])
|
||||
(exec* (vector-copy program) #:ptr pointer #:base base))
|
||||
(let* ([hash-program (vector->hash program)])
|
||||
(exec* hash-program #:ptr pointer #:base base)))
|
Loading…
Reference in New Issue