puzzle-02.scm 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. (import
  2. (except (rnrs base) let-values map error)
  3. (only (guile) lambda* λ command-line string-null?)
  4. (ice-9 peg)
  5. (srfi srfi-1) ; list procs
  6. (srfi srfi-69) ; hash-tables
  7. (fileio))
  8. ;; GRAMMAR
  9. (define-peg-pattern DIGIT body
  10. (or (range #\0 #\9)))
  11. (define-peg-pattern ALPHA-NUMERIC body
  12. (or (range #\0 #\9) (range #\a #\z) (range #\A #\Z)))
  13. (define-peg-pattern HEXADECIMAL body
  14. (or (range #\0 #\9) (range #\a #\f)))
  15. (define-peg-pattern KEY-VALUE-SEP none ":")
  16. (define-peg-pattern PAIR-SEP body " ")
  17. (define-peg-pattern LENGTH-UNIT all (or "in" "cm"))
  18. (define-peg-pattern LENGTH-NUMBER all (+ DIGIT))
  19. (define-peg-pattern EYE-COLOR body (or "amb" "blu" "brn" "gry" "grn" "hzl" "oth"))
  20. (define-peg-pattern VALUE-CHARS body
  21. (or (range #\0 #\9) (range #\a #\z) (range #\A #\Z) "#"))
  22. (define-peg-pattern BIRTH-YEAR-VALUE all (and DIGIT DIGIT DIGIT DIGIT))
  23. (define-peg-pattern ISSUE-YEAR-VALUE all (and DIGIT DIGIT DIGIT DIGIT))
  24. (define-peg-pattern EXPIRATION-YEAR-VALUE all (and DIGIT DIGIT DIGIT DIGIT))
  25. (define-peg-pattern HEIGHT-VALUE all (and LENGTH-NUMBER LENGTH-UNIT))
  26. (define-peg-pattern HAIR-COLOR-VALUE all
  27. (and "#" HEXADECIMAL HEXADECIMAL HEXADECIMAL HEXADECIMAL HEXADECIMAL HEXADECIMAL))
  28. (define-peg-pattern EYE-COLOR-VALUE all EYE-COLOR)
  29. (define-peg-pattern PASSPORT-ID-VALUE all
  30. (and DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT))
  31. (define-peg-pattern COUNTRY-ID-VALUE all (+ VALUE-CHARS))
  32. (define-peg-pattern BIRTH-YEAR-KEY body "byr")
  33. (define-peg-pattern ISSUE-YEAR-KEY body "iyr")
  34. (define-peg-pattern EXPIRATION-YEAR-KEY body "eyr")
  35. (define-peg-pattern HEIGHT-KEY body "hgt")
  36. (define-peg-pattern HAIR-COLOR-KEY body "hcl")
  37. (define-peg-pattern EYE-COLOR-KEY body "ecl")
  38. (define-peg-pattern PASSPORT-ID-KEY body "pid")
  39. (define-peg-pattern COUNTRY-ID-KEY body "cid")
  40. (define-peg-pattern KNOWN-KEY-VALUE-PAIRS body
  41. (or (and BIRTH-YEAR-KEY KEY-VALUE-SEP BIRTH-YEAR-VALUE)
  42. (and ISSUE-YEAR-KEY KEY-VALUE-SEP ISSUE-YEAR-VALUE)
  43. (and EXPIRATION-YEAR-KEY KEY-VALUE-SEP EXPIRATION-YEAR-VALUE)
  44. (and HEIGHT-KEY KEY-VALUE-SEP HEIGHT-VALUE)
  45. (and HAIR-COLOR-KEY KEY-VALUE-SEP HAIR-COLOR-VALUE)
  46. (and EYE-COLOR-KEY KEY-VALUE-SEP EYE-COLOR-VALUE)
  47. (and PASSPORT-ID-KEY KEY-VALUE-SEP PASSPORT-ID-VALUE)
  48. (and COUNTRY-ID-KEY KEY-VALUE-SEP COUNTRY-ID-VALUE)))
  49. (define-peg-pattern PASSPORT body
  50. (and (* (and KNOWN-KEY-VALUE-PAIRS PAIR-SEP))
  51. KNOWN-KEY-VALUE-PAIRS))
  52. (define get-value-from-peg-tree
  53. (λ (peg-tree label)
  54. ;; (simple-format (current-output-port) "looking for ~a in ~a\n" label peg-tree)
  55. (cond
  56. [(null? peg-tree) #f]
  57. [(pair? (car peg-tree))
  58. (or (get-value-from-peg-tree (car peg-tree) label)
  59. (get-value-from-peg-tree (cdr peg-tree) label))]
  60. [(string? (car peg-tree))
  61. (cond
  62. [(string=? (car peg-tree) label)
  63. (cdr peg-tree)]
  64. [else
  65. (get-value-from-peg-tree (cdr peg-tree) label)])]
  66. [else
  67. (get-value-from-peg-tree (cdr peg-tree) label)])))
  68. (define println
  69. (λ (thing)
  70. (simple-format (current-output-port) "~a\n" thing)))
  71. (define lines->passport-data
  72. (lambda* (lines #:key (passport-separator-test string-null?) (key-separator " "))
  73. (let next-line ([remaining-lines lines] [single-passport-data ""])
  74. (cond
  75. [(null? remaining-lines)
  76. (list (string-trim-both single-passport-data))]
  77. [else
  78. (let ([cur-line (car remaining-lines)])
  79. (cond
  80. ;; The passport-separator finishes an entry in
  81. ;; the lines. We cons the single passport data
  82. ;; onto the recursion and start collecting data
  83. ;; for the next single passport.
  84. [(passport-separator-test cur-line)
  85. (cons (string-trim-both single-passport-data)
  86. (next-line (cdr remaining-lines) ""))]
  87. ;; If more data for a single passport follows, we
  88. ;; append it onto the single passport data and
  89. ;; look at the next line.
  90. [else
  91. (next-line (cdr remaining-lines)
  92. (string-append single-passport-data
  93. key-separator
  94. cur-line))]))]))))
  95. (define valid-birth-year?
  96. (λ (byr)
  97. ;; (simple-format (current-output-port) "validating birth year: ~s\n" byr)
  98. (let* ([year (second (first byr))]
  99. [num-byr (string->number year)])
  100. ;; (simple-format (current-output-port) "validating num-byr: ~s\n" num-byr)
  101. (and (>= num-byr 1920)
  102. (<= num-byr 2002)))))
  103. (define valid-issue-year?
  104. (λ (iyr)
  105. ;; (simple-format (current-output-port) "validating issue year: ~s\n" iyr)
  106. (let* ([year (second (first iyr))]
  107. [num-iyr (string->number year)])
  108. ;; (simple-format (current-output-port) "validating num-iyr: ~s\n" num-iyr)
  109. (and (>= num-iyr 2010)
  110. (<= num-iyr 2020)))))
  111. (define valid-expiration-year?
  112. (λ (eyr)
  113. ;; (simple-format (current-output-port) "validating expiration year: ~s\n" eyr)
  114. (let* ([year (second (first eyr))]
  115. [num-eyr (string->number year)])
  116. ;; (simple-format (current-output-port) "validating num-eyr: ~s\n" num-eyr)
  117. (and (>= num-eyr 2020)
  118. (<= num-eyr 2030)))))
  119. (define valid-height?
  120. (λ (hgt)
  121. ;; (simple-format (current-output-port) "validating height: ~s\n" hgt)
  122. ;; (HEIGHT-VALUE (LENGTH-NUMBER 174) (LENGTH-UNIT cm))
  123. (let ([unit (second (third (car hgt)))]
  124. [num (string->number (second (second (car hgt))))])
  125. ;; (simple-format (current-output-port) "validating num: ~s\n" num)
  126. ;; (simple-format (current-output-port) "validating unit: ~s\n" unit)
  127. (or (and (string=? unit "in")
  128. (>= num 59)
  129. (<= num 76))
  130. (and (string=? unit "cm")
  131. (>= num 150)
  132. (<= num 193))))))
  133. (define valid-hair-color?
  134. (λ (hcl)
  135. ;; is already validated through parsing
  136. #t))
  137. (define valid-eye-color?
  138. (λ (ecl)
  139. ;; is already validated through parsing
  140. #t))
  141. (define valid-passport-id?
  142. (λ (pid)
  143. ;; is already validated through parsing
  144. #t))
  145. (define valid-country-id?
  146. (λ (cid)
  147. ;; is already validated through parsing
  148. #t))
  149. (define valid-passport?
  150. (λ (passport)
  151. (let ([tree (peg:tree passport)]
  152. [mandatory-keys-to-pred
  153. (alist->hash-table `(("byr" . ,valid-birth-year?)
  154. ("iyr" . ,valid-issue-year?)
  155. ("eyr" . ,valid-expiration-year?)
  156. ("hgt" . ,valid-height?)
  157. ("hcl" . ,valid-hair-color?)
  158. ("ecl" . ,valid-eye-color?)
  159. ("pid" . ,valid-passport-id?)))]
  160. [optional-keys '("cid")])
  161. (hash-table-fold mandatory-keys-to-pred
  162. (λ (key valid? prior-result)
  163. (and prior-result
  164. (if tree
  165. (let ([val (get-value-from-peg-tree tree key)])
  166. (if val (valid? val) #f))
  167. #f)))
  168. #t))))
  169. (define main
  170. (λ (cmd-line-args)
  171. (let* ([lines (get-lines-from-file (second cmd-line-args))]
  172. [passport-data (lines->passport-data lines)]
  173. [passports (map (λ (datum) (match-pattern PASSPORT datum))
  174. passport-data)])
  175. (length (filter (λ (pp) (valid-passport? pp))
  176. passports)))))
  177. (simple-format (current-output-port)
  178. "~a\n"
  179. (main (command-line)))