Getting down to marking, so these may get sparse.
Day 11
Easy. Convert the passwords to integer lists and write code to increment them, then write some more code to test each password for validity (sometimes converting back to strings and doing a regex, but leaving that as the last case for speed). The only irritating part was having to write a function to test the equality of three values:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
`regex library drop # passwords are lists of integers, 0-25 :strtopass |s/string:| [] ?s each {i asc <<"a" asc>> -,}; :passtostr |p/list:| "" ?p each {i <<"a" asc>> + chr +}; :incpass |p/list:i,t| ?p reverse !p 0!i { ?i ?p get 1 + !t ?t 26 = if 0 ?i ?p set else ?t ?i ?p set leave then ?i 1+ !i } ?p reverse ; :threq |a,b,c:| ?a ?b = if ?a ?c = else 0 then; :chkstraight |p/list:i,ln| ?p len !ln 2!i { ?i ?ln = if 0 stop then ?i ?p get 2- ?i 1- ?p get 1- ?i 2- ?p get threq if 1 stop then ?i 1+ !i } ; #"cqjxjnds" "cqjxxyzz" (|p:t| ?p strtopass !p { ?p incpass !p ?p chkstraight if 1!t ?p each { i <<"iol" strtopass>> in if 0!t leave then } ?t if ?p passtostr <<"([a-z])\\1" regex$compile>> regex$match len 2 >= if "Got" p ?p passtostr. then then then } )@ |
Day 12
The first part of this was easy, once I realised that strings never contained numbers. I could just parse all the numbers and add them up:
1 2 3 4 5 6 7 8 9 |
[`io,`regex] each {i library drop} require "util.ang" import :process |n,s:| ?n ?s <<"\\-?[0-9]+" regex$compile>> regex$match each {?s i show. i fst i snd substr toint +}; 0 "day12in" "r" io$open io$readfilestr process |
For second part there was no way around actually parsing the JSON, so I wrote a JSON parser. This is really pushing the limit of what it’s sensible to do in a language like Angort, which, while a full functional language, has no clever optimisation tricks to make it faster. I had to considerably increase the size of the both the return stack and the variable stack, and it runs like a dog. But it does work:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
require "./parse.ang" import require "util.ang" import # oh lordy, a JSON parser. This is a full-on tokenising parser, but # doesn't deal with spaces. # this matches "foo", returning the characters between the quotes [ "\"" charparser, ("\"" !=) matchparser many concatcons, "\"" charparser ] seq 1 return !String # this matches integers with a possible leading "-", returning # the integer value [ "-" charparser maybe, ("0123456789" in) matchparser many concatcons ] seq concatcons (toint) moditem !Number # Items are a choice of a few things. Note the weird anonymous # functions for Array and Object - I've had to do that because # the values for them are set further down, so these are anonymous # functions which actually look up the value each time. Ugly. [?String,?Number,(?Array@),(?Object@)] choice !Item # An array is separated by commas. [ "[" charparser, ?Item "," charparser sepby, "]" charparser ] seq 1 return !Array # Objects are complicated. They're wrapped in brackets, and we # skip those and just return the zeroth item in the sequence, # which after the skip will be the actual object hash. # This is constructed by generating a sequence for each key-value # pair, which a moditem turns into a single item hash. All these # hashes are then merged together in the next clause, which combines # the object items using a sepby with a comma. [ "{" charparser skip, [ ?String, ":" charparser skip, ?Item "bad item in object" error ] seq (|l:| [% ?l fst ?l snd]) moditem # generates single item # all items are separated by commas, and the single item hashes # are merged. "," charparser sepby (|l:| [%] ?l each {i+}) moditem, "}" charparser skip ] seq 0 return !Object # And that's the parser. # Handy function for getting all the values in a hash :vals |h:| [] ?h each {ival,}; # Visit the final structure, adding all the integers # which are not in a hash where "red" is a value. 0!T :visit |h:| cases ?h type `hash = if "red" ?h vals in not if ?h each{ival visit} then case ?h type `list = if ?h each { i visit } case ?h type `integer = if ?h ?T + !T case ?h type `string = if case "Huh?". ?h type. otherwise ; # read the file, convert to a list, parse and visit. "day12in" "r" io$open io$readfilestr strtolist ?Item@?`item visit ?T.quit |
This certainly pushed the language to its limits, and helped me find an awful lot of bugs!