diff --git a/Cargo.lock b/Cargo.lock index 81a581c..c524fde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,3 +3,35 @@ [[package]] name = "aoc2019" version = "0.1.0" +dependencies = [ + "ncurses", +] + +[[package]] +name = "cc" +version = "1.0.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" + +[[package]] +name = "libc" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" + +[[package]] +name = "ncurses" +version = "5.99.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15699bee2f37e9f8828c7b35b2bc70d13846db453f2d507713b758fabe536b82" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" diff --git a/Cargo.toml b/Cargo.toml index a5a2b2b..32204e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors = ["Stefan Schwarz "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +ncurses = "5.99.0" [[bin]] name = "day1" @@ -32,4 +33,28 @@ path = "src/day7/main.rs" [[bin]] name = "day9" -path = "src/day9/main.rs" \ No newline at end of file +path = "src/day9/main.rs" + +[[bin]] +name = "day10" +path = "src/day10/main.rs" + +[[bin]] +name = "day11" +path = "src/day11/main.rs" + +[[bin]] +name = "day12" +path = "src/day12/main.rs" + +[[bin]] +name = "day13" +path = "src/day13/main.rs" + +[[bin]] +name = "day14" +path = "src/day14/main.rs" + +[[bin]] +name = "day15" +path = "src/day15/main.rs" diff --git a/README.md b/README.md new file mode 100644 index 0000000..b81605e --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Advent of Code 2019 diff --git a/input10 b/input10 new file mode 100644 index 0000000..51520ab --- /dev/null +++ b/input10 @@ -0,0 +1,24 @@ +.###.#...#.#.##.#.####.. +.#....#####...#.######.. +#.#.###.###.#.....#.#### +##.###..##..####.#.####. +###########.#######.##.# +##########.#########.##. +.#.##.########.##...###. +###.#.##.#####.#.###.### +##.#####.##..###.#.##.#. +.#.#.#####.####.#..##### +.###.#####.#..#..##.#.## +########.##.#...######## +.####..##..#.###.###.#.# +....######.##.#.######.# +###.####.######.#....### +############.#.#.##.#### +##...##..####.####.#..## +.###.#########.###..#.## +#.##.#.#...##...#####..# +##.#..###############.## +##.###.#####.##.######.. +##.#####.#.#.##..####### +...#######.######...#### +#....#.#.#.####.#.#.#.## diff --git a/input11 b/input11 new file mode 100644 index 0000000..d6581e1 --- /dev/null +++ b/input11 @@ -0,0 +1 @@ +3,8,1005,8,320,1106,0,11,0,0,0,104,1,104,0,3,8,1002,8,-1,10,101,1,10,10,4,10,1008,8,1,10,4,10,102,1,8,29,2,1005,1,10,1006,0,11,3,8,1002,8,-1,10,101,1,10,10,4,10,108,0,8,10,4,10,102,1,8,57,1,8,15,10,1006,0,79,1,6,3,10,3,8,102,-1,8,10,101,1,10,10,4,10,108,0,8,10,4,10,101,0,8,90,2,103,18,10,1006,0,3,2,105,14,10,3,8,102,-1,8,10,1001,10,1,10,4,10,108,0,8,10,4,10,101,0,8,123,2,9,2,10,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,1,10,4,10,1001,8,0,150,1,2,2,10,2,1009,6,10,1,1006,12,10,1006,0,81,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,1,10,4,10,102,1,8,187,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,0,10,4,10,101,0,8,209,3,8,1002,8,-1,10,1001,10,1,10,4,10,1008,8,1,10,4,10,101,0,8,231,1,1008,11,10,1,1001,4,10,2,1104,18,10,3,8,102,-1,8,10,1001,10,1,10,4,10,108,1,8,10,4,10,1001,8,0,264,1,8,14,10,1006,0,36,3,8,1002,8,-1,10,1001,10,1,10,4,10,108,0,8,10,4,10,101,0,8,293,1006,0,80,1006,0,68,101,1,9,9,1007,9,960,10,1005,10,15,99,109,642,104,0,104,1,21102,1,846914232732,1,21102,1,337,0,1105,1,441,21102,1,387512115980,1,21101,348,0,0,1106,0,441,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,21102,209533824219,1,1,21102,1,395,0,1106,0,441,21101,0,21477985303,1,21102,406,1,0,1106,0,441,3,10,104,0,104,0,3,10,104,0,104,0,21101,868494234468,0,1,21101,429,0,0,1106,0,441,21102,838429471080,1,1,21102,1,440,0,1106,0,441,99,109,2,21201,-1,0,1,21101,0,40,2,21102,472,1,3,21101,0,462,0,1106,0,505,109,-2,2106,0,0,0,1,0,0,1,109,2,3,10,204,-1,1001,467,468,483,4,0,1001,467,1,467,108,4,467,10,1006,10,499,1102,1,0,467,109,-2,2106,0,0,0,109,4,2101,0,-1,504,1207,-3,0,10,1006,10,522,21101,0,0,-3,21202,-3,1,1,22101,0,-2,2,21102,1,1,3,21102,541,1,0,1106,0,546,109,-4,2105,1,0,109,5,1207,-3,1,10,1006,10,569,2207,-4,-2,10,1006,10,569,22102,1,-4,-4,1105,1,637,22102,1,-4,1,21201,-3,-1,2,21202,-2,2,3,21102,588,1,0,1105,1,546,22101,0,1,-4,21102,1,1,-1,2207,-4,-2,10,1006,10,607,21101,0,0,-1,22202,-2,-1,-2,2107,0,-3,10,1006,10,629,21201,-1,0,1,21102,629,1,0,105,1,504,21202,-2,-1,-2,22201,-4,-2,-4,109,-5,2105,1,0 diff --git a/input13 b/input13 new file mode 100644 index 0000000..f96e040 --- /dev/null +++ b/input13 @@ -0,0 +1 @@ +1,380,379,385,1008,2563,747932,381,1005,381,12,99,109,2564,1101,0,0,383,1101,0,0,382,20102,1,382,1,21002,383,1,2,21101,37,0,0,1105,1,578,4,382,4,383,204,1,1001,382,1,382,1007,382,37,381,1005,381,22,1001,383,1,383,1007,383,26,381,1005,381,18,1006,385,69,99,104,-1,104,0,4,386,3,384,1007,384,0,381,1005,381,94,107,0,384,381,1005,381,108,1106,0,161,107,1,392,381,1006,381,161,1102,1,-1,384,1105,1,119,1007,392,35,381,1006,381,161,1101,0,1,384,20101,0,392,1,21102,1,24,2,21101,0,0,3,21102,138,1,0,1105,1,549,1,392,384,392,21001,392,0,1,21102,1,24,2,21102,1,3,3,21101,161,0,0,1106,0,549,1102,1,0,384,20001,388,390,1,21002,389,1,2,21101,180,0,0,1105,1,578,1206,1,213,1208,1,2,381,1006,381,205,20001,388,390,1,20101,0,389,2,21101,205,0,0,1106,0,393,1002,390,-1,390,1101,0,1,384,21001,388,0,1,20001,389,391,2,21101,0,228,0,1105,1,578,1206,1,261,1208,1,2,381,1006,381,253,20102,1,388,1,20001,389,391,2,21101,0,253,0,1106,0,393,1002,391,-1,391,1102,1,1,384,1005,384,161,20001,388,390,1,20001,389,391,2,21101,0,279,0,1105,1,578,1206,1,316,1208,1,2,381,1006,381,304,20001,388,390,1,20001,389,391,2,21101,0,304,0,1106,0,393,1002,390,-1,390,1002,391,-1,391,1101,1,0,384,1005,384,161,21002,388,1,1,20102,1,389,2,21102,0,1,3,21101,0,338,0,1105,1,549,1,388,390,388,1,389,391,389,20102,1,388,1,20101,0,389,2,21101,0,4,3,21101,0,365,0,1105,1,549,1007,389,25,381,1005,381,75,104,-1,104,0,104,0,99,0,1,0,0,0,0,0,0,369,16,21,1,1,18,109,3,22101,0,-2,1,21202,-1,1,2,21102,0,1,3,21101,0,414,0,1105,1,549,21201,-2,0,1,21201,-1,0,2,21102,429,1,0,1105,1,601,1201,1,0,435,1,386,0,386,104,-1,104,0,4,386,1001,387,-1,387,1005,387,451,99,109,-3,2105,1,0,109,8,22202,-7,-6,-3,22201,-3,-5,-3,21202,-4,64,-2,2207,-3,-2,381,1005,381,492,21202,-2,-1,-1,22201,-3,-1,-3,2207,-3,-2,381,1006,381,481,21202,-4,8,-2,2207,-3,-2,381,1005,381,518,21202,-2,-1,-1,22201,-3,-1,-3,2207,-3,-2,381,1006,381,507,2207,-3,-4,381,1005,381,540,21202,-4,-1,-1,22201,-3,-1,-3,2207,-3,-4,381,1006,381,529,21202,-3,1,-7,109,-8,2106,0,0,109,4,1202,-2,37,566,201,-3,566,566,101,639,566,566,1201,-1,0,0,204,-3,204,-2,204,-1,109,-4,2105,1,0,109,3,1202,-1,37,593,201,-2,593,593,101,639,593,593,21001,0,0,-2,109,-3,2106,0,0,109,3,22102,26,-2,1,22201,1,-1,1,21102,1,487,2,21101,0,575,3,21102,1,962,4,21102,1,630,0,1105,1,456,21201,1,1601,-2,109,-3,2105,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,2,2,2,2,0,2,0,0,0,2,0,2,2,2,2,2,2,0,2,2,2,0,2,0,2,0,2,2,0,2,2,0,2,0,1,1,0,2,0,0,2,0,2,2,2,0,2,2,2,2,2,2,2,2,2,0,2,2,0,2,2,2,2,2,0,2,2,2,0,0,0,1,1,0,2,0,2,2,2,0,0,2,2,2,2,0,2,0,2,0,0,2,2,2,2,2,2,2,0,2,2,0,2,0,0,2,0,0,1,1,0,2,0,2,2,2,2,0,2,2,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,2,2,2,0,0,2,0,0,2,0,1,1,0,0,2,0,0,0,0,2,0,2,0,2,2,2,2,0,0,2,0,2,2,0,0,2,2,0,2,0,2,2,2,2,0,2,0,1,1,0,2,2,0,2,2,0,0,0,0,0,2,0,2,0,0,2,2,2,0,2,2,0,2,2,2,2,0,2,2,2,2,0,0,0,1,1,0,2,2,2,2,2,2,0,2,0,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,0,2,0,0,1,1,0,2,2,2,2,2,0,0,2,0,2,0,2,2,0,0,0,0,2,0,0,2,2,2,2,2,2,0,0,0,2,2,2,0,0,1,1,0,0,2,2,2,2,0,0,2,0,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,0,2,2,2,0,0,0,2,0,1,1,0,2,0,2,2,2,0,2,0,2,2,2,2,0,2,2,2,2,0,0,2,2,0,0,2,0,0,0,2,2,2,2,0,0,0,1,1,0,2,0,2,2,2,2,2,2,2,0,0,2,0,0,2,2,2,0,0,2,2,0,2,0,2,0,2,2,2,2,2,0,2,0,1,1,0,2,2,2,0,2,0,2,2,0,2,2,2,2,2,0,0,0,2,2,0,2,2,0,2,0,0,0,0,2,2,2,0,0,0,1,1,0,2,2,0,0,2,2,2,0,0,2,0,2,2,2,0,0,2,2,2,0,0,2,2,2,2,0,0,0,2,0,2,2,2,0,1,1,0,0,0,0,0,2,2,0,2,2,0,2,2,0,2,2,2,0,0,0,2,2,0,0,2,2,0,2,0,0,2,2,0,2,0,1,1,0,2,0,2,0,2,2,0,0,2,2,0,2,0,2,0,2,2,0,2,0,0,2,2,0,2,2,0,2,2,2,2,2,2,0,1,1,0,2,0,0,2,0,2,2,2,0,2,2,2,2,2,2,0,0,2,0,0,2,2,0,0,2,2,2,2,0,0,2,2,0,0,1,1,0,2,2,2,2,0,2,2,2,0,2,2,2,2,0,0,2,2,2,0,2,0,2,0,2,2,0,2,2,0,0,2,2,0,0,1,1,0,2,2,2,2,2,0,0,0,2,2,2,2,2,2,0,2,2,2,0,2,2,0,2,2,2,2,2,0,0,0,2,2,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,36,15,49,65,33,64,90,69,80,38,6,11,42,79,20,83,19,67,76,39,23,24,21,94,31,77,25,39,50,66,66,19,40,89,50,59,3,34,57,47,41,75,90,15,29,53,32,72,19,20,88,89,42,54,77,15,96,66,74,77,51,46,71,16,76,96,20,52,26,56,86,80,43,98,91,64,12,86,11,44,14,18,35,40,53,17,44,43,29,69,75,25,8,46,26,18,14,61,89,42,10,21,57,69,13,74,3,68,8,46,35,29,35,79,81,28,30,87,3,67,75,1,95,98,88,18,29,40,28,23,17,5,52,51,5,40,42,89,31,86,67,42,53,43,42,45,63,43,10,46,95,10,26,65,77,38,24,17,30,21,18,25,59,25,72,54,83,34,36,48,60,48,28,3,6,14,92,84,20,31,68,38,16,11,95,36,89,38,69,70,73,49,15,23,70,65,23,11,34,69,6,60,11,38,70,75,18,43,29,53,26,59,95,27,46,3,78,68,7,61,36,20,77,54,43,54,45,26,86,98,21,11,83,60,30,47,46,83,25,74,36,3,54,22,98,70,10,49,35,14,24,38,31,77,95,33,8,17,43,42,93,81,56,13,72,60,18,70,36,64,15,24,49,60,92,47,67,34,24,58,15,96,13,83,55,67,17,43,84,72,55,38,43,90,94,55,11,56,16,8,68,87,14,19,93,6,23,21,41,17,19,13,37,85,69,77,83,91,70,61,5,13,98,87,45,88,13,71,63,98,41,13,81,19,30,34,83,44,70,84,76,22,68,30,55,42,96,1,71,42,32,95,14,33,50,96,61,95,35,18,67,84,7,39,10,95,7,33,69,55,82,19,94,52,60,46,63,62,93,92,39,69,42,60,35,64,69,62,50,29,13,53,90,62,1,45,92,16,89,3,8,81,45,61,88,12,34,27,23,31,73,65,30,43,19,9,44,45,81,17,57,18,3,64,84,70,15,49,34,53,62,58,11,39,90,28,81,61,38,11,96,52,92,71,49,22,69,25,23,4,98,98,3,83,29,70,39,59,79,56,21,45,75,82,48,52,60,44,89,57,42,63,67,30,16,57,26,28,17,65,90,73,22,8,26,72,47,13,68,19,45,45,49,26,20,6,35,65,85,1,59,51,27,13,88,84,63,66,12,78,43,60,79,92,31,44,72,1,18,12,95,6,50,61,66,79,81,21,8,81,33,63,67,31,12,92,48,13,17,27,15,43,45,1,7,58,17,97,45,36,61,28,23,87,97,27,5,97,2,84,30,29,36,60,95,21,97,32,76,78,83,93,28,35,73,26,27,10,90,50,29,24,78,1,71,6,76,44,89,6,94,44,17,80,66,5,43,23,49,52,40,47,39,81,80,80,87,38,26,2,43,97,15,50,79,73,32,73,12,20,53,73,82,7,38,63,78,68,29,96,14,29,52,54,95,6,59,93,98,46,66,91,16,88,95,55,37,2,44,16,97,30,35,19,96,3,8,47,64,4,49,74,89,1,76,90,77,80,46,48,63,11,93,97,71,37,82,75,91,7,33,20,59,8,93,83,83,49,85,92,33,89,58,72,37,27,56,37,91,39,7,52,19,77,20,3,52,57,12,63,14,34,6,89,93,21,62,53,75,3,97,76,75,68,24,83,84,26,66,16,45,46,6,57,48,84,29,1,60,89,63,40,29,63,63,70,10,74,97,94,95,49,55,87,98,2,98,50,93,18,88,39,80,34,41,57,78,12,41,15,13,11,55,22,65,37,21,46,78,17,78,8,62,1,16,9,33,94,26,55,33,22,25,22,93,71,62,82,51,86,66,97,88,82,9,93,9,30,46,37,95,36,21,80,21,36,89,96,44,97,80,42,29,82,87,78,4,58,19,80,95,85,90,64,4,27,65,5,64,71,43,64,92,92,23,80,14,61,12,11,41,12,16,49,93,67,27,68,29,35,66,14,10,46,11,12,79,76,26,62,4,51,35,22,67,83,62,94,95,53,1,94,61,91,5,54,68,24,3,24,98,38,33,78,72,15,9,82,21,59,73,39,23,97,5,13,39,90,61,10,73,92,48,34,47,54,3,54,69,89,67,13,54,41,51,92,51,59,53,76,3,38,93,45,28,10,90,78,40,24,14,58,72,98,19,70,79,18,62,20,79,3,79,73,54,17,10,31,1,70,42,77,747932 diff --git a/input14 b/input14 new file mode 100644 index 0000000..ea6fb95 --- /dev/null +++ b/input14 @@ -0,0 +1,56 @@ +18 FHWM => 1 XGQNZ +4 FVPWN, 9 CQGW => 7 QGHT +22 KZMS, 1 DMCJL => 8 TWGCK +1 LMGQN, 1 DSWDM, 1 GKGZ => 1 TGPH +22 WCSW => 1 LVTG +13 JSWR => 4 GKGZ +162 ORE => 3 FVPWN +59 CQGW, 15 MSNG, 6 XGKRF, 10 LJRQ, 1 HRKGV, 15 RKVC => 1 FUEL +5 DMCJL => 1 QBLH +2 XDRJ, 2 RKVC => 8 CTCNL +1 QXHX => 5 GFPSK +22 QGHT, 6 GFPSK, 5 DHTPL => 3 CSDR +4 QGHT, 2 HFXD => 4 XDRJ +10 WQCGV, 1 JSWR, 21 RHTLN => 7 VTPC +11 CQGW, 1 FVPWN => 3 HFXD +5 VTPC => 2 NCXW +8 LDZVS => 6 DQLH +117 ORE => 2 KWZNB +3 TGPH, 1 JPFQ, 2 WHWLK, 5 RKVC, 16 DQLH => 9 LJRQ +14 KWZNB, 2 CQGW => 8 MLPK +6 LDZVS => 2 JSWR +1 RKVC, 8 HCGT, 9 DHTPL => 6 FHWM +3 DHTPL, 1 HWSR, 36 LDZVS => 6 DSWDM +5 WHWLK, 1 LJHWT, 8 HSTHS => 7 VMPX +22 ZJCDZ, 3 WQCGV => 5 DHTPL +10 LJHWT, 32 GFPSK, 2 RHTLN => 4 HFRMP +2 FKVD, 3 TWGCK, 1 HWSR => 1 RNLZW +2 CSDR, 3 DQLH, 2 HSTHS => 9 JPFQ +1 JSWR, 1 PCWS, 1 HFRMP => 3 XGKRF +2 QGHT, 9 LVTG, 3 QBLH => 7 RHTLN +10 LJHWT, 4 CTCNL => 8 QXHX +16 MLPK, 1 HFXD => 9 ZJCDZ +6 QGHT => 9 WCSW +4 HWSR, 4 MLPK, 1 KZMS => 3 BGZHQ +12 MLPK => 8 RKVC +1 HWSR, 1 VNWFS => 7 BGFJ +7 FHWM, 11 CTDF, 1 LDZVS => 2 VNWFS +4 CTDF => 4 HSTHS +2 ZJCDZ => 6 LJHWT +1 VMPX, 1 NCXW, 1 HSTHS, 41 XGKRF, 30 HLNG, 1 GKGZ => 7 HRKGV +1 XGQNZ, 10 PCWS, 3 BGFJ => 8 FKVD +1 GFPSK, 1 DMCJL, 1 LVTG => 5 XDTZB +3 WCSW => 5 KZMS +6 TWGCK, 1 QXHX, 4 BGFJ => 2 LMGQN +1 WCSW => 7 LDZVS +1 XDTZB, 9 VNWFS => 3 WHWLK +3 HFXD, 4 WCSW, 1 MLPK => 5 WQCGV +2 BGFJ, 1 HSTHS, 22 MDCB, 10 HWSR, 6 RNLZW, 8 GKGZ => 5 MSNG +4 QGHT, 1 FKVD => 7 MDCB +9 MLPK, 3 LJHWT => 7 DMCJL +121 ORE => 2 CQGW +9 DHTPL, 2 BGZHQ => 8 CTDF +2 JSWR, 30 RHTLN => 7 HLNG +2 QBLH => 7 PCWS +14 LVTG => 8 HWSR +7 DMCJL => 1 HCGT diff --git a/input15 b/input15 new file mode 100644 index 0000000..ca5836c --- /dev/null +++ b/input15 @@ -0,0 +1 @@ +3,1033,1008,1033,1,1032,1005,1032,31,1008,1033,2,1032,1005,1032,58,1008,1033,3,1032,1005,1032,81,1008,1033,4,1032,1005,1032,104,99,1002,1034,1,1039,102,1,1036,1041,1001,1035,-1,1040,1008,1038,0,1043,102,-1,1043,1032,1,1037,1032,1042,1106,0,124,1002,1034,1,1039,1001,1036,0,1041,1001,1035,1,1040,1008,1038,0,1043,1,1037,1038,1042,1105,1,124,1001,1034,-1,1039,1008,1036,0,1041,1001,1035,0,1040,1002,1038,1,1043,101,0,1037,1042,1105,1,124,1001,1034,1,1039,1008,1036,0,1041,102,1,1035,1040,1001,1038,0,1043,101,0,1037,1042,1006,1039,217,1006,1040,217,1008,1039,40,1032,1005,1032,217,1008,1040,40,1032,1005,1032,217,1008,1039,1,1032,1006,1032,165,1008,1040,9,1032,1006,1032,165,1102,1,2,1044,1105,1,224,2,1041,1043,1032,1006,1032,179,1102,1,1,1044,1106,0,224,1,1041,1043,1032,1006,1032,217,1,1042,1043,1032,1001,1032,-1,1032,1002,1032,39,1032,1,1032,1039,1032,101,-1,1032,1032,101,252,1032,211,1007,0,35,1044,1106,0,224,1101,0,0,1044,1105,1,224,1006,1044,247,102,1,1039,1034,1002,1040,1,1035,1002,1041,1,1036,102,1,1043,1038,101,0,1042,1037,4,1044,1105,1,0,1,5,41,19,22,1,39,81,29,20,15,82,33,18,45,30,32,55,28,26,70,13,56,32,28,18,3,59,90,11,95,15,85,8,61,25,59,24,34,1,85,5,25,54,57,18,20,54,80,91,28,65,36,12,44,36,13,92,24,56,13,39,69,29,79,10,41,27,23,25,72,20,3,61,15,51,11,12,12,48,10,45,13,29,49,90,30,17,9,41,21,18,7,30,48,17,83,71,4,10,31,10,96,81,77,9,50,39,21,36,33,72,12,3,23,79,18,4,75,17,58,64,8,7,97,60,72,72,1,94,55,42,2,94,2,21,88,19,82,57,96,19,25,27,41,62,15,40,23,61,86,27,73,61,13,46,52,81,12,34,23,73,23,59,1,30,47,9,99,10,37,17,28,98,5,92,73,8,63,4,86,76,79,7,30,68,28,91,12,12,98,74,4,22,44,10,23,45,37,16,90,76,23,74,75,12,21,38,14,15,76,28,49,71,7,6,6,71,53,33,12,87,15,92,66,21,38,13,53,92,34,49,25,6,67,21,27,89,24,61,25,30,41,30,99,28,19,41,90,51,74,14,33,54,48,10,14,42,2,67,76,10,21,2,67,43,27,69,11,16,78,7,36,9,24,48,63,81,53,29,94,34,25,99,66,47,17,97,33,52,11,62,22,52,30,23,89,95,15,13,50,48,26,10,6,69,78,13,6,94,1,28,67,10,70,16,50,19,24,15,79,50,27,3,19,62,4,31,83,20,17,83,67,5,80,26,36,62,87,3,10,80,22,65,60,10,78,4,20,60,30,11,7,83,10,13,72,81,37,22,14,55,63,51,27,32,77,52,20,50,16,48,2,55,10,53,26,84,6,87,43,37,26,3,85,62,25,78,50,16,10,37,22,54,5,80,24,7,32,49,18,27,12,41,70,82,20,34,91,15,98,77,22,6,79,3,8,54,17,32,4,44,2,97,14,15,65,30,97,14,79,75,11,77,5,61,37,20,91,20,45,74,19,40,2,41,89,12,34,44,18,62,57,17,68,22,96,7,59,63,2,60,70,2,26,75,26,3,53,19,80,16,97,7,34,58,52,66,24,75,25,30,75,42,13,12,89,13,3,84,92,1,75,30,54,43,2,56,15,1,15,84,99,6,98,42,17,29,1,18,26,70,71,29,91,23,21,87,66,18,38,32,18,81,65,2,58,99,12,4,84,24,32,88,30,67,49,29,59,64,18,70,10,24,56,5,27,97,50,4,28,85,65,16,67,83,15,16,61,18,86,8,36,25,36,29,97,45,19,81,41,29,45,30,69,26,57,93,27,72,34,30,99,61,2,48,16,12,76,98,28,14,32,32,90,48,10,30,57,23,39,2,8,39,33,13,88,34,31,74,15,60,8,47,60,31,5,79,1,98,86,33,3,99,33,62,11,96,25,22,38,98,84,3,56,70,49,3,8,56,87,4,29,59,65,26,34,77,7,14,78,26,25,70,49,3,31,45,92,24,95,17,4,9,4,96,64,92,27,67,4,99,6,44,7,16,86,2,75,1,6,68,81,4,1,44,49,7,92,8,40,36,25,81,13,56,99,10,2,30,72,6,43,30,12,43,93,19,20,23,95,10,19,66,63,28,96,40,50,8,15,56,38,13,93,42,71,12,18,87,8,4,21,85,9,2,66,77,10,80,26,61,9,43,20,88,10,39,67,55,31,49,17,58,26,80,20,84,54,49,5,73,11,52,15,63,7,62,24,57,92,61,25,87,56,37,31,38,14,99,0,0,21,21,1,10,1,0,0,0,0,0,0 diff --git a/src/day10/main.rs b/src/day10/main.rs new file mode 100644 index 0000000..2269e31 --- /dev/null +++ b/src/day10/main.rs @@ -0,0 +1,384 @@ +use std::collections::{HashMap, HashSet}; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::ops::Sub; + +#[derive(Clone, Debug, PartialEq)] +enum Position { + Empty, + Astroid, +} + +impl From<&u8> for Position { + fn from(c: &u8) -> Self { + match c { + b'.' => Position::Empty, + b'#' => Position::Astroid, + _ => panic!("invalid input"), + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +struct XY { + x: isize, + y: isize, +} + +impl From<(usize, usize)> for XY { + fn from((x, y): (usize, usize)) -> Self { + XY { + x: x as isize, + y: y as isize, + } + } +} + +impl Sub for &XY { + type Output = XY; + fn sub(self, other: &XY) -> Self::Output { + let xy = XY { + x: self.x - other.x, + y: self.y - other.y, + }; + xy + } +} + +impl XY { + fn direction(&self) -> Self { + let d = gcd(self.y, self.x).abs(); + if d != 0 { + XY { + x: self.x / d, + y: self.y / d, + } + } else { + self.clone() + } + } + + fn distance(&self) -> f64 { + let sum = (self.x.pow(2) + self.y.pow(2)) as f64; + (sum as f64).sqrt() + } + + fn angle(&self) -> f64 { + let a = (-self.y as f64).atan2(-self.x as f64) * (180 as f64 / std::f64::consts::PI) + - 90 as f64; + if a < 0 as f64 { + a + 360 as f64 + } else { + a + } + } +} + +struct MonitoringStation { + map: Vec, +} + +impl MonitoringStation { + fn laser(&self, root: &XY, skip: usize, n: usize) -> Vec { + let mut map: HashMap> = HashMap::new(); + // collect vec for each direction + map = self + .map + .iter() + .filter_map(|xy| { + if xy == root { + return None; + } + let rel = xy - &root; + Some((xy, rel.direction())) + }) + .fold(map, |mut acc, (xy, dir)| { + acc.entry(dir).or_insert(vec![]).push(xy); + acc + }); + + // order angle vecs by distance + map.iter_mut().for_each(|(_, val)| { + val.sort_by(|a, b| { + ((root - b).distance().partial_cmp(&(root - a).distance())).unwrap() + }); + }); + + // lets iterate over sorted angles + let mut angles = map.keys().cloned().collect::>(); + angles.sort_by(|a, b| a.angle().partial_cmp(&b.angle()).unwrap()); + + // cycle around and return first element of each vec + angles + .iter() + .cycle() + .map(|xy| match map.get_mut(xy) { + Some(xy) => { + if let Some(xy) = xy.pop() { + Some(xy) + } else { + None + } + } + None => None, + }) + .flatten() + .skip(skip) + .take(n) + .cloned() + .collect::>() + } + + fn find(&self) -> (usize, XY) { + let best = (0, &XY::from((0, 0))); + let best = self + .map + .iter() + .map(|root| { + let insight = self + .map + .iter() + // get direction to any of the astroids + .filter_map(|xy| { + let rel = xy - root; + Some(rel.direction()) + }) + // and keep only one in each direction + .fold(HashSet::new(), |mut acc, xy| { + acc.insert(xy); + acc + }) + .len() + - 1; + (insight, root) + }) + .fold( + best, + |acc, astroid| { + if astroid.0 > acc.0 { + astroid + } else { + acc + } + }, + ); + (best.0, best.1.clone()) + } +} + +impl From for MonitoringStation +where + T: BufRead, +{ + fn from(reader: T) -> Self { + let map = reader + .split(b'\n') + .enumerate() + .flat_map(|(y, line)| { + line.unwrap() + .into_iter() + .enumerate() + .filter_map(move |(x, c)| { + let xy = XY::from((x, y)); + match Position::from(&c) { + Position::Astroid => Some(xy), + Position::Empty => None, + } + }) + }) + .collect::>(); + MonitoringStation { map } + } +} + +fn main() -> Result<(), Box> { + let f = File::open("input10")?; + let reader = BufReader::new(f); + let station = MonitoringStation::from(reader); + let best = station.find(); + println!("best: {:?}", best); + let lasered = station.laser(&best.1, 199, 1); + let lasered = lasered.iter().next().unwrap(); + println!("lasered: {:?}", lasered); + Ok(()) +} + +fn gcd(x: isize, y: isize) -> isize { + let mut x = x; + let mut y = y; + while y != 0 { + let t = y; + y = x % y; + x = t; + } + x +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn example1() { + let reader = BufReader::new(".#..#\n.....\n#####\n....#\n...##".as_bytes()); + let (count, xy) = MonitoringStation::from(reader).find(); + assert_eq!(xy, XY { x: 3, y: 4 }); + assert_eq!(count, 8); + } + + #[test] + fn example2() { + let reader = BufReader::new( + " +......#.#. +#..#.#.... +..#######. +.#.#.###.. +.#..#..... +..#....#.# +#..#....#. +.##.#..### +##...#..#. +.#....####" + .trim() + .as_bytes(), + ); + let (count, xy) = MonitoringStation::from(reader).find(); + assert_eq!(xy, XY { x: 5, y: 8 }); + assert_eq!(count, 33); + } + + #[test] + fn example3() { + let reader = BufReader::new( + " +#.#...#.#. +.###....#. +.#....#... +##.#.#.#.# +....#.#.#. +.##..###.# +..#...##.. +..##....## +......#... +.####.###." + .trim() + .as_bytes(), + ); + let (count, xy) = MonitoringStation::from(reader).find(); + assert_eq!(xy, XY { x: 1, y: 2 }); + assert_eq!(count, 35); + } + + #[test] + fn example4() { + let reader = BufReader::new( + " +.#..#..### +####.###.# +....###.#. +..###.##.# +##.##.#.#. +....###..# +..#.#..#.# +#..#.#.### +.##...##.# +.....#.#.." + .trim() + .as_bytes(), + ); + let (count, xy) = MonitoringStation::from(reader).find(); + assert_eq!(xy, XY { x: 6, y: 3 }); + assert_eq!(count, 41); + } + + #[test] + fn example5() { + let reader = BufReader::new( + " +.#..##.###...####### +##.############..##. +.#.######.########.# +.###.#######.####.#. +#####.##.#.##.###.## +..#####..#.######### +#################### +#.####....###.#.#.## +##.################# +#####.##.###..####.. +..######..##.####### +####.##.####...##..# +.#####..#.######.### +##...#.##########... +#.##########.####### +.####.#.###.###.#.## +....##.##.###..##### +.#.#.###########.### +#.#.#.#####.####.### +###.##.####.##.#..##" + .trim() + .as_bytes(), + ); + let (count, xy) = MonitoringStation::from(reader).find(); + assert_eq!(xy, XY { x: 11, y: 13 }); + assert_eq!(count, 210); + } + + #[test] + fn example6() { + let reader = BufReader::new( + " +.#....#####...#.. +##...##.#####..## +##...#...#.#####. +..#.....#...###.. +..#.#.....#....## +" + .trim() + .as_bytes(), + ); + let ms = MonitoringStation::from(reader); + ms.find(); + assert_eq!( + ms.laser(&XY::from((8, 3)), 0, 36), + vec![ + XY { x: 8, y: 1 }, + XY { x: 9, y: 0 }, + XY { x: 9, y: 1 }, + XY { x: 10, y: 0 }, + XY { x: 9, y: 2 }, + XY { x: 11, y: 1 }, + XY { x: 12, y: 1 }, + XY { x: 11, y: 2 }, + XY { x: 15, y: 1 }, + XY { x: 12, y: 2 }, + XY { x: 13, y: 2 }, + XY { x: 14, y: 2 }, + XY { x: 15, y: 2 }, + XY { x: 12, y: 3 }, + XY { x: 16, y: 4 }, + XY { x: 15, y: 4 }, + XY { x: 10, y: 4 }, + XY { x: 4, y: 4 }, + XY { x: 2, y: 4 }, + XY { x: 2, y: 3 }, + XY { x: 0, y: 2 }, + XY { x: 1, y: 2 }, + XY { x: 0, y: 1 }, + XY { x: 1, y: 1 }, + XY { x: 5, y: 2 }, + XY { x: 1, y: 0 }, + XY { x: 5, y: 1 }, + XY { x: 6, y: 1 }, + XY { x: 6, y: 0 }, + XY { x: 7, y: 0 }, + XY { x: 8, y: 0 }, + XY { x: 10, y: 1 }, + XY { x: 14, y: 0 }, + XY { x: 16, y: 1 }, + XY { x: 13, y: 3 }, + XY { x: 14, y: 3 } + ] + ) + } +} diff --git a/src/day11/main.rs b/src/day11/main.rs new file mode 100644 index 0000000..3417d6b --- /dev/null +++ b/src/day11/main.rs @@ -0,0 +1,186 @@ +use std::collections::HashMap; +use std::env; +use std::fs::File; +use std::io; + +use aoc2019::intcode; + +#[derive(Hash, Default, PartialEq, Eq, Clone)] +struct Panel { + x: isize, + y: isize, +} + +enum Color { + Black, + White, +} + +impl From for Color { + fn from(color: isize) -> Self { + match color { + 0 => Color::Black, + 1 => Color::White, + _ => panic!("invalid color"), + } + } +} + +impl Into for &Color { + fn into(self) -> isize { + match self { + Color::Black => 0, + Color::White => 1, + } + } +} + +impl Color { + fn char(&self) -> char { + match self { + Color::Black => ' ', + Color::White => '\u{2588}', + } + } +} + +enum Direction { + Up, + Down, + Left, + Right, +} + +enum Turn { + Left, + Right, +} + +impl From for Turn { + fn from(turn: isize) -> Self { + match turn { + 0 => Turn::Left, + 1 => Turn::Right, + _ => panic!("invalid turn direction"), + } + } +} + +struct Robot { + panel: Panel, + direction: Direction, +} + +impl Robot { + fn turn(&mut self, direction: Turn) { + self.direction = match self.direction { + Direction::Up => match direction { + Turn::Left => Direction::Left, + Turn::Right => Direction::Right, + }, + Direction::Right => match direction { + Turn::Left => Direction::Up, + Turn::Right => Direction::Down, + }, + Direction::Down => match direction { + Turn::Left => Direction::Right, + Turn::Right => Direction::Left, + }, + Direction::Left => match direction { + Turn::Left => Direction::Down, + Turn::Right => Direction::Up, + }, + } + } + + fn step(&mut self) { + self.panel = match self.direction { + Direction::Up => Panel { + x: self.panel.x, + y: self.panel.y + 1, + }, + Direction::Left => Panel { + x: self.panel.x + 1, + y: self.panel.y, + }, + Direction::Down => Panel { + x: self.panel.x, + y: self.panel.y - 1, + }, + Direction::Right => Panel { + x: self.panel.x - 1, + y: self.panel.y, + }, + } + } +} + +fn main() -> io::Result<()> { + let filename = env::args().nth(1).expect("provide file as first param"); + let mut computer = intcode::Computer::from(File::open(filename)?); + + let mut canvas: HashMap = HashMap::new(); + canvas.insert(Panel { x: 0, y: 0 }, Color::White); + let mut robot = Robot { + panel: Panel::default(), + direction: Direction::Up, + }; + + let mut input: isize = canvas + .get(&robot.panel) + .or_else(|| Some(&Color::Black)) + .unwrap() + .into(); + while let Some(output) = computer.run_io(input) { + canvas.insert(robot.panel.clone(), Color::from(output)); + + let output = computer.run_must_output(); + robot.turn(Turn::from(output)); + robot.step(); + + input = canvas + .get(&robot.panel) + .or_else(|| Some(&Color::Black)) + .unwrap() + .into(); + } + + let mut xmin: isize = 0; + let mut xmax: isize = 0; + let mut ymin: isize = 0; + let mut ymax: isize = 0; + let painted = canvas + .iter() + .map(|(k, _)| { + if k.x < xmin { + xmin = k.x; + } + if k.x > xmax { + xmax = k.x; + } + if k.y < ymin { + ymin = k.y; + } + if k.y > ymax { + ymax = k.y; + } + }) + .count(); + println!("painted: {}", painted); + println!("canvas: x:{}-{} y:{}-{}", xmin, xmax, ymin, ymax); + + for y in (ymin..=ymax).rev() { + for x in (xmin..=xmax).rev() { + print!( + "{}", + canvas + .get(&Panel { x, y }) + .unwrap_or_else(|| &Color::Black) + .char() + ) + } + println!() + } + + Ok(()) +} diff --git a/src/day12/main.rs b/src/day12/main.rs new file mode 100644 index 0000000..367448f --- /dev/null +++ b/src/day12/main.rs @@ -0,0 +1,332 @@ +use std::cmp::Ordering; +use std::collections::hash_map::DefaultHasher; +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; +use std::iter::FromIterator; + +#[derive(PartialEq, Debug, Default, Clone, Hash)] +struct Object { + x: isize, + y: isize, + z: isize, +} + +impl From<(isize, isize, isize)> for Object { + fn from(o: (isize, isize, isize)) -> Self { + Object { + x: o.0, + y: o.1, + z: o.2, + } + } +} + +impl Object { + fn attract(&self, other: &Object, v: &mut Object) { + v.x += Object::cmp(self.x, other.x); + v.y += Object::cmp(self.y, other.y); + v.z += Object::cmp(self.z, other.z); + } + + fn apply(&mut self, v: &Object) { + self.x += v.x; + self.y += v.y; + self.z += v.z; + } + + fn cmp(a: isize, b: isize) -> isize { + match b.cmp(&a) { + Ordering::Less => -1, + Ordering::Equal => 0, + Ordering::Greater => 1, + } + } + + fn energy(&self) -> usize { + return (self.x.abs() + self.y.abs() + self.z.abs()) as usize; + } +} + +struct Space { + iteration: u128, + moons: Vec, + velocy: Vec, +} + +impl From> for Space { + fn from(moons: Vec) -> Self { + let iteration = 0; + let velocy = Vec::from_iter((0..moons.len()).map(|_| Object::default())); + Space { + iteration, + moons, + velocy, + } + } +} + +impl Hash for Space { + fn hash(&self, state: &mut H) { + self.moons.iter().for_each(|m| m.hash(state)); + self.velocy.iter().for_each(|v| v.hash(state)); + } +} + +impl Space { + fn step(&mut self) { + self.iteration += 1; + let moons: &mut Vec = self.moons.as_mut(); + let velocy: &mut Vec = self.velocy.as_mut(); + + moons.iter().enumerate().for_each(|(idx, a)| { + moons.iter().for_each(|b| { + if a == b { + return; + } + a.attract(b, velocy.get_mut(idx).unwrap()) + }) + }); + moons + .iter_mut() + .enumerate() + .for_each(|(idx, m)| m.apply(velocy.get(idx).unwrap())); + } + + fn status(&self) -> impl Iterator { + self.moons + .iter() + .zip(self.velocy.iter()) + .map(|(a, b)| (a, b)) + } + + fn iteration(&self) -> u128 { + return self.iteration; + } + + fn hashed(&self) -> u64 { + let mut hash = DefaultHasher::new(); + self.hash(&mut hash); + hash.finish() + } + + fn reapeats(mut self) -> u128 { + let mut known: HashMap = HashMap::new(); + loop { + println!("a lot"); + known.insert(self.hashed(), self.iteration()); + // 4686774924 + for _ in 0..10000000 { + self.step(); + if let Some(iteration) = known.get(&self.hashed()) { + let ago = self.iteration() - iteration; + return ago; + } + } + } + } +} + +#[derive(Eq, PartialEq, Clone)] +struct Axis { + p: isize, +} + +impl From for Axis { + fn from(p: isize) -> Self { + Axis { p } + } +} + +impl Axis { + fn attract(&self, other: &Axis, v: &mut Axis) { + v.p += Axis::cmp(self.p, other.p); + } + + fn apply(&mut self, v: &Axis) { + self.p += v.p; + } + + fn cmp(a: isize, b: isize) -> isize { + match b.cmp(&a) { + Ordering::Less => -1, + Ordering::Equal => 0, + Ordering::Greater => 1, + } + } + + fn energy(&self) -> usize { + return self.p.abs() as usize; + } +} + +struct AxisSpace { + iteration: usize, + moons: Vec, + velocy: Vec, +} + +impl From> for AxisSpace { + fn from(moons: Vec) -> Self { + let iteration = 0; + let moons = Vec::from_iter(moons.iter().map(|i| Axis::from(*i))); + let velocy = Vec::from_iter((0..moons.len()).map(|_| Axis::from(0))); + AxisSpace { + iteration, + moons, + velocy, + } + } +} + +impl AxisSpace { + fn step(&mut self) { + self.iteration += 1; + let moons: &mut Vec = self.moons.as_mut(); + let velocy: &mut Vec = self.velocy.as_mut(); + moons.iter().enumerate().for_each(|(idx, a)| { + moons.iter().for_each(|b| { + if a == b { + return; + } + a.attract(&b, &mut velocy[idx]) + }) + }); + moons + .iter_mut() + .enumerate() + .for_each(|(idx, m)| m.apply(velocy.get(idx).unwrap())); + } + + fn iteration(&self) -> usize { + return self.iteration; + } + + fn reapeats(mut self) -> usize { + let want = self.moons.clone(); + self.step(); + while self.moons != want { + self.step(); + } + return self.iteration() + 1; + } +} + +fn gcd(x: usize, y: usize) -> usize { + let mut x = x; + let mut y = y; + while y != 0 { + let t = y; + y = x % y; + x = t; + } + x +} + +fn lcm(a: usize, b: usize) -> usize { + a * b / gcd(a, b) +} + +fn lcm3(a: usize, b: usize, c: usize) -> usize { + lcm(a, lcm(b, c)) +} + +fn main() { + // part1 + let moons = vec![ + Object::from((17, 5, 1)), + Object::from((-2, -8, 8)), + Object::from((7, -6, 14)), + Object::from((1, -10, 4)), + ]; + let mut space = Space::from(moons); + let mut step_energy = 0; + (1..=100).for_each(|_i| { + (1..=10).for_each(|_| { + space.step(); + }); + step_energy = space + .status() + .map(|(o, v)| o.energy() * v.energy()) + .fold(0, |mut acc, c| { + acc += c; + acc + }); + }); + println!("energy: {}", step_energy); + + // part2 + let xspace = AxisSpace::from(vec![-1, 2, 4, 3]); + let yspace = AxisSpace::from(vec![0, -10, -8, 5]); + let zspace = AxisSpace::from(vec![2, -7, 8, -1]); + let total = lcm3(xspace.reapeats(), yspace.reapeats(), zspace.reapeats()); + println!("matching space found {} iterations ago", total); + + let xspace = AxisSpace::from(vec![-8, 5, 2, 9]); + let yspace = AxisSpace::from(vec![-10, 5, -7, -8]); + let zspace = AxisSpace::from(vec![0, 10, 3, -3]); + let total = lcm3(xspace.reapeats(), yspace.reapeats(), zspace.reapeats()); + println!("matching space found {} iterations ago", total); + + let xspace = AxisSpace::from(vec![17, -2, 7, 1]); + let yspace = AxisSpace::from(vec![5, -8, -6, -10]); + let zspace = AxisSpace::from(vec![1, 8, 14, 4]); + let total = lcm3(xspace.reapeats(), yspace.reapeats(), zspace.reapeats()); + println!("matching space found {} iterations ago", total); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn day12example1() { + let space = Space::from(vec![ + Object::from((-1, 0, 2)), + Object::from((2, -10, -7)), + Object::from((4, -8, 8)), + Object::from((3, 5, -1)), + ]); + assert!(space.status().eq(vec![ + (&Object::from((-1, 0, 2)), &Object::default()), + (&Object::from((2, -10, -7)), &Object::from((0, 0, 0))), + (&Object::from((4, -8, 8)), &Object::from((0, 0, 0))), + (&Object::from((3, 5, -1)), &Object::from((0, 0, 0))), + ])); + } + + #[test] + fn day12part1() { + let mut space = Space::from(vec![ + Object::from((17, 5, 1)), + Object::from((-2, -8, 8)), + Object::from((7, -6, 14)), + Object::from((1, -10, 4)), + ]); + (1..=1000).for_each(|_| { + space.step(); + }); + + let energy = space + .status() + .map(|(o, v)| { + println!("{:?} {:?}", o, v); + o.energy() * v.energy() + }) + .fold(0, |mut acc, c| { + acc += c; + acc + }); + assert_eq!(energy, 9876) + } + + #[test] + fn day12example2() { + let space = Space::from(vec![ + Object::from((-1, 0, 2)), + Object::from((2, -10, -7)), + Object::from((4, -8, 8)), + Object::from((3, 5, -1)), + ]); + assert_eq!(space.reapeats(), 2772) + } +} diff --git a/src/day13/main.rs b/src/day13/main.rs new file mode 100644 index 0000000..f0c3a94 --- /dev/null +++ b/src/day13/main.rs @@ -0,0 +1,135 @@ +use aoc2019::intcode; +use std::cmp::Ordering; +use std::collections::HashMap; +use std::error::Error; +use std::fs::File; + +use ncurses; + +fn main() -> Result<(), Box> { + let f = File::open("input13")?; + let mut computer = intcode::Computer::from(f); + let mut grid: Grid = HashMap::new(); + while let Some(output) = computer.run_until_n_output(3) { + let mut output = output.into_iter(); + let xy = XY { + x: output.next().unwrap(), + y: output.next().unwrap(), + }; + let tile = Tile::from(output.next().unwrap()); + grid.insert(xy, tile); + } + + // part 1 + let blocks = grid.iter().filter(|(_, v)| **v == Tile::Block).count(); + println!("blocks: {}", blocks); + + // part2 + ncurses::initscr(); + ncurses::noecho(); + let mut computer = computer.clone_with_modified_program(vec![(0, 2)]); + let mut paddlex = 0; + let mut score = 0; + while !computer.halted { + if let Some(output) = &computer.run_until_input() { + output.chunks(3).for_each(|c| { + if let &[x, y, value] = c { + if x == -1 && y == 0 { + score = value; + } else { + let tile = Tile::from(value); + if Tile::from(value) == Tile::HorizontalPaddle { + paddlex = x; + } + ncurses::mvprintw((y + 1) as i32, x as i32, tile.char()); + } + } + }); + } + let np = next_pos(&mut computer.clone()); + ncurses::mvprintw( + 0, + 0, + &format!( + "willx: {:0>2} paddlex: {:0>2} score: {:0>7}", + np, paddlex, score + ), + ); + let joystick = match np.cmp(&paddlex) { + Ordering::Less => -1, + Ordering::Equal => 0, + Ordering::Greater => 1, + }; + computer.push_input_and_step(joystick); + ncurses::refresh(); + } + ncurses::endwin(); + println!("score: {}", score); + Ok(()) +} + +fn next_pos(computer: &mut intcode::Computer) -> isize { + while !computer.halted { + if let Some(output) = computer.run_until_input() { + for c in output.chunks(3) { + if let &[x, y, value] = c { + if x == -1 && y == 0 { + // skip score + } else if Tile::from(value) == Tile::Ball && y == 23 { + return x; + } + } + } + } + computer.push_input_and_step(0); + } + 0 +} + +#[derive(Eq, PartialEq)] +enum Tile { + Empty, + Wall, + Block, + HorizontalPaddle, + Ball, +} + +impl From for Tile { + fn from(i: isize) -> Self { + match i { + 0 => Tile::Empty, + 1 => Tile::Wall, + 2 => Tile::Block, + 3 => Tile::HorizontalPaddle, + 4 => Tile::Ball, + _ => unreachable!("there are only 5 tiles..."), + } + } +} + +impl Tile { + fn char(&self) -> &str { + match self { + Tile::Empty => " ", + Tile::Wall => "X", + Tile::Block => "O", + Tile::HorizontalPaddle => "-", + Tile::Ball => "*", + } + } +} + +#[derive(Eq, PartialEq, Hash)] +struct XY { + x: isize, + y: isize, +} + +impl From<(isize, isize)> for XY { + fn from(xy: (isize, isize)) -> Self { + XY { x: xy.0, y: xy.1 } + } +} + +type Grid = HashMap; diff --git a/src/day14/main.rs b/src/day14/main.rs new file mode 100644 index 0000000..b36d8be --- /dev/null +++ b/src/day14/main.rs @@ -0,0 +1,385 @@ +use std::cmp::Ordering; +use std::collections::HashMap; +use std::convert::*; +use std::fs::File; +use std::io::{BufRead, BufReader}; + +type Resource = String; +type Pipeline = HashMap; + +#[derive(Debug)] +struct ResourceBuilder { + requirements: Vec, + out_count: usize, +} + +impl From<(Vec, usize)> for ResourceBuilder { + fn from(build: (Vec, usize)) -> Self { + ResourceBuilder { + requirements: build.0, + out_count: build.1, + } + } +} + +#[derive(Default, Debug)] +struct NanoFactory { + pipeline: Pipeline, + ordered: Vec, +} + +#[derive(Debug)] +enum NanoFactoryBuildState { + InputCount, + InputResource, + OutputArrow, + OutputCount, + OutputResource, +} + +impl NanoFactory { + /// sort resources + fn resolve_order(&mut self) { + self.ordered = vec![Resource::from("ORE")]; + let mut sortable: HashMap> = self + .pipeline + .iter() + .map(|(k, v)| (k.clone(), v.requirements.clone())) + .collect(); + while sortable.len() > 0 { + sortable = sortable + .into_iter() + .filter_map(|(k, v)| { + if v.iter().all(|i| self.ordered.contains(i)) { + self.ordered.push(k); + None + } else { + Some((k, v)) + } + }) + .collect(); + } + self.ordered.reverse(); + self.ordered.retain(|res| res != "ORE"); + } + + /// calculate how much ores are required for required_fuel + fn resource_usage(&self, required_fuel: u128) -> usize { + // create storage to track leftover resources + let mut storage: HashMap = HashMap::new(); + storage.insert(Resource::from("FUEL"), required_fuel); + + // calculate resource costs + self.ordered.iter().for_each(|res| { + let rb = self.pipeline.get(res).unwrap(); + let count = (storage.get(res).unwrap().clone() + (rb.out_count as u128) - 1) + / (rb.out_count as u128); + rb.requirements.iter().for_each(|req| { + storage + .entry(req.clone()) + .and_modify(|v| *v += count) + .or_insert(count); + storage.insert(res.clone(), 0); + }) + }); + + // return cost for ore + println!("{:?}", storage); + *storage.get("ORE").unwrap() as usize + } + + /// generate a single fuel + fn generate_fuel(&self) -> usize { + let mut storage: HashMap = HashMap::new(); + self.lookup_resource(&mut storage, "FUEL") + } + + /// recursively calculate the cost of a resource in ores + fn lookup_resource(&self, storage: &mut HashMap, resource: &str) -> usize { + if resource == "ORE" { + return 1; + } + + // use from storage + if let Some(count) = storage.get_mut(resource) { + if *count > 0 { + *count -= 1; + return 0; + } + } + + // initialize storage with zero + if storage.get(resource).is_none() { + storage.insert(resource.into(), 0); + } + + // build resource and and to storage + let required = self + .pipeline + .get(resource) + .expect("unable to find resource"); + let cost = required.requirements.iter().fold(0, |mut acc, c| { + acc += self.lookup_resource(storage, c); + acc + }); + + // update storage + let stored = storage.get_mut(resource).unwrap(); + *stored += required.out_count - 1; + + cost + } +} + +impl From for NanoFactory +where + T: BufRead, +{ + fn from(bufreader: T) -> Self { + let mut f = NanoFactory::default(); + let mut state = NanoFactoryBuildState::InputCount; + bufreader.split(b'\n').enumerate().for_each(|(nr, line)| { + let line = line.unwrap(); + let mut line = line.into_iter().peekable(); + let mut count_var = 0; + let mut cur_requirements = Vec::new(); + loop { + match state { + // numeric input + NanoFactoryBuildState::InputCount | NanoFactoryBuildState::OutputCount => { + while let Some(c) = next_if_in_range(&mut line, 48..=57) { + count_var = count_var * 10 + (c as usize) - 48; + } + assert_eq!(line.next().unwrap(), b' '); + state = match state { + NanoFactoryBuildState::InputCount => { + NanoFactoryBuildState::InputResource + } + NanoFactoryBuildState::OutputCount => { + NanoFactoryBuildState::OutputResource + } + _ => unreachable!(), + } + } + // input resouce name + NanoFactoryBuildState::InputResource => { + let mut r = Resource::new(); + while let Some(c) = next_if_in_range(&mut line, 65..=90) { + r.push(c.into()); + } + for _ in 0..count_var { + cur_requirements.push(r.clone()); + } + r.truncate(0); + count_var = 0; + let next = line.next(); + match next { + Some(b',') => { + assert_eq!(line.next().unwrap(), b' '); + state = NanoFactoryBuildState::InputCount; + } + Some(b' ') => state = NanoFactoryBuildState::OutputArrow, + _ => panic!( + "invalid format in line {}, expected ' ' or ',', got '{:?}'", + nr, next + ), + } + } + // output resource name + NanoFactoryBuildState::OutputResource => { + let mut r = Resource::new(); + while let Some(c) = next_if_in_range(&mut line, 65..=90) { + r.push(c.into()); + } + f.pipeline + .insert(r, ResourceBuilder::from((cur_requirements, count_var))); + state = NanoFactoryBuildState::InputCount; + return; + } + // arrow between input and output ' => ' + NanoFactoryBuildState::OutputArrow => { + assert_eq!(line.next().unwrap(), b'='); + assert_eq!(line.next().unwrap(), b'>'); + assert_eq!(line.next().unwrap(), b' '); + state = NanoFactoryBuildState::OutputCount + } + }; + } + }); + f + } +} + +fn next_if_in_range( + peekable: &mut std::iter::Peekable, + range: std::ops::RangeInclusive, +) -> Option +where + I: std::iter::Iterator, + T: Copy + std::cmp::PartialOrd, +{ + if let Some(peeked) = peekable.peek() { + if range.contains(peeked) { + peekable.next() + } else { + None + } + } else { + None + } +} + +fn main() { + let f = File::open("input14").unwrap(); + let bufreader = BufReader::new(f); + let mut factory = NanoFactory::from(bufreader); + + // part1 + println!("ores: {}", factory.generate_fuel()); + + // part2 as binary search + factory.resolve_order(); + let mut min = 1; + let mut max = 1_000_000_000_000; + let mut fuel; + loop { + fuel = (min + max) / 2; + let ore = factory.resource_usage(fuel); + match 1_000_000_000_000.cmp(&ore) { + Ordering::Less => max = fuel - 1, + Ordering::Greater => { + min = fuel + 1; + if min > max { + break; + } + } + Ordering::Equal => { + break; + } + }; + } + println!("ammount: {}", fuel); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn example1() { + let bufreader = BufReader::new( + "10 ORE => 10 A +1 ORE => 1 B +7 A, 1 B => 1 C +7 A, 1 C => 1 D +7 A, 1 D => 1 E +7 A, 1 E => 1 FUEL +" + .trim() + .as_bytes(), + ); + let mut factory = NanoFactory::from(bufreader); + factory.resolve_order(); + assert_eq!(factory.generate_fuel(), 31); + assert_eq!(factory.resource_usage(1), 31); + } + + #[test] + fn example2() { + let bufreader = BufReader::new( + " +9 ORE => 2 A +8 ORE => 3 B +7 ORE => 5 C +3 A, 4 B => 1 AB +5 B, 7 C => 1 BC +4 C, 1 A => 1 CA +2 AB, 3 BC, 4 CA => 1 FUEL +" + .trim() + .as_bytes(), + ); + let mut factory = NanoFactory::from(bufreader); + factory.resolve_order(); + assert_eq!(factory.generate_fuel(), 165); + assert_eq!(factory.resource_usage(1), 165); + } + + #[test] + fn example3() { + let bufreader = BufReader::new( + " +157 ORE => 5 NZVS +165 ORE => 6 DCFZ +44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL +12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ +179 ORE => 7 PSHF +177 ORE => 5 HKGWZ +7 DCFZ, 7 PSHF => 2 XJWVT +165 ORE => 2 GPVTF +3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT +" + .trim() + .as_bytes(), + ); + let factory = NanoFactory::from(bufreader); + assert_eq!(factory.generate_fuel(), 13312); + } + + #[test] + fn example4() { + let bufreader = BufReader::new( + " +2 VPVL, 7 FWMGM, 2 CXFTF, 11 MNCFX => 1 STKFG +17 NVRVD, 3 JNWZP => 8 VPVL +53 STKFG, 6 MNCFX, 46 VJHF, 81 HVMC, 68 CXFTF, 25 GNMV => 1 FUEL +22 VJHF, 37 MNCFX => 5 FWMGM +139 ORE => 4 NVRVD +144 ORE => 7 JNWZP +5 MNCFX, 7 RFSQX, 2 FWMGM, 2 VPVL, 19 CXFTF => 3 HVMC +5 VJHF, 7 MNCFX, 9 VPVL, 37 CXFTF => 6 GNMV +145 ORE => 6 MNCFX +1 NVRVD => 8 CXFTF +1 VJHF, 6 MNCFX => 4 RFSQX +176 ORE => 6 VJHF +" + .trim() + .as_bytes(), + ); + let mut factory = NanoFactory::from(bufreader); + factory.resolve_order(); + assert_eq!(factory.generate_fuel(), 180697); + assert_eq!(factory.resource_usage(1), 180697); + } + + #[test] + fn example5() { + let bufreader = BufReader::new( + " +171 ORE => 8 CNZTR +7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL +114 ORE => 4 BHXH +14 VRPVC => 6 BMBT +6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL +6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT +15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW +13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW +5 BMBT => 4 WPTQ +189 ORE => 9 KTJDG +1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP +12 VRPVC, 27 CNZTR => 2 XDBXC +15 KTJDG, 12 BHXH => 5 XCVML +3 BHXH, 2 VRPVC => 7 MZWV +121 ORE => 7 VRPVC +7 XCVML => 6 RJRHP +5 BHXH, 4 VRPVC => 5 LTCX +" + .trim() + .as_bytes(), + ); + let mut factory = NanoFactory::from(bufreader); + factory.resolve_order(); + assert_eq!(factory.generate_fuel(), 2210736); + assert_eq!(factory.resource_usage(1), 2210736); + } +} diff --git a/src/day15/main.rs b/src/day15/main.rs new file mode 100644 index 0000000..8fa0f5a --- /dev/null +++ b/src/day15/main.rs @@ -0,0 +1,205 @@ +use std::collections::HashMap; +use std::error::Error; +use std::fs::File; + +use aoc2019::intcode; + +fn main() -> Result<(), Box> { + let f = File::open("input15")?; + let computer = intcode::Computer::from(f); + let mut map = Map::new(); + let steps = map.walk(&XY { x: 0, y: 0 }, &computer, 0).unwrap(); + + for y in map.ymin..=map.ymax { + for x in map.xmin..=map.xmax { + if x == 0 && y == 0 { + print!("0"); + continue; + } + let status = map.m.get(&XY { x, y }).unwrap_or(&Status::Empty); + let c = match status { + Status::HitWall => "#", + Status::MoveComplete => ".", + Status::Finished => "X", + Status::Empty => " ", + Status::Oxigenated => "o", + }; + print!("{}", c); + } + println!(""); + } + + println!("i walked {} steps", steps); + let steps = map.fill(); + println!("filled with oxygen after {} minutes", steps); + + Ok(()) +} + +#[derive(Default, Debug)] +struct Map { + m: HashMap, + xmin: isize, + xmax: isize, + ymin: isize, + ymax: isize, +} + +impl Map { + fn new() -> Self { + Map::default() + } + + fn walk(&mut self, start: &XY, computer: &intcode::Computer, steps: usize) -> Option { + let steps = steps + 1; + let mut result = None; + for dir in 1..=4 { + let direction = Direction::from(dir); + let xy = start.add((&direction).into()); + if self.m.contains_key(&xy) { + continue; + } + self.update_limits(&xy); + let mut computer = computer.clone(); + if let Some(status) = computer.run_io(dir) { + let status = Status::from(status); + self.m.insert(xy.clone(), status.clone()); + match status { + Status::HitWall => (), + Status::MoveComplete => match self.walk(&xy, &computer, steps) { + Some(i) => result = Some(i + 1), + None => (), + }, + Status::Finished => result = Some(1), + _ => unreachable!("unknown status"), + } + } else { + unreachable!("computer unexpectedly halted"); + } + } + result + } + + fn fill(&mut self) -> usize { + let mut src: Vec = self + .m + .iter() + .filter_map(|(xy, status)| match status { + Status::Finished => Some(xy), + _ => None, + }) + .cloned() + .collect(); + let mut steps = 0; + while src.len() > 0 { + steps += 1; + src.iter() + .for_each(|xy| *self.m.get_mut(xy).unwrap() = Status::Oxigenated); + src = src + .into_iter() + .map(|xy| { + (1..=4) + .map(Direction::from) + .map(move |d| xy.add((&d).into())) + }) + .flatten() + .filter(|xy| match self.m.get(xy) { + Some(Status::MoveComplete) => true, + _ => false, + }) + .collect(); + } + assert_eq!( + self.m + .values() + .filter(|status| match status { + Status::MoveComplete => true, + _ => false, + }) + .count(), + 0 + ); + steps - 1 + } + + fn update_limits(&mut self, xy: &XY) { + if self.xmin > xy.x { + self.xmin = xy.x + } + if self.xmax < xy.x { + self.xmax = xy.x + } + if self.ymin > xy.y { + self.ymin = xy.y + } + if self.ymax < xy.y { + self.ymax = xy.y + } + } +} + +#[derive(Debug)] +enum Direction { + North = 1, + South = 2, + West = 3, + East = 4, +} + +impl From for Direction { + fn from(i: isize) -> Self { + match i { + 1 => Direction::North, + 2 => Direction::South, + 3 => Direction::West, + 4 => Direction::East, + _ => unreachable!("no status for computer output"), + } + } +} + +impl Into<(isize, isize)> for &Direction { + fn into(self) -> (isize, isize) { + match self { + Direction::North => (0, 1), + Direction::South => (0, -1), + Direction::West => (-1, 0), + Direction::East => (1, 0), + } + } +} + +#[derive(Clone, Debug)] +enum Status { + HitWall = 0, + MoveComplete = 1, + Finished = 2, + Empty = 3, + Oxigenated = 4, +} + +impl From for Status { + fn from(i: isize) -> Self { + match i { + 0 => Status::HitWall, + 1 => Status::MoveComplete, + 2 => Status::Finished, + _ => unreachable!("no status for computer output"), + } + } +} + +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +struct XY { + x: isize, + y: isize, +} + +impl XY { + fn add(&self, delta: (isize, isize)) -> XY { + XY { + x: self.x + delta.0, + y: self.y + delta.1, + } + } +} diff --git a/src/intcode.rs b/src/intcode.rs new file mode 100644 index 0000000..654d369 --- /dev/null +++ b/src/intcode.rs @@ -0,0 +1,477 @@ +use std::collections::VecDeque; +use std::fs::File; +use std::io::{BufRead, BufReader}; + +#[derive(Copy, Clone)] +enum Var { + A = 1, + B, + C, +} + +#[derive(Copy, Clone, Debug)] +enum Mode { + Position, + Immediate, + Relative, +} + +impl Default for Mode { + fn default() -> Self { + Mode::Position + } +} + +impl From for Mode { + fn from(b: isize) -> Self { + match b { + 0 => Mode::Position, + 1 => Mode::Immediate, + 2 => Mode::Relative, + _ => panic!("invalid mode {}", b), + } + } +} + +#[derive(Copy, Clone, Default, Debug)] +struct Modes { + a: Mode, + b: Mode, + c: Mode, +} + +impl Modes { + fn mode_for(&self, v: &Var) -> Mode { + match v { + Var::A => self.a, + Var::B => self.b, + Var::C => self.c, + } + } +} + +impl From<(Mode, Mode, Mode)> for Modes { + fn from(modes: (Mode, Mode, Mode)) -> Self { + Modes { + a: modes.0, + b: modes.1, + c: modes.2, + } + } +} + +impl From<&ModedOpcode> for Modes { + fn from(mo: &ModedOpcode) -> Self { + mo.modes + } +} + +#[derive(Copy, Clone)] +enum Opcode { + Add, + Multiply, + Input, + Output, + JumpIfTrue, + JumpIfFalse, + LessThan, + Equals, + RelativeAdj, + Halt, +} + +impl From<&ModedOpcode> for Opcode { + fn from(mo: &ModedOpcode) -> Self { + mo.op + } +} + +impl From for Opcode { + fn from(i: isize) -> Self { + match i { + 1 => Opcode::Add, + 2 => Opcode::Multiply, + 3 => Opcode::Input, + 4 => Opcode::Output, + 5 => Opcode::JumpIfTrue, + 6 => Opcode::JumpIfFalse, + 7 => Opcode::LessThan, + 8 => Opcode::Equals, + 9 => Opcode::RelativeAdj, + 99 => Opcode::Halt, + _ => panic!("invalid opcode {}", i), + } + } +} + +struct ModedOpcode { + modes: Modes, + op: Opcode, +} + +impl From for ModedOpcode { + fn from(i: isize) -> Self { + let code = i % 100; + let modes = i / 100; + let a = (modes % 10) / 1; + let b = (modes % 100) / 10; + let c = (modes % 1000) / 100; + let modes = (Mode::from(a), Mode::from(b), Mode::from(c)); + ModedOpcode { + modes: Modes::from(modes), + op: Opcode::from(code), + } + } +} + +pub struct Computer { + pos: usize, + rel: isize, + program: Vec, + modes: Modes, + inputs: VecDeque, + outputs: VecDeque, + pub halted: bool, +} + +impl Clone for Computer { + fn clone(&self) -> Self { + Computer::from(self.program.clone()) + } +} + +impl From for Computer { + fn from(f: File) -> Self { + let f = BufReader::new(f); + let program: Vec = f + .split(b',') + .map(|o| { + let o = o.unwrap(); + let mut o = o.iter().peekable(); + let sign = if o.peek() == Some(&&b'-') { -1 } else { 1 }; + o.filter(|i| **i >= b'0' && **i <= b'9') + .fold(0, |s, i| ((i - b'0') as isize + 10 * s)) + * sign + }) + .collect(); + Computer::from(program) + } +} + +impl From> for Computer { + fn from(program: Vec) -> Self { + Computer { + pos: 0, + rel: 0, + program, + modes: Default::default(), + inputs: VecDeque::new(), + outputs: VecDeque::new(), + halted: false, + } + } +} + +impl Computer { + pub fn get_outputs(&self) -> &VecDeque { + &self.outputs + } + + pub fn pop_output(&mut self) -> Option { + self.outputs.pop_front() + } + + pub fn push_input_and_step(&mut self, input: isize) { + self.inputs.push_back(input); + self.step() + } + + pub fn clone_with_modified_program(&self, u: Vec<(usize, isize)>) -> Computer { + let mut computer = self.clone(); + for (pos, val) in u { + computer.program[pos] = val; + } + computer + } + + pub fn clone_with_input(&self, input: Vec) -> Computer { + let mut computer = self.clone(); + computer.inputs = VecDeque::from(input); + computer + } + + pub fn run(&mut self) { + while !self.halted { + self.step(); + } + } + + pub fn run_until_input(&mut self) -> Option> { + self.step_until_input(); + if self.outputs.len() > 0 { + return Some(self.outputs.split_off(0).into_iter().collect()); + } + return None; + } + + pub fn run_until_n_output(&mut self, n: usize) -> Option> { + loop { + self.step(); + if self.outputs.len() >= n { + return Some(self.outputs.split_off(0).into_iter().collect()); + } + if self.halted { + return None; + } + } + } + + pub fn run_until_output(&mut self) -> Option { + loop { + self.step(); + if self.outputs.len() > 0 { + return self.outputs.pop_front(); + } + if self.halted { + return None; + } + } + } + + pub fn run_must_output(&mut self) -> isize { + self.run_until_output() + .expect("computer halted without output") + } + + pub fn run_io(&mut self, input: isize) -> Option { + self.inputs.push_back(input); + self.run_until_output() + } + + fn step_until_input(&mut self) { + while !self.halted { + let instruction = self.program[self.pos]; + let instruction = ModedOpcode::from(instruction); + self.modes = Modes::from(&instruction); + let advance = match Opcode::from(&instruction) { + Opcode::Add => self.add(), + Opcode::Multiply => self.multiply(), + Opcode::Input => return, + Opcode::Output => self.output(), + Opcode::JumpIfTrue => self.jump_if_true(), + Opcode::JumpIfFalse => self.jump_if_false(), + Opcode::LessThan => self.less_than(), + Opcode::Equals => self.equals(), + Opcode::RelativeAdj => self.relative_adj(), + Opcode::Halt => self.halt(), + }; + self.pos += advance; + } + } + + pub fn step(&mut self) { + let instruction = self.program[self.pos]; + let instruction = ModedOpcode::from(instruction); + self.modes = Modes::from(&instruction); + let advance = match Opcode::from(&instruction) { + Opcode::Add => self.add(), + Opcode::Multiply => self.multiply(), + Opcode::Input => self.input(), + Opcode::Output => self.output(), + Opcode::JumpIfTrue => self.jump_if_true(), + Opcode::JumpIfFalse => self.jump_if_false(), + Opcode::LessThan => self.less_than(), + Opcode::Equals => self.equals(), + Opcode::RelativeAdj => self.relative_adj(), + Opcode::Halt => self.halt(), + }; + self.pos += advance; + } + + fn get_pos(&self, pos: usize) -> isize { + *self.program.get(pos).unwrap_or(&0) + } + + fn get(&self, v: &Var) -> isize { + let want = self.pos + *v as usize; + let want = match self.modes.mode_for(v) { + Mode::Position => self.get_pos(want as usize) as usize, + Mode::Immediate => want, + Mode::Relative => (self.rel + self.get_pos(want)) as usize, + }; + self.get_pos(want) + } + + fn get_a(&self) -> isize { + self.get(&Var::A) + } + + fn get_b(&self) -> isize { + self.get(&Var::B) + } + + fn set(&mut self, v: &Var, value: isize) { + let length = self.program.len(); + let want = self.pos + *v as usize; + let want = match self.modes.mode_for(v) { + Mode::Position => self.get_pos(want) as usize, + Mode::Immediate => want, + Mode::Relative => (self.rel + self.get_pos(want)) as usize, + }; + if length <= want { + self.program.resize(want + 1, 0); + } + self.program[want] = value; + } + + fn set_a(&mut self, value: isize) { + self.set(&Var::A, value) + } + + fn set_c(&mut self, value: isize) { + self.set(&Var::C, value) + } + + fn add(&mut self) -> usize { + self.set_c(self.get_a() + self.get_b()); + 4 + } + + fn multiply(&mut self) -> usize { + self.set_c(self.get_a() * self.get_b()); + 4 + } + + fn input(&mut self) -> usize { + let value = self.inputs.pop_front().expect("no input provided"); + self.set_a(value); + 2 + } + + fn output(&mut self) -> usize { + self.outputs.push_back(self.get_a()); + 2 + } + + fn jump_if_true(&mut self) -> usize { + if self.get_a() > 0 { + self.pos = self.get_b() as usize; + return 0; + } + 3 + } + + fn jump_if_false(&mut self) -> usize { + if self.get_a() == 0 { + self.pos = self.get_b() as usize; + return 0; + } + 3 + } + + fn less_than(&mut self) -> usize { + if self.get_a() < self.get_b() { + self.set_c(1) + } else { + self.set_c(0) + } + 4 + } + + fn equals(&mut self) -> usize { + if self.get_a() == self.get_b() { + self.set_c(1) + } else { + self.set_c(0) + } + 4 + } + + fn relative_adj(&mut self) -> usize { + self.rel = self.rel + self.get_a(); + 2 + } + + fn halt(&mut self) -> usize { + self.halted = true; + 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn example1() { + let program = vec![ + 109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99, + ]; + let mut computer = Computer::from(program.clone()); + computer.run(); + assert_eq!(computer.get_outputs().iter().eq(program.iter()), true) + } + + #[test] + fn example2() { + assert_eq!( + Computer::from(vec![1102, 34915192, 34915192, 7, 4, 7, 99, 0]).run_must_output(), + 1219070632396864 + ) + } + + #[test] + fn example3() { + assert_eq!( + Computer::from(vec![104, 1125899906842624, 99]).run_must_output(), + 1125899906842624 + ) + } + + #[test] + fn day5exampe1() { + let computer = Computer::from(vec![3, 9, 8, 9, 10, 9, 4, 9, 99, -1, 8]); + assert_eq!(computer.clone_with_input(vec![1]).run_must_output(), 0); + assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); + } + + #[test] + fn day5exampe2() { + let computer = Computer::from(vec![3, 3, 1108, -1, 8, 3, 4, 3, 99]); + assert_eq!(computer.clone_with_input(vec![1]).run_must_output(), 0); + assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); + } + + #[test] + fn day5exampe3() { + let computer = Computer::from(vec![ + 3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9, + ]); + assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 0); + assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); + let computer = Computer::from(vec![3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1]); + assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 0); + assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); + } + + #[test] + fn day5exampe4() { + let computer = Computer::from(vec![ + 3, 21, 1008, 21, 8, 20, 1005, 20, 22, 107, 8, 21, 20, 1006, 20, 31, 1106, 0, 36, 98, 0, + 0, 1002, 21, 125, 20, 4, 20, 1105, 1, 46, 104, 999, 1105, 1, 46, 1101, 1000, 1, 20, 4, + 20, 1105, 1, 46, 98, 99, + ]); + assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 999); + assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1000); + assert_eq!(computer.clone_with_input(vec![9]).run_must_output(), 1001); + } + + #[test] + fn day9part1() { + let computer = Computer::from(File::open("input9").unwrap()); + assert_eq!( + computer.clone_with_input(vec![1]).run_must_output(), + 3063082071 + ); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b6b007e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod intcode; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -}