diff --git a/.travis.yml b/.travis.yml index 6d18553..34590a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ jobs: - cd python script: - pytest + - mypy 1 2 3 - language: rust rust: stable diff --git a/python/2/day2.py b/python/2/day2.py index c48e104..eeb898c 100644 --- a/python/2/day2.py +++ b/python/2/day2.py @@ -49,6 +49,8 @@ def part2() -> int: if cl[0] == 19690720: return 100 * noun + verb + return -1 + if __name__ == '__main__': print(part1()) diff --git a/python/3/day3.py b/python/3/day3.py new file mode 100644 index 0000000..2114574 --- /dev/null +++ b/python/3/day3.py @@ -0,0 +1,80 @@ +from typing import Tuple, List + + +def parse_path(path: str): + commands = path.split(",") + + return [(c[0], int(c[1:])) for c in commands] + + +def record_path(path) -> List[Tuple[int, int]]: + pos = [0, 0] + poslist = [] + for direction, length in path: + while length > 0: + if direction == "U": + pos[1] += 1 + elif direction == "D": + pos[1] -= 1 + elif direction == "R": + pos[0] += 1 + elif direction == "L": + pos[0] -= 1 + else: + raise ValueError(f"invalid direction: {direction}") + poslist.append((pos[0], pos[1])) + length -= 1 + return poslist + + +def manhatten_distance(point: Tuple[int, int]) -> int: + return abs(point[0]) + abs(point[1]) + + +def closest_crossing(first_path, second_path) -> int: + p1 = record_path(first_path) + p2 = record_path(second_path) + commons = set(p1).intersection(set(p2)) + min_dist = 100000 + for crossing in commons: + dist = manhatten_distance(crossing) + if dist < min_dist: + min_dist = dist + return min_dist + + +def shortest_signal(first_path, second_path) -> int: + p1 = record_path(first_path) + p2 = record_path(second_path) + commons = set(p1).intersection(set(p2)) + shortest = 10000000 + for crossing in commons: + total = 0 + for path in [p1, p2]: + i = 0 + for segment in path: + i += 1 + if segment == crossing: + total += i + break + if total < shortest: + shortest = total + + return shortest + + +def part1() -> int: + with open("3/input.txt") as f: + w1, w2 = f.readlines() + return closest_crossing(parse_path(w1), parse_path(w2)) + + +def part2() -> int: + with open("3/input.txt") as f: + w1, w2 = f.readlines() + return shortest_signal(parse_path(w1), parse_path(w2)) + + +if __name__ == '__main__': + print(part1()) + print(part2()) diff --git a/python/3/input.txt b/python/3/input.txt new file mode 100644 index 0000000..9002b7e --- /dev/null +++ b/python/3/input.txt @@ -0,0 +1,2 @@ +R990,U796,R784,U604,R6,U437,L96,U285,L361,U285,L339,D512,L389,D840,L425,U444,L485,D528,L262,U178,L80,U2,R952,U459,L361,D985,R56,U135,R953,D913,L361,U120,L329,U965,L294,U890,L126,U214,R232,D444,L714,U791,R888,U923,R378,U233,L654,D703,R902,D715,R469,D60,R990,U238,R755,U413,L409,D601,R452,U504,R472,D874,L766,D594,R696,U398,R593,D889,R609,D405,L962,U176,L237,U642,L393,D91,L463,U936,R199,D136,R601,D8,R359,D863,L410,U598,L444,D34,R664,D323,R72,D98,L565,D476,L197,D132,R510,U665,R936,U3,R385,U144,L284,D713,L605,U106,R543,D112,R528,D117,R762,U330,R722,U459,L229,U375,L870,D81,R623,U95,L148,D530,L622,D62,R644,D365,L214,U847,R31,D832,L648,D293,R79,D748,L270,U159,L8,U83,R195,U912,L409,D649,L750,D286,L623,D956,R81,U775,R44,D437,L199,U698,L42,U419,L883,U636,L323,U89,L246,D269,L992,U739,R62,U47,R63,U17,L234,U135,R126,D208,L69,U550,L123,D66,R463,U992,R411,D276,L851,U520,R805,D300,L894,U171,L922,D901,R637,U907,R328,U433,L316,D644,L398,U10,L648,D190,R884,U474,R397,D718,L925,D578,R249,U959,L697,D836,R231,U806,R982,U827,R579,U830,L135,D666,R818,D502,L898,D585,R91,D190,L255,U535,R56,U390,R619,D815,L300,D81,R432,D70,L940,D587,L259,D196,R241,U4,R440,U678,R185,U451,R733,D984,R464,D298,L738,U600,R353,D44,L458,U559,L726,D786,L307,D333,L226,D463,R138,D142,L521,D201,R51,D202,L204,U130,L333,U597,R298,U42,L951,U66,R312,U707,L555,D225,L360,D12,L956,D361,L989,D625,L944,D398,L171,D982,L377,U114,L339,U164,R39,D793,R992,U834,R675,U958,R334,D697,L734,D40,L149,U394,R976 +L1005,D52,L125,U787,L761,U262,L466,D966,R895,U789,R6,U2,R870,U971,R238,D946,L752,D240,R721,U349,L679,D518,L104,U417,L462,U544,L519,U797,R873,U70,R298,U45,L779,D921,R468,D421,R803,U108,L812,D498,R226,D309,R766,U724,L961,U472,R940,U944,R418,D682,R328,U55,R737,U961,L343,U397,R112,D292,L155,U162,R398,U445,L524,U256,R323,D587,L862,D726,R624,D230,R460,U539,R723,U93,L507,U608,L150,U159,R35,U458,R208,U546,L495,D835,L636,U960,L322,U408,L78,D250,L994,U818,R107,U978,R401,D147,R574,D549,R983,U698,L99,D63,L772,U409,R975,U990,L893,U467,L860,D721,R504,U102,R678,D672,L406,D933,R743,D788,R142,D44,R208,D424,R28,D674,R331,D968,L154,U206,R222,D354,R687,D331,L539,D390,L373,D514,L622,U673,R345,U943,L508,D337,R265,D785,L189,U429,R344,D719,R622,U199,L765,U350,R833,U309,R95,U911,R548,U746,R107,D867,L648,D680,R28,U596,L891,U168,R933,U571,R365,U267,R916,D130,L149,D898,L513,D167,R587,U799,R134,D328,R562,D929,L399,U568,R565,U241,L395,U822,L624,D145,L995,U516,R474,D609,R153,U52,R561,D15,R283,U321,L850,U218,L225,D635,L630,U102,L84,D672,L128,D885,L506,U911,R355,D132,R155,D120,L110,U368,R149,D343,L708,U378,R591,D585,L381,D517,R852,U432,R342,U273,R893,D277,L548,U859,L891,U311,L901,U255,R421,U90,L72,D474,L654,U12,L146,D867,L485,D663,R123,D82,L21,U408,L38,D864,L114,D645,R936,U765,L832,D668,L482,U79,L594,U276,L559,D469,R314,D162,R621,U230,L688,U82,R605,U191,L381,D327,L91,D217,R714,D942,R851,U358,R22,U952,R279,D897,R485,D867,L940,U891,L223,D264,L634,D560,R645,D705,L289,U584,R97,U920,R41 diff --git a/python/3/test_day3.py b/python/3/test_day3.py new file mode 100644 index 0000000..f4760fa --- /dev/null +++ b/python/3/test_day3.py @@ -0,0 +1,33 @@ +from day3 import parse_path, manhatten_distance, closest_crossing, part1, shortest_signal, part2 + + +def test_parse_path(): + assert parse_path("R8,U5,L5,D3") == [('R', 8), ('U', 5), ('L', 5), ('D', 3)] + + +def test_manhatten_distance(): + assert manhatten_distance((1, 4)) == 5 + + +def test_closest_crossing(): + assert closest_crossing(parse_path("R8,U5,L5,D3"), parse_path("U7,R6,D4,L4")) == 6 + assert closest_crossing(parse_path("R75,D30,R83,U83,L12,D49,R71,U7,L72"), + parse_path("U62,R66,U55,R34,D71,R55,D58,R83")) == 159 + assert closest_crossing(parse_path("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51"), + parse_path("U98,R91,D20,R16,D67,R40,U7,R15,U6,R7")) == 135 + + +def test_shortest_signal(): + assert shortest_signal(parse_path("R8,U5,L5,D3"), parse_path("U7,R6,D4,L4")) == 30 + assert shortest_signal(parse_path("R75,D30,R83,U83,L12,D49,R71,U7,L72"), + parse_path("U62,R66,U55,R34,D71,R55,D58,R83")) == 610 + assert shortest_signal(parse_path("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51"), + parse_path("U98,R91,D20,R16,D67,R40,U7,R15,U6,R7")) == 410 + + +def test_part1(): + assert part1() == 316 + + +def test_part2(): + assert part2() == 16368 diff --git a/rust/src/day2.rs b/rust/src/day2.rs new file mode 100644 index 0000000..93e7fa7 --- /dev/null +++ b/rust/src/day2.rs @@ -0,0 +1,109 @@ +use std::fs; + +type IntCode = Vec; + +fn parse_intcode(codestr: &str) -> IntCode { + let split: IntCode = codestr + .split(",") + .map(|codestr| codestr.parse::().expect("can't parse intcode")) + .collect(); + return split; +} + +fn run_intcode(mut cl: IntCode) -> IntCode { + let mut p = 0; // pointer, + loop { + let code = cl[p]; + if code == 99 { + break; + } + let from1 = cl[p + 1] as usize; + let from2 = cl[p + 2] as usize; + let to = cl[p + 3] as usize; + cl[to] = match code { + 1 => cl[from1] + cl[from2], + 2 => cl[from1] * cl[from2], + _ => panic!("invalid intcode: {}", code), + }; + p += 4 + } + + return cl; +} + +pub fn part1() -> i32 { + let mut data = fs::read_to_string("../python/2/input.txt").expect("Unable to read file"); + data.pop(); + let mut cl = parse_intcode(data.as_str()); + cl[1] = 12; + cl[2] = 2; + + cl = run_intcode(cl); + return cl[0]; +} +pub fn part2() -> i32 { + let mut data = fs::read_to_string("../python/2/input.txt").expect("Unable to read file"); + data.pop(); + let inital_cl = parse_intcode(data.as_str()); + let mut cl: IntCode; + for noun in 0..99 { + for verb in 0..99 { + cl = inital_cl.clone(); + cl[1] = noun; + cl[2] = verb; + cl = run_intcode(cl); + if cl[0] == 19690720 { + return 100 * noun + verb; + } + } + } + return -1; +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_parse_intcode() { + assert_eq!(parse_intcode("2,2,3,45,5"), [2, 2, 3, 45, 5]) + } + #[test] + #[should_panic(expected = "invalid intcode: 123")] + fn test_opcode_panics() { + let cl = vec![123, 123, 123, 123]; + run_intcode(cl); + } + #[test] + fn test_run_intcode1() { + let mut cl: IntCode = vec![1, 0, 0, 0, 99]; + cl = run_intcode(cl); + assert_eq!(cl, vec![2, 0, 0, 0, 99]) + } + #[test] + fn test_run_intcode2() { + let mut cl: IntCode = vec![2, 3, 0, 3, 99]; + cl = run_intcode(cl); + assert_eq!(cl, vec![2, 3, 0, 6, 99]) + } + #[test] + fn test_run_intcode3() { + let mut cl: IntCode = vec![2, 4, 4, 5, 99, 0]; + cl = run_intcode(cl); + assert_eq!(cl, vec![2, 4, 4, 5, 99, 9801]) + } + #[test] + fn test_run_intcode4() { + let mut cl: IntCode = vec![1, 1, 1, 4, 99, 5, 6, 0, 99]; + cl = run_intcode(cl); + assert_eq!(cl, vec![30, 1, 1, 4, 2, 5, 6, 0, 99]) + } + #[test] + fn test_part1() { + assert_eq!(part1(), 4714701) + } + + #[test] + fn test_part2() { + assert_eq!(part2(), 5121) + } +}