summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--01/input.txt1
-rwxr-xr-x01/main.py35
-rwxr-xr-x01/test.py60
-rw-r--r--02/input.txt1000
-rwxr-xr-x02/main.py42
-rwxr-xr-x02/test.py32
-rw-r--r--03/input.txt1
-rwxr-xr-x03/main.py48
-rwxr-xr-x03/test.py40
-rwxr-xr-x04/main.py33
-rwxr-xr-x04/test.py21
-rw-r--r--05/input.txt1000
-rwxr-xr-x05/main.py73
-rwxr-xr-x05/test.py32
-rw-r--r--06/input.txt300
-rwxr-xr-x06/main.py53
-rwxr-xr-x06/test.py36
-rw-r--r--07/input.txt339
-rwxr-xr-x07/main.py61
-rwxr-xr-x07/test.py57
-rw-r--r--08/input.txt300
-rwxr-xr-x08/main.py64
-rwxr-xr-x08/test.py18
-rw-r--r--09/input.txt28
-rwxr-xr-x09/main.py60
-rwxr-xr-x09/test.py22
-rwxr-xr-x10/main.py41
-rwxr-xr-x10/test.py28
-rwxr-xr-x11/main.py71
-rwxr-xr-x11/test.py23
-rw-r--r--12/input.txt1
-rwxr-xr-x12/main.py41
-rwxr-xr-x12/test.py65
-rw-r--r--13/input.txt56
-rwxr-xr-x13/main.py72
-rwxr-xr-x13/test.py30
-rw-r--r--14/input.txt9
-rwxr-xr-x14/main.py75
-rwxr-xr-x14/test.py21
-rw-r--r--15/input.txt4
-rwxr-xr-x15/main.py78
-rwxr-xr-x15/test.py23
-rw-r--r--16/input.txt500
-rwxr-xr-x16/main.py66
-rw-r--r--17/input.txt20
-rwxr-xr-x17/main.py52
-rwxr-xr-x17/test.py19
-rw-r--r--18/input.txt100
-rwxr-xr-x18/main.py59
-rwxr-xr-x18/test.py25
-rw-r--r--19/input.txt45
-rwxr-xr-x19/main.py128
-rwxr-xr-x19/test.py39
-rwxr-xr-x20/main.py45
-rwxr-xr-x20/test.py16
-rw-r--r--21/input.txt3
-rwxr-xr-x21/main.py155
-rwxr-xr-x21/test.py27
-rw-r--r--22/input.txt2
-rwxr-xr-x22/main.py137
-rwxr-xr-x22/test.py39
-rw-r--r--23/input.txt47
-rwxr-xr-x23/main.py65
-rwxr-xr-x23/test.py22
-rw-r--r--24/input.txt28
-rwxr-xr-x24/main.py86
-rwxr-xr-x24/test.py18
-rw-r--r--25/input.txt1
-rwxr-xr-x25/main.py47
-rwxr-xr-x25/test.py17
71 files changed, 6203 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c18dd8d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+__pycache__/
diff --git a/01/input.txt b/01/input.txt
new file mode 100644
index 0000000..becde71
--- /dev/null
+++ b/01/input.txt
@@ -0,0 +1 @@
+((((()(()(((((((()))(((()((((()())(())()(((()((((((()((()(()(((()(()((())))()((()()())))))))))()((((((())((()))(((((()(((((((((()()))((()(())()((())((()(()))((()))()))()(((((()(((()()))()())((()((((())()())()((((())()(()(()(((()(())(()(())(((((((())()()(((())(()(()(()(())))(()((((())((()))(((()(()()(((((()()(()(((()(((((())()))()((()(()))()((()((((())((((())(()(((())()()(()()()()()(())((((())((())(()()))()((((())))((((()())()((((())((()())((())(())(((((()((((()(((()((((())(()(((()()))()))((((((()((())()())))(((()(()))(()()(()(((()(()))((()()()())((()()()(((())())()())())())((()))(()(()))(((((()(()(())((()(())(())()((((()())()))((((())(())((())())((((()(((())(())((()()((((()((((((()(())()()(()(()()((((()))(())()())()))(())))(())))())()()(())(()))()((()(()(())()()))(()())))))(()))(()()))(())(((((()(()(()()((())()())))))((())())((())(()(())((()))(())(((()((((((((()()()(()))()()(((()))()((()()(())(())())()(()(())))(((((()(())(())(()))))())()))(()))()(()(((((((()((((())))())())())())()((((((((((((((()()((((((()()()())())()())())())(())(())))())((()())((()(()))))))()))))))))))))))))())((())((())()()))))))(((()((()(()()))((())(()()))()()())))(())))))))(()(((())))())()())))()()(())()))()(()))())((()()))))(()))))()))(()()(())))))))()(((()))))()(()))(())())))))()))((()))((()))())(())))))))))((((())()))()))()))())(())()()(())))())))(()())()))((()()(())))(())((((((()(())((()(((()(()()(())))()))))))()))()(()((()))()(()))(()(((())((((())())(())(()))))))))())))))))())())))))())))))()()(((())()(()))))))))())))))(())()()()))()))()))(()(())()()())())))))))())()(()(()))))()()()))))())(()))))()()))))()())))))(((())()()))(()))))))))))()()))))()()()))))(()())())()()())()(()))))()(()))(())))))))(((((())(())())()()))()()))(())))))()(()))))(())(()()))()())()))()))()))()))))())()()))())())))(()))(()))))))())()(((())()))))))))()))()())))())))())))()))))))))))()()))(()()))))))(())()(()))))())(()))))(()))))(()())))))())())()()))))())()))))))))(()))))()))))))()(()())))))))()))())))())))())))())))))))())(()()))))))(()())())))()())()))))))))))))))())))()(())))()))())()()(())(()()))(())))())()())(()(()(()))))())))))))))))())(()))()))()))))(())()())()())))))))))))()()))))))))))))())())))))(()())))))))))))())(())))()))))))))())())(()))()))(())))()))()()(())()))))))()((((())()))())())))))()))()))))((()())()))))())))(())))))))))))))))))()))))()()())()))()()))))())()))((()())))())))(()))(()())))))))()))()))))(())))))))(())))))())()()(()))())()))()()))))())()()))))())()))())))))))(()))))()())()))))))))(()))())))(()))()))))(())()))())())(())())())))))))((((())))))()))()))()())()(())))()))()))()())(()())()()(()())()))))())())))))(()))()))))())(()()(())))))(())()()((())())))))(())(())))))))())))))))))()(())))))))()())())())()(()))))))))(()))))))))())()()))()(()))))))()))))))())))))))(())))()()(())()())))))(((())))()((())()))())))(()()))())(())())))()(((()())))))()(()()())))()()(()()(()()))())()(()()()))())()()))()())(()))))())))))())))(())()()))))(()))))(())(()))(())))))()()))()))))())()))()()(())())))((()))())()))))))()()))))((()(()))))()()))))))())))))())(()((()())))))))))))()())())))()))(()))))))(()))(())()())))(()))))))))())()()()()))))(()())))))))((())))()))(()))(())(())()())()))))))))(())))())))(()))()()))(()()))(()))())))()(())))())((()((()(())))((())))()))))((((())())()())))(())))()))))))())(()()((())))())()(()())))))(()())()))())))))))((())())))))))(()(()))())()()(()()(((()(((()())))))()))))))()(())(()()((()()(())()()))())()())()))()())())())))))))(((())))))))()()))))))(((())()))(()()))(()()))))(()(()()((((())()())((()()))))(()(())))))()((()()()())()()((()((()()))(()))(((()()()))(((())))()(((())()))))))((()(())())))(()())(((((()(()))(()((()))(()())()))))(()(()))()(()))(())(((())(()()))))()()))(((()))))(()()()()))())))((()()()(())()))()))))()()))()))))))((((((()()()))))())((()()(((()))))(()(())(()()())())())))()(((()()))(())((())))(()))(()()()())((())())())(()))))()))()((()(())()(()()(())(()))(())()))(())(()))))(())(())())(()()(()((()()((())))((()))()((())))(((()()()()((((()))(()()))()()()(((())((())())(()()(()()()))()((())(())()))())(((()()(())))()((()()())()())(()(())())(((())(())())((())(())()(((()()))(())))((())(()())())(())((()()()((((((())))((()(((((())()))()))(())(()()))()))(())()()))(())((()()())()()(()))())()((())))()((()()())((((()())((())())())((()((()))()))((())((()()(()((()()(((())(()()))))((()((())()(((())(()((())())((())(()((((((())())()(()())()(())(((())((((((()(())(()((()()()((()()(()()()())))()()(((((()()))()((((((()))()(()(()(()(((()())((()))())()((()))(())))()))()()))())()()))())((((())(()(()))(((((((())(((()(((((()(((()()((((())(((())())))(()()()(()(()))()))((((((()))((()(((()(())((()((((()((((((())(((((())))(((()(()))))(((()(((())()((())(()((()))(((()()(((())((((()(()(((((()))(((()(((((((()(()()()(()(()(()()())(())(((((()(())())()())(()(()(()))()(()()()())(()()(()((()))()((())())()(()))((())(()))()(()))()(((()(()(()((((((()()()()())()(((((()()(((()()()((()(((((()))((((((((()()()(((((()))))))(()()()(())(()))(()()))))(())()))(((((()(((((()()(()(()())(((()))((((()((()(()(()((()(()((())))()(((()((()))((()))(((((((((()((()((()(())))()((((()((()()))((())(((()(((((()()(()(()()((()(()()()(((((((())())()())))))((((()()(()))()))(()((())()(()(((((((((()()(((()(()())(()((()())((())())((((()(((()(((()((((()((()((((()(()((((((())((((((((((((()()(()()((((((((((((((()((()()))()((((((((((((())((((()(()())((()(()(()))()(((((()()(((()()))()())(())((()(((((()((())(((((()((()(((((()))()()((((())()((((())(((((((((()(())(()(())))())(()((())(((())(())(())())(()(()(())()()((()((())()(((()(((((()(())))()(((()((())))((()()()(((()(((()((()(()(())(()((()())(()(()(((()(((((((((())(()((((()()))(()((((()()()()(((()((((((((()(()()((((((()(()()(()((()((((((((((()()(((((((()())(())))(((()()))(((((()((()()())(()()((((())((()((((()))))(())((()(()()(((()(()(((()((((()(((((()))())())(()((())()))(((()())((())((())((((()((()((((((())(()((((()()))((((((())()(()))((()(((())((((((((((()()(((((()(((((()((()()()((((())))(()))()((()(())()()((()((((((((((()((())(())(((((()(()(()()))((((()((((()()((()(((()(((((((((()(()((()((()))((((((()(((())()()((()(((((((()())))()()(()((()((()()(((()(()()()()((((()((())((((()(((((((((()(((()()(((()(()(((()(((()((())()(()((()(()(()(()))()(((()))(()((((()((())((((())((((((())(()))(()((((())((()(()((((((((()()((((((()(()(()()()(())((()((()()(((()(((((((()()((()(((((((()))(((((()(((()(()()()(()(((()((()()((())(()(((((((((()(()((()((((((()()((())()))(((((()((())()())()(((((((((((()))((((()()()()())(()()(()(()()))()))(()))(()(((()()))())(()(()))()()((())(()())()())()(()))()))(()()(()((((((())((()(((((((((((()(())()((()(()((()((()(()((()((((((((((()()())((())()(())))((())()())()(((((()(()())((((()((()(())(()))(((())()((()))(((((())(()))()()(()))(((())((((()((((()(())))(((((((()))))())()())(())((())()(()()((()(()))()(()()(()()((()())((())((()()))((((()))()()))(()()(())()()(((((()(())((()((((()))()))(()())())(((()()(()()))(())))))(()))((())(((((()((((()))()((((()))()((())(((())))(((()())))((()(()()((
diff --git a/01/main.py b/01/main.py
new file mode 100755
index 0000000..cd3ec34
--- /dev/null
+++ b/01/main.py
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+
+
+
+def solve_partn(partn, data):
+ floor = 0
+ for i, d in enumerate(data):
+ if d == "(":
+ floor += 1
+ elif d == ")":
+ floor -= 1
+
+ if partn == 2:
+ if floor < 0:
+ return i + 1
+
+ if partn == 1:
+ return floor
+ else: # partn == 2
+ return -1 # only reached if he never goes underground
+
+
+
+def main():
+ # Read instructions from input text file
+ with open("input.txt", "r") as f:
+ data = f.read()
+
+ print("Part 1 solution:", solve_partn(1, data)) # 74 for me
+ print("Part 2 solution:", solve_partn(2, data)) # 1795 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/01/test.py b/01/test.py
new file mode 100755
index 0000000..98cbd7d
--- /dev/null
+++ b/01/test.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ data = "(())"
+ self.assertEqual(main.solve_partn(1, data), 0)
+
+ def test_example2(self):
+ data = "()()"
+ self.assertEqual(main.solve_partn(1, data), 0)
+
+ def test_example3(self):
+ data = "((("
+ self.assertEqual(main.solve_partn(1, data), 3)
+
+ def test_example4(self):
+ data = "(()(()("
+ self.assertEqual(main.solve_partn(1, data), 3)
+
+ def test_example5(self):
+ data = "))((((("
+ self.assertEqual(main.solve_partn(1, data), 3)
+
+ def test_example6(self):
+ data = "())"
+ self.assertEqual(main.solve_partn(1, data), -1)
+
+ def test_example7(self):
+ data = "))("
+ self.assertEqual(main.solve_partn(1, data), -1)
+
+ def test_example8(self):
+ data = ")))"
+ self.assertEqual(main.solve_partn(1, data), -3)
+
+ def test_example9(self):
+ data = ")())())"
+ self.assertEqual(main.solve_partn(1, data), -3)
+
+
+
+class ExamplesPart2(unittest.TestCase):
+ def test_example1(self):
+ data = ")"
+ self.assertEqual(main.solve_partn(2, data), 1)
+
+ def test_example2(self):
+ data = "()())"
+ self.assertEqual(main.solve_partn(2, data), 5)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/02/input.txt b/02/input.txt
new file mode 100644
index 0000000..5f3335f
--- /dev/null
+++ b/02/input.txt
@@ -0,0 +1,1000 @@
+29x13x26
+11x11x14
+27x2x5
+6x10x13
+15x19x10
+26x29x15
+8x23x6
+17x8x26
+20x28x3
+23x12x24
+11x17x3
+19x23x28
+25x2x25
+1x15x3
+25x14x4
+23x10x23
+29x19x7
+17x10x13
+26x30x4
+16x7x16
+7x5x27
+8x23x6
+2x20x2
+18x4x24
+30x2x26
+6x14x23
+10x23x9
+29x29x22
+1x21x14
+22x10x13
+10x12x10
+20x13x11
+12x2x14
+2x16x29
+27x18x26
+6x12x20
+18x17x8
+14x25x1
+30x15x22
+17x18x7
+28x23x24
+15x12x25
+14x7x20
+29x23x8
+24x5x22
+6x22x8
+1x15x26
+14x5x1
+24x28x28
+17x23x23
+4x15x7
+23x8x11
+6x15x1
+23x18x13
+17x1x26
+23x13x17
+2x18x8
+22x22x1
+10x22x6
+28x29x20
+22x21x25
+14x8x23
+12x30x14
+8x7x5
+3x30x15
+4x3x29
+25x18x3
+16x7x16
+4x3x8
+9x16x30
+20x28x3
+28x24x6
+4x18x2
+23x18x5
+22x4x30
+15x30x9
+7x12x12
+3x22x29
+12x1x9
+9x2x25
+17x11x10
+25x24x7
+7x27x26
+26x4x12
+29x2x26
+19x24x12
+23x23x3
+26x28x16
+18x4x16
+25x30x18
+29x19x19
+16x3x27
+29x25x29
+18x19x5
+14x21x30
+19x13x26
+19x10x15
+9x4x7
+18x6x6
+24x25x29
+9x12x27
+15x3x22
+30x17x21
+18x19x28
+9x11x12
+8x28x22
+11x3x4
+28x17x20
+24x18x15
+11x12x13
+6x19x24
+28x4x5
+28x22x23
+13x29x2
+9x16x15
+29x28x1
+10x18x30
+19x11x12
+26x28x25
+23x17x13
+25x1x21
+17x1x27
+17x27x28
+28x13x15
+14x13x25
+11x29x7
+22x29x5
+13x6x14
+23x18x13
+25x7x17
+18x9x20
+21x11x2
+28x11x13
+13x25x1
+19x29x25
+16x29x4
+10x21x10
+7x25x17
+5x9x3
+1x15x6
+8x27x29
+23x6x30
+22x22x29
+6x20x30
+26x25x29
+10x19x19
+20x30x9
+5x30x24
+17x10x27
+30x14x30
+8x17x4
+7x18x6
+3x5x4
+24x17x15
+14x20x17
+22x27x15
+18x14x15
+23x9x11
+21x16x29
+7x18x21
+9x3x29
+10x13x4
+2x30x4
+23x20x4
+8x22x21
+29x28x4
+13x16x25
+21x9x11
+7x26x26
+13x23x30
+19x7x10
+9x23x21
+21x9x17
+9x21x15
+20x29x22
+23x13x15
+19x25x2
+12x11x30
+20x21x6
+21x6x17
+24x26x9
+29x21x29
+29x26x16
+6x16x1
+2x12x6
+6x7x20
+7x2x22
+6x22x4
+13x11x27
+25x27x14
+11x8x6
+26x11x14
+30x3x29
+27x21x20
+15x16x26
+6x22x10
+11x9x25
+23x13x6
+13x9x3
+30x22x13
+29x23x14
+25x19x6
+7x29x11
+19x18x5
+29x25x13
+25x24x27
+1x9x12
+22x9x17
+14x12x28
+19x21x17
+13x25x17
+14x25x12
+4x14x30
+7x15x28
+3x6x25
+6x2x16
+15x19x11
+17x30x20
+20x23x7
+26x21x6
+26x29x24
+2x4x30
+4x22x18
+13x3x28
+27x6x21
+5x3x27
+12x7x11
+28x11x9
+12x9x2
+1x22x20
+15x13x28
+14x19x16
+28x20x3
+20x4x9
+26x7x26
+18x19x25
+7x1x13
+20x23x29
+27x26x8
+11x15x15
+10x21x23
+29x2x11
+21x28x20
+3x18x23
+26x17x17
+14x26x17
+20x7x17
+18x12x8
+4x8x8
+8x15x23
+24x29x5
+1x25x8
+1x28x17
+16x18x13
+29x24x22
+13x16x10
+14x7x16
+15x11x29
+12x15x19
+17x6x28
+4x3x9
+15x16x8
+29x27x11
+2x24x20
+4x21x3
+29x24x27
+18x22x22
+7x8x18
+20x7x8
+19x9x2
+20x17x2
+2x29x10
+19x25x1
+28x9x3
+29x27x20
+7x21x7
+10x4x22
+26x8x5
+26x14x1
+5x27x9
+2x18x3
+3x27x17
+30x17x23
+30x11x20
+4x6x7
+6x29x27
+30x16x20
+24x30x28
+19x20x26
+18x1x25
+26x12x12
+19x15x29
+16x21x24
+23x13x26
+25x16x10
+8x9x18
+24x14x1
+24x15x21
+19x9x14
+8x23x11
+22x2x16
+29x9x26
+3x16x25
+15x20x30
+3x11x12
+15x2x3
+13x7x4
+2x7x27
+9x26x11
+30x24x19
+28x17x21
+10x8x2
+11x15x26
+10x12x20
+24x24x27
+25x26x16
+13x4x20
+25x13x11
+12x22x3
+20x7x1
+12x18x6
+26x8x20
+14x2x7
+23x12x1
+26x24x24
+27x26x23
+26x17x5
+17x24x2
+26x5x6
+23x5x1
+5x18x30
+24x21x19
+5x28x11
+21x20x14
+25x4x22
+26x24x11
+7x5x8
+13x1x30
+5x1x6
+14x5x2
+8x11x7
+13x20x1
+17x30x14
+29x22x10
+12x26x3
+27x17x3
+26x27x4
+5x26x17
+22x11x19
+8x26x3
+24x19x22
+7x1x4
+6x27x30
+4x28x14
+16x14x18
+4x5x20
+19x25x4
+15x15x1
+10x14x14
+16x18x24
+21x27x15
+5x5x10
+1x7x13
+16x2x8
+13x15x11
+3x25x10
+20x29x8
+12x3x2
+10x13x12
+25x27x1
+11x30x19
+7x19x13
+27x6x18
+16x21x19
+21x29x5
+16x23x12
+29x19x15
+5x5x10
+27x15x1
+13x16x22
+29x19x5
+8x12x9
+3x18x5
+13x25x3
+5x9x21
+10x20x16
+9x9x11
+23x21x1
+22x2x15
+27x8x13
+23x7x3
+26x30x15
+29x15x16
+16x27x13
+2x18x9
+10x27x8
+20x9x25
+10x2x17
+16x13x13
+21x26x1
+27x26x24
+9x30x16
+19x17x28
+25x15x1
+10x26x6
+10x11x11
+5x26x25
+30x4x15
+9x8x23
+14x25x7
+8x28x8
+28x18x24
+4x4x25
+16x25x11
+17x27x8
+15x16x9
+24x13x21
+17x3x27
+27x5x26
+8x27x12
+29x2x8
+24x23x30
+1x30x21
+6x18x20
+13x14x12
+25x30x23
+24x6x24
+12x7x21
+11x6x8
+8x30x30
+26x3x12
+28x6x5
+18x7x1
+7x6x20
+14x16x18
+11x22x15
+4x20x10
+19x24x19
+8x24x11
+4x9x10
+6x6x22
+10x9x29
+1x5x28
+19x25x29
+20x30x3
+15x13x13
+9x9x24
+20x14x29
+26x24x13
+2x25x8
+10x26x2
+12x19x12
+18x6x20
+4x5x14
+26x27x10
+16x26x20
+3x21x15
+2x26x18
+14x11x17
+26x26x25
+10x1x11
+17x19x19
+27x28x26
+9x2x10
+19x30x15
+23x30x14
+15x3x20
+2x14x22
+21x18x8
+22x4x29
+19x6x29
+9x26x29
+16x10x9
+22x12x22
+13x28x14
+25x14x28
+28x3x30
+10x17x1
+10x27x22
+10x23x19
+14x25x9
+11x24x8
+30x25x10
+22x13x28
+2x7x6
+11x20x8
+9x22x14
+19x16x9
+11x24x4
+11x17x2
+6x4x10
+26x10x10
+12x14x5
+27x10x3
+15x3x6
+11x7x19
+22x10x12
+21x26x10
+13x20x3
+27x8x8
+1x24x23
+24x9x22
+23x17x23
+3x28x19
+2x20x28
+23x17x24
+26x1x4
+4x1x12
+5x6x16
+13x22x13
+25x21x21
+20x21x12
+9x24x25
+17x16x12
+12x28x9
+18x16x27
+29x12x2
+30x12x15
+24x11x10
+4x9x22
+4x24x5
+19x11x5
+6x25x6
+1x20x17
+22x8x21
+11x26x4
+16x19x3
+8x12x8
+13x2x18
+10x5x11
+8x12x17
+21x2x5
+26x17x26
+23x18x17
+28x11x14
+1x4x27
+29x5x28
+5x9x10
+5x7x25
+20x15x27
+15x11x17
+12x14x1
+29x14x4
+18x14x18
+14x25x24
+26x14x18
+13x8x11
+30x1x23
+3x4x12
+12x24x9
+8x6x16
+14x15x30
+12x30x8
+22x11x18
+16x30x28
+17x18x4
+13x14x23
+2x28x8
+3x28x30
+29x30x8
+4x6x26
+6x30x17
+11x30x30
+19x4x3
+12x15x20
+22x28x4
+26x30x2
+6x12x7
+1x10x5
+25x29x7
+17x9x18
+16x21x29
+21x14x7
+15x16x11
+26x6x15
+8x24x7
+2x20x4
+2x9x3
+19x8x13
+18x7x22
+27x14x17
+2x13x8
+18x15x26
+15x27x27
+18x11x15
+1x29x20
+21x12x11
+20x2x15
+28x23x9
+1x1x17
+7x23x9
+30x9x27
+9x16x18
+15x24x28
+30x11x18
+29x26x10
+9x5x25
+2x1x19
+14x3x14
+6x3x6
+30x15x20
+20x17x27
+28x10x9
+14x24x28
+17x11x6
+12x3x6
+8x8x15
+23x14x21
+11x21x7
+5x13x30
+4x29x25
+30x28x24
+18x4x9
+3x15x6
+13x9x19
+30x14x7
+7x9x9
+17x11x26
+24x26x13
+16x21x16
+27x17x25
+2x21x11
+9x11x27
+3x3x7
+13x8x14
+20x20x26
+13x29x22
+30x11x1
+7x10x19
+27x5x9
+23x17x15
+21x6x13
+24x15x16
+18x4x14
+18x16x6
+22x11x18
+14x2x5
+15x3x7
+10x20x29
+16x1x10
+30x23x1
+10x15x11
+17x14x5
+22x8x13
+7x11x28
+26x17x3
+2x23x2
+28x13x19
+18x12x28
+22x23x16
+14x12x1
+20x8x19
+17x19x13
+29x2x12
+2x26x27
+29x16x4
+13x8x18
+16x15x30
+23x16x2
+28x8x27
+21x8x23
+13x20x26
+19x6x17
+17x30x15
+7x4x30
+2x13x30
+18x7x19
+4x13x27
+8x6x5
+18x20x25
+2x3x30
+23x27x13
+22x30x4
+23x25x25
+23x16x19
+25x3x1
+5x6x15
+11x29x12
+25x24x7
+16x7x20
+20x3x2
+12x27x15
+16x10x12
+1x3x14
+22x1x26
+2x24x18
+11x29x16
+15x2x9
+10x1x24
+21x8x11
+30x11x23
+6x30x21
+13x27x29
+14x6x5
+18x29x19
+12x4x28
+29x3x14
+10x30x28
+5x7x15
+14x1x10
+9x25x14
+7x24x18
+28x17x21
+18x13x25
+26x15x1
+21x1x19
+12x16x21
+4x6x13
+7x15x26
+17x19x5
+12x28x2
+1x20x19
+27x7x5
+17x26x8
+12x15x19
+5x23x10
+8x2x8
+16x13x12
+14x27x1
+26x29x3
+24x16x14
+14x13x13
+7x22x23
+2x9x30
+4x27x8
+26x27x15
+23x1x6
+25x29x18
+5x18x1
+20x8x20
+5x10x25
+30x25x15
+7x22x25
+28x26x17
+29x4x1
+21x11x27
+20x9x8
+25x22x12
+2x11x11
+23x2x16
+23x27x20
+2x13x28
+27x2x24
+11x1x17
+12x4x27
+16x20x22
+30x12x10
+5x15x4
+5x2x27
+12x4x25
+1x16x4
+27x4x4
+21x16x3
+27x26x3
+24x6x6
+24x12x12
+20x20x25
+8x29x2
+21x4x5
+2x4x8
+4x13x19
+3x20x10
+12x15x16
+6x5x4
+12x16x20
+22x19x17
+8x17x22
+25x16x15
+7x1x19
+10x1x7
+23x23x5
+28x6x12
+2x25x12
+10x27x12
+24x27x19
+14x14x20
+4x1x5
+16x27x29
+20x20x24
+28x24x30
+6x15x15
+9x15x30
+23x26x3
+17x24x21
+22x25x25
+18x29x10
+20x25x1
+24x11x16
+20x7x21
+20x7x9
+7x26x2
+5x18x1
+16x26x28
+4x10x18
+27x30x21
+26x9x9
+8x16x14
+6x27x8
+28x9x20
+13x13x4
+9x18x16
+18x15x18
+22x19x14
+14x10x17
+25x29x11
+1x18x19
+8x11x26
+18x6x14
+30x24x13
+27x1x27
+15x9x3
+2x29x17
+2x26x21
+22x9x9
+20x20x20
+22x28x2
+26x5x16
+11x3x14
+21x16x16
+18x26x7
+18x30x6
+7x11x12
+15x10x2
+27x2x16
+27x30x24
+28x14x24
+7x4x8
+6x28x15
+13x19x1
+22x26x30
+7x30x24
+2x17x21
+19x26x2
+19x24x15
+14x23x2
+21x27x15
+30x15x14
+21x29x5
+23x30x2
+4x1x2
+15x5x13
+21x2x30
+20x7x16
+1x21x25
+2x25x1
+12x29x5
+28x13x16
+26x3x12
+29x20x23
+28x12x20
+4x30x8
+16x15x16
+6x16x29
+2x28x13
+24x25x2
+26x15x22
+17x20x11
+18x12x7
+19x1x18
+8x27x13
+22x16x8
+19x26x17
+13x11x10
+22x12x3
+13x12x14
+29x17x9
+6x14x10
+14x20x10
+8x26x9
+25x13x22
+3x30x25
+14x28x1
+30x29x12
+3x17x15
+3x24x14
+28x24x22
+16x6x1
+20x25x14
+17x17x13
+6x19x27
+10x15x20
+8x23x20
+7x29x21
+18x9x25
+10x5x22
+2x27x27
+16x18x30
+15x5x12
+26x29x29
+28x11x10
+9x29x28
+24x15x23
+26x9x10
+5x1x25
+22x27x16
+7x29x3
+1x3x5
+8x7x29
+19x21x11
+28x13x30
+17x16x20
+5x10x25
+9x14x15
+15x14x23
+16x4x17
+21x8x2
+9x9x8
+22x22x4
+10x2x27
+12x19x10
+15x29x4
+22x14x7
+29x18x5
+1x7x27
+24x1x15
+23x23x26
+12x17x23
+26x10x24
+8x22x2
+8x1x10
+22x19x12
+2x23x13
+11x27x25
+26x15x27
+27x7x21
+18x9x6
+22x21x22
+7x12x26
+23x21x13
+14x3x8
+5x9x28
+29x29x15
+27x25x23
+12x2x24
+8x2x20
+29x19x4
+12x24x29
+2x27x28
+14x20x9
+28x6x25
+18x29x8
+19x11x30
+15x11x23
+18x7x7
+14x20x14
+26x18x22
+27x25x13
+12x10x30
+30x2x7
+28x10x1
+18x10x30
+22x11x5
+22x16x3
+25x15x9
+5x10x24
+4x28x8
+19x24x18
+3x4x25
+14x4x30
+11x26x3
+12x12x12
+26x7x24
+3x2x14
+1x27x7
+2x2x13
+3x26x26
+12x4x11
+12x17x20
+4x19x30
+5x18x10
+17x6x18
+19x30x20
+11x2x17
+30x13x19
+22x23x7
+17x28x2
+5x17x30
+7x11x4
+21x26x18
+15x28x4
+5x6x27
+12x6x16
+9x17x12
+27x20x5
+14x5x20
+27x14x6
+2x14x21
+4x28x30
+24x5x1
+19x29x29
+11x23x1
+8x16x21
+3x17x19
+10x13x5
+20x21x16
+23x3x6
+27x26x11
+3x2x22
+14x3x5
+10x9x8
diff --git a/02/main.py b/02/main.py
new file mode 100755
index 0000000..0a1697d
--- /dev/null
+++ b/02/main.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+
+
+def solve_part1(boxes):
+ result = 0
+ for b in boxes:
+ faces = [b[0]* b[1], b[1] * b[2], b[2] * b[0]]
+ result += 2 * sum(faces) + min(faces)
+ return result
+
+
+
+def solve_part2(boxes):
+ result = 0
+ for b in boxes:
+ perims = [2 * (b[0] + b[1]), 2 * (b[1] + b[2]), 2 * (b[2] + b[0])]
+ volume = b[0] * b[1] * b[2]
+ result += min(perims) + volume
+ return result
+
+
+
+def main():
+ # Read box dimensions from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ # Parse the dimensions into tuples (x, y, z)
+ boxes = []
+ for line in lines:
+ dims = line.split("x")
+ b = tuple([int(d) for d in dims])
+ boxes.append(b)
+
+ print("Part 1 solution:", solve_part1(boxes)) # 1586300 for me
+ print("Part 2 solution:", solve_part2(boxes)) # 3737498 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/02/test.py b/02/test.py
new file mode 100755
index 0000000..965adef
--- /dev/null
+++ b/02/test.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ boxes = [(2, 3, 4)]
+ self.assertEqual(main.solve_part1(boxes), 58)
+
+ def test_example2(self):
+ boxes = [(1, 1, 10)]
+ self.assertEqual(main.solve_part1(boxes), 43)
+
+
+
+class ExamplesPart2(unittest.TestCase):
+ def test_example1(self):
+ boxes = [(2, 3, 4)]
+ self.assertEqual(main.solve_part2(boxes), 34)
+
+ def test_example2(self):
+ boxes = [(1, 1, 10)]
+ self.assertEqual(main.solve_part2(boxes), 14)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/03/input.txt b/03/input.txt
new file mode 100644
index 0000000..1757219
--- /dev/null
+++ b/03/input.txt
@@ -0,0 +1 @@
+>^^v^<>v<<<v<v^>>v^^^<v<>^^><^<<^vv>>>^<<^>><vv<<v^<^^><>>><>v<><>^^<^^^<><>>vv>vv>v<<^>v<>^>v<v^<>v>><>^v<<<<v^vv^><v>v^>>>vv>v^^^<^^<>>v<^^v<>^<vv^^<^><<>^>><^<>>><><vv><>v<<<><><>v><<>^^^^v>>^>^<v<<vv^^<v<^<^>^^v^^^^^v<><^v><<><^v^>v<<>^<>^^v^<>v<v^>v>^^<vv^v><^<>^v<><^><v^><><><<<<>^vv^>^vvvvv><><^<vv^v^v>v<<^<^^v^<>^<vv><v<v^v<<v<<^^>>^^^v^>v<><^vv<<^<>v<v><><v^^><v<>^^>^^>v^>^<<<<v><v<<>v><^v>^>><v^^<^>v<vvvv<>>>>>^v^^>v<v<^<vv>^>^vv^>vv^^v<<^<^^<>v>vv^v>><>>>v^>^>^^v<>^<v<<>^vv>v^<<v>v<<><v>^vvv<v<vvv^v<vv<v^^^>v><<^<>><v^^>^v^>>^v<^<><v<>>v^<>>v<>>v^^^><^>>vvvv>^v<^><<>>^<>^>vv><v<<>>^^>v^^^><^<<^^v>v<^<<>v>^^vvv^v^>v^<>^^<>v^v>v>v<v^>vv>^^v<>v>>^<>><>v>v^<<vvvv<vvv><v^<^>^v<>>^><v>><>^<v>v<v>vv^>>vvv<>v>v<v^>>^>>v<<>^<>^<>>>^v<<<^<^v>vv^>><<><v^>^v^^^v<>^^vv><>><>>^>v^<v<>v<>>^<<^v>^^^<>^v^><>v<<v>vv^>vv<<>>><<^v^<>v<vv>>>^^<>^><<^>vv>>^<<v^^vv<>>><v>v><^<v<<>>>^^<>>^<^v><>vv^^^v>vvv>^><<>^^>^<<v^<v<^v<<>vvv<^<<>^>^v<vv<^>vvv>v>vv^<v^><>>^vv<^^^vv><^vv<v^<><v^vvv><<^>^^><v<<vv^>v<vv<v>^<>^v<<>v<v^v^>^>^>v<<^vvv<<<v>^^>^<<<<>vv>>^<>^>>>v<v>^^<v^<v<>>>vv>^^v<<>>>^^v><<<v<v<^v<>^^><v<^v<<v^><><^<><v<^^v>>><v^^v<<v^><^<><<v^>><^<>v>v^<><^<v>^v^>^>^vv^>^^<<vv^>vv<^vvv<>>^^<^>v^>^>^<v^><v<v>>>v<<<><^v<<><^<vv^v^^^>v<^^<v^vvv<v<><v<vv<^vv<>vv<v^<>>vvvvv<<>^v^v>vv>>>vvv^^<^<^<><>v<v>><^v><^<<<>><<<v>^>v<>^>^v>>^<>v^<^>><<>^<v>^>^^^>^^<v>>>><>^v^v><<<<vv^<vv<>vv>v<>v^<v^>v><>>>v^<><^vvv>vv^<^<<^<^^v>^>>>v<^<^v^^<^<^>>><v>vv>^<<><>^>>v>^<<>><^<>v<>vv^^>^>vvv^v<<^<^^<vv<>^vvv<^^v^vv^>>v<^>^^<v^<>v<^<^vv>v<<vv>vv>^>vvv>>>^^>v<>^v>v^<^>>v>^^v>>>>v^<v>v<^>v<v<<>>^v<^^<v><^<>>^<<vv^>>v<<v>^v<>><^>vv<v<^>>^^<vvvvvvvvv>>>v<v<>v^<>>^vv<v^^v<<^vvv^<<^><>vv<><<>>v>vv^><>>^^v^>>v^v^><<<>>^^<^v<<^<>>>>^<^>v^><<^>v<^v<^>>^^<<<<><^<^v^v<>>^v<^<<vv^<><^^vv><v^v^v>^>>^>^vv^>^v<v^v<<vvv^><>>^v^^><>v>vv><^>>vv<vvv<<<<^<>vvv^v<v>^<v<^>^<^<v<><>v^^^^<<vv<^^vv<v>><<v^><>>><v^>^v><^>^><vv^<><^<v>><<^vv<>>v^<<v<>v><v<><><vv>^>>v^<^<v>^><>>><^><v^v<>>>^^<^>v<v>vvv<>^<<><v^^>^>>v<^v>^>v>>>vv>v>>v^^^<^<vvv^<>^>^<v^<v^v>v>^>vv>vvv<>v<^>v>^^>>^<vv^^v>v^^^^^v^vv><^<><>^>vv<^>>^vvvv^^^>^<vv>^v<<^><^^>^<>^^>^<<v<^>>>^><<^^>v^v>>^>vvvv>^^v><v>>vv><<<vv<^>v>^^^<v>v^vvv<^><<^>^<>^><<<<<v^<<vv^v>^<>v<v>^>^>><>v^v<^vv^^>vv<<v^v>vv^vvv<<<<>^v<v^^v^v>v<<v>^^<>^vv^^>^>^v^vv^>>v^vv^^<vv><<v^v^^v><vv<^vvv<vv^^<<v>v^v^^^^v<^<^>v>^>v>^vv^v^^<v<^vvvv<<<>^<^^^<^^<>^<><vv<^^<<^>>><v^vvvv>^<>>^^>v^^v^<<v^^^<<<><^<v^v^^v<v^<>v><<v<>^v>v<^><^>vv^^<vvv<^v>>v>^<><v^><^^^<v^>>vv<<<<<^<>^v^v>^vv^<>v>v<^>vv<<^vv>vv<v<><>>v>><v<^<^^>><<v^v<<^><v<^<vv<v<<vv^>^<<><^^>^<^>>^<vv>><v<<vvv<^^v^>^^<^v>^v<v<>v><v^v^<<^<><<v<<^v>v<<>>^>v>>v>>v<^<<^<^>>>v>^^^v><^>^^>>v<<>^v><v>vvv^vv<<<>vvv<<>^>>>v<v<v^<^<^>^<^>v^^v<^^<v<>v<>>^^>^v^>v<<<<^<>v^><<<v>>>><<v^<^vv>v>><>>^<<<^<^^>v<>>v<>vv<<^<<><<^>v^^^vv^>vvvv>>v>v^><<v<>vv^<<><<vvv>^>>>^<<<^<^<<v>^>v<>>v>>vv^^><<<<^^^v>><<^><v><v^^><v<<v^^v^^v>>v<><><<>^><v><^<vv>><^v<>v<vvv<>^>><v>>v<^><<v>^<>^v><^><^^<v>^><^^v^<<><>>^>v^<^v^vv<><^>vv^>v^vvv^<>>^><^<^<>^<<v^v<^v><>^v<v>>^>>^v^vv>><vv><v^^<<^v^<>^v<<>^><^>><v>>v<<<v^^vv<>^^v>>><><><<v^<<<v^<^^><v^>v^^vv<v^<>>vv^<^v<>^v>>v^v>v<^^vv><>^v<<>v^<>v^>>v>vvv<^><><^^>^vv^>>v^>^<^^<><>><<>^^^><^v^v><<<><<^v^vv>v>><^>>><v^>v<v><><v^v<>v^^>>v<<>v>v<v<v<^^<><>v^^<>>v<^v<v>v<><v<v>^<<>v>vv^^<>>^^^<>^^>^v>v>>>^v^v><v^^<><v>^^v^v<^<^^><<v<^<^<>^<>><<>^>>^>^^><v><>v<><>><<<>>>>vv>>>^>>^v<^>v^^^v<<vv>><<<^<<<>>>>>^>vv<^v^<>^<v^>^v><v>vvv<>>>^v^^^v<<<<>>^^<vv<^<^^>^<>v<^<<<>><>>v<^<>^<vvv<^<>><><<v>^^^>^^<<v<v^>^^v^>><<^vv><v>^v>>^<v>v>^^>^v>^vvv<>v^v^^<><vv>vv^>>><>v<^><v<v^<><<<>^v>^v<<<^>^>^>v^v<<><vvv<<v^^<><v>^>>><vv>><v>>v^<vv>>vv<<^v^v<<><^v<vv>>>vv<>>>>^vv>v^<>vv>v^v<v^><v<^^^^^>vv<><<vvv^<v><^<vv><^^^vv^<>^^^^<^><^<>v^<v^v<<^v<<^^<>>^<v^^>>>vv<vvv<>v<<>><^vvv^<<^^<<>>>^<>>>v^^><>><<>><v^v>>>>>><>>><v^<<vvv^>v<>>v^<>vv<><^^^^v^<<^<v^vv><<^^>v<^vvv^v>>v>^>>v>^^><<v^<>v<>vv<^v^vv><v><<vv^v>>v^>>v<^^^>^><<v<>^><>v>>>vvv<v<vv<^>>^v<v>^<^^^^^v><>v><>v^v^v<v^vv^v>vvvv<>vv<<<vv<v<<>^<^>^^v^<<>^<v><^><v<v<><<>v^<<^<><vv>v<<^v>>^v<><v>^>>^^><>v^<^<vvv^>^>^<<<<>vv>^v^v<^^^<vv>><>^^<<v<^<^^>>>v^v<<^^^<v<v<^<>^v<v><v^vv^^v^^v^^<vv<>^<><vv^<^v^<<^><<vvv>^^<^^^<^v>^>^vv><<<^v<v>vv>v<>v^v<v^>v^>>>v^v<>^v<<>^vv>v>v>v^<^>v^^<^>^^^^vv>^^><^>vv^>>^^v>><<<<^><>v<>^<v<vv^>^^><<^><v>v^>^^<^>>><>><v^v<v^<v<vv^v^<<^<vvv>>><vv<^^>>^>^><<v^<>>v>v^v^^><<>vv^v>v^<v><^<>^^<^>v>^<><<<v>^<^<^>^>^>^^v^<<^^v^^<^<>><^>v>>^^<>^^^<<<<v^>^v<^vv>^<<<v<><<v<>vv>>>v><>>><>>v<<<vv><>^v>v<^>><^><><v<>^v^>^v>^v<<><<^<>>v>^><>^>><>><^<v^><v^^<><v><^^>^v^^<>v^<v^<^v<v^^^^^v^<<^>^^^<^v><>^^<<<><<<<<^^>v^vvvv>v<>>vv<^>^v^>v<^vv^v<<><<v>v^v>^^><><^<v^>v><vv><>>><<>^vv<>v>>v<^v>>>v<v>v>v>^vv<<>^^vv<v<^v^<v<v>vv<>^<^<vv<v^<^v^^><<>^>><^v>vv^^v<<^^><<>v^^<><><v^^<v^v>^>^>^>v<^<v>^v^^>v<>vvv<^v<v^v><<v^><<^^><^<<v^v^>v<>^>v><><v>^<v<v>^<^^^>^v<<><<><>vv>v^<>v^><v^v<v><><<v>v<vv><<v>>v>^<<<>vv>>vvv>^^vv^v^^<^^<>v^^<>v>>^^>^>^>v>><^>><>>^<<>><^>v<<<<<<<^v^v<v^<v^^>^<><<v<^>v^>v^vv<<^^vv^>>>>^<>v<^v<>v<vv<^>>v^vv>vv><vv<<^>v>><vv>>>vv^<<<<vv^>v<<<<^^>^^v^><<^<v^>v^>^^<v<>vvv^>^<>vvv<v<^^>v^<<v>><>v<v<>^^<vvv>^>vv><><<<^^vv<v^<v<>v<>><<v><^vv^>^<^>^^^<<<v>vv^<^<<>^>^<vv>v><v<<^><^>^^<vv^v^^>>>>vv^><^^vv><>^<v^v>v<vv>v><<<v>v<v>^><v^^><v>v<^v^>>^^<v^>^^>vv>>vv^><^vv^vv<<^>vv>^v<v><vv><v<vvvvv>^^v^v><v>>>^vv<>v>^^^^<^>><>^v^^^>v<^^<<^^v<vv<>vvv<^>><><^>>^><^<>v<v<<><<v><v^v<>><^>v><<v^<v>v<^<vv^v^v^>vvv^^>v>^<vv^>v^v^<>v>^>>vv>><^^<v<<>^vv<><><<^v<v>v<<vv><>><^v<v>>v^>vvv^v^<<^><v<>^vv^>v^<v<^>>v<v><v><v>>^<<<v^<><<>v>^>^^<v<>>^<>^>^><<<^<<^<<^>^v>>><vvv>><<<<v>>>>>>>^<^v<^>v<>vv<><>v>>^>>^>vv^^><<^<v<v>>^^<<^>v<^>>vv>^<>v><^>v<vv>>>>>>^v<^<<<v^><vv<<>>vv<<><v<><<<v<^<v<>>v<^^^^v^^<^^^<^<vv><<^>><>v<<>v<v<>>>><>v^vv>^>^>>vv^v<v<<><^v>vv^><v<<>v^v<^>vv<<^^v><^>>^^vv<^<>>v^^>><v>^v>>>^>>v>v<>v<^vv><>^<<^>vv>>><><>v^><>v^>v>v><^v<><v<v>^v<<^vv^><^^>><^^^<<<^>v>^v>>><^>><^>>>^^^<^>vv<><<<v^>^<^^>>^^^v^v^v>v<v>>>><^>>>v>^vv<<^^^<^^vv>v<<><v<<^^>v>><<v^^><^>^<^>^v^>v><^<^vv>v>><>^<<vv<<v>v<vv<v>^>^>><^^<v>^v^v<><<>vvv<^<v>^><>^>vvv>>>^><<>><v^^<^<<^v>>^v<v<vv>vv^v^>v<<vvv<^^v^v>^<^>>^>v<^>^v<<><<<^>^<^^^>vv<^^^^vv<v<^^v<<<<v<^v^<><v<<^><<>vv>>><^<^<>>>^>^>>^<<<<<^^v>^>^<>vvv^^<^><^>^^v>^vv^><v^<^<<v^<vvv<<^v<><^><^>>>v>^v>^>^v<vv^v>><v><^><v^^>v^>^<><<><>v<v^>vvv^>^>>v<>^><^>^><vvv>^^v^v>v<>^v^><^>>v>v^><<<^>>^<>^<>>v><>>v^>^>^^<>>v^>^<vvvv<^vvvv^>>vv^<v^v>^vv<>v<>^<v<v>v>^^><^>vv^<^v^<<^<^<><vv<^v<^v><>>>^v^<<^><^>vv<v>v<^>vv^>v<<<>^<><v<^^^>v><^^<>^<^<v^vv^<<^>><<v^v<^vvv<<<>>vvvv^v^^^>v<>>><<>vvv<<^^^>v>v>>v<<v<v^v^>^^v>^><^<><<v^<v<v^^^><>v^^^<v>vv<>^>^^vv>^<<^v<^v><v>>>^>>><^<<>^v>>^>vv<<<v<>^<v><v^<^<>v>v^^v^>><<^v<<<<>v>v>v^^<^><>^^<<<v>vv<>>>^>>v<><v^>^<><vv>v>v^v<v^<^>>^>><<^^<^^v<vv<>><<<v<^<<^^^>vvv^<vvv<^>vv><>><<<^<v^v^^<<^vvv^^<^<><<>^<^<>>vvv<>^<>v^v<><>>v^v><<>>>vvv>v<>^>>^><^>vv<<>>v<<^><>v>>^^<v>^>^<<>><^<<vv<^<vv^vv><>>>><^<v>^>vv<v><>^<>vvvvv^vv<<v<>>>^<<><>^^vvv>>>vv<<^^><^v^^v<>^^>^><^>v^^^^v<^<<vv<vv<>vv^^>v^vv>v><>>vv>^<^<v^v^>>v^v^^v>^>vv^>v<vvvv<^v<^v>^v>^^v<<^>^^<<>^><^v>>>vv^>^^>vvvv>>v<^<v>^>>>v^<><^<^^<v>vv^^><v>v^<>^^^>>><^^v>v>^<<>^<v^>vvv^>^^^><v<^>>v<v>>^v><<><<>v<^<<>^><>^>vv>^<v>^^v<<^v^vvv^^>^vv^<^>^>^^v>v^>^<<><<^>v>>vv^vv><v>>^<<^<v^^<^<v^^vv^><^^<^^><v^^>v^^^<^<>^<>>^v<^vvv^^v^<><^>>>>>v><><<<>vv<^v>><<>vvv<><<vv<<<^>v^^>>^>^v>><><^^v<>><>>v^>^<vv><<<>><><<v>^^<>>v<><^<vv>vv<^v>^<<<<v<^<<^^>>^<><^>><<>^>v>^^^v>>^<^^v><v^v>^><<><>>^>>^<<v<>^v<>^>^<v>>vv>^vvv<<v<<^>^>^<<^^<>^^^^vvv<>^vv<vvvvv^^>^^<^>>><>v^<><^<<^>v^^v<>>^vv<>v^^<>>v^vvvvv<<v^<v^^>>><vvvvv>><^>vv>v^v^<v<^>^^><^>^^^^v<><^v<<>v^>v>>vv<<>^<v^^>vvv>^^<v^<>vv^><>><v^^v<>^>>^>v><>>^^v>^>^>>>^>v<^v>v>^<^^^^^>>v<v<>>v<<^>^<v<<>^^>><<^><>v<>^^^vv<>^^>><<^^>v>vv>vv>v^>^v>v^^<>>><<v><v<<>>v><>vvv^^v>^^>^vvvv^>^<>^vvvv><v><v<>>><>^<^vv<>^v<^v<>^vvv<<>><vvv^>>^><<vv^<v^>^<v<<^^>^^<^^v^>v<>v^v><>><v^^>>^vvv><^vv>v^<^<^v>>v^^>^vvv^<v^^v^^>v<^<>>^<>>>^^<><^^vv<>^vv^<>>>>^^<<^^<>vv^^><>^^<v<<v>^<v^^>^v<><><>vvv>^v^>>vv<<^v<<>><v>^><^>>>^<^<^^>vv^<<^<>>^^><><<v>^^<v>>v<<vvvv>^v^vv>><^^<<^>>v>v<^^^<^><^^vv>^vv<^<vv<>v><^<><v><^^^>>^<><^<v>>>>v^<v>>>>>v<><^^>v<^<^>><v<>^>vv>^^v^v^<<v<><<<^v^><<^<><<<<v<^>><<<>v>>vv><vv<><<^<^<><vv>^^^^<>v<<<<v>vv<>vv^^^>><>vv^><>>^vv<<><^^vv<>v^>>^<<>^<v^<^>v<
diff --git a/03/main.py b/03/main.py
new file mode 100755
index 0000000..35c2308
--- /dev/null
+++ b/03/main.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+
+
+def get_path(data):
+ visited = []
+ loc = (0, 0)
+ visited.append(loc)
+
+ for d in data:
+ if d == "^":
+ loc = (loc[0], loc[1] + 1)
+ elif d == "v":
+ loc = (loc[0], loc[1] - 1)
+ elif d == "<":
+ loc = (loc[0] - 1, loc[1])
+ elif d == ">":
+ loc = (loc[0] + 1, loc[1])
+ visited.append(loc)
+
+ return visited
+
+
+
+def solve_part1(data):
+ return len([*set(get_path(data))])
+
+
+
+def solve_part2(data):
+ data1 = data[0::2]
+ data2 = data[1::2]
+ return len([*set(get_path(data1) + get_path(data2))])
+
+
+
+def main():
+ # Read delivery instructions from input text file
+ with open("input.txt", "r") as f:
+ data = f.read()
+
+ print("Part 1 solution:", solve_part1(data)) # 2592 for me
+ print("Part 2 solution:", solve_part2(data)) # 2360 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/03/test.py b/03/test.py
new file mode 100755
index 0000000..693584b
--- /dev/null
+++ b/03/test.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ data = ">"
+ self.assertEqual(main.solve_part1(data), 2)
+
+ def test_example2(self):
+ data = "^>v<"
+ self.assertEqual(main.solve_part1(data), 4)
+
+ def test_example3(self):
+ data = "^v^v^v^v^v"
+ self.assertEqual(main.solve_part1(data), 2)
+
+
+
+class ExamplesPart2(unittest.TestCase):
+ def test_example1(self):
+ data = "^v"
+ self.assertEqual(main.solve_part2(data), 3)
+
+ def test_example2(self):
+ data = "^>v<"
+ self.assertEqual(main.solve_part2(data), 3)
+
+ def test_example3(self):
+ data = "^v^v^v^v^v"
+ self.assertEqual(main.solve_part2(data), 11)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/04/main.py b/04/main.py
new file mode 100755
index 0000000..1014062
--- /dev/null
+++ b/04/main.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+from hashlib import md5
+
+
+
+def solve_partn(partn, key):
+ if partn == 1:
+ prefix = "00000"
+ else: # partn == 2
+ prefix = "000000"
+
+ n = 0
+ h = "undefined"
+ while not h.startswith(prefix):
+ n += 1
+ d = key + str(n)
+ h = md5(d.encode()).hexdigest()
+ return n
+
+
+
+def main():
+ # My personal input string
+ key = "ckczppom"
+
+ print("Part 1 solution:", solve_partn(1, key)) # 117946 for me
+ print("Part 2 solution:", solve_partn(2, key)) # 3938038 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/04/test.py b/04/test.py
new file mode 100755
index 0000000..4078e83
--- /dev/null
+++ b/04/test.py
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ key = "abcdef"
+ self.assertEqual(main.solve_partn(1, key), 609043)
+
+ def test_example2(self):
+ key = "pqrstuv"
+ self.assertEqual(main.solve_partn(1, key), 1048970)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/05/input.txt b/05/input.txt
new file mode 100644
index 0000000..5642100
--- /dev/null
+++ b/05/input.txt
@@ -0,0 +1,1000 @@
+rthkunfaakmwmush
+qxlnvjguikqcyfzt
+sleaoasjspnjctqt
+lactpmehuhmzwfjl
+bvggvrdgjcspkkyj
+nwaceixfiasuzyoz
+hsapdhrxlqoiumqw
+lsitcmhlehasgejo
+hksifrqlsiqkzyex
+dfwuxtexmnvjyxqc
+iawwfwylyrcbxwak
+mamtkmvvaeeifnve
+qiqtuihvsaeebjkd
+skerkykytazvbupg
+kgnxaylpgbdzedoo
+plzkdktirhmumcuf
+pexcckdvsrahvbop
+jpocepxixeqjpigq
+vnsvxizubavwrhtc
+lqveclebkwnajppk
+ikbzllevuwxscogb
+xvfmkozbxzfuezjt
+ukeazxczeejwoxli
+tvtnlwcmhuezwney
+hoamfvwwcarfuqro
+wkvnmvqllphnsbnf
+kiggbamoppmfhmlf
+ughbudqakuskbiik
+avccmveveqwhnjdx
+llhqxueawluwmygt
+mgkgxnkunzbvakiz
+fwjbwmfxhkzmwtsq
+kzmtudrtznhutukg
+gtvnosbfetqiftmf
+aoifrnnzufvhcwuy
+cldmefgeuwlbxpof
+xdqfinwotmffynqz
+pajfvqhtlbhmyxai
+jkacnevnrxpgxqal
+esxqayxzvortsqgz
+glfoarwvkzgybqlz
+xdjcnevwhdfsnmma
+jyjktscromovdchb
+pvguwmhdvfxvapmz
+iheglsjvxmkzgdbu
+lwjioxdbyhqnwekv
+zcoguugygkwizryj
+ogvnripxxfeqpxdh
+hkvajhsbfnzsygbm
+cnjqeykecopwabpq
+wojjtbcjinoiuhsj
+kpwpvgxbyzczdzjq
+wrvhylisemlewgzk
+uiezkmnhilfzahtm
+mucteynnuxpxzmvt
+zaiwbgxefusfhmst
+apptbogpxivjwink
+qryboarjtwjhjgjb
+irehxupgyseaahzd
+fobstqxguyubggoh
+ysriumfghtxtfxwe
+auchdmasvfeliptw
+mztuhefcrnknyrdl
+tyjmkhihbwabjtaa
+yquzkdtgsljkaebw
+almvdvofjtkyzbmd
+emqftiuqqpdwwbrv
+hrrhmqfpepvbawvw
+atrkgykycvgxbpyb
+dhthetnealksbdan
+zzqafhgicubptiyo
+qdtaieaziwhbttnw
+kyskgapdgqrtrefw
+edwzlpqztpydmdlr
+awszjnlmvlyqsuvl
+kcrtmtshtsgixvcp
+jtaskgkijivbbkri
+mmggfwapsetemiuj
+itagrrnjbnmhgppd
+uqmbezechbrpbnqq
+nnyimvtascflpzsa
+knqeimypkdttyudj
+vgoiyvtvegwyxjjd
+qubzdxsbecktzrho
+zehojtvktsbbxijb
+xepmjrekwcgoxyoh
+bnptxnocbpbqbyeq
+sfvynsywscbnymos
+dsltfbpcmffbluba
+kncrlzlmkikylppa
+siwudrvmildgaozv
+jhhefbvbvneqzvtc
+lqjgztxitbuccqbp
+himmwlbhjqednltt
+vwognchyertnnfil
+eejakhapkbodrntf
+qxuijkkhhlskgrba
+aankpfxxicfpllog
+vuxykvljyqexfhrn
+epgygflbxlbwybzq
+zuxmwvetmvcszayc
+xttwhfqmemgtjnkf
+hftwldmivyfunfvl
+bejlyxfamzliilrj
+zkehazcxyyvtrxti
+dsgafehmcfpycvgz
+igremmqdojqdvwmb
+swnjzvmhcslvkmiw
+fchzbfbmtqtxmaef
+xwjmyyrlznxrcytq
+brwcwzpcvbwdrthl
+fvrlridacsiojdmb
+mhsturxdlmtxozvy
+usxvqyrwywdyvjvz
+gwazuslvmarfpnzm
+rgkbudaqsnolbcqo
+dpxvlbtavdhdedkj
+nnqmjzejhodyfgyd
+ozoazxkfhujgtzvy
+psdgvhzdiwnuaxpl
+tznkilxpogbzgijz
+wnpytcseirtborhh
+lhauurlfsmagfges
+oqfbzixnlywkzwwy
+yoehapoyjpakziom
+vtjftdcsfdzbmtrn
+zcshfnodiwixcwqj
+wapbxpaxgjvtntkm
+qfyypkyvblrtaenh
+bsxhbxkovgukhcza
+kitdmvpiwzdonoyy
+slkbhxmehzavbdsf
+dovzjouqkzkcmbkl
+qpbigdcqkfnfkxvq
+eaiaquhnesvtcdsv
+mhbezlhqojdsuryj
+dqprkkzxlghkoccx
+xqepmorryeivhrhm
+frwmrjpezwmjflvf
+gjpfgwghodfslwlf
+fzyvajisdjbhfthq
+pvzxkxdscdbilrdb
+mtaxmqcnagmplvnm
+rlyafujuuydrqwnc
+gvqvrcxwyohufehq
+lmrkircgfrfusmfd
+ovlpnkxcpimyaspb
+xhyjremmqhdqywju
+pxfczlhpzbypfarm
+utjhprzhtggausyp
+utzkkzlnyskjtlqh
+cecbcnxpazvkedic
+xwvoaggihrbhmijq
+krredhmtwlfmyagw
+lwfhxgbknhwudkzw
+vyczyvuxzmhxmdmn
+swcoaosyieqekwxx
+waohmlfdftjphpqw
+gaclbbfqtiqasijg
+ybcyaxhluxmiiagp
+xgtxadsytgaznndw
+wzqhtjqpaihyxksm
+fdwltsowtcsmsyhm
+rpoelfbsararhfja
+tswgdacgnlhzwcvz
+xjgbhdlxllgeigor
+ksgthvrewhesuvke
+whgooqirdjwsfhgi
+toztqrxzavxmjewp
+hbkayxxahipxnrtl
+lazimkmdnhrtflcu
+ndoudnupbotwqgmr
+niwuwyhnudxmnnlk
+hlmihzlrpnrtwekr
+wzkttdudlgbvhqnc
+rfyzzgytifkqlxjx
+skddrtwxcyvhmjtb
+mljspkvjxbuyhari
+xwkhozaoancnwaud
+nookruxkdffeymdz
+oiqfvpxmcplyfgoa
+qoxggshmrjlzarex
+lsroezewzkrwdchx
+nkoonmvdydgzspcl
+lygxeqztdqklabov
+jempjyzupwboieye
+hpdaqkhjiddzybly
+cvcizjlnzdjfjlbh
+vaaddsbkcgdjhbkj
+pjxmtxoyrkmpnenf
+ujqdvyqnkbusxlps
+miyvzkzqploqaceb
+gapcsbkulicvlnmo
+xqpcyriqhjhaeqlj
+ipumdjwlldzqhmgh
+swdstecnzttmehxe
+ucmqordmzgioclle
+aywgqhmqlrzcxmqx
+ptkgyitqanvjocjn
+wcesxtmzbzqedgfl
+rnetcouciqdesloe
+chpnkwfdjikqxwms
+onpyrjowcuzdtzfg
+tydnqwaqwkskcycz
+dhamguhmkjzzeduy
+oecllwyrlvsyeeuf
+gsukajpoewxhqzft
+sgdnffdixtxidkih
+pqqzjxzydcvwwkmw
+wnjltltufkgnrtgm
+hylaicyfrqwolnaq
+ovfnugjjwyfjunkm
+xknyzsebmqodvhcl
+uwfmrjzjvvzoaraw
+zaldjvlcnqbessds
+zphvjuctrsksouvz
+ceqbneqjwyshgyge
+wmelhaoylbyxcson
+nghuescieaujhgkj
+dhjmflwwnskrdpph
+exvanqpoofjgiubf
+aidkmnongrzjhsvn
+mdbtkyjzpthewycc
+izctbwnzorqwcqwz
+hrvludvulaopcbrv
+mrsjyjmjmbxyqbnz
+sjdqrffsybmijezd
+geozfiuqmentvlci
+duzieldieeomrmcg
+ehkbsecgugsulotm
+cymnfvxkxeatztuq
+bacrjsgrnbtmtmdl
+kbarcowlijtzvhfb
+uwietqeuupewbjav
+ypenynjeuhpshdxw
+fwwqvpgzquczqgso
+wjegagwkzhmxqmdi
+vocvrudgxdljwhcz
+nnytqwspstuwiqep
+axapfrlcanzgkpjs
+lklrjiszochmmepj
+gxadfpwiovjzsnpi
+qidsjxzgwoqdrfie
+wgszciclvsdxxoej
+kwewlmzxruoojlaq
+ywhahockhioribnz
+ucbqdveieawzucef
+mdyyzmfoaxmzddfv
+hsxnabxyqfzceijv
+vivruyvbrtaqeebr
+jxfeweptjtgvmcjc
+mmypqxmpurhculwd
+mpiaphksvctnryli
+xqzqnuxmuzylkkun
+fndmtefjxxcygtji
+dnorqlldvzqprird
+nutokyajmjpwjaqu
+vlupfperqyqkjcaj
+dgihjeokrphkpdnk
+nvbdyrlheqzixuku
+mhrkntnxvsmvrpka
+kvhkyanlhhymwljf
+fhipumtegqfgeqqw
+vpfjgveycdefuabu
+kzincljffncylcsf
+tsezxymwmjtyegqw
+wxhcdrqedkdcwxli
+ueihvxviirnooomi
+kfelyctfvwyovlyh
+horzapuapgtvzizz
+iiqkdpmfvhwwzmtj
+rsaclclupiicstff
+quwkkhrafypkaoum
+gyrgkgmwqfkeudfe
+noydhbqacwptyfmy
+efwwuipzgtkwffhf
+suyojcitomdxsduh
+lbcxnsykojkufkml
+zpglsvoutvzkgdep
+usgrufyvgsbsmbpr
+katrrwuhwvunjqor
+btngwrpcxoyfbgbc
+bxjscjdiowjrkpns
+nwxvnfrnlkgqxvhf
+ikhyqkvljucgdlag
+xibnxsjopmxvflkl
+mzplumcfivqcjqnz
+jqflcxoxzlbwlxry
+fcscvmfepdxrshxe
+wlpffwunffklzbuc
+emvrlqajjgwzfmle
+rhaheurtzrfoqkyq
+ifuuhpxmadaysfsx
+ncyfvleyzqntpcoo
+zeogmyaqccmtvokd
+jqppbzebppdnpurn
+xixarswxsiwjzgni
+ezruwzajsoombphs
+hmiqfeizyprielxf
+jnaoxljnftymsfey
+extgzrxzovlsixnf
+yhyfmovvlrwoezsv
+ffnybaolppuzpjym
+pqowimdiusccaagn
+jgceiosiihpjsmnu
+hkoexeaopebktngx
+njhzuvsygymejqav
+yjkgcclgtvushcfk
+gmbjxhnkkxlihups
+pdlwysadiebsidjz
+omrwmgzulfoaqros
+ofvvgdezwvcffdcy
+otytpuklhxcpxhgd
+eyfaosxdauumvlux
+mvdthjfstrlqlyuo
+mdgdchgnlxaxspdm
+bakjezmhbwqxzevd
+msakswaphdwaodhg
+vjcqscgdbnsxdllh
+jjywaovewbuzreoj
+nqvplhwacylifvwk
+lpwmpixbxysmsign
+flcvbpxrchcpbgcb
+qjpkeuenenwawlok
+bnqkflfmdmntctya
+fzsgzpoqixvpsneq
+icwfdisutoilejld
+relchofohnkwbumi
+aljalgdaqwhzhfwr
+cahkvnwnbwhodpqs
+dnrzeunxiattlvdm
+nsmkhlrpwlunppjs
+mqqsexlwfqnogwub
+tfavelkqrtndpait
+ooguafrnmprfxcnz
+ntynkiordzxtwrqa
+rkkyzlxekqqlkvym
+ofxcivdnwcmgfnme
+ywotqwbrqxlrnobh
+nrbbiypwhrqihvev
+flqsjixxtydheufs
+lcfrfzypstrqctja
+hyzbuzawuzjrynny
+exfbywcnstebnvmq
+vydzwnbmcihvqrnj
+qmwqaaylinzrdmiw
+lpxpztpvfggspeun
+lhxmqqbracsuyrfm
+zgkwsrabaseidbrw
+yjlmbhbqsqgszsun
+mqfzqtbxtuteabtd
+izomzdmcqmfrevwd
+iqijrlqurdwrkoln
+fxhqzpgoxxjkkhql
+oulwontmgrjeopnk
+edaigfydjexvzzvj
+vjhybiklxpxjqpwc
+ypxfbfnpbmqmwtte
+xzvcsgasztrxdzud
+rpulqmobptfarboo
+palacmdijxzzykrf
+jmllwukplufohiby
+dnswayomusiekfmy
+sxbrjqtqgzzwhcfo
+lylvndsgbnbqiejm
+jaxxhoulxnxnaenr
+nblissutfazbcpwn
+zmlsjszzldvbiacr
+kewojtlchfkclqwk
+eqvfjasddggvfame
+yibzqlvxtraxpdon
+dgnbxsbmdrtyvaac
+uoxrcxfimhgtxqhy
+xfdxalrwcwudlviq
+xmtbdklqptoswpwl
+zezyopzdztdjerfl
+xuzluhjsqvhytgbc
+qdjtmeckispmgzki
+phakupesplzmmmvc
+gpuoqfffumzszybn
+bhywxqkrrlwuebbw
+ibvwgoyvelzenkzl
+ncohvvbmiekbaksa
+fzuvqzvxvdbeirrp
+lshtzniokucwojjd
+punrduvlnrulkium
+gnfpikidnfobrrme
+vxkvweekmnvkzgyl
+rhydssudkcjlqgxn
+cjtqvlaahohcgumo
+jwzmfyinsfwecgcb
+blpeseqhlzfilpuf
+jvtpjkyokzcvagon
+qjomincbcobjczpe
+ugsyzkzgdhxtmsfz
+hleaqgwzqjwajcra
+coumfghptpnxvvov
+hqpnbupnzwpdvgqd
+cpouyodqxgviasem
+lljvxeyozckifhfd
+huqtnvutdyfgwtwa
+yenlveuynmlmmymu
+ojdyufkomxiwjmbf
+spjzgvcwvzgffjkk
+vxykmjhyvmhyssbp
+tazdeqggfcjfvwwn
+uumwcngwcytvpufx
+avovuzkrevloneop
+owczrtbnrvjfemkt
+hzpugcanaxyvaokj
+iishlodnxvjtgzyn
+qosdonclrnxirham
+eonqlnwevahydddg
+ryqmnuikftlxuoqy
+whqepbcwabzbthha
+vekisvnwhgpyemxr
+lrwxzoamnvpnlhap
+ywepvqthnorfswjv
+evqwvsoazmwyypjy
+bgwoojddubppmjxf
+jypkfrthzgtyeddi
+tynabbhfjzkrqsju
+adxstbfqheuqbcuk
+gqwqiocdyqoiblrx
+ybuddlyuskdlegxv
+luwynbsmpgyeqsbr
+ltyqgqoyljibqndo
+jaedpajzphfybajh
+epglnrxofptsqvmy
+zjdpxkngfkstxbxh
+ekegphcwanoickfu
+cqvhuucvejqirvfs
+uqudnnqumsqcgefo
+qnzunermlnpcfflo
+ovyxaniqaawzfuxx
+djekxcezjowdhopq
+bwtwbmdehrhpjnlk
+nilsnlacerweikfa
+hyrigsrmsrzcyaus
+gvmdmgddduylmxic
+ewzovdblhmjgjwsk
+ojjfsknlonzguzlq
+yjgfruvpjvlvrvvq
+cyoryodwyhzwprbv
+crsjclrurcquqgut
+sjhfhobwtojxcmem
+ibxfjudilmdeksea
+uqbhdbjoeupyhbcz
+uqbxigzxuxgmjgnw
+jashafmtzrhswirg
+dexiolovaucyooka
+czjbwwnlwcoqnoiu
+ojigosazigfhttjc
+zfiqtgrqbmftknzn
+dlzbmvmolssbqlzl
+sgmchcurrutdtsmw
+scdwjqsdohcdrwry
+cgtdvecqwplpprxn
+iiplenflfczaktwi
+wmgnwfxfcjhyeiqg
+giihshowtcatecvl
+nqhzfincclumvkaz
+kxstpzgdfvepionc
+agbhxcijxjxerxyi
+hmgfqevgdyvisyvs
+tthakmvpowpvhtao
+ottalcghygpaafbo
+aplvozayycremgqg
+dbjxlnaouxqtdpfz
+peeyallzjsdvpalc
+ndtdjyboixuyhfox
+llabnbcobexfoldn
+cweuvfnfyumbjvxr
+ewkhhepaosalnvkk
+pivyiwsiqpwhagyx
+auzsnwdcerfttawt
+grbfrekupciuzkrt
+byfwzadtzrbndluf
+lluypxjeljzquptk
+pskwsnhqanemtfou
+sxvrtqqjdjkfhhrm
+ulsmqgmshvijyeqh
+qigofesfhekoftkf
+zhatniakqtqcxyqa
+uuczvylgnxkenqee
+mlitvtuxknihmisc
+srrtrxdvcokpyfmz
+osispuucklxcfkeb
+vqhazlaulmnpipql
+umkiueljberqhdig
+knvpbkbvgoqzwprp
+nbsocqikhuvsbloj
+wjnpepjkzkednqbm
+agbhmytsofuyqcor
+gvogzhkkpxyfecko
+ardafguxifeipxcn
+yiajcskbgykyzzkw
+sejunbydztyibnpq
+dqrgfggwcnxeiygy
+xnqqwilzfbhcweel
+jjtifhlvmyfxajqi
+gwszrpgpmbpiwhek
+kydzftzgcidiohfd
+efprvslgkhboujic
+kecjdfwqimkzuynx
+rildnxnexlvrvxts
+dlnhjbqjrzpfgjlk
+qluoxmzyhkbyvhub
+crydevvrjfmsypbi
+dosaftwumofnjvix
+pwsqxrfwigeffvef
+nzyfmnpwqyygjvfx
+iccbckrkxlwjsjat
+bmputypderxzrwab
+bhuakynbwnlreixb
+qmrzfyqjiwaawvvk
+juvtixbkwyludftn
+zapmjxmuvhuqlfol
+paiwrqjhpjavuivm
+tsepfbiqhhkbyriz
+jpprewufiogxoygk
+mmapyxbsugcsngef
+pduhmgnepnpsshnh
+aetndoqjvqyjrwut
+fnfvlorhwpkkemhz
+gedfidpwvoeazztl
+beclvhospgtowaue
+wsclsvthxustmczm
+tjbxhnpniuikijhe
+rhetyhvfcemponeg
+mavonujurprbeexi
+argbrpomztrdyasa
+bzvtffbtygjxmkvh
+maqyqkhsqgzfzvve
+seeirbiynilkhfcr
+wxmanwnozfrlxhwr
+dieulypsobhuvswb
+nxevassztkpnvxtb
+jclxuynjsrezvlcy
+xlolzyvgmwjsbmyf
+tguzoeybelluxwxc
+fkchoysvdoaasykz
+cyynwbfcqpqapldf
+rhifmzpddjykktuy
+ndvufsyusbxcsotm
+txutnzvdsorrixgg
+qjoczhukbliojneu
+ufhwujotncovjjsz
+kclsgsdwcrxsycbr
+yscwmlrdaueniiic
+nxhivrovpkgsmugb
+fdxqfyvwwvgeuqkv
+femtamfylysohmpr
+amsyzslvyxsoribh
+nhmqxncwsonhgbcz
+uomqsvcbpthlmcue
+kxtfapcqrnjkkslj
+xtieihonlfubeync
+adpcjqxgydulchgj
+cjynnzsmmujsxxpd
+neeapmzweidordog
+szoivgqyqwnyjsnk
+uwgrtzaqezgphdcu
+ptpgttqxocjwxohi
+fhltebsizfwzpgpf
+emmsazsidspkhgnh
+dxcprkbcjeqxqzgn
+tpxzqwxbzwigdtlt
+afsmksnmzustfqyt
+xyehnftstacyfpit
+vcrfqumhjcmnurlw
+rrznpjzcjgnugoch
+gbxnzkwsjmepvgzk
+jwobshgwerborffm
+zmuvfkhohoznmifs
+buyuwgynbtujtura
+bevncenmpxfyzwtf
+hqqtcrhzfsrcutjh
+kbpzshllpiowepgc
+alspewedcukgtvso
+xvsvzzdcgjuvutrw
+pmwulqraatlbuski
+abuzsiinbueowpqn
+oedruzahyfuchijk
+avhcuhqqjuqkesoq
+azqgplkzsawkvnhb
+rjyoydogkzohhcvx
+aezxwucqvqxuqotb
+kxobnsjvzvenyhbu
+nnjoiilshoavzwly
+aijttlxjrqwaewgk
+cvsaujkqfoixarsw
+zngtoacpxcsplgal
+qhkxliqtokvepcdv
+aixihrtdmxkfvcqw
+owbgdgdymxhhnoum
+tajsagmruwzuakkd
+ckrfduwmsodeuebj
+alfdhuijuwyufnne
+xpchlkijwuftgmnm
+rwcrvgphistiihlg
+xdaksnorrnkihreq
+akeschycpnyyuiug
+rgputhzsvngfuovz
+lerknhznuxzdhvre
+mqiqmyladulbkzve
+csnmupielbbpyops
+kwgrwgmhfzjbwxxz
+npwtvbslvlxvtjsd
+zxleuskblzjfmxgf
+hexvporkmherrtrn
+rhtdhcagicfndmbm
+qhnzyuswqwoobuzz
+dpvanjuofrbueoza
+kjcqujmnhkjdmrrf
+gholddsspmxtpybg
+jihlvyqdyzkshfsi
+zuviqmuqqfmtneur
+kzexjowatvkohrtx
+wgijnfhibsiruvnl
+zevkrkmhsxmicijb
+khxrcteqourjvoxa
+ylpxlkcnenbxxtta
+zrfsvctbojjkpvtw
+nlzbudxibnmcrxbt
+cqnscphbicqmyrex
+ywvdohheukipshcw
+riwatbvjqstubssf
+idlztqqaxzjiyllu
+sdpdgzemlqtizgxn
+rjtbovqlgcgojyjx
+fnfrfwujmjwdrbdr
+osnppzzmrpxmdhtj
+ljhwngclvydkwyoe
+chwqkrkzrvjwarat
+jmydkwpibkvmqlgs
+zvhfmbxnlxtujpcz
+jsnhsphowlqupqwj
+fzhkkbpasthopdev
+jerntjdsspdstyhf
+gctwmaywbyrzwdxz
+xemeaiuzlctijykr
+xulrqevtbhplmgxc
+yfejfizzsycecqpu
+gboxrvvxyzcowtzm
+lpvhcxtchwvpgaxp
+wdiwucbdyxwnjdqf
+qgwoqazzjlvnjrwj
+prtlnkakjfqcjngn
+fagvxsvjpuvqxniz
+xacmxveueaakfbsm
+ginvtonnfbnugkpz
+qpvggsppewfzvwin
+reoqnlzruyyfraxa
+kolwtqhifjbbuzor
+vrkcywvdhdprztww
+ngdvyfmvjqhbzbxt
+rooxeoilqzqjunmp
+efxmdprtogtxgyqs
+qrhjuqndgurcmwgu
+ouitjprueefafzpl
+kirdwcksqrbwbchp
+fpumsmogojuywezo
+lgjrgykywugzjees
+xigioqcpjabpbdas
+ewkhuprpqzikmeop
+fgrgxsqeducigxvr
+bclkursnqkzmjihl
+jozidniwvnqhvsbc
+oghcilcyozrmmpta
+xbgmaungzcpasapi
+iqowypfiayzbcvhv
+opdehgwdgkocrgkf
+zfzvdjeinlegcjba
+vhakxvlcayuzukap
+xyradgyiebpevnwe
+eamhtflgedwyshkn
+igteqdgchjeulfth
+kwsfkigxzpbgdxod
+vapnpsbdboiewpzp
+wbuqhjsngxpqshen
+vxxilouxuytitwgm
+cpnwlkwnkeanqnet
+wdmbtqvvlowftvgb
+wjtmcecpyqzwpbqg
+jnxmoxdhvsphcdeg
+wabxfxpotoywwodn
+mwbsoxzlqpqobvvh
+coktshbyzjkxnwlt
+rzhnggpslwzvyqrp
+dgzuqbzarbutlkfx
+wunajaiiwgijfvjh
+uotdbcgmsvbsfqlb
+kxdtlgmqbccjqldb
+ngmjzjwvwbegehfr
+cvpsabqfpyygwncs
+wqluvqlhdhskgmzj
+rbveperybfntcfxs
+fbmoypqdyyvqyknz
+zxpgzwnvmuvkbgov
+yexcyzhyrpluxfbj
+ltqaihhstpzgyiou
+munhsdsfkjebdicd
+plecvjctydfbanep
+kjrxnnlqrpcieuwx
+zbcdtcqakhobuscf
+kgovoohchranhmsh
+llxufffkyvuxcmfx
+tgaswqyzqopfvxtw
+kojcqjkdpzvbtjtv
+xggdlkmkrsygzcfk
+vvitpsnjtdqwyzhh
+gcqjuwytlhxsecci
+vbsghygcsokphnrg
+vejqximdopiztjjm
+hudqtwmwkviiuslp
+vwswfvpcwwpxlyry
+gxmfiehdxptweweq
+qjmekjdcedfasopf
+pqyxdxtryfnihphf
+felnavctjjojdlgp
+hbimufguekgdxdac
+dhxhtnqgfczywxlr
+pssottpdjxkejjrh
+edieanguabapxyig
+sciinanyqblrbzbb
+irxpsorkpcpahiqi
+qsxecaykkmtfisei
+ivfwlvxlbnrzixff
+hqxzzfulfxpmivcw
+vvbpaepmhmvqykdg
+cetgicjasozykgje
+wuetifzdarhwmhji
+gaozwhpoickokgby
+eldnodziomvdfbuv
+favpaqktqaqgixtv
+twbcobsayaecyxvu
+lzyzjihydpfjgqev
+wnurwckqgufskuoh
+fxogtycnnmcbgvqz
+aetositiahrhzidz
+dyklsmlyvgcmtswr
+ykaxtdkjqevtttbx
+kfmnceyxyhiczzjm
+nnizopcndipffpko
+yjmznhzyfinpmvkb
+sljegcvvbnjhhwdd
+zmkeadxlwhfahpwg
+rwvcogvegcohcrmx
+aguqwrfymwbpscau
+vlusytjagzvsnbwe
+smvzhburcgvqtklh
+rfuprvjkhazrcxpv
+megqlnoqmymcrclc
+gvldhkewtmlwqvqv
+awynhvtyziemnjoa
+voprnvtnzspfvpeh
+dhlguqwmunbbekih
+goayirdhnjrfuiqi
+eoghydfykxdslohz
+chpippjykogxpbxq
+hqbycjweqczwjwgf
+pvefsrvwumrlvhmt
+eghwdovaynmctktk
+crwkxoucibumzawc
+bzbtahvhkdigvvtj
+bnbptgihhfubxhho
+ddqmbwyfmfnjjaro
+gvtswqyzazihctif
+vmqctjpgadxztqqb
+dgnndowtpeooaqqf
+sxdvctfdtalufxty
+ylgeexosibsmmckw
+sxplpyskbpqnojvw
+coarhxtsvrontyeg
+fyoaurggjupvzvlv
+jlyrkqsiwuggvjem
+uwbsjoxonreuucyi
+gihuqvwxovbgokes
+dxzaaxupbcgnxcwf
+gidrgmvyrlqqslve
+csflmlvqmonoywpx
+jkxkpixlythlacnk
+ejkarcdkdslldugv
+dbzmsusevohhjkmr
+cbrqzualjpdtworc
+kpgidqlmcbpfmmwu
+zwghjuofexfowqam
+ncdlxmcrsmsocetz
+kfprzqacefifjkbd
+swwzivrxulkhvldc
+wgqejhigbjwunscp
+rsstnwcyybfauqxu
+qhngfxyhdqopyfgk
+zrndpyyejsmqsiaj
+xxknxwpvafxiwwjc
+mmaahwgoiwbxloem
+tabacndyodmpuovp
+yriwomauudscvdce
+duvyscvfidmtcugl
+mgipxnqlfpjdilge
+imeeqcdetjuhfjnw
+dvkutrdofpulqkyh
+jefvtlktxegpmbya
+iyzudqgpvlzjfydh
+giohapxnpaqayryd
+qheqdprmnqlpztls
+rdxhijmzegxkotoq
+hdnmaspumdwnrcdz
+wafpbgehbuzdgsnc
+tbtrfztsferdmhsy
+vusndcyjngtkrtmk
+ilqblestzxebcifh
+urfgjbjgzlrfsdlv
+aptcdvpsqwleqttn
+bigczjvzokvfofiw
+zjnjeufonyqgkbpx
+trcdebioegfqrrdi
+jrdvdriujlmbqewt
+jqrcmuxpwurdhaue
+yjlermsgruublkly
+zwarvgszuqeesuwq
+xthhhqzwvqiyctvs
+mzwwaxnbdxhajyyv
+nclsozlqrjvqifyi
+gcnyqmhezcqvksqw
+deuakiskeuwdfxwp
+tclkbhqqcydlgrrl
+qbpndlfjayowkcrx
+apjhkutpoiegnxfx
+oaupiimsplsvcsie
+sdmxrufyhztxzgmt
+ukfoinnlbqrgzdeh
+azosvwtcipqzckns
+mydyeqsimocdikzn
+itfmfjrclmglcrkc
+swknpgysfscdrnop
+shyyuvvldmqheuiv
+tljrjohwhhekyhle
+dayinwzuvzimvzjw
+qgylixuuervyylur
+klqqaiemurawmaaz
+hdmzgtxxjabplxvf
+xiivzelzdjjtkhnj
+ktgplkzblgxwrnvo
+gvbpyofzodnknytd
+lqhlmnmhakqeffqw
+ltzdbngrcxwuxecy
+obxnfjeebvovjcjz
+zexpwallpocrxpvp
+tjpkkmcqbbkxaiak
+qiedfixxgvciblih
+qcxkhghosuslbyih
+gnsfidwhzaxjufgm
+xrghwgvyjakkzidw
+tftftwedtecglavz
+wquqczzkzqrlfngr
+twibtkijpvzbsfro
+bmplypdsvzuhrjxp
+zanrfmestvqpwbuh
+zonrhfqowyimcukm
+kpvajjfmqpbhrjma
+kujzluicngigjbtp
+iusguantsrwxdjal
+kwxeuylcnszswahw
+visdhnkobxnemldu
+rogeadmmaicwtabl
+pxqycifbgevqudvs
+osaiozyvlyddylqr
+vffjxrolrpuxcatx
+jbmsetccdrywssjd
+qgxyhjfpbfifmvgc
+npejgalglldxjdhs
+mbbtqgmttastrlck
+whapaqwdtpkropek
+dulbdboxazfyjgkg
+xaymnudlozbykgow
+lebvqmxeaymkkfoy
+bmicnfuubkregouj
+dieatyxxxlvhneoj
+yglaapcsnsbuvrva
+bbpjaslqpzqcwkpk
+xehuznbayagrbhnd
+ikqmeovaurmqfuvr
+ylyokwuzxltvxmgv
+hqtfinrkllhqtoiz
+pjmhtigznoaejifx
+fqdbmowkjtmvvrmx
+uvqtqfoulvzozfxv
+rpajajukuxtchrjd
+sznucejifktvxdre
+ufvibsmoushmjbne
+xirdqoshngthfvax
+iafpkddchsgdqmzl
+vmualmlduipvykzh
+fnmuahmblwyceejb
+ilsaapnswfoymiov
+lenvylifraahaclv
+cukqxlipuyxedqfh
+zgwecslpniqvtvuz
+cdcdfpsxuyrhsmag
+dszjinhantnxgqra
+ioimwotsgnjeacgt
+dqcymnvjystbynhp
+yibaudyfefbfgunx
+cabslcvunjavqkbf
+goymzvmgkvlsmugf
+zxteiitpthzskjjx
+agnxcnaqhjhlurzs
+cvmgyxhhnykuxbmb
+cgqmjexydmvgwxpp
+sygjajofieojiuna
+clpvxbrbjvqfbzvu
+cbntswqynsdqnhyv
+bztpbtwbefiotkfa
+pnxccbgajvhyeybu
+asyzrvgzumtuissa
+facjyblvcqqginxa
+rvwnucnbsvberxuv
+ghrbeykzrxclasie
+ekujtselepgjtaql
+krtrzsmduhsifyiw
+ticjswvsnyrwhpnt
+clmjhsftkfjzwyke
+lbxlcixxcztddlam
+xhfeekmxgbloguri
+azxqwlucwhahtvep
+kitdjrwmockhksow
+keznwwcusgbtvfrs
+ljvzxoywcofgwajj
+vebjnhnkcfzbhrcw
+eqfcxkavstxcuels
+ldattkyawjrvcido
+bsqqeilshcwtqyil
+foqqsxahfiozcqrw
+liswfmuhzfbyzjhf
+sulbdcyzmolapfbs
+zuggzkelwxjpsgxb
+betioxrgtnhpivcw
+xmtbixstdipibhgs
+ttvurgqmulryyaji
+viobnljznzppfmxw
+qlzabfopydtxrlet
+tusvydegfxhaxolk
+thoufvvfjferxhwp
+cfyyzppfarjiilbs
+jwmhxtgafkkgseqs
+pqwuuaxbeklodwpt
+vndyveahdiwgkjyx
+ssrjgasfhdouwyoh
+thbavfcisgvvyekf
+yjdvxmubvqadgypa
+tlbmcxaelkouhsvu
+bonohfnlboxiezzr
+rktlxcbkhewyvcjl
+rsmoutcbcssodvsc
+qszdratuxcrhsvoh
+eypyfahpuzqwzwhi
+yhkrleqmqlmwdnio
+vpnvxusvmngsobmq
+hkzyhopvxrsimzys
+dblriiwnrvnhxykl
+xkriqxkrprjwpncs
+rcymltrbszhyhqti
+mzbvneplsnpiztzn
+vkqtnptgbqefvfoc
+nwdtfiaozkcjtlax
+crximadpvdaccrsm
+lrbajafxwwnxvbei
+rbexzesrytpwwmjf
+stxwjarildpnzfpg
+btamaihdivrhhrrv
+acqbucebpaulpotl
+dkjhzghxxtxgdpvm
+rsbzwsnvlpqzyjir
+mizypbwvpgqoiams
+nvrslorjpqaasudn
+wvexcpzmconqkbvk
+rfwfumhjwzrvdzam
+eaghdaqorkhdsmth
+gtuntmpqaivosewh
+nzlsmdgjrigghrmy
+dhuvxwobpzbuwjgk
+kkcuvbezftvkhebf
+aeediumxyljbuyqu
+rfkpqeekjezejtjc
+wkzasuyckmgwddwy
+eixpkpdhsjmynxhi
+elrlnndorggmmhmx
+ayxwhkxahljoxggy
+mtzvvwmwexkberaw
+evpktriyydxvdhpx
+otznecuqsfagruls
+vrdykpyebzyblnut
+cnriedolerlhbqjy
+uajaprnrrkvggqgx
+xdlxuguloojvskjq
+mfifrjamczjncuym
+otmgvsykuuxrluky
+oiuroieurpyejuvm
diff --git a/05/main.py b/05/main.py
new file mode 100755
index 0000000..876d20c
--- /dev/null
+++ b/05/main.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+
+
+
+def solve_part1(lines):
+ result = 0
+
+ for line in lines:
+ # Rule 1: at least three vowels
+ nvowels = 0
+ for v in "aeiou":
+ nvowels += line.count(v)
+ rule1 = nvowels >= 3
+
+ # Rule 2: at least one double letter
+ rule2 = False
+ for i in range(len(line) - 1):
+ if line[i] == line[i + 1]:
+ rule2 = True
+ break
+
+ # Rule 3: no "ab", "cd", "pq" or "xy"
+ rule3 = True
+ for s in ["ab", "cd", "pq", "xy"]:
+ if line.count(s) > 0:
+ rule3 = False
+ break
+
+ if rule1 and rule2 and rule3:
+ result += 1
+
+ return result
+
+
+
+def solve_part2(lines):
+ result = 0
+
+ for line in lines:
+ # Rule 1: twice-occurring two-letter sequence
+ rule1 = False
+ for i in range(len(line) - 1):
+ s = line[i : i + 2]
+ if line.count(s) >= 2:
+ rule1 = True
+ break
+
+ # Rule 2: ABA-like sequence
+ rule2 = False
+ for i, c in enumerate(line):
+ if i < len(line) - 2 and line[i + 2] == c:
+ rule2 = True
+ break
+
+ if rule1 and rule2:
+ result += 1
+
+ return result
+
+
+
+def main():
+ # Read list of strings from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_part1(lines)) # 258 for me
+ print("Part 2 solution:", solve_part2(lines)) # 53 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/05/test.py b/05/test.py
new file mode 100755
index 0000000..66fc927
--- /dev/null
+++ b/05/test.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ lines = ["ugknbfddgicrmopn", "aaa"]
+ self.assertEqual(main.solve_part1(lines), 2)
+
+ def test_example2(self):
+ lines = ["jchzalrnumimnmhp", "haegwjzuvuyypxyu", "dvszwmarrgswjxmb"]
+ self.assertEqual(main.solve_part1(lines), 0)
+
+
+
+class ExamplesPart2(unittest.TestCase):
+ def test_example1(self):
+ lines = ["qjhvhtzxzqqjkmpb", "xxyxx"]
+ self.assertEqual(main.solve_part2(lines), 2)
+
+ def test_example2(self):
+ lines = ["uurcxstgmygtbstg", "ieodomkazucvgmuy"]
+ self.assertEqual(main.solve_part2(lines), 0)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/06/input.txt b/06/input.txt
new file mode 100644
index 0000000..495311e
--- /dev/null
+++ b/06/input.txt
@@ -0,0 +1,300 @@
+turn off 660,55 through 986,197
+turn off 341,304 through 638,850
+turn off 199,133 through 461,193
+toggle 322,558 through 977,958
+toggle 537,781 through 687,941
+turn on 226,196 through 599,390
+turn on 240,129 through 703,297
+turn on 317,329 through 451,798
+turn on 957,736 through 977,890
+turn on 263,530 through 559,664
+turn on 158,270 through 243,802
+toggle 223,39 through 454,511
+toggle 544,218 through 979,872
+turn on 313,306 through 363,621
+toggle 173,401 through 496,407
+toggle 333,60 through 748,159
+turn off 87,577 through 484,608
+turn on 809,648 through 826,999
+toggle 352,432 through 628,550
+turn off 197,408 through 579,569
+turn off 1,629 through 802,633
+turn off 61,44 through 567,111
+toggle 880,25 through 903,973
+turn on 347,123 through 864,746
+toggle 728,877 through 996,975
+turn on 121,895 through 349,906
+turn on 888,547 through 931,628
+toggle 398,782 through 834,882
+turn on 966,850 through 989,953
+turn off 891,543 through 914,991
+toggle 908,77 through 916,117
+turn on 576,900 through 943,934
+turn off 580,170 through 963,206
+turn on 184,638 through 192,944
+toggle 940,147 through 978,730
+turn off 854,56 through 965,591
+toggle 717,172 through 947,995
+toggle 426,987 through 705,998
+turn on 987,157 through 992,278
+toggle 995,774 through 997,784
+turn off 796,96 through 845,182
+turn off 451,87 through 711,655
+turn off 380,93 through 968,676
+turn on 263,468 through 343,534
+turn on 917,936 through 928,959
+toggle 478,7 through 573,148
+turn off 428,339 through 603,624
+turn off 400,880 through 914,953
+toggle 679,428 through 752,779
+turn off 697,981 through 709,986
+toggle 482,566 through 505,725
+turn off 956,368 through 993,516
+toggle 735,823 through 783,883
+turn off 48,487 through 892,496
+turn off 116,680 through 564,819
+turn on 633,865 through 729,930
+turn off 314,618 through 571,922
+toggle 138,166 through 936,266
+turn on 444,732 through 664,960
+turn off 109,337 through 972,497
+turn off 51,432 through 77,996
+turn off 259,297 through 366,744
+toggle 801,130 through 917,544
+toggle 767,982 through 847,996
+turn on 216,507 through 863,885
+turn off 61,441 through 465,731
+turn on 849,970 through 944,987
+toggle 845,76 through 852,951
+toggle 732,615 through 851,936
+toggle 251,128 through 454,778
+turn on 324,429 through 352,539
+toggle 52,450 through 932,863
+turn off 449,379 through 789,490
+turn on 317,319 through 936,449
+toggle 887,670 through 957,838
+toggle 671,613 through 856,664
+turn off 186,648 through 985,991
+turn off 471,689 through 731,717
+toggle 91,331 through 750,758
+toggle 201,73 through 956,524
+toggle 82,614 through 520,686
+toggle 84,287 through 467,734
+turn off 132,367 through 208,838
+toggle 558,684 through 663,920
+turn on 237,952 through 265,997
+turn on 694,713 through 714,754
+turn on 632,523 through 862,827
+turn on 918,780 through 948,916
+turn on 349,586 through 663,976
+toggle 231,29 through 257,589
+toggle 886,428 through 902,993
+turn on 106,353 through 236,374
+turn on 734,577 through 759,684
+turn off 347,843 through 696,912
+turn on 286,699 through 964,883
+turn on 605,875 through 960,987
+turn off 328,286 through 869,461
+turn off 472,569 through 980,848
+toggle 673,573 through 702,884
+turn off 398,284 through 738,332
+turn on 158,50 through 284,411
+turn off 390,284 through 585,663
+turn on 156,579 through 646,581
+turn on 875,493 through 989,980
+toggle 486,391 through 924,539
+turn on 236,722 through 272,964
+toggle 228,282 through 470,581
+toggle 584,389 through 750,761
+turn off 899,516 through 900,925
+turn on 105,229 through 822,846
+turn off 253,77 through 371,877
+turn on 826,987 through 906,992
+turn off 13,152 through 615,931
+turn on 835,320 through 942,399
+turn on 463,504 through 536,720
+toggle 746,942 through 786,998
+turn off 867,333 through 965,403
+turn on 591,477 through 743,692
+turn off 403,437 through 508,908
+turn on 26,723 through 368,814
+turn on 409,485 through 799,809
+turn on 115,630 through 704,705
+turn off 228,183 through 317,220
+toggle 300,649 through 382,842
+turn off 495,365 through 745,562
+turn on 698,346 through 744,873
+turn on 822,932 through 951,934
+toggle 805,30 through 925,421
+toggle 441,152 through 653,274
+toggle 160,81 through 257,587
+turn off 350,781 through 532,917
+toggle 40,583 through 348,636
+turn on 280,306 through 483,395
+toggle 392,936 through 880,955
+toggle 496,591 through 851,934
+turn off 780,887 through 946,994
+turn off 205,735 through 281,863
+toggle 100,876 through 937,915
+turn on 392,393 through 702,878
+turn on 956,374 through 976,636
+toggle 478,262 through 894,775
+turn off 279,65 through 451,677
+turn on 397,541 through 809,847
+turn on 444,291 through 451,586
+toggle 721,408 through 861,598
+turn on 275,365 through 609,382
+turn on 736,24 through 839,72
+turn off 86,492 through 582,712
+turn on 676,676 through 709,703
+turn off 105,710 through 374,817
+toggle 328,748 through 845,757
+toggle 335,79 through 394,326
+toggle 193,157 through 633,885
+turn on 227,48 through 769,743
+toggle 148,333 through 614,568
+toggle 22,30 through 436,263
+toggle 547,447 through 688,969
+toggle 576,621 through 987,740
+turn on 711,334 through 799,515
+turn on 541,448 through 654,951
+toggle 792,199 through 798,990
+turn on 89,956 through 609,960
+toggle 724,433 through 929,630
+toggle 144,895 through 201,916
+toggle 226,730 through 632,871
+turn off 760,819 through 828,974
+toggle 887,180 through 940,310
+toggle 222,327 through 805,590
+turn off 630,824 through 885,963
+turn on 940,740 through 954,946
+turn on 193,373 through 779,515
+toggle 304,955 through 469,975
+turn off 405,480 through 546,960
+turn on 662,123 through 690,669
+turn off 615,238 through 750,714
+turn on 423,220 through 930,353
+turn on 329,769 through 358,970
+toggle 590,151 through 704,722
+turn off 884,539 through 894,671
+toggle 449,241 through 984,549
+toggle 449,260 through 496,464
+turn off 306,448 through 602,924
+turn on 286,805 through 555,901
+toggle 722,177 through 922,298
+toggle 491,554 through 723,753
+turn on 80,849 through 174,996
+turn off 296,561 through 530,856
+toggle 653,10 through 972,284
+toggle 529,236 through 672,614
+toggle 791,598 through 989,695
+turn on 19,45 through 575,757
+toggle 111,55 through 880,871
+turn off 197,897 through 943,982
+turn on 912,336 through 977,605
+toggle 101,221 through 537,450
+turn on 101,104 through 969,447
+toggle 71,527 through 587,717
+toggle 336,445 through 593,889
+toggle 214,179 through 575,699
+turn on 86,313 through 96,674
+toggle 566,427 through 906,888
+turn off 641,597 through 850,845
+turn on 606,524 through 883,704
+turn on 835,775 through 867,887
+toggle 547,301 through 897,515
+toggle 289,930 through 413,979
+turn on 361,122 through 457,226
+turn on 162,187 through 374,746
+turn on 348,461 through 454,675
+turn off 966,532 through 985,537
+turn on 172,354 through 630,606
+turn off 501,880 through 680,993
+turn off 8,70 through 566,592
+toggle 433,73 through 690,651
+toggle 840,798 through 902,971
+toggle 822,204 through 893,760
+turn off 453,496 through 649,795
+turn off 969,549 through 990,942
+turn off 789,28 through 930,267
+toggle 880,98 through 932,434
+toggle 568,674 through 669,753
+turn on 686,228 through 903,271
+turn on 263,995 through 478,999
+toggle 534,675 through 687,955
+turn off 342,434 through 592,986
+toggle 404,768 through 677,867
+toggle 126,723 through 978,987
+toggle 749,675 through 978,959
+turn off 445,330 through 446,885
+turn off 463,205 through 924,815
+turn off 417,430 through 915,472
+turn on 544,990 through 912,999
+turn off 201,255 through 834,789
+turn off 261,142 through 537,862
+turn off 562,934 through 832,984
+turn off 459,978 through 691,980
+turn off 73,911 through 971,972
+turn on 560,448 through 723,810
+turn on 204,630 through 217,854
+turn off 91,259 through 611,607
+turn on 877,32 through 978,815
+turn off 950,438 through 974,746
+toggle 426,30 through 609,917
+toggle 696,37 through 859,201
+toggle 242,417 through 682,572
+turn off 388,401 through 979,528
+turn off 79,345 through 848,685
+turn off 98,91 through 800,434
+toggle 650,700 through 972,843
+turn off 530,450 through 538,926
+turn on 428,559 through 962,909
+turn on 78,138 through 92,940
+toggle 194,117 through 867,157
+toggle 785,355 through 860,617
+turn off 379,441 through 935,708
+turn off 605,133 through 644,911
+toggle 10,963 through 484,975
+turn off 359,988 through 525,991
+turn off 509,138 through 787,411
+toggle 556,467 through 562,773
+turn on 119,486 through 246,900
+turn on 445,561 through 794,673
+turn off 598,681 through 978,921
+turn off 974,230 through 995,641
+turn off 760,75 through 800,275
+toggle 441,215 through 528,680
+turn off 701,636 through 928,877
+turn on 165,753 through 202,780
+toggle 501,412 through 998,516
+toggle 161,105 through 657,395
+turn on 113,340 through 472,972
+toggle 384,994 through 663,999
+turn on 969,994 through 983,997
+turn on 519,600 through 750,615
+turn off 363,899 through 948,935
+turn on 271,845 through 454,882
+turn off 376,528 through 779,640
+toggle 767,98 through 854,853
+toggle 107,322 through 378,688
+turn off 235,899 through 818,932
+turn on 445,611 through 532,705
+toggle 629,387 through 814,577
+toggle 112,414 through 387,421
+toggle 319,184 through 382,203
+turn on 627,796 through 973,940
+toggle 602,45 through 763,151
+turn off 441,375 through 974,545
+toggle 871,952 through 989,998
+turn on 717,272 through 850,817
+toggle 475,711 through 921,882
+toggle 66,191 through 757,481
+turn off 50,197 through 733,656
+toggle 83,575 through 915,728
+turn on 777,812 through 837,912
+turn on 20,984 through 571,994
+turn off 446,432 through 458,648
+turn on 715,871 through 722,890
+toggle 424,675 through 740,862
+toggle 580,592 through 671,900
+toggle 296,687 through 906,775
diff --git a/06/main.py b/06/main.py
new file mode 100755
index 0000000..cf9f85c
--- /dev/null
+++ b/06/main.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+
+import numpy as np
+
+
+
+def solve_partn(partn, lines):
+ # In part 1, we treat an int32 as a boolean: 1 = True, 0 = False
+ grid = np.zeros((1000, 1000), dtype="int32")
+
+ for line in lines:
+ words = line.split()
+ if words[0] == "turn":
+ xi, yi = [int(n) for n in words[2].split(",")]
+ xf, yf = [int(n) + 1 for n in words[4].split(",")]
+
+ if partn == 1:
+ if words[1] == "on":
+ grid[xi : xf, yi : yf] = 1
+ elif words[1] == "off":
+ grid[xi : xf, yi : yf] = 0
+ else: # partn == 2
+ if words[1] == "on":
+ grid[xi : xf, yi : yf] += 1
+ elif words[1] == "off":
+ grid[xi : xf, yi : yf] -= 1
+ grid = np.clip(grid, 0, 1000000)
+
+ elif words[0] == "toggle":
+ xi, yi = [int(n) for n in words[1].split(",")]
+ xf, yf = [int(n) + 1 for n in words[3].split(",")]
+
+ if partn == 1:
+ grid[xi : xf, yi : yf] = 1 - grid[xi : xf, yi : yf] # NOT
+ else: # partn == 2
+ grid[xi : xf, yi : yf] += 2
+
+ return np.sum(grid)
+
+
+
+def main():
+ # Read instructions from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_partn(1, lines)) # 400410 for me
+ print("Part 2 solution:", solve_partn(2, lines)) # 15343601 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/06/test.py b/06/test.py
new file mode 100755
index 0000000..ea9380a
--- /dev/null
+++ b/06/test.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ lines = ["turn on 0,0 through 999,999"]
+ self.assertEqual(main.solve_partn(1, lines), 1000000)
+
+ def test_example2(self):
+ lines = ["turn on 0,0 through 999,999", "toggle 0,0 through 999,0"]
+ self.assertEqual(main.solve_partn(1, lines), 999000)
+
+ def test_example3(self):
+ lines = ["turn on 0,0 through 999,999", "turn off 499,499 through 500,500"]
+ self.assertEqual(main.solve_partn(1, lines), 999996)
+
+
+
+class ExamplesPart2(unittest.TestCase):
+ def test_example1(self):
+ lines = ["turn on 0,0 through 0,0"]
+ self.assertEqual(main.solve_partn(2, lines), 1)
+
+ def test_example2(self):
+ lines = ["toggle 0,0 through 999,999"]
+ self.assertEqual(main.solve_partn(2, lines), 2000000)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/07/input.txt b/07/input.txt
new file mode 100644
index 0000000..1b7dfc9
--- /dev/null
+++ b/07/input.txt
@@ -0,0 +1,339 @@
+bn RSHIFT 2 -> bo
+lf RSHIFT 1 -> ly
+fo RSHIFT 3 -> fq
+cj OR cp -> cq
+fo OR fz -> ga
+t OR s -> u
+lx -> a
+NOT ax -> ay
+he RSHIFT 2 -> hf
+lf OR lq -> lr
+lr AND lt -> lu
+dy OR ej -> ek
+1 AND cx -> cy
+hb LSHIFT 1 -> hv
+1 AND bh -> bi
+ih AND ij -> ik
+c LSHIFT 1 -> t
+ea AND eb -> ed
+km OR kn -> ko
+NOT bw -> bx
+ci OR ct -> cu
+NOT p -> q
+lw OR lv -> lx
+NOT lo -> lp
+fp OR fv -> fw
+o AND q -> r
+dh AND dj -> dk
+ap LSHIFT 1 -> bj
+bk LSHIFT 1 -> ce
+NOT ii -> ij
+gh OR gi -> gj
+kk RSHIFT 1 -> ld
+lc LSHIFT 1 -> lw
+lb OR la -> lc
+1 AND am -> an
+gn AND gp -> gq
+lf RSHIFT 3 -> lh
+e OR f -> g
+lg AND lm -> lo
+ci RSHIFT 1 -> db
+cf LSHIFT 1 -> cz
+bn RSHIFT 1 -> cg
+et AND fe -> fg
+is OR it -> iu
+kw AND ky -> kz
+ck AND cl -> cn
+bj OR bi -> bk
+gj RSHIFT 1 -> hc
+iu AND jf -> jh
+NOT bs -> bt
+kk OR kv -> kw
+ks AND ku -> kv
+hz OR ik -> il
+b RSHIFT 1 -> v
+iu RSHIFT 1 -> jn
+fo RSHIFT 5 -> fr
+be AND bg -> bh
+ga AND gc -> gd
+hf OR hl -> hm
+ld OR le -> lf
+as RSHIFT 5 -> av
+fm OR fn -> fo
+hm AND ho -> hp
+lg OR lm -> ln
+NOT kx -> ky
+kk RSHIFT 3 -> km
+ek AND em -> en
+NOT ft -> fu
+NOT jh -> ji
+jn OR jo -> jp
+gj AND gu -> gw
+d AND j -> l
+et RSHIFT 1 -> fm
+jq OR jw -> jx
+ep OR eo -> eq
+lv LSHIFT 15 -> lz
+NOT ey -> ez
+jp RSHIFT 2 -> jq
+eg AND ei -> ej
+NOT dm -> dn
+jp AND ka -> kc
+as AND bd -> bf
+fk OR fj -> fl
+dw OR dx -> dy
+lj AND ll -> lm
+ec AND ee -> ef
+fq AND fr -> ft
+NOT kp -> kq
+ki OR kj -> kk
+cz OR cy -> da
+as RSHIFT 3 -> au
+an LSHIFT 15 -> ar
+fj LSHIFT 15 -> fn
+1 AND fi -> fj
+he RSHIFT 1 -> hx
+lf RSHIFT 2 -> lg
+kf LSHIFT 15 -> kj
+dz AND ef -> eh
+ib OR ic -> id
+lf RSHIFT 5 -> li
+bp OR bq -> br
+NOT gs -> gt
+fo RSHIFT 1 -> gh
+bz AND cb -> cc
+ea OR eb -> ec
+lf AND lq -> ls
+NOT l -> m
+hz RSHIFT 3 -> ib
+NOT di -> dj
+NOT lk -> ll
+jp RSHIFT 3 -> jr
+jp RSHIFT 5 -> js
+NOT bf -> bg
+s LSHIFT 15 -> w
+eq LSHIFT 1 -> fk
+jl OR jk -> jm
+hz AND ik -> im
+dz OR ef -> eg
+1 AND gy -> gz
+la LSHIFT 15 -> le
+br AND bt -> bu
+NOT cn -> co
+v OR w -> x
+d OR j -> k
+1 AND gd -> ge
+ia OR ig -> ih
+NOT go -> gp
+NOT ed -> ee
+jq AND jw -> jy
+et OR fe -> ff
+aw AND ay -> az
+ff AND fh -> fi
+ir LSHIFT 1 -> jl
+gg LSHIFT 1 -> ha
+x RSHIFT 2 -> y
+db OR dc -> dd
+bl OR bm -> bn
+ib AND ic -> ie
+x RSHIFT 3 -> z
+lh AND li -> lk
+ce OR cd -> cf
+NOT bb -> bc
+hi AND hk -> hl
+NOT gb -> gc
+1 AND r -> s
+fw AND fy -> fz
+fb AND fd -> fe
+1 AND en -> eo
+z OR aa -> ab
+bi LSHIFT 15 -> bm
+hg OR hh -> hi
+kh LSHIFT 1 -> lb
+cg OR ch -> ci
+1 AND kz -> la
+gf OR ge -> gg
+gj RSHIFT 2 -> gk
+dd RSHIFT 2 -> de
+NOT ls -> lt
+lh OR li -> lj
+jr OR js -> jt
+au AND av -> ax
+0 -> c
+he AND hp -> hr
+id AND if -> ig
+et RSHIFT 5 -> ew
+bp AND bq -> bs
+e AND f -> h
+ly OR lz -> ma
+1 AND lu -> lv
+NOT jd -> je
+ha OR gz -> hb
+dy RSHIFT 1 -> er
+iu RSHIFT 2 -> iv
+NOT hr -> hs
+as RSHIFT 1 -> bl
+kk RSHIFT 2 -> kl
+b AND n -> p
+ln AND lp -> lq
+cj AND cp -> cr
+dl AND dn -> do
+ci RSHIFT 2 -> cj
+as OR bd -> be
+ge LSHIFT 15 -> gi
+hz RSHIFT 5 -> ic
+dv LSHIFT 1 -> ep
+kl OR kr -> ks
+gj OR gu -> gv
+he RSHIFT 5 -> hh
+NOT fg -> fh
+hg AND hh -> hj
+b OR n -> o
+jk LSHIFT 15 -> jo
+gz LSHIFT 15 -> hd
+cy LSHIFT 15 -> dc
+kk RSHIFT 5 -> kn
+ci RSHIFT 3 -> ck
+at OR az -> ba
+iu RSHIFT 3 -> iw
+ko AND kq -> kr
+NOT eh -> ei
+aq OR ar -> as
+iy AND ja -> jb
+dd RSHIFT 3 -> df
+bn RSHIFT 3 -> bp
+1 AND cc -> cd
+at AND az -> bb
+x OR ai -> aj
+kk AND kv -> kx
+ao OR an -> ap
+dy RSHIFT 3 -> ea
+x RSHIFT 1 -> aq
+eu AND fa -> fc
+kl AND kr -> kt
+ia AND ig -> ii
+df AND dg -> di
+NOT fx -> fy
+k AND m -> n
+bn RSHIFT 5 -> bq
+km AND kn -> kp
+dt LSHIFT 15 -> dx
+hz RSHIFT 2 -> ia
+aj AND al -> am
+cd LSHIFT 15 -> ch
+hc OR hd -> he
+he RSHIFT 3 -> hg
+bn OR by -> bz
+NOT kt -> ku
+z AND aa -> ac
+NOT ak -> al
+cu AND cw -> cx
+NOT ie -> if
+dy RSHIFT 2 -> dz
+ip LSHIFT 15 -> it
+de OR dk -> dl
+au OR av -> aw
+jg AND ji -> jj
+ci AND ct -> cv
+dy RSHIFT 5 -> eb
+hx OR hy -> hz
+eu OR fa -> fb
+gj RSHIFT 3 -> gl
+fo AND fz -> gb
+1 AND jj -> jk
+jp OR ka -> kb
+de AND dk -> dm
+ex AND ez -> fa
+df OR dg -> dh
+iv OR jb -> jc
+x RSHIFT 5 -> aa
+NOT hj -> hk
+NOT im -> in
+fl LSHIFT 1 -> gf
+hu LSHIFT 15 -> hy
+iq OR ip -> ir
+iu RSHIFT 5 -> ix
+NOT fc -> fd
+NOT el -> em
+ck OR cl -> cm
+et RSHIFT 3 -> ev
+hw LSHIFT 1 -> iq
+ci RSHIFT 5 -> cl
+iv AND jb -> jd
+dd RSHIFT 5 -> dg
+as RSHIFT 2 -> at
+NOT jy -> jz
+af AND ah -> ai
+1 AND ds -> dt
+jx AND jz -> ka
+da LSHIFT 1 -> du
+fs AND fu -> fv
+jp RSHIFT 1 -> ki
+iw AND ix -> iz
+iw OR ix -> iy
+eo LSHIFT 15 -> es
+ev AND ew -> ey
+ba AND bc -> bd
+fp AND fv -> fx
+jc AND je -> jf
+et RSHIFT 2 -> eu
+kg OR kf -> kh
+iu OR jf -> jg
+er OR es -> et
+fo RSHIFT 2 -> fp
+NOT ca -> cb
+bv AND bx -> by
+u LSHIFT 1 -> ao
+cm AND co -> cp
+y OR ae -> af
+bn AND by -> ca
+1 AND ke -> kf
+jt AND jv -> jw
+fq OR fr -> fs
+dy AND ej -> el
+NOT kc -> kd
+ev OR ew -> ex
+dd OR do -> dp
+NOT cv -> cw
+gr AND gt -> gu
+dd RSHIFT 1 -> dw
+NOT gw -> gx
+NOT iz -> ja
+1 AND io -> ip
+NOT ag -> ah
+b RSHIFT 5 -> f
+NOT cr -> cs
+kb AND kd -> ke
+jr AND js -> ju
+cq AND cs -> ct
+il AND in -> io
+NOT ju -> jv
+du OR dt -> dv
+dd AND do -> dq
+b RSHIFT 2 -> d
+jm LSHIFT 1 -> kg
+NOT dq -> dr
+bo OR bu -> bv
+gk OR gq -> gr
+he OR hp -> hq
+NOT h -> i
+hf AND hl -> hn
+gv AND gx -> gy
+x AND ai -> ak
+bo AND bu -> bw
+hq AND hs -> ht
+hz RSHIFT 1 -> is
+gj RSHIFT 5 -> gm
+g AND i -> j
+gk AND gq -> gs
+dp AND dr -> ds
+b RSHIFT 3 -> e
+gl AND gm -> go
+gl OR gm -> gn
+y AND ae -> ag
+hv OR hu -> hw
+1674 -> b
+ab AND ad -> ae
+NOT ac -> ad
+1 AND ht -> hu
+NOT hn -> ho
diff --git a/07/main.py b/07/main.py
new file mode 100755
index 0000000..7babff9
--- /dev/null
+++ b/07/main.py
@@ -0,0 +1,61 @@
+#!/usr/bin/python
+
+
+
+def eval_wire(wires, w):
+ # Shortcut for cleaner code
+ if w.isdigit():
+ return int(w) & 65535
+
+ result = 0
+ tokens = wires[w]
+
+ if len(tokens) == 1:
+ result = eval_wire(wires, tokens[0])
+
+ if len(tokens) == 2:
+ if tokens[0] == "NOT":
+ result = ~eval_wire(wires, tokens[1])
+
+ if len(tokens) == 3:
+ if tokens[1] == "AND":
+ result = eval_wire(wires, tokens[0]) & eval_wire(wires, tokens[2])
+ if tokens[1] == "OR":
+ result = eval_wire(wires, tokens[0]) | eval_wire(wires, tokens[2])
+ if tokens[1] == "LSHIFT":
+ result = eval_wire(wires, tokens[0]) << eval_wire(wires, tokens[2])
+ if tokens[1] == "RSHIFT":
+ result = eval_wire(wires, tokens[0]) >> eval_wire(wires, tokens[2])
+
+ result &= 65535
+ wires[w] = [str(result)] # cache result
+ return int(result)
+
+
+
+def solve_partn(partn, lines):
+ # Parse input into a marginally nicer form
+ wires = {}
+ for line in lines:
+ words = line.split(" ")
+ wires[words[-1]] = words[0 : -2]
+
+ if partn == 2:
+ wires["b"] = ["46065"]
+
+ return eval_wire(wires, "a")
+
+
+
+def main():
+ # Read wire configuration from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_partn(1, lines)) # 46065 for me
+ print("Part 2 solution:", solve_partn(2, lines)) # 14134 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/07/test.py b/07/test.py
new file mode 100755
index 0000000..a171571
--- /dev/null
+++ b/07/test.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def setUp(self):
+ self.lines = [
+ "123 -> x",
+ "456 -> y",
+ "x AND y -> d",
+ "x OR y -> e",
+ "x LSHIFT 2 -> f",
+ "y RSHIFT 2 -> g",
+ "NOT x -> h",
+ "NOT y -> i"
+ ]
+
+ def test_example1(self):
+ lines = "\n".join(self.lines).replace("d", "a").splitlines()
+ self.assertEqual(main.solve_partn(1, lines), 72)
+
+ def test_example2(self):
+ lines = "\n".join(self.lines).replace("e", "a").splitlines()
+ self.assertEqual(main.solve_partn(1, lines), 507)
+
+ def test_example3(self):
+ lines = "\n".join(self.lines).replace("f", "a").splitlines()
+ self.assertEqual(main.solve_partn(1, lines), 492)
+
+ def test_example4(self):
+ lines = "\n".join(self.lines).replace("g", "a").splitlines()
+ self.assertEqual(main.solve_partn(1, lines), 114)
+
+ def test_example5(self):
+ lines = "\n".join(self.lines).replace("h", "a").splitlines()
+ self.assertEqual(main.solve_partn(1, lines), 65412)
+
+ def test_example6(self):
+ lines = "\n".join(self.lines).replace("i", "a").splitlines()
+ self.assertEqual(main.solve_partn(1, lines), 65079)
+
+ def test_example7(self):
+ lines = "\n".join(self.lines).replace("x", "a").splitlines()
+ self.assertEqual(main.solve_partn(1, lines), 123)
+
+ def test_example8(self):
+ lines = "\n".join(self.lines).replace("y", "a").splitlines()
+ self.assertEqual(main.solve_partn(1, lines), 456)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/08/input.txt b/08/input.txt
new file mode 100644
index 0000000..3309605
--- /dev/null
+++ b/08/input.txt
@@ -0,0 +1,300 @@
+"sjdivfriyaaqa\xd2v\"k\"mpcu\"yyu\"en"
+"vcqc"
+"zbcwgmbpijcxu\"yins\"sfxn"
+"yumngprx"
+"bbdj"
+"czbggabkzo\"wsnw\"voklp\"s"
+"acwt"
+"aqttwnsohbzian\"evtllfxwkog\"cunzw"
+"ugvsgfv"
+"xlnillibxg"
+"kexh\"pmi"
+"syvugow"
+"m\"ktqnw"
+"yrbajyndte\\rm"
+"f\"kak\x70sn\xc4kjri"
+"yxthr"
+"alvumfsjni\"kohg"
+"trajs\x5brom\xf1yoijaumkem\"\"tahlzs"
+"\"oedr\"pwdbnnrc"
+"qsmzhnx\""
+"\"msoytqimx\\tbklqz"
+"mjdfcgwdshrehgs"
+"\"rivyxahf\""
+"ciagc\x04bp"
+"xkfc"
+"xrgcripdu\x4c\xc4gszjhrvumvz\"mngbirb"
+"gvmae\"yiiujoqvr\"mkxmgbbut\"u"
+"ih"
+"ncrqlejehs"
+"mkno\x43pcfdukmemycp"
+"uanzoqxkpsksbvdnkji\"feamp"
+"axoufpnbx\\ao\x61pfj\"b"
+"dz\\ztawzdjy"
+"ihne\"enumvswypgf"
+"\"dgazthrphbshdo\\vuqoiy\""
+"dlnmptzt\\zahwpylc\\b\"gmslrqysk"
+"mhxznyzcp"
+"rebr\"amvxw\x5fmbnfpkkeghlntavj"
+"lades\x47ncgdof\"\"jmbbk"
+"dwxuis\xa5wdkx\\z\"admgnoddpgkt\\zs"
+"g\\k\x27qsl\x34hwfglcdxqbeclt\xca\\"
+"lhyjky\\m\"pvnm\\xmynpxnlhndmahjl"
+"c\"uxabbgorrpprw\"xas\\vefkxioqpt"
+"rfrvjxpevcmma\x71gtfipo"
+"fgh\"kcwoqwfnjgdlzfclprg\"q"
+"onxnwykrba"
+"hkkg\x60f\"tjzsanpvarzgkfipl"
+"\"aintes\"ofq\"juiaqlqxmvpe\\a"
+"wiyczzs\"ciwk"
+"mfqeu"
+"v\xe1z\x7ftzalmvdmncfivrax\\rjwq"
+"k\"vtg"
+"exhrtdugeml\xf0"
+"behnchkpld"
+"mhgxy\"mfcrg\xc5gnp\"\"osqhj"
+"rlvjy"
+"awe"
+"ctwy"
+"vt"
+"\x54t"
+"zugfmmfomz"
+"cv\"cvcvfaada\x04fsuqjinbfh\xa9cq\xd2c\"d"
+"oj"
+"xazanf\"wbmcrn"
+"\\\\zkisyjpbzandqikqjqvee"
+"dpsnbzdwnxk\\v"
+"sj\"tuupr\\oyoh"
+"myvkgnw\x81q\xaaokt\\emgejbsyvxcl\\\xee"
+"ejeuqvunjcirdkkpt\"nlns"
+"twmlvwxyvfyqqzu"
+"\"xwtzdp\x98qkcis\"dm\\\"ep\"xyykq"
+"vvcq\\expok"
+"wgukjfanjgpdjb"
+"\"mjcjajnxy\\dcpc"
+"wdvgnecw\\ab\x44klceduzgsvu"
+"dqtqkukr\"iacngufbqkdpxlwjjt"
+"\"xj\"\x66qofsqzkoah"
+"nptiwwsqdep"
+"gsnlxql\x30mjl"
+"yeezwokjwrhelny\""
+"bjauamn\\izpmzqqasid"
+"tvjdbkn\"tiziw\x82r"
+"w"
+"xwoakbbnjnypnaa\xa9wft\"slrmoqkl"
+"vwxtnlvaaasyruykgygrvpiopzygf\"vq"
+"qdancvnvmhlmpj\\isdxs"
+"xzc\\elw"
+"b\"wxeqvy\"qf\"g\xcaoklsucwicyw\"dovr"
+"yomlvvjdbngz\"rly\"afr"
+"bfb\"x\"aweuwbwmoa\x13\"t\"zhr"
+"\"dmfoxb\"qvpjzzhykt\xd2\"\"ryhxi"
+"psqef\"yu\\qiflie\"\x79w"
+"arzewkej\"lqmh\\sayyusxxo\\"
+"vuvvp"
+"hc\"lg\x6bcpupsewzklai\"l"
+"cjdfygc\"auorqybnuqghsh\x10"
+"j"
+"wqjexk\"eyq\\lbroqhk\\dqzsqk"
+"dws\"ru\"dvxfiwapif\"oqwzmle"
+"agcykg\\jt\\vzklqjvknoe"
+"kksd\"jmslja\\z\"y\\b\xaagpyojct"
+"nnpipxufvbfpoz\"jno"
+"dtw"
+"xlolvtahvgqkx\\dgnhj\\spsclpcxv\\"
+"mxea\\mbjpi"
+"lgbotkk\"zmxh\\\\qji\"jszulnjsxkqf"
+"lwckmhwhx\"gmftlb\x91am"
+"xxdxqyxth"
+"\"lmqhwkjxmvayxy"
+"tf"
+"qy"
+"wdqmwxdztax\"m\"\x09\x11xdxmfwxmtqgwvf"
+"\xcbnazlf\"ghziknszmsrahaf"
+"e\x6aupmzhxlvwympgjjpdvo\"kylfa"
+"\x81vhtlillb\xactgoatva"
+"dvnlgr"
+"f"
+"xg\xfacwizsadgeclm"
+"vnnrzbtw\"\\prod\\djbyppngwayy\""
+"lrt\xf4jahwvfz"
+"aqpnjtom\"ymkak\\dadfybqrso\\fwv"
+"gz\"aac\"mrbk\"ktommrojraqh"
+"wycamwoecsftepfnlcdkm"
+"nrhddblbuzlqsl\x9cben"
+"vckxhyqkmqmdseazcykrbysm"
+"sil\xbbtevmt\"gvrvybui\"faw\"j"
+"cjex\\tp\x45pzf"
+"asjobvtxszfodgf\"ibftg"
+"gkyjyjdrxdcllnh\"sjcibenrdnxv"
+"oswsdpjyxpbwnqbcpl\"yrdvs\\zq"
+"\"\"tyowzc\\fycbp\"jbwrbvgui"
+"cbpcabqkdgzmpgcwjtrchxp"
+"iyrzfh\x45gw\"fdlfpiaap\x31xqq"
+"evgksznidz"
+"b\\w\\"
+"loufizbiy\x57aim\"bgk"
+"qjfyk"
+"g\"anmloghvgr\x07zwqougqhdz"
+"usbbmwcxd\\bdgg"
+"htitqcpczml"
+"eke\\cqvpexqqk\"to\"tqmljrpn\xe6lji\""
+"g\xd2ifdsej"
+"h\"sk\"haajajpagtcqnzrfqn\xe6btzo"
+"wfkuffdxlvm\\cvlyzlbyunclhmpp"
+"myaavh\"spue"
+"hqvez\x68d\"eo\"eaioh"
+"s\"qd\"oyxxcglcdnuhk"
+"ilqvar"
+"srh"
+"puuifxrfmpc\"bvalwi\x2blu\\"
+"yywlbutufzysbncw\\nqsfbhpz\"mngjq"
+"zbl\\jfcuop"
+"hjdouiragzvxsqkreup\\"
+"qi"
+"ckx\\funlj\xa7ahi"
+"k"
+"ufrcnh\"ajteit"
+"cqv\"bgjozjj\x60x\xa8yhvmdvutchjotyuz"
+"hkuiet\"oku\x8cfhumfpasl"
+"\"\\sbe\x4d"
+"vhknazqt"
+"eyyizvzcahgflvmoowvs\\jhvygci"
+"kki\x3ewcefkgtjap\"xtpxh\"lzepoqj"
+"wvtk"
+"\"ynet"
+"zh\\obk\"otagx\x59txfzf"
+"ocowhxlx\xe6zqg\x63wx\\tclkhq\\vmaze"
+"w\"cf"
+"qpniprnrzrnvykghqnalr"
+"jctcqra\"\x05dhlydpqamorqjsijt\\xjdgt"
+"sig"
+"qhlbidbflwxe\"xljbwls\x20vht"
+"irmrebfla\xefsg\"j"
+"nep"
+"hjuvsqlizeqobepf"
+"guzbcdp\"obyh"
+"\"mjagins\xf9tqykaxy\""
+"knvsdnmtr\"zervsb"
+"hzuy"
+"zza\"k\"buapb\\elm\xfeya"
+"lrqar\"dfqwkaaqifig\"uixjsz"
+"\"azuo\x40rmnlhhluwsbbdb\x32pk\\yu\"pbcf"
+"dplkdyty"
+"rfoyciebwlwphcycmguc"
+"ivnmmiemhgytmlprq\\eh"
+"lhkyzaaothfdhmbpsqd\\yyw"
+"tnlzifupcjcaj"
+"\\qiyirsdrfpmu\\\x15xusifaag"
+"\\lcomf\\s"
+"uramjivcirjhqcqcg"
+"kkbaklbxfxikffnuhtu\xc6t\"d"
+"n\xefai"
+"\"toy\"bnbpevuzoc\"muywq\"gz\"grbm"
+"\"muu\\wt"
+"\\srby\"ee"
+"erf\"gvw\"swfppf"
+"pbqcgtn\"iuianhcdazfvmidn\\nslhxdf"
+"uxbp"
+"up\\mgrcyaegiwmjufn"
+"nulscgcewj\\dvoyvhetdegzhs\""
+"masv\"k\\rzrb"
+"qtx\x79d\"xdxmbxrvhj"
+"fid\\otpkgjlh\"qgsvexrckqtn\xf4"
+"tagzu"
+"bvl\\\"noseec"
+"\\xgicuuh"
+"w\"a\"npemf"
+"sxp"
+"nsmpktic\x8awxftscdcvijjobnq\"gjd"
+"uks\"\"jxvyvfezz\"aynxoev\"cuoav"
+"m"
+"lkvokj"
+"vkfam\"yllr\"q\x92o\x4ebecnvhshhqe\\"
+"efdxcjkjverw"
+"lmqzadwhfdgmep\x02tzfcbgrbfekhat"
+"cpbk\x9azqegbpluczssouop\x36ztpuoxsw"
+"cqwoczxdd\"erdjka"
+"cwvqnjgbw\\fxdlby"
+"mvtm"
+"lt\"bbqzpumplkg"
+"ntd\xeeuwweucnuuslqfzfq"
+"y\xabl\"dbebxjrlbmuoo\\\x1au"
+"qjoqx\\a"
+"pu\"ekdnfpmly\xbago\""
+"fjhhdy"
+"arl"
+"xcywisim\"bwuwf\"\"raepeawwjub"
+"pbe"
+"dbnqfpzyaumxtqnd\xc5dcqrkwyop"
+"ojv\x40vtkwgkqepm\x8bzft\\vedrry"
+"wggqkfbwqumsgajqwphjec\"mstxpwz"
+"zjkbem"
+"icpfqxbelxazlls"
+"pvpqs\\abcmtyielugfgcv\"tjxapxqxnx"
+"oqddwlvmtv\"\x39lyybylfb\"jmngnpjrdw"
+"gisgbve"
+"\"aglg"
+"y\"\"ss\xafvhxlrjv"
+"qbgqjsra"
+"ihshbjgqpdcljpmdwdprwloy"
+"djja\\wcdn\"svkrgpqn\"uz\"hc\x43hj"
+"cbjm"
+"pnn"
+"pqvh\"noh"
+"\"\\fdktlp"
+"ncea"
+"pqgzphiyy"
+"\xbedovhxuipaohlcvkwtxwmpz\"ckaif\"r"
+"arjuzbjowqciunfwgxtph\"vlhy\"n"
+"c"
+"nrpdxunulgudqzlhtae"
+"iefheu\"uru\""
+"aqijysxuijud\"np\\opbichhudil\xbesum"
+"pfpevmtstl\"lde\"bzr\"vspdxs"
+"vparfbdjwvzsocpnzhp"
+"g\x4ffxaarafrsjthq\\\xc1rw"
+"ng\\rqx\\gwpzucbh\xafl"
+"rw\"nf\\dna"
+"jkkeahxurxla\\g\xb3czrlsyimmwcwthr"
+"twaailoypu\"oas\"kpuuyedlaw\\\xb0vzt"
+"hznex\\gdiqvtugi"
+"imdibsunjeswhk"
+"ta\\icileuzpxro\"cfmv\"mzp"
+"coykr\x57luiysucfaflmilhlehmvzeiepo"
+"u\x3dfh\xd4yt"
+"piw\x1bz\"eowy\"vfk\"wqiekw"
+"gan\"y"
+"p\"bevidoazcznr\"hddxuuq\""
+"bwzucczznutbxe"
+"z\"viqgyqjisior\\iecosmjbknol"
+"dmlpcglcfkfsctxydjvayhymv\x3c\\gp"
+"bfvkqrintbbvgfv"
+"xlzntrgdck\"cprc\xadczyarbznqmuhxyuh"
+"uqdxnuwioc\"kdytxq\\ig"
+"xrafmucpmfi"
+"vr\"hltmfrge"
+"eonf\"nt\\wtcnsocs"
+"j\xb7xoslyjeyjksplkqixncgkylkw"
+"njw\"pefgfbez\x9axshdmplxzquqe"
+"di\x58bvptfsafirpc"
+"l\x1fkco"
+"x"
+"mprndo\"n"
+"psegit"
+"svbdnkkuuqs\"sqxu\"oqcyz\"aizashk"
+"cwkljukxer\\\"\\nff\"esjwiyaoy"
+"ilxrkgbjjxpvhdtq\"cpiuoofdnkpp"
+"hlngi\"ulxep\\qohtmqnqjb\"rkgerho"
+"gxws\"bcgm\"p"
+"bv\"mds\\zhfusiepgrz\\b\x32fscdzz"
+"l\xfampwtme\x69qvxnx\"\"\xc4jruuymjxrpsv"
+"qqmxhrn"
+"xziq\\\x18ybyv\x9am\"neacoqjzytertisysza"
+"aqcbvlvcrzceeyx\\j\"\"x"
+"yjuhhb"
+"\x5em\"squulpy"
+"dpbntplgmwb"
+"utsgfkm\\vbftjknlktpthoeo"
+"ccxjgiocmuhf\"ycnh"
+"lltj\"kbbxi"
diff --git a/08/main.py b/08/main.py
new file mode 100755
index 0000000..bfd5b08
--- /dev/null
+++ b/08/main.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+
+
+
+def solve_part1(lines):
+ result = 0
+
+ for line in lines:
+ len_code = len(line)
+
+ len_data = 0
+ i = 0
+ while i < len(line):
+ if line[i] == "\"":
+ pass
+ elif line[i] == "\\":
+ if line[i + 1] == "\"" or line[i + 1] == "\\":
+ len_data += 1
+ i += 1
+ elif line[i + 1] == "x":
+ len_data += 1
+ i += 3
+ else:
+ len_data += 1
+ i += 1
+
+ result += len_code - len_data
+
+ return result
+
+
+
+def solve_part2(lines_old):
+ # Escape all the provided strings once
+ lines_new = []
+ for old in lines_old:
+ new = "\""
+ for c in old:
+ if c == "\"":
+ new += "\\\""
+ elif c == "\\":
+ new += "\\\\"
+ else:
+ new += c
+ new += "\""
+ lines_new.append(new)
+
+ # Then we can just run part 1 on the new input
+ return solve_part1(lines_new)
+
+
+
+def main():
+ # Read (escaped) strings from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_part1(lines)) # 1333 for me
+ print("Part 2 solution:", solve_part2(lines)) # 2046 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/08/test.py b/08/test.py
new file mode 100755
index 0000000..1d97b53
--- /dev/null
+++ b/08/test.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class Examples(unittest.TestCase):
+ def test_example1(self):
+ lines = ["\"\"", "\"abc\"", "\"aaa\\\"aaa\"", "\"\\x27\""]
+ self.assertEqual(main.solve_part1(lines), 12)
+ self.assertEqual(main.solve_part2(lines), 19)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/09/input.txt b/09/input.txt
new file mode 100644
index 0000000..9850564
--- /dev/null
+++ b/09/input.txt
@@ -0,0 +1,28 @@
+Faerun to Norrath = 129
+Faerun to Tristram = 58
+Faerun to AlphaCentauri = 13
+Faerun to Arbre = 24
+Faerun to Snowdin = 60
+Faerun to Tambi = 71
+Faerun to Straylight = 67
+Norrath to Tristram = 142
+Norrath to AlphaCentauri = 15
+Norrath to Arbre = 135
+Norrath to Snowdin = 75
+Norrath to Tambi = 82
+Norrath to Straylight = 54
+Tristram to AlphaCentauri = 118
+Tristram to Arbre = 122
+Tristram to Snowdin = 103
+Tristram to Tambi = 49
+Tristram to Straylight = 97
+AlphaCentauri to Arbre = 116
+AlphaCentauri to Snowdin = 12
+AlphaCentauri to Tambi = 18
+AlphaCentauri to Straylight = 91
+Arbre to Snowdin = 129
+Arbre to Tambi = 53
+Arbre to Straylight = 40
+Snowdin to Tambi = 15
+Snowdin to Straylight = 99
+Tambi to Straylight = 70
diff --git a/09/main.py b/09/main.py
new file mode 100755
index 0000000..d082678
--- /dev/null
+++ b/09/main.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+
+from itertools import permutations
+
+
+
+def parse_input(lines):
+ edges = {}
+
+ for line in lines:
+ words = line.split()
+ x = words[0]
+ y = words[2]
+ d = int(words[4])
+
+ if x not in edges:
+ edges[x] = {}
+ if y not in edges:
+ edges[y] = {}
+
+ edges[x][y] = d
+ edges[y][x] = d
+
+ return edges
+
+
+
+def solve_partn(partn, lines):
+ edges = parse_input(lines)
+ nodes = list(edges.keys())
+
+ # This is simply the famous travelling salesman problem,
+ # so exhaustively trying all routes is the only solution.
+ routes = permutations(nodes)
+ totals = []
+ for r in routes:
+ t = 0
+ for i in range(len(r) - 1):
+ t += edges[r[i]][r[i + 1]]
+ totals.append(t)
+
+ if partn == 1:
+ return min(totals)
+ else: # partn == 2
+ return max(totals)
+
+
+
+def main():
+ # Read distances (graph edges) from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_partn(1, lines)) # 207 for me
+ print("Part 2 solution:", solve_partn(2, lines)) # 804 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/09/test.py b/09/test.py
new file mode 100755
index 0000000..932c12b
--- /dev/null
+++ b/09/test.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class Examples(unittest.TestCase):
+ def test_example1(self):
+ lines = [
+ "London to Dublin = 464",
+ "London to Belfast = 518",
+ "Dublin to Belfast = 141"
+ ]
+ self.assertEqual(main.solve_partn(1, lines), 605)
+ self.assertEqual(main.solve_partn(2, lines), 982)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/10/main.py b/10/main.py
new file mode 100755
index 0000000..b2ec42c
--- /dev/null
+++ b/10/main.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+
+
+
+def play_round(old):
+ new = ""
+
+ i = 0
+ while i < len(old):
+ c = old[i]
+
+ count = 0
+ while i < len(old) and old[i] == c:
+ count += 1
+ i += 1
+
+ new += str(count) + c
+
+ return new
+
+
+
+def solve_partn(partn, string):
+ rounds = 40 if partn == 1 else 50
+ for i in range(rounds):
+ string = play_round(string)
+ return len(string)
+
+
+
+def main():
+ # My personal input string
+ string = "1113122113"
+
+ print("Part 1 solution:", solve_partn(1, string)) # 360154 for me
+ print("Part 2 solution:", solve_partn(2, string)) # 5103798 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/10/test.py b/10/test.py
new file mode 100755
index 0000000..a311b2e
--- /dev/null
+++ b/10/test.py
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ self.assertEqual(main.play_round("1"), "11")
+
+ def test_example2(self):
+ self.assertEqual(main.play_round("11"), "21")
+
+ def test_example3(self):
+ self.assertEqual(main.play_round("21"), "1211")
+
+ def test_example4(self):
+ self.assertEqual(main.play_round("1211"), "111221")
+
+ def test_example5(self):
+ self.assertEqual(main.play_round("111221"), "312211")
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/11/main.py b/11/main.py
new file mode 100755
index 0000000..9939d42
--- /dev/null
+++ b/11/main.py
@@ -0,0 +1,71 @@
+#!/usr/bin/python
+
+
+
+def increment(string):
+ last = string[-1]
+ if last == "z":
+ return increment(string[0:-1]) + "a"
+ else:
+ return string[0:-1] + chr(ord(last) + 1)
+
+
+
+def solve_part1(old):
+ valid = False
+ while not valid:
+ new = increment(old)
+
+ # Rule 2: no "i", "o" or "l"
+ # Cheap to check and likely to fail, so we do this first
+ rule2 = True
+ for c in ["i", "o", "l"]:
+ if new.count(c) > 0:
+ rule2 = False
+ break
+ if not rule2:
+ old = new
+ continue
+
+ # Rule 1: one increasing straight of three letters
+ rule1 = False
+ ords = [ord(c) for c in new]
+ for i in range(len(new) - 2):
+ if ords[i + 1] == ords[i] + 1 and ords[i + 2] == ords[i] + 2:
+ rule1 = True
+ break
+
+ # Rule 3: two different pair of letters
+ rule3 = False
+ count = 0
+ taken = []
+ for i in range(len(new) - 1):
+ if new[i] not in taken and new[i] == new[i + 1]:
+ count += 1
+ taken.append(new[i])
+ if count >= 2:
+ rule3 = True
+
+ old = new
+ valid = rule1 and rule3
+
+ return new
+
+
+
+def solve_part2(old):
+ return solve_part1(solve_part1(old))
+
+
+
+def main():
+ # My personal input password
+ passwd = "vzbxkghb"
+
+ print("Part 1 solution:", solve_part1(passwd)) # "vzbxxyzz" for me
+ print("Part 2 solution:", solve_part2(passwd)) # "vzcaabcc" for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/11/test.py b/11/test.py
new file mode 100755
index 0000000..b77bd46
--- /dev/null
+++ b/11/test.py
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ old = "abcdefgh"
+ new = "abcdffaa"
+ self.assertEqual(main.solve_part1(old), new)
+
+ def test_example2(self):
+ old = "ghijklmn"
+ new = "ghjaabcc"
+ self.assertEqual(main.solve_part1(old), new)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/12/input.txt b/12/input.txt
new file mode 100644
index 0000000..d813484
--- /dev/null
+++ b/12/input.txt
@@ -0,0 +1 @@
+{"e":{"a":{"e":-39,"c":119,"a":{"c":65,"a":"orange","b":"green","d":"orange"},"g":"violet","b":{"e":6,"c":{"c":"violet","a":8,"b":["red",{"a":37},"green",84,"yellow","green",[24,45,"blue","blue",56,"yellow"],"orange"]},"a":"violet","b":{"a":85},"d":[109,66,["yellow","violet",21,-30],"violet","blue",-43,{"e":"violet","c":"red","a":"blue","b":-22,"d":[71,"red",30,"violet","red",26,120],"f":["red"]},"red"]},"d":{"e":"violet","a":"blue","d":"blue","c":"blue","h":"orange","b":{"e":"red","a":{"c":115,"a":137,"b":"green"},"d":-25,"c":"blue","h":{"a":161,"b":["yellow",56,129,-31,"yellow","red","green",105,"orange",130]},"b":142,"g":194,"f":122,"i":-16},"g":173,"f":["orange","green",54,-9],"i":-23},"f":{"c":110,"a":"yellow","b":[{"a":155},156,"violet",94,"yellow"],"d":{"e":91,"a":-18,"d":"red","c":["green","orange","orange",190,"yellow",158,"blue","orange","blue",4],"h":143,"b":"orange","g":145,"f":["orange",37,"yellow",-22,{"c":30,"a":78,"b":196,"d":84},-7,["yellow"]]}}},"b":[[{"c":0,"a":108,"b":"green","d":{"e":59,"c":119,"a":104,"b":167,"d":"blue"}},[189,"blue",121,[["green","orange","orange",-17,192,"red"],{"a":"violet"},"green",{"c":42,"a":"blue","b":"red"},{"e":78,"a":"blue","d":"violet","c":-9,"h":"violet","b":115,"g":"orange","f":"violet","i":"red"}],57,"violet"],"green"],[["blue",[1,53,"orange"],{"e":["green",-12,"blue","orange","green",136,173],"a":"violet","d":-43,"c":{"e":144,"c":133,"a":"yellow","g":154,"b":"orange","d":127,"f":194},"h":{"e":52,"a":-43,"d":"orange","c":-45,"h":"orange","b":150,"g":-12,"f":91,"i":6},"b":{"e":"yellow","c":"blue","a":"violet","g":112,"b":174,"d":"violet","f":90},"g":177,"f":"blue"},"red","violet",96],"green","violet",[{"a":["red","red",46,"red"],"b":["green",193,54,"orange"]},["orange",8,1,["violet",84,"violet"],155,"yellow",151,"blue",196],"yellow","red",{"a":["green","orange","green",61,"blue",39,-2,46,"red",54]},"violet",128]]]},"a":{"e":[{"e":["yellow"],"c":93,"a":"violet","b":{"a":{"a":"yellow","b":"blue"},"b":-4},"d":"violet"},171,103,[13,"orange",[[51,"violet","yellow",{"c":85,"a":103,"b":"green"},97,{"e":"orange","a":-11,"d":62,"j":"yellow","c":"orange","h":47,"b":83,"g":119,"f":180,"i":136},{"a":177},80],{"e":{"c":"yellow","a":"orange","b":3,"d":197},"a":130,"d":"red","j":"red","c":-44,"h":-15,"b":64,"g":125,"f":82,"i":"green"}],{"e":["orange",42,["orange",197,"violet","yellow","blue",11,"yellow"],189,"yellow","blue","green","violet"],"a":{"a":149,"b":69},"d":128,"c":[["green",150,45,86,"red",-8,41,"orange","blue"]],"h":[[-4,127,"yellow","violet",124,112,196,"violet",161,40],37,0,"orange",-30,-43,[-24,"orange",142,"violet","red"],"blue",66],"b":{"c":"violet","a":["yellow",91,182,20,"orange",159,46,55,141],"b":{"c":173,"a":-40,"b":"green","d":"violet"},"d":[67,80,27,-15]},"g":"red","f":{"c":"orange","a":99,"b":"green"}},{"e":{"c":40,"a":"orange","b":"green"},"c":"green","a":-44,"b":{"e":"blue","c":56,"a":"yellow","g":62,"b":188,"d":141,"f":-21},"d":"yellow","f":{"e":"yellow","c":67,"a":33,"g":"yellow","b":"yellow","d":51,"f":195}}],["orange","violet",["red"],["green",35,[170,-30,"orange",140,"green","violet","violet",["orange","yellow","yellow",35,"blue","violet",-36,182,"yellow",141],146]],{"a":61,"b":"orange"},[31,"blue","green",65,"red","green"],"violet"],{"c":"blue","a":["blue"],"b":17},95],"c":[[48,"blue",[49],"orange",{"e":"violet","c":"green","a":"red","b":"red","d":-29,"f":["orange",20,190,97,["orange","blue",-30,"blue","green"],"blue","yellow",-47,[123,"yellow","green"],-41]},"green",{"a":170,"b":32},[{"c":"green","a":"violet","b":"red"},["yellow",36,"yellow","violet",149,{"e":"red","c":141,"a":-24,"b":"yellow","d":-13,"f":"red"},69,"orange",19,[87,"red",167,"red",77,110]],"orange","violet"],{"a":-16}],[["red"],"violet"],{"a":[["red",83,{"e":"red","c":"blue","a":"blue","b":"orange","d":"orange"},49,"green","violet"],{"e":"yellow","a":"violet","d":17,"j":"green","c":{"e":"orange","c":"green","a":"green","b":99,"d":"yellow","f":"orange"},"h":9,"b":159,"g":"yellow","f":167,"i":147},["blue","green","violet"],"yellow",["violet",197,"blue",[170,81,"yellow","orange",196],-24,99,193],-8,["red",81,-11,"green","red","blue","yellow","blue",["blue","violet",131,184,160,-1]],"green"]},"orange"],"a":[{"e":"blue","a":"violet","d":[{"a":"blue","b":46},"violet",72,35,61,161],"j":["green",78,144,[168,["red",77,38,"green","red"],"green","yellow",-8,"yellow"],190,40,"yellow",17,171],"c":{"e":"red","c":"violet","a":"violet","b":-14,"d":"red","f":167},"h":[[[83,"green",69,"red"],"green",155,133],106,1,"orange"],"b":-41,"g":{"a":27},"f":"red","i":{"e":"green","c":"green","a":182,"g":"orange","b":"orange","d":["orange"],"f":"orange"}},"yellow","green",-26],"b":{"e":[-32,-11,{"e":"red","c":"yellow","a":{"e":"green","a":"red","d":105,"c":-20,"h":85,"b":{"e":47,"a":89,"d":"green","c":"violet","h":"orange","b":"green","g":140,"f":"green"},"g":111,"f":"orange","i":"yellow"},"b":143,"d":{"e":{"e":"orange","c":129,"a":"blue","b":142,"d":"violet"},"c":"blue","a":122,"b":["violet","orange",84,"orange"],"d":"red","f":"blue"}},58,[147]],"a":{"e":"orange","c":[{"e":141,"c":114,"a":137,"g":"orange","b":61,"d":105,"f":33}],"a":["red","red",{"e":"green","c":4,"a":"violet","b":"red","d":"blue"},"yellow",["green",15,"green",-40,149,"orange",{"e":-7,"c":74,"a":"red","b":"green","d":32}],"blue","yellow",146,[191,"blue",["orange","blue",187,"blue","orange",127,"yellow",38],120,161,55,-30,"green",-10,"violet"],"orange"],"b":{"a":-32,"b":"blue"},"d":{"e":["green",27],"c":[94,"violet","red",18,166,"yellow"],"a":{"e":"green","a":95,"d":"green","j":176,"c":84,"h":"violet","b":"yellow","g":-25,"f":51,"i":119},"b":144,"d":{"c":"violet","a":"yellow","b":"red"}}},"d":{"e":{"e":140,"a":{"e":"green","c":"green","a":0,"b":68,"d":152,"f":"red"},"d":["blue",6],"c":-29,"h":"green","b":["violet",106,"violet","orange",-38,175],"g":54,"f":[177,31,"violet","yellow"],"i":185},"a":"green","d":[111,49,"yellow","blue","orange",{"e":"yellow","a":"orange","d":"violet","j":"blue","c":"red","h":88,"b":-1,"g":"red","f":"red","i":{"e":121,"a":112,"d":195,"j":103,"c":94,"h":"red","b":12,"g":8,"f":22,"i":"orange"}},64,["violet",["blue",76,"blue","red","red"],["violet","blue","orange","yellow",144],185,{"a":66,"b":"orange"},199,"green","green"],14],"c":{"e":"green","a":"orange","d":{"c":[-23,189,-11,"green","violet",178,-4,"blue",68,"violet"],"a":["orange","red"],"b":"yellow","d":"yellow"},"j":{"c":-33,"a":"blue","b":"violet"},"c":107,"h":{"e":-16,"a":174,"d":{"e":"orange","c":"green","a":-28,"b":-30,"d":73},"j":"yellow","c":"orange","h":"orange","b":["blue","violet",-4,76,"red","red"],"g":59,"f":12,"i":199},"b":{"a":"green","b":54},"g":"violet","f":174,"i":["orange",41,85,"yellow","green",25,"red",-20,156,143]},"h":"yellow","b":"red","g":90,"f":-37,"i":{"a":146,"b":"violet"}},"j":"blue","c":[1,["yellow",-11,"green",66,"red",90,"green","yellow",3,{"e":-34,"a":194,"d":"green","c":[-11,"yellow"],"h":59,"b":"yellow","g":"blue","f":162,"i":"violet"}],81,158,170],"h":"blue","b":"green","g":["violet",["blue","blue"]],"f":[-44,"yellow",[35,[122,"red","yellow",{"a":"red","b":"violet"},"blue","orange","violet"],"violet",{"e":-48,"a":6,"d":-6,"c":4,"h":"yellow","b":"blue","g":"red","f":"red"}],[{"e":-2,"a":156,"d":"red","j":69,"c":0,"h":"violet","b":"orange","g":61,"f":102,"i":["orange","blue","violet",-1,137]},{"a":183,"b":89}],{"a":167,"b":"orange"},193,95,[97,[66,154,-32,"orange",121,{"e":198,"a":"blue","d":102,"c":"red","h":"green","b":135,"g":"orange","f":-49,"i":151},97,7,"red"],195,"blue",49,"green",10]],"i":["violet",1,{"e":"orange","c":196,"a":"blue","g":"red","b":190,"d":87,"f":128},"blue",128,147]},"d":[[{"a":"green","b":43},169,-8,"orange",{"a":["violet",["red","violet","orange","yellow",-49,"violet",-25],"violet",60,33,"violet",["yellow","orange",31,144,"red","yellow",73,"orange","red","green"],["green",-31,"red"],"orange","blue"]},"violet","blue"],40,154,{"a":{"c":"violet","a":145,"b":166,"d":["blue",["blue",125,185,"yellow","red",152,89,-18,"blue",141],26,2,35,"orange",190]}},170,"violet",-31,[[151,"orange","green",["blue",114,-5,"yellow"],"blue","green","orange","yellow",62,{"c":22,"a":"green","b":"blue"}],17,[176],"violet",129,67,{"c":{"c":"red","a":140,"b":25},"a":["blue","green",163],"b":30,"d":[60,"green","red",126,[48,"green"],-26,["yellow","green",150,"red","violet","red","violet",59,-12],167,"yellow"]},"yellow","yellow"],"blue"]},"d":{"c":"green","a":52,"b":[136,{"c":{"a":[197,["orange","red",138,67,"orange",172,2,"orange"],"red"]},"a":"violet","b":{"a":{"e":172,"c":171,"a":"yellow","b":191,"d":{"e":171,"a":"red","d":"violet","c":"green","h":"yellow","b":"yellow","g":73,"f":"yellow"}}},"d":[86,-11,-5,["orange","green",64,["blue",15,"orange","yellow","violet",181,"green","blue"],"yellow","yellow",{"e":27,"c":156,"a":"blue","g":"violet","b":38,"d":51,"f":23},"orange","violet"],10]},[55,{"e":191,"c":"blue","a":"orange","b":"yellow","d":109},"blue",{"c":164,"a":[198,-9,183,{"e":14,"a":176,"d":117,"c":"violet","h":"violet","b":-44,"g":"violet","f":"red","i":"orange"},"yellow",0],"b":58,"d":33},"red",[125,23,"blue",149,[["blue",-44,22,133,"orange","yellow","yellow","violet","violet",131],"violet",-1,"red",66,"blue"],"green","red","red",[-31,"blue",["orange","orange","yellow",44,"green","yellow","green",160,"violet"],"yellow",["orange","violet","green","violet",194,"blue",-27],"green",{"e":"violet","a":"red","d":67,"c":68,"h":"blue","b":"orange","g":"orange","f":"violet","i":"violet"},"yellow"]],"violet",[59,158,{"e":"blue","c":"blue","a":"orange","g":73,"b":97,"d":"red","f":"orange"}],["red",[31,[95,72,"orange","yellow"],"blue",192,63],[197,"green",{"e":112,"a":"violet","d":"blue","c":-2,"h":"blue","b":"green","g":124,"f":"blue"},177,"green","blue",162,107]]],43,[{"e":"red","c":{"e":["green",-33,11,154,"yellow",-4],"c":"yellow","a":[7,"orange","orange","yellow",118,169,"red","blue"],"b":"green","d":114},"a":{"e":"green","a":"green","d":187,"j":{"e":106,"c":-41,"a":"violet","b":173,"d":12},"c":"red","h":"violet","b":-4,"g":{"c":"violet","a":-26,"b":87,"d":-33},"f":"green","i":-46},"b":"green","d":13,"f":"yellow"}],["blue","violet","orange","red",{"e":"green","a":["violet",140,[76,"green",94,33,"green",31,"orange",53],"violet","yellow","violet","blue",["blue",196,"orange","yellow","orange",-42,"orange",171,-47,"violet"],"green",110],"d":31,"c":"orange","h":{"e":-45,"a":"green","d":["violet"],"c":"red","h":105,"b":130,"g":-36,"f":"orange","i":{"e":"red","c":137,"a":54,"g":"blue","b":"violet","d":102,"f":178}},"b":-41,"g":{"e":6,"a":-25,"d":66,"j":"violet","c":39,"h":30,"b":63,"g":46,"f":56,"i":"blue"},"f":6,"i":"violet"},[84,175,"orange",{"a":"green"},"green",66,{"e":"red","a":{"a":178,"b":"yellow"},"d":"yellow","c":["red","red",38,"orange","blue",136,"red",137,"red"],"h":163,"b":["orange",84,"red",178],"g":65,"f":"blue"}],[98,"yellow",[46,"orange","yellow","yellow"],"blue",151,189,["yellow",{"e":"orange","a":27,"d":45,"c":48,"h":"green","b":90,"g":180,"f":-43},"yellow"],"blue"],{"a":"green"},"blue",{"c":37,"a":{"e":59,"c":["blue"],"a":"yellow","g":38,"b":"blue","d":"violet","f":"orange"},"b":"orange","d":"violet"}],{"c":75,"a":{"e":{"a":130},"a":5,"d":1,"c":72,"h":{"e":"red","a":11,"d":157,"c":97,"h":-24,"b":"red","g":111,"f":21},"b":{"e":145,"a":"red","d":"violet","j":[-11,191,-43,"blue","orange",105,158],"c":"red","h":143,"b":{"e":"orange","c":"yellow","a":-31,"g":177,"b":"violet","d":"blue","f":"green"},"g":"green","f":48,"i":{"e":18,"a":142,"d":"yellow","c":116,"h":"violet","b":135,"g":37,"f":36}},"g":-26,"f":[186,192,"orange",117,-9,"violet",-19,55,"green",167],"i":"green"},"b":"yellow","d":{"a":138}}]},"c":"blue","h":[[19],"orange",[{"e":["yellow",68,28,29,{"e":"red","a":"violet","d":"green","c":143,"h":"red","b":"orange","g":44,"f":123,"i":"orange"},38,28,65,{"e":-26,"c":["red",132,"red",124,"yellow",115],"a":170,"g":"yellow","b":"green","d":-18,"f":{"c":1,"a":"red","b":-1}}],"a":"orange","d":129,"c":33,"h":"violet","b":"orange","g":"green","f":-24},["violet",-22],[64,-20,{"e":46,"a":76,"d":97,"c":-21,"h":98,"b":"violet","g":{"e":-7,"c":"violet","a":190,"b":"violet","d":138,"f":"violet"},"f":[178,"blue","blue"],"i":"red"},{"e":"orange","c":"red","a":{"e":"orange","a":["green"],"d":[19,"red"],"c":-32,"h":-15,"b":"yellow","g":116,"f":"blue","i":"orange"},"b":96,"d":"green","f":176},[[104,99,"yellow",-13,"red",{"c":"blue","a":"red","b":46,"d":60},-13,9],-22,-26,133,["green",["red",188,"green","green"],166,"yellow",{"e":"yellow","c":197,"a":65,"b":83,"d":-19,"f":"yellow"},[157,"violet","blue","yellow",-30,"violet",-3],21,{"a":-15}],43,["orange","blue",88,"yellow",103,31],"orange",-24]],{"c":"violet","a":{"a":26,"b":"yellow"},"b":["violet","violet",{"e":102,"a":"red","d":178,"j":187,"c":"orange","h":"violet","b":72,"g":-37,"f":"violet","i":"orange"},44,114,"yellow",85]}],{"e":150,"c":"green","a":{"e":{"e":{"e":{"e":"orange","a":48,"d":41,"j":"orange","c":"orange","h":30,"b":"yellow","g":41,"f":-40,"i":8},"c":199,"a":32,"b":"yellow","d":-28},"a":46,"d":[50,"red","violet",63,"red",56,-18,"orange","violet","red"],"c":"blue","h":{"e":"green","c":172,"a":"green","b":-30,"d":22,"f":"yellow"},"b":"orange","g":65,"f":"yellow","i":174},"a":"green","d":[196,{"e":{"e":176,"a":"violet","d":"orange","j":-37,"c":19,"h":31,"b":155,"g":"red","f":106,"i":"green"},"c":"red","a":64,"b":"orange","d":179,"f":8},"yellow",103,"violet",{"e":142,"a":111,"d":"yellow","c":"violet","h":"red","b":148,"g":29,"f":179,"i":"green"},-26],"c":-19,"h":["red",134,"green","green",105],"b":"orange","g":126,"f":76,"i":158},"b":55,"d":"green"},[[[61],[["orange","red"],[151,"yellow",127,"yellow",185,"yellow",{"e":105,"c":"yellow","a":198,"b":"orange","d":"blue","f":89},140,{"e":"violet","a":-15,"d":169,"j":3,"c":"yellow","h":74,"b":-41,"g":29,"f":112,"i":18}]],101,{"e":[45,"green","yellow","blue","violet",["violet","yellow",159,0,"orange","yellow",100,"green","blue",49],[5,-37],"blue","orange"],"c":88,"a":"blue","b":-32,"d":23},77,{"e":["yellow",119,197,["orange",-28,"yellow",179,130,74,-10,115,"violet",79],"orange",63,-15,17,"blue","violet"],"c":{"e":164,"a":83,"d":"yellow","c":119,"h":"yellow","b":148,"g":-22,"f":[-17,17,"violet","green","red"],"i":67},"a":{"e":54,"a":147,"d":"yellow","c":86,"h":113,"b":"yellow","g":77,"f":101,"i":"blue"},"g":"orange","b":[193,"orange","orange","red",39,44,43,-29],"d":"violet","f":191}],"orange",["red","violet","yellow",["red",54,{"e":{"a":41,"b":"violet"},"c":"red","a":{"e":"red","a":"yellow","d":"green","j":-33,"c":96,"h":137,"b":"yellow","g":30,"f":"green","i":"blue"},"b":181,"d":"violet","f":"green"},49,"yellow"],"orange","yellow",{"e":"orange","c":[46,"orange",["blue","green","blue","yellow","yellow","violet","orange","orange",1],"yellow",155,194,"yellow",149],"a":"green","b":-3,"d":153,"f":[-21,-26,-25,"blue","red",108,169,["green",100,43],51,-9]}],71,[[[{"a":"violet","b":27},148,109,["blue",60,47,"violet","yellow",-47,"violet"],"red",{"e":97,"c":-21,"a":"yellow","b":"green","d":126},"yellow",85,89],{"a":["red","orange","violet","blue","blue","blue",128,"blue"]},{"e":-48,"c":{"c":90,"a":"orange","b":"yellow"},"a":"yellow","b":"red","d":172},"yellow",{"a":"orange"},[{"e":"yellow","a":40,"d":-9,"j":"violet","c":153,"h":79,"b":"violet","g":178,"f":2,"i":"yellow"},"green",-29],"red",-9,[9,{"a":39,"b":"green"},5,"violet",26,{"e":167,"c":"blue","a":"yellow","b":90,"d":33},"green",88,12,"blue"]],"red",{"c":{"e":"yellow","a":-44,"d":62,"j":-19,"c":96,"h":"green","b":-12,"g":"green","f":"red","i":134},"a":22,"b":24,"d":33},"red","violet",{"e":181,"c":64,"a":"violet","g":"green","b":"yellow","d":"violet","f":101},"green",[132,93,[-6,[-3,28,"red"],-21,"red","violet",{"a":180},"red","blue",-15],{"e":171,"a":{"e":"green","c":49,"a":"orange","g":22,"b":"violet","d":"orange","f":"orange"},"d":"red","c":113,"h":"green","b":[30,"blue"],"g":{"a":139,"b":47},"f":"red","i":"red"},"violet",158,"green",5,-1],19],"red"],{"e":{"c":["orange",93,162,"green","violet",["green",178],{"a":"green","b":{"e":-12,"a":-16,"d":144,"c":"red","h":"violet","b":43,"g":"green","f":-42}}],"a":"blue","b":"blue","d":{"e":["orange","red",{"c":-19,"a":"green","b":93},"yellow","green","orange","orange","red","green"],"a":"green","d":{"e":"yellow","a":2,"d":"violet","j":"green","c":"blue","h":19,"b":"violet","g":"blue","f":[157,"green",109,59,"red",74,"red","blue","green"],"i":"orange"},"j":"yellow","c":166,"h":"yellow","b":"yellow","g":["violet",138,["violet",141,"green"]],"f":28,"i":{"e":52,"c":"yellow","a":"green","b":5,"d":{"e":153,"a":"yellow","d":191,"j":"green","c":"green","h":124,"b":"green","g":181,"f":134,"i":"yellow"},"f":193}}},"c":"violet","a":{"a":{"a":"green"},"b":[132]},"b":[{"e":11,"a":"green","d":{"e":{"e":119,"a":"violet","d":"red","c":"red","h":"violet","b":-6,"g":"blue","f":"orange","i":"orange"},"a":183,"d":[-36,"yellow"],"c":"red","h":71,"b":"yellow","g":2,"f":"orange"},"c":"green","h":"yellow","b":29,"g":"green","f":"blue","i":{"e":"yellow","a":-24,"d":[55,125,193,70,60,190,199],"c":"green","h":[49,"yellow","yellow",74,"red",163],"b":198,"g":50,"f":"blue","i":70}},"orange"],"d":"blue"},{"c":{"e":-20,"a":8,"d":["orange",157,152,"green",46,"green",7,89,"violet",[-22,-49,81,127]],"c":35,"h":["blue","yellow","orange",94,"orange","yellow"],"b":"red","g":{"a":"green","b":["yellow","orange",198]},"f":"violet","i":17},"a":-48,"b":"blue"}],"b":[[149,[{"e":"blue","c":-30,"a":"violet","g":"violet","b":"yellow","d":178,"f":-4}],"orange",131,"yellow",{"a":"red","b":"violet"},"blue"],[11],{"e":[35,118,{"e":39,"a":"yellow","d":[119,"orange",120,-43],"j":"violet","c":"orange","h":"blue","b":-32,"g":[{"e":"green","c":106,"a":144,"b":147,"d":"green"},"violet","orange"],"f":116,"i":"orange"},112,"yellow"],"a":{"e":19,"a":"orange","d":61,"c":"red","h":"blue","b":164,"g":{"c":"red","a":-10,"b":{"e":{"e":"green","c":82,"a":103,"g":67,"b":153,"d":"violet","f":22},"c":-22,"a":101,"b":71,"d":{"a":152,"b":"green"},"f":"orange"}},"f":157,"i":{"e":"green","a":"orange","d":-8,"j":[159,73,182,"red","green"],"c":13,"h":"blue","b":"yellow","g":186,"f":"orange","i":81}},"d":13,"c":[186,["yellow",["violet","violet"],"green",-28],54,["blue",[119,"red",119,91,181],117],-15,190,{"c":"red","a":13,"b":[{"c":"green","a":70,"b":8,"d":175},"orange","green","yellow","green"]},"blue",-43],"h":{"e":[{"a":"green"},88,"red","violet",10],"a":{"a":"orange","b":[62,"yellow","green"]},"d":151,"c":"red","h":"orange","b":126,"g":{"a":"orange"},"f":"orange","i":46},"b":[[-2,"violet","violet","red",{"a":192},"green",122],["orange","red",{"c":62,"a":52,"b":-45},{"e":{"a":"violet","b":-45},"a":"red","d":"orange","c":"yellow","h":{"e":67,"c":-27,"a":116,"b":"violet","d":"green","f":-18},"b":"yellow","g":"blue","f":"blue"},27,{"c":37,"a":-39,"b":"blue"}],107,"yellow",["blue","red",143],"blue",{"e":"orange","a":["yellow",["blue",8,149,141,"red",-28,"red"],18],"d":29,"c":"violet","h":-21,"b":[{"e":66,"c":"green","a":"blue","b":-29,"d":"orange","f":"violet"},"blue",-21],"g":"green","f":112},73],"g":["red",{"a":{"e":93,"a":13,"d":"violet","c":175,"h":158,"b":9,"g":194,"f":-10},"b":"blue"}],"f":130}],"g":[["red","orange","orange",172,154,{"e":{"a":"blue","b":{"a":"yellow","b":53}},"c":-11,"a":"orange","g":"yellow","b":{"e":{"e":94,"a":123,"d":184,"j":-4,"c":193,"h":152,"b":"blue","g":"red","f":101,"i":178},"c":"blue","a":178,"b":154,"d":[103,109,190,"yellow",29,"red","orange","yellow",79,"green"],"f":"orange"},"d":{"e":25,"a":{"a":"blue"},"d":"green","c":"orange","h":{"e":144,"c":["red","blue","violet",15,"green",109,72],"a":"yellow","b":"orange","d":"yellow"},"b":151,"g":[141,"orange",134,"blue","blue",4,21,"blue","green"],"f":96},"f":"blue"},"blue"],80,186,[[194,"violet",70,"green"],{"a":"orange","b":"green"},[{"e":"orange","a":179,"d":{"e":164,"c":-14,"a":"blue","g":"yellow","b":"violet","d":76,"f":-33},"c":"green","h":"violet","b":"orange","g":"blue","f":"orange","i":"green"},"blue"]],"orange","yellow",66],"f":{"a":["violet"],"b":87},"i":{"e":{"c":[{"e":"red","a":19,"d":100,"j":"red","c":"red","h":"red","b":"yellow","g":-41,"f":10,"i":"blue"},42,92,"violet","red",[149,"green",91,"blue"],-33,["green",73,129],110,{"e":168,"c":153,"a":-30,"b":"yellow","d":[192,{"c":"blue","a":"blue","b":-16,"d":-18},{"e":"red","c":"yellow","a":"violet","b":31,"d":"green"},"blue"],"f":{"c":9,"a":"yellow","b":-16,"d":128}}],"a":[[["green","violet",136,59,"orange",173,116,113,"yellow"],"green",{"e":"blue","c":"green","a":"blue","g":"red","b":98,"d":-25,"f":21},"orange",184],4,"yellow","red",-24,{"c":"orange","a":15,"b":{"e":109,"c":179,"a":61,"b":"orange","d":190,"f":9}}],"b":[[92,148,{"e":"blue","a":0,"d":108,"c":197,"h":"red","b":"orange","g":-22,"f":105,"i":"blue"},"blue"],-7,149]},"a":{"e":{"a":{"e":"green","a":["violet",172],"d":-10,"c":42,"h":"blue","b":80,"g":{"e":"red","c":"orange","a":"yellow","g":87,"b":{"e":31,"c":129,"a":"orange","b":43,"d":"blue"},"d":"blue","f":81},"f":"violet"},"b":"orange"},"a":182,"d":{"c":{"a":154},"a":"blue","b":{"e":31,"a":["yellow","blue","red",{"a":"orange"},"red",62,39,"red",["green","orange","yellow",47,"orange",55,"blue"]],"d":[72,187,"red","orange",59,"yellow","violet","green"],"j":"orange","c":"yellow","h":185,"b":{"e":"blue","c":"violet","a":["orange",7,180,150,46,"yellow",176,"orange"],"b":148,"d":"blue"},"g":"blue","f":"yellow","i":102}},"c":"red","h":-9,"b":14,"g":{"a":"green","b":{"c":-18,"a":81,"b":104}},"f":[[{"e":-33,"c":"green","a":"orange","b":"blue","d":"blue"},"yellow",141,[42,197],[-12,61,{"e":"violet","a":"violet","d":"green","c":-21,"h":-5,"b":"orange","g":39,"f":"green"},"blue"],31,[[101,"blue",-14,"red",88],58,["red","blue","violet",34],-14,"yellow",98,106,91,131],"yellow",[151,"red","green",{"e":"violet","c":"green","a":49,"g":155,"b":96,"d":"blue","f":"orange"},-18,184,{"c":"blue","a":1,"b":162},{"e":115,"a":94,"d":97,"j":-34,"c":"blue","h":115,"b":"red","g":"orange","f":149,"i":105}],38],{"c":{"e":"blue","c":90,"a":"yellow","b":142,"d":"violet"},"a":-38,"b":"violet"}],"i":189},"d":"orange","c":[[103,8,"green",13,23,"violet",55],[{"e":79,"c":74,"a":{"e":"violet","a":{"c":"red","a":"violet","b":168},"d":142,"j":"blue","c":173,"h":"yellow","b":"green","g":"blue","f":"blue","i":"blue"},"g":[{"c":"violet","a":82,"b":90},-24,{"a":"orange"},"yellow",[126,53,153,6],52,137,"violet",181],"b":105,"d":166,"f":{"e":"blue","c":-35,"a":"blue","b":188,"d":-14,"f":63}},{"e":95,"a":"green","d":"yellow","j":"red","c":81,"h":107,"b":-46,"g":162,"f":"green","i":"red"},[{"e":"violet","a":150,"d":126,"c":10,"h":{"e":"green","a":"orange","d":19,"c":"green","h":"green","b":79,"g":"red","f":"yellow","i":"blue"},"b":{"a":25,"b":147},"g":180,"f":126},{"e":-48,"a":192,"d":-45,"c":25,"h":"green","b":{"c":165,"a":"orange","b":"red","d":"blue"},"g":"green","f":-24},95,{"e":"blue","a":"violet","d":"yellow","j":"blue","c":44,"h":["blue","green","red",142,"red"],"b":[-43,"violet","green",53],"g":33,"f":"orange","i":196},"orange","green",43,[113,"violet","orange",129,{"c":6,"a":"violet","b":"green","d":"green"},20]],"green"]],"h":{"a":-11},"b":{"e":"violet","c":174,"a":"violet","g":[{"e":105,"a":{"c":-5,"a":177,"b":63},"d":-41,"c":80,"h":[110,109,113,"blue"],"b":-28,"g":"red","f":129},["green",[108],{"a":4},[182,96,29,[181,14,"yellow","violet"],13,{"e":132,"a":115,"d":"red","c":"violet","h":"violet","b":"violet","g":"green","f":"green"},{"e":"orange","a":"yellow","d":"blue","j":"red","c":137,"h":"violet","b":"green","g":-22,"f":"yellow","i":-12},"green","yellow",57],-44,{"a":88,"b":-3},22,{"c":"red","a":"violet","b":-21}],[[14,"orange"]],-36,128,"yellow",[-47,[-7,36,177],"blue","blue",["yellow","violet",-23,"violet",-40,"orange",{"e":129,"a":21,"d":51,"j":"violet","c":"red","h":15,"b":174,"g":191,"f":101,"i":105}],23,[-47,[133,66,"violet"],177,"violet","yellow","green",159,"yellow"]],{"e":[{"a":"orange"},"green",69,"orange",43,"violet","violet",192,140,"green"],"a":62,"d":"violet","j":{"c":"violet","a":-25,"b":"violet"},"c":120,"h":"red","b":{"a":13},"g":"green","f":124,"i":33},[[19,176,174,"orange","violet","violet",105,128,"red"],{"a":10,"b":"red"},["orange",37,187,"green",176],"blue","red",[153,"yellow","violet",137,"orange","blue",[70,"red",174,"blue","green","yellow",99,"red"],"violet","violet","red"]]],"b":[{"c":133,"a":"orange","b":98,"d":{"c":"red","a":-9,"b":103}},"blue",[155,"yellow"],[["yellow",-8,{"e":"blue","c":59,"a":71,"g":"orange","b":88,"d":"orange","f":17},"blue",-37],{"e":82,"c":"violet","a":99,"b":81,"d":"yellow","f":161},"blue",{"c":-7,"a":154,"b":"violet"},-9,-13,53,{"e":"violet","a":"yellow","d":-26,"c":179,"h":"green","b":"red","g":-24,"f":133,"i":-36}]],"d":{"e":[9,[49],28,"red",{"c":23,"a":157,"b":{"c":"violet","a":"green","b":-11,"d":"green"},"d":139},"orange","green",93,44,[-19]],"a":-6,"d":7,"c":{"e":{"c":"violet","a":-21,"b":12},"c":["green",[-23,-40,157],"red",["orange","blue"],"violet","red",85,"violet","yellow",150],"a":162,"g":"yellow","b":"green","d":["green","yellow",181,"green",-2,{"e":"red","a":"yellow","d":129,"c":-34,"h":129,"b":152,"g":"yellow","f":80,"i":"red"},"green","red",-9,"red"],"f":6},"h":{"e":136,"a":"violet","d":["violet",-15,129,"green",5,"green",[-28,156,141,"blue",22,"green",34],"green"],"c":149,"h":["violet"],"b":149,"g":"green","f":149},"b":{"e":123,"a":"red","d":{"e":0,"c":"green","a":"violet","b":"yellow","d":"red","f":"green"},"c":{"e":{"e":"yellow","c":"red","a":"red","b":"violet","d":"yellow"},"a":"yellow","d":{"e":-39,"a":-11,"d":63,"c":179,"h":4,"b":44,"g":"orange","f":"violet","i":"yellow"},"c":-43,"h":"green","b":"violet","g":"blue","f":"yellow","i":124},"h":48,"b":129,"g":["orange",147,174,"blue","green",115],"f":172,"i":-36},"g":"red","f":30,"i":"violet"},"f":"red"},"g":{"e":{"e":[[126,"orange",196,"orange","red",{"e":23,"a":"yellow","d":128,"c":12,"h":-49,"b":"green","g":"yellow","f":41,"i":45},"red","violet"]],"c":[["red",103,{"e":69,"a":"violet","d":"yellow","c":"green","h":"red","b":133,"g":25,"f":"violet"}],88,"green","red",-29,"red"],"a":37,"b":"green","d":{"e":"orange","c":{"e":{"e":109,"c":144,"a":"yellow","b":70,"d":83},"c":-3,"a":"green","b":"yellow","d":146},"a":86,"b":-1,"d":{"a":68}}},"c":{"c":[155,18,"blue",-16,"orange",-36,49,"red",["yellow",136,140,-10,11,"violet","red",134,156,"violet"]],"a":64,"b":{"e":"violet","c":193,"a":101,"b":["green"],"d":182,"f":86},"d":[198]},"a":"yellow","g":"violet","b":1,"d":-27,"f":"orange"},"f":{"e":20,"c":{"e":155,"c":{"c":[181,{"e":-32,"a":"orange","d":"orange","j":"yellow","c":66,"h":-39,"b":"violet","g":"violet","f":"red","i":23},4,"blue",70,"violet","blue",141,{"a":"yellow","b":184},"violet"],"a":160,"b":158,"d":197},"a":138,"g":[48],"b":26,"d":11,"f":{"c":"yellow","a":179,"b":"red","d":90}},"a":148,"g":"yellow","b":[[{"a":["yellow"]},"yellow",19,"green",39],{"c":{"a":"blue"},"a":{"e":41,"c":191,"a":173,"b":"green","d":-14,"f":19},"b":{"a":["orange",4,48],"b":193},"d":"green"},["yellow",{"e":"orange","a":"orange","d":"blue","c":-39,"h":28,"b":"yellow","g":"red","f":"orange","i":[116,"red",173,76,24,-1,"green",101,-10]},70,{"c":"violet","a":44,"b":"violet","d":36},128,{"c":"yellow","a":"red","b":["yellow",-10]},-10,6]],"d":"violet","f":{"e":"violet","c":5,"a":"violet","g":173,"b":100,"d":["violet",194,{"e":["blue",181,"violet","yellow","blue",-7,137,43,112],"c":-19,"a":120,"b":"green","d":165},-1,195,"green",104],"f":128}}}}
diff --git a/12/main.py b/12/main.py
new file mode 100755
index 0000000..0887e88
--- /dev/null
+++ b/12/main.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+
+import json
+
+
+
+def solve_partn(partn, data):
+ # Primitive data types
+ if isinstance(data, int) or isinstance(data, float):
+ return data
+ elif isinstance(data, str):
+ return 0
+
+ # Composite data types
+ result = 0
+ if isinstance(data, list):
+ for d in data:
+ result += solve_partn(partn, d)
+ elif isinstance(data, dict):
+ for d in data.values():
+ # Part 2: skip all objects containing "red" as value
+ if partn == 2 and isinstance(d, str) and d == "red":
+ return 0
+ else:
+ result += solve_partn(partn, d)
+ return result
+
+
+
+def main():
+ # Read the JSON data from the input text file
+ with open("input.txt", "r") as f:
+ data = json.loads(f.read())
+
+ print("Part 1 solution:", solve_partn(1, data)) # 119433 for me
+ print("Part 2 solution:", solve_partn(2, data)) # 68466 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/12/test.py b/12/test.py
new file mode 100755
index 0000000..c4a2672
--- /dev/null
+++ b/12/test.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+
+import json
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ data = json.loads('[1,2,3]')
+ self.assertEqual(main.solve_partn(1, data), 6)
+
+ def test_example2(self):
+ data = json.loads('{"a":2,"b":4}')
+ self.assertEqual(main.solve_partn(1, data), 6)
+
+ def test_example3(self):
+ data = json.loads('[[[3]]]')
+ self.assertEqual(main.solve_partn(1, data), 3)
+
+ def test_example4(self):
+ data = json.loads('{"a":{"b":4},"c":-1}')
+ self.assertEqual(main.solve_partn(1, data), 3)
+
+ def test_example5(self):
+ data = json.loads('{"a":[-1,1]}')
+ self.assertEqual(main.solve_partn(1, data), 0)
+
+ def test_example6(self):
+ data = json.loads('[-1,{"a":1}]')
+ self.assertEqual(main.solve_partn(1, data), 0)
+
+ def test_example7(self):
+ data = json.loads('[]')
+ self.assertEqual(main.solve_partn(1, data), 0)
+
+ def test_example8(self):
+ data = json.loads('{}')
+ self.assertEqual(main.solve_partn(1, data), 0)
+
+
+
+class ExamplesPart2(unittest.TestCase):
+ def test_example1(self):
+ data = json.loads('[1,2,3]')
+ self.assertEqual(main.solve_partn(2, data), 6)
+
+ def test_example2(self):
+ data = json.loads('[1,{"c":"red","b":2},3]')
+ self.assertEqual(main.solve_partn(2, data), 4)
+
+ def test_example3(self):
+ data = json.loads('{"d":"red","e":[1,2,3,4],"f":5}')
+ self.assertEqual(main.solve_partn(2, data), 0)
+
+ def test_example4(self):
+ data = json.loads('[1,"red",5]')
+ self.assertEqual(main.solve_partn(2, data), 6)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/13/input.txt b/13/input.txt
new file mode 100644
index 0000000..35be357
--- /dev/null
+++ b/13/input.txt
@@ -0,0 +1,56 @@
+Alice would gain 54 happiness units by sitting next to Bob.
+Alice would lose 81 happiness units by sitting next to Carol.
+Alice would lose 42 happiness units by sitting next to David.
+Alice would gain 89 happiness units by sitting next to Eric.
+Alice would lose 89 happiness units by sitting next to Frank.
+Alice would gain 97 happiness units by sitting next to George.
+Alice would lose 94 happiness units by sitting next to Mallory.
+Bob would gain 3 happiness units by sitting next to Alice.
+Bob would lose 70 happiness units by sitting next to Carol.
+Bob would lose 31 happiness units by sitting next to David.
+Bob would gain 72 happiness units by sitting next to Eric.
+Bob would lose 25 happiness units by sitting next to Frank.
+Bob would lose 95 happiness units by sitting next to George.
+Bob would gain 11 happiness units by sitting next to Mallory.
+Carol would lose 83 happiness units by sitting next to Alice.
+Carol would gain 8 happiness units by sitting next to Bob.
+Carol would gain 35 happiness units by sitting next to David.
+Carol would gain 10 happiness units by sitting next to Eric.
+Carol would gain 61 happiness units by sitting next to Frank.
+Carol would gain 10 happiness units by sitting next to George.
+Carol would gain 29 happiness units by sitting next to Mallory.
+David would gain 67 happiness units by sitting next to Alice.
+David would gain 25 happiness units by sitting next to Bob.
+David would gain 48 happiness units by sitting next to Carol.
+David would lose 65 happiness units by sitting next to Eric.
+David would gain 8 happiness units by sitting next to Frank.
+David would gain 84 happiness units by sitting next to George.
+David would gain 9 happiness units by sitting next to Mallory.
+Eric would lose 51 happiness units by sitting next to Alice.
+Eric would lose 39 happiness units by sitting next to Bob.
+Eric would gain 84 happiness units by sitting next to Carol.
+Eric would lose 98 happiness units by sitting next to David.
+Eric would lose 20 happiness units by sitting next to Frank.
+Eric would lose 6 happiness units by sitting next to George.
+Eric would gain 60 happiness units by sitting next to Mallory.
+Frank would gain 51 happiness units by sitting next to Alice.
+Frank would gain 79 happiness units by sitting next to Bob.
+Frank would gain 88 happiness units by sitting next to Carol.
+Frank would gain 33 happiness units by sitting next to David.
+Frank would gain 43 happiness units by sitting next to Eric.
+Frank would gain 77 happiness units by sitting next to George.
+Frank would lose 3 happiness units by sitting next to Mallory.
+George would lose 14 happiness units by sitting next to Alice.
+George would lose 12 happiness units by sitting next to Bob.
+George would lose 52 happiness units by sitting next to Carol.
+George would gain 14 happiness units by sitting next to David.
+George would lose 62 happiness units by sitting next to Eric.
+George would lose 18 happiness units by sitting next to Frank.
+George would lose 17 happiness units by sitting next to Mallory.
+Mallory would lose 36 happiness units by sitting next to Alice.
+Mallory would gain 76 happiness units by sitting next to Bob.
+Mallory would lose 34 happiness units by sitting next to Carol.
+Mallory would gain 37 happiness units by sitting next to David.
+Mallory would gain 40 happiness units by sitting next to Eric.
+Mallory would gain 18 happiness units by sitting next to Frank.
+Mallory would gain 7 happiness units by sitting next to George.
diff --git a/13/main.py b/13/main.py
new file mode 100755
index 0000000..31bd337
--- /dev/null
+++ b/13/main.py
@@ -0,0 +1,72 @@
+#!/usr/bin/python
+
+from itertools import permutations
+
+
+
+def parse_input(lines):
+ data = {}
+
+ for l in lines:
+ words = l.split()
+ name1 = words[ 0]
+ name2 = words[10].rstrip(".")
+ if words[2] == "gain":
+ score = int(words[3])
+ else: # words[2] == "lose"
+ score = -int(words[3])
+
+ if name1 not in data:
+ data[name1] = {}
+ data[name1][name2] = score
+
+ return data
+
+
+
+def best_score(data):
+ names = list(data.keys())
+ perms = permutations(names)
+ scores = []
+ for p in perms:
+ score = 0
+ for i in range(len(p)):
+ n1 = p[i]
+ n2 = p[(i + 1) % len(p)]
+ score += data[n1][n2] + data[n2][n1]
+ scores.append(score)
+ return max(scores)
+
+
+
+def solve_part1(lines):
+ data = parse_input(lines)
+ return best_score(data)
+
+
+
+def solve_part2(lines):
+ data = parse_input(lines)
+
+ data["Myself"] = {}
+ names = list(data.keys())
+ for n in names:
+ data["Myself"][n] = 0
+ data[n]["Myself"] = 0
+
+ return best_score(data)
+
+
+
+def main():
+ # Read happiness relations from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_part1(lines)) # 709 for me
+ print("Part 2 solution:", solve_part2(lines)) # 668 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/13/test.py b/13/test.py
new file mode 100755
index 0000000..7600159
--- /dev/null
+++ b/13/test.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ lines = [
+ "Alice would gain 54 happiness units by sitting next to Bob.",
+ "Alice would lose 79 happiness units by sitting next to Carol.",
+ "Alice would lose 2 happiness units by sitting next to David.",
+ "Bob would gain 83 happiness units by sitting next to Alice.",
+ "Bob would lose 7 happiness units by sitting next to Carol.",
+ "Bob would lose 63 happiness units by sitting next to David.",
+ "Carol would lose 62 happiness units by sitting next to Alice.",
+ "Carol would gain 60 happiness units by sitting next to Bob.",
+ "Carol would gain 55 happiness units by sitting next to David.",
+ "David would gain 46 happiness units by sitting next to Alice.",
+ "David would lose 7 happiness units by sitting next to Bob.",
+ "David would gain 41 happiness units by sitting next to Carol."
+ ]
+ self.assertEqual(main.solve_part1(lines), 330)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/14/input.txt b/14/input.txt
new file mode 100644
index 0000000..6cf5489
--- /dev/null
+++ b/14/input.txt
@@ -0,0 +1,9 @@
+Dancer can fly 27 km/s for 5 seconds, but then must rest for 132 seconds.
+Cupid can fly 22 km/s for 2 seconds, but then must rest for 41 seconds.
+Rudolph can fly 11 km/s for 5 seconds, but then must rest for 48 seconds.
+Donner can fly 28 km/s for 5 seconds, but then must rest for 134 seconds.
+Dasher can fly 4 km/s for 16 seconds, but then must rest for 55 seconds.
+Blitzen can fly 14 km/s for 3 seconds, but then must rest for 38 seconds.
+Prancer can fly 3 km/s for 21 seconds, but then must rest for 40 seconds.
+Comet can fly 18 km/s for 6 seconds, but then must rest for 103 seconds.
+Vixen can fly 18 km/s for 5 seconds, but then must rest for 84 seconds.
diff --git a/14/main.py b/14/main.py
new file mode 100755
index 0000000..4af4940
--- /dev/null
+++ b/14/main.py
@@ -0,0 +1,75 @@
+#!/usr/bin/python
+
+
+
+class Reindeer:
+ pass
+
+
+
+def parse_input(lines):
+ result = []
+ for line in lines:
+ words = line.split()
+
+ r = Reindeer()
+ r.name = words[0]
+ r.dist = 0
+ r.score = 0
+ r.state = "moving"
+ r.speed = int(words[ 3])
+ r.tmove = int(words[ 6])
+ r.trest = int(words[13])
+ r.timer = 0
+
+ result.append(r)
+
+ return result
+
+
+
+def solve_partn(partn, lines, tmax):
+ contestants = parse_input(lines)
+
+ for t in range(tmax):
+ for r in contestants:
+ if r.state == "moving":
+ r.dist += r.speed
+ r.timer += 1
+ if r.timer >= r.tmove:
+ r.state = "resting"
+ r.timer = 0
+ else: # r.state == "resting"
+ r.timer += 1
+ if r.timer >= r.trest:
+ r.state = "moving"
+ r.timer = 0
+
+ # Award 1 point to leader (and to others in case of a tie)
+ # Only for part 2, but doesn't hurt part 1, so not disabled.
+ leader = max(contestants, key = lambda r: r.dist)
+ for r in contestants:
+ if r.dist == leader.dist:
+ r.score += 1
+
+ if partn == 1:
+ winner = max(contestants, key = lambda r: r.dist)
+ return winner.dist
+ else: # partn == 2
+ winner = max(contestants, key = lambda r: r.score)
+ return winner.score
+
+
+
+def main():
+ # Read reindeer stats from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_partn(1, lines, 2503)) # 2640 for me
+ print("Part 2 solution:", solve_partn(2, lines, 2503)) # 1102 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/14/test.py b/14/test.py
new file mode 100755
index 0000000..25012d4
--- /dev/null
+++ b/14/test.py
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class Examples(unittest.TestCase):
+ def test_example1(self):
+ lines = [
+ "Comet can fly 14 km/s for 10 seconds, but then must rest for 127 seconds.",
+ "Dancer can fly 16 km/s for 11 seconds, but then must rest for 162 seconds."
+ ]
+ self.assertEqual(main.solve_partn(1, lines, 1000), 1120)
+ self.assertEqual(main.solve_partn(2, lines, 1000), 689)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/15/input.txt b/15/input.txt
new file mode 100644
index 0000000..d1af06e
--- /dev/null
+++ b/15/input.txt
@@ -0,0 +1,4 @@
+Sugar: capacity 3, durability 0, flavor 0, texture -3, calories 2
+Sprinkles: capacity -3, durability 3, flavor 0, texture 0, calories 9
+Candy: capacity -1, durability 0, flavor 4, texture 0, calories 1
+Chocolate: capacity 0, durability 0, flavor -2, texture 2, calories 8
diff --git a/15/main.py b/15/main.py
new file mode 100755
index 0000000..cb2077b
--- /dev/null
+++ b/15/main.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python
+
+
+
+def parse_input(lines):
+ ingredients = []
+
+ for l in lines:
+ words = l.split()
+
+ # Rather than label the properties by name, we number them 0-4
+ props = []
+ for n in range(5):
+ props.append(int(words[2 + 2 * n].rstrip(",")))
+
+ name = words[0].rstrip(":")
+ ingredients.append(props)
+
+ return ingredients
+
+
+
+def all_recipes(ingredients):
+ recipes = {}
+
+ for i1 in range(101):
+ for i2 in range(101 - i1):
+ for i3 in range(101 - i1 - i2):
+ i4 = 100 - i1 - i2 - i3
+ amounts = [i1, i2, i3, i4] # amount of each ingredient
+
+ props = [0, 0, 0, 0] # score for each property
+ for p in range(len(props)):
+ # Accumulate properties of ingredients
+ for i in range(len(amounts)):
+ prop = amounts[i] * ingredients[i][p]
+ props[p] += prop
+ # Clamp negative totals to zero
+ if props[p] < 0:
+ props[p] = 0
+
+ # `recipes' is a dictionary: key is recipe, value is total score
+ recipes[tuple(amounts)] = props[0] * props[1] * props[2] * props[3]
+
+ return recipes
+
+
+
+def solve_partn(partn, lines):
+ ingredients = parse_input(lines)
+ recipes = all_recipes(ingredients)
+
+ if partn == 2:
+ suitable = {}
+ for amounts, score in recipes.items():
+ calories = 0
+ for i in range(len(amounts)):
+ calories += amounts[i] * ingredients[i][4]
+ if calories == 500:
+ suitable[amounts] = score
+ recipes = suitable
+
+ return max(recipes.values())
+
+
+
+def main():
+ # Read ingredient information from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_partn(1, lines)) # 222870 for me
+ print("Part 2 solution:", solve_partn(2, lines)) # 117936 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/15/test.py b/15/test.py
new file mode 100755
index 0000000..fb71ad8
--- /dev/null
+++ b/15/test.py
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class Examples(unittest.TestCase):
+ def test_example1(self):
+ lines = [
+ "Butterscotch: capacity -1, durability -2, flavor 6, texture 3, calories 8",
+ "Cinnamon: capacity 2, durability 3, flavor -2, texture -1, calories 3",
+ "Dummy: capacity 0, durability 0, flavor 0, texture 0, calories 0",
+ "Dummy: capacity 0, durability 0, flavor 0, texture 0, calories 0"
+ ]
+ self.assertEqual(main.solve_partn(1, lines), 62842880)
+ self.assertEqual(main.solve_partn(2, lines), 57600000)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/16/input.txt b/16/input.txt
new file mode 100644
index 0000000..b1bd498
--- /dev/null
+++ b/16/input.txt
@@ -0,0 +1,500 @@
+Sue 1: children: 1, cars: 8, vizslas: 7
+Sue 2: akitas: 10, perfumes: 10, children: 5
+Sue 3: cars: 5, pomeranians: 4, vizslas: 1
+Sue 4: goldfish: 5, children: 8, perfumes: 3
+Sue 5: vizslas: 2, akitas: 7, perfumes: 6
+Sue 6: vizslas: 0, akitas: 1, perfumes: 2
+Sue 7: perfumes: 8, cars: 4, goldfish: 10
+Sue 8: perfumes: 7, children: 2, cats: 1
+Sue 9: pomeranians: 3, goldfish: 10, trees: 10
+Sue 10: akitas: 7, trees: 8, pomeranians: 4
+Sue 11: goldfish: 1, perfumes: 4, cars: 6
+Sue 12: samoyeds: 6, trees: 6, perfumes: 2
+Sue 13: akitas: 10, pomeranians: 0, vizslas: 2
+Sue 14: cars: 2, perfumes: 3, children: 4
+Sue 15: goldfish: 2, children: 8, cars: 5
+Sue 16: goldfish: 9, cars: 0, vizslas: 5
+Sue 17: cats: 5, trees: 6, perfumes: 6
+Sue 18: cars: 0, perfumes: 8, pomeranians: 7
+Sue 19: trees: 2, goldfish: 5, perfumes: 4
+Sue 20: akitas: 4, vizslas: 4, trees: 0
+Sue 21: pomeranians: 7, trees: 0, goldfish: 10
+Sue 22: cars: 4, vizslas: 0, perfumes: 3
+Sue 23: vizslas: 8, trees: 1, akitas: 2
+Sue 24: children: 7, trees: 0, akitas: 1
+Sue 25: goldfish: 3, akitas: 2, trees: 2
+Sue 26: pomeranians: 4, vizslas: 4, samoyeds: 2
+Sue 27: cars: 0, trees: 8, akitas: 5
+Sue 28: perfumes: 6, cats: 0, cars: 2
+Sue 29: trees: 7, akitas: 1, vizslas: 1
+Sue 30: perfumes: 9, cars: 9, trees: 10
+Sue 31: pomeranians: 5, akitas: 9, samoyeds: 1
+Sue 32: pomeranians: 10, vizslas: 5, goldfish: 5
+Sue 33: vizslas: 2, akitas: 3, trees: 7
+Sue 34: goldfish: 10, perfumes: 0, samoyeds: 7
+Sue 35: akitas: 6, cats: 7, perfumes: 10
+Sue 36: pomeranians: 8, vizslas: 7, akitas: 6
+Sue 37: goldfish: 2, cars: 10, children: 7
+Sue 38: goldfish: 2, perfumes: 3, cars: 7
+Sue 39: trees: 9, vizslas: 10, cars: 5
+Sue 40: goldfish: 1, pomeranians: 0, trees: 2
+Sue 41: trees: 2, goldfish: 6, vizslas: 3
+Sue 42: akitas: 1, cars: 3, vizslas: 3
+Sue 43: akitas: 1, pomeranians: 1, vizslas: 3
+Sue 44: goldfish: 7, akitas: 3, vizslas: 10
+Sue 45: akitas: 8, samoyeds: 8, goldfish: 2
+Sue 46: trees: 0, vizslas: 4, cars: 9
+Sue 47: cars: 9, trees: 10, perfumes: 4
+Sue 48: akitas: 0, vizslas: 5, perfumes: 4
+Sue 49: goldfish: 9, trees: 1, cars: 4
+Sue 50: goldfish: 2, perfumes: 5, cars: 2
+Sue 51: samoyeds: 1, goldfish: 2, perfumes: 7
+Sue 52: cars: 0, perfumes: 4, goldfish: 8
+Sue 53: goldfish: 9, vizslas: 2, akitas: 9
+Sue 54: trees: 1, goldfish: 9, children: 5
+Sue 55: cars: 0, akitas: 5, trees: 4
+Sue 56: trees: 4, samoyeds: 5, children: 9
+Sue 57: children: 0, vizslas: 8, cars: 3
+Sue 58: trees: 4, pomeranians: 5, akitas: 5
+Sue 59: vizslas: 10, cats: 3, children: 2
+Sue 60: cats: 6, vizslas: 2, cars: 2
+Sue 61: akitas: 1, vizslas: 0, children: 4
+Sue 62: akitas: 4, trees: 9, children: 10
+Sue 63: pomeranians: 6, vizslas: 6, cars: 4
+Sue 64: perfumes: 8, pomeranians: 1, children: 8
+Sue 65: perfumes: 3, goldfish: 6, trees: 5
+Sue 66: goldfish: 10, akitas: 8, vizslas: 4
+Sue 67: vizslas: 10, samoyeds: 3, trees: 2
+Sue 68: samoyeds: 4, cars: 7, perfumes: 3
+Sue 69: perfumes: 2, goldfish: 0, trees: 2
+Sue 70: trees: 8, vizslas: 7, akitas: 6
+Sue 71: cars: 2, children: 7, perfumes: 3
+Sue 72: cars: 1, akitas: 9, perfumes: 0
+Sue 73: vizslas: 4, akitas: 7, cars: 5
+Sue 74: samoyeds: 3, cars: 3, akitas: 2
+Sue 75: trees: 2, cars: 1, vizslas: 7
+Sue 76: samoyeds: 9, perfumes: 1, trees: 6
+Sue 77: trees: 6, perfumes: 10, cars: 7
+Sue 78: trees: 0, children: 8, vizslas: 5
+Sue 79: vizslas: 0, trees: 0, samoyeds: 1
+Sue 80: trees: 6, goldfish: 8, perfumes: 0
+Sue 81: samoyeds: 8, pomeranians: 6, akitas: 5
+Sue 82: vizslas: 6, perfumes: 9, akitas: 4
+Sue 83: cats: 0, vizslas: 3, pomeranians: 10
+Sue 84: cars: 4, perfumes: 6, samoyeds: 5
+Sue 85: vizslas: 7, trees: 5, goldfish: 7
+Sue 86: goldfish: 2, trees: 2, vizslas: 1
+Sue 87: trees: 6, goldfish: 10, pomeranians: 4
+Sue 88: vizslas: 1, akitas: 0, perfumes: 8
+Sue 89: goldfish: 8, akitas: 3, vizslas: 7
+Sue 90: vizslas: 9, akitas: 7, perfumes: 9
+Sue 91: children: 7, cars: 7, trees: 9
+Sue 92: vizslas: 10, akitas: 8, goldfish: 1
+Sue 93: goldfish: 7, vizslas: 2, pomeranians: 0
+Sue 94: cats: 2, samoyeds: 6, pomeranians: 3
+Sue 95: samoyeds: 4, children: 4, pomeranians: 10
+Sue 96: pomeranians: 9, cats: 1, goldfish: 3
+Sue 97: trees: 1, akitas: 6, goldfish: 1
+Sue 98: vizslas: 7, akitas: 2, perfumes: 7
+Sue 99: pomeranians: 6, perfumes: 2, trees: 1
+Sue 100: cars: 3, children: 9, trees: 10
+Sue 101: children: 0, perfumes: 0, vizslas: 3
+Sue 102: cars: 4, goldfish: 5, children: 2
+Sue 103: pomeranians: 3, perfumes: 7, cats: 8
+Sue 104: akitas: 0, perfumes: 5, vizslas: 5
+Sue 105: akitas: 7, vizslas: 2, samoyeds: 8
+Sue 106: goldfish: 7, perfumes: 0, cats: 8
+Sue 107: cats: 6, pomeranians: 9, cars: 6
+Sue 108: akitas: 3, vizslas: 10, cats: 5
+Sue 109: akitas: 10, perfumes: 2, cars: 7
+Sue 110: goldfish: 7, pomeranians: 1, trees: 1
+Sue 111: akitas: 10, samoyeds: 6, vizslas: 6
+Sue 112: cats: 6, akitas: 7, trees: 9
+Sue 113: akitas: 1, trees: 9, vizslas: 8
+Sue 114: vizslas: 2, cats: 1, cars: 4
+Sue 115: akitas: 0, trees: 5, goldfish: 7
+Sue 116: goldfish: 2, trees: 10, akitas: 2
+Sue 117: cars: 4, goldfish: 10, perfumes: 5
+Sue 118: cars: 5, perfumes: 6, trees: 0
+Sue 119: perfumes: 5, vizslas: 1, cats: 0
+Sue 120: perfumes: 8, akitas: 9, vizslas: 4
+Sue 121: samoyeds: 2, vizslas: 7, perfumes: 6
+Sue 122: children: 6, trees: 9, perfumes: 2
+Sue 123: cars: 7, akitas: 0, pomeranians: 0
+Sue 124: akitas: 7, cats: 8, vizslas: 5
+Sue 125: goldfish: 3, trees: 1, cars: 4
+Sue 126: cars: 4, perfumes: 3, akitas: 0
+Sue 127: children: 10, vizslas: 5, akitas: 9
+Sue 128: akitas: 3, samoyeds: 2, cats: 8
+Sue 129: cats: 8, akitas: 1, vizslas: 8
+Sue 130: trees: 4, cars: 6, perfumes: 6
+Sue 131: akitas: 7, perfumes: 6, goldfish: 9
+Sue 132: akitas: 6, vizslas: 7, trees: 1
+Sue 133: akitas: 5, vizslas: 7, children: 9
+Sue 134: cars: 8, goldfish: 4, pomeranians: 4
+Sue 135: samoyeds: 1, pomeranians: 6, akitas: 4
+Sue 136: perfumes: 10, goldfish: 1, cars: 3
+Sue 137: cars: 3, samoyeds: 6, vizslas: 7
+Sue 138: samoyeds: 10, akitas: 3, perfumes: 4
+Sue 139: perfumes: 10, vizslas: 2, goldfish: 7
+Sue 140: samoyeds: 7, cars: 1, trees: 2
+Sue 141: children: 6, cats: 5, cars: 9
+Sue 142: cats: 0, trees: 1, akitas: 10
+Sue 143: samoyeds: 4, cars: 0, children: 7
+Sue 144: trees: 0, cars: 4, perfumes: 8
+Sue 145: goldfish: 7, cars: 5, trees: 1
+Sue 146: perfumes: 7, cars: 7, goldfish: 0
+Sue 147: trees: 2, goldfish: 7, vizslas: 5
+Sue 148: samoyeds: 8, perfumes: 1, trees: 0
+Sue 149: vizslas: 2, samoyeds: 5, trees: 0
+Sue 150: akitas: 4, perfumes: 4, pomeranians: 2
+Sue 151: trees: 2, cars: 0, goldfish: 10
+Sue 152: goldfish: 7, vizslas: 0, trees: 0
+Sue 153: children: 9, cats: 0, pomeranians: 10
+Sue 154: cars: 6, goldfish: 10, akitas: 5
+Sue 155: perfumes: 9, trees: 2, akitas: 3
+Sue 156: pomeranians: 9, perfumes: 5, cars: 9
+Sue 157: akitas: 0, trees: 2, cars: 7
+Sue 158: goldfish: 10, trees: 8, akitas: 7
+Sue 159: akitas: 5, trees: 10, cars: 10
+Sue 160: akitas: 3, trees: 5, cars: 8
+Sue 161: samoyeds: 2, cars: 7, perfumes: 4
+Sue 162: cars: 6, vizslas: 10, pomeranians: 5
+Sue 163: cars: 10, perfumes: 6, vizslas: 9
+Sue 164: pomeranians: 7, cars: 4, vizslas: 2
+Sue 165: goldfish: 9, vizslas: 3, trees: 1
+Sue 166: goldfish: 1, samoyeds: 3, trees: 1
+Sue 167: vizslas: 4, goldfish: 7, cats: 5
+Sue 168: children: 1, cars: 5, samoyeds: 7
+Sue 169: trees: 1, samoyeds: 3, goldfish: 6
+Sue 170: goldfish: 2, cars: 3, perfumes: 9
+Sue 171: cars: 4, goldfish: 0, trees: 6
+Sue 172: cats: 8, perfumes: 6, trees: 1
+Sue 173: akitas: 9, goldfish: 7, cars: 10
+Sue 174: vizslas: 2, trees: 0, akitas: 1
+Sue 175: perfumes: 3, vizslas: 8, akitas: 4
+Sue 176: perfumes: 0, akitas: 6, goldfish: 3
+Sue 177: perfumes: 6, children: 1, goldfish: 10
+Sue 178: cars: 5, vizslas: 3, children: 10
+Sue 179: perfumes: 3, trees: 8, cats: 9
+Sue 180: perfumes: 8, vizslas: 4, trees: 7
+Sue 181: perfumes: 7, vizslas: 9, samoyeds: 4
+Sue 182: vizslas: 9, trees: 4, pomeranians: 4
+Sue 183: trees: 9, cars: 3, goldfish: 5
+Sue 184: perfumes: 2, cars: 4, vizslas: 3
+Sue 185: children: 10, akitas: 10, cats: 9
+Sue 186: cars: 5, samoyeds: 0, trees: 0
+Sue 187: trees: 2, goldfish: 3, cars: 4
+Sue 188: goldfish: 3, vizslas: 1, cats: 6
+Sue 189: trees: 2, pomeranians: 10, cars: 7
+Sue 190: perfumes: 10, akitas: 3, samoyeds: 0
+Sue 191: cats: 5, vizslas: 6, akitas: 6
+Sue 192: samoyeds: 5, trees: 1, perfumes: 8
+Sue 193: pomeranians: 0, akitas: 9, cats: 0
+Sue 194: trees: 1, goldfish: 0, perfumes: 10
+Sue 195: perfumes: 2, akitas: 7, cars: 5
+Sue 196: perfumes: 5, samoyeds: 8, cars: 1
+Sue 197: vizslas: 2, pomeranians: 9, trees: 1
+Sue 198: trees: 8, vizslas: 6, children: 8
+Sue 199: pomeranians: 4, cars: 7, vizslas: 5
+Sue 200: trees: 0, perfumes: 10, akitas: 10
+Sue 201: cats: 9, akitas: 4, vizslas: 0
+Sue 202: goldfish: 9, pomeranians: 9, cats: 6
+Sue 203: cars: 5, perfumes: 5, trees: 2
+Sue 204: pomeranians: 7, children: 2, akitas: 6
+Sue 205: samoyeds: 7, pomeranians: 7, children: 6
+Sue 206: trees: 1, cars: 1, pomeranians: 4
+Sue 207: goldfish: 2, perfumes: 5, trees: 0
+Sue 208: perfumes: 2, samoyeds: 4, trees: 1
+Sue 209: cars: 8, perfumes: 6, goldfish: 9
+Sue 210: perfumes: 4, cars: 8, samoyeds: 3
+Sue 211: perfumes: 2, cars: 8, trees: 9
+Sue 212: trees: 7, perfumes: 2, akitas: 5
+Sue 213: children: 3, goldfish: 5, vizslas: 0
+Sue 214: akitas: 6, goldfish: 0, children: 0
+Sue 215: trees: 8, akitas: 3, goldfish: 1
+Sue 216: goldfish: 6, perfumes: 8, akitas: 3
+Sue 217: children: 7, trees: 2, vizslas: 6
+Sue 218: goldfish: 8, samoyeds: 4, pomeranians: 6
+Sue 219: goldfish: 8, samoyeds: 0, children: 9
+Sue 220: perfumes: 1, cars: 8, vizslas: 6
+Sue 221: perfumes: 9, cars: 10, children: 10
+Sue 222: perfumes: 9, vizslas: 1, trees: 0
+Sue 223: goldfish: 1, akitas: 2, vizslas: 8
+Sue 224: samoyeds: 8, akitas: 7, vizslas: 4
+Sue 225: goldfish: 1, cars: 4, perfumes: 10
+Sue 226: goldfish: 9, trees: 4, perfumes: 5
+Sue 227: vizslas: 5, trees: 4, goldfish: 7
+Sue 228: cars: 1, cats: 10, perfumes: 4
+Sue 229: vizslas: 8, cars: 10, akitas: 4
+Sue 230: cats: 1, children: 8, vizslas: 3
+Sue 231: perfumes: 7, cats: 6, samoyeds: 7
+Sue 232: cars: 3, children: 9, perfumes: 7
+Sue 233: vizslas: 1, samoyeds: 2, children: 2
+Sue 234: trees: 1, samoyeds: 8, children: 2
+Sue 235: trees: 6, akitas: 9, goldfish: 7
+Sue 236: children: 10, trees: 0, samoyeds: 8
+Sue 237: pomeranians: 4, trees: 1, goldfish: 2
+Sue 238: vizslas: 4, akitas: 2, cars: 0
+Sue 239: goldfish: 9, cars: 10, perfumes: 4
+Sue 240: perfumes: 3, vizslas: 6, trees: 6
+Sue 241: pomeranians: 6, akitas: 4, trees: 2
+Sue 242: cars: 8, perfumes: 5, children: 7
+Sue 243: trees: 4, perfumes: 7, cars: 3
+Sue 244: perfumes: 6, akitas: 1, vizslas: 7
+Sue 245: akitas: 3, perfumes: 9, samoyeds: 0
+Sue 246: pomeranians: 3, vizslas: 9, samoyeds: 1
+Sue 247: cars: 0, goldfish: 7, cats: 2
+Sue 248: trees: 5, goldfish: 6, perfumes: 3
+Sue 249: trees: 0, pomeranians: 7, perfumes: 9
+Sue 250: cars: 9, trees: 1, goldfish: 10
+Sue 251: perfumes: 3, cars: 8, trees: 7
+Sue 252: cars: 5, akitas: 7, trees: 8
+Sue 253: perfumes: 7, akitas: 3, trees: 8
+Sue 254: goldfish: 8, samoyeds: 1, vizslas: 7
+Sue 255: perfumes: 3, cars: 4, children: 6
+Sue 256: perfumes: 9, trees: 8, children: 7
+Sue 257: trees: 8, children: 6, cars: 4
+Sue 258: vizslas: 1, trees: 10, goldfish: 9
+Sue 259: vizslas: 5, trees: 6, goldfish: 9
+Sue 260: trees: 0, goldfish: 6, cars: 7
+Sue 261: cars: 1, perfumes: 4, goldfish: 9
+Sue 262: cars: 7, goldfish: 9, cats: 9
+Sue 263: cars: 0, children: 5, goldfish: 8
+Sue 264: cars: 2, akitas: 8, trees: 0
+Sue 265: perfumes: 9, children: 8, samoyeds: 7
+Sue 266: cats: 1, children: 1, vizslas: 10
+Sue 267: vizslas: 8, children: 2, trees: 6
+Sue 268: akitas: 10, vizslas: 3, cats: 2
+Sue 269: children: 4, goldfish: 1, cats: 6
+Sue 270: vizslas: 5, cars: 9, akitas: 9
+Sue 271: vizslas: 5, children: 4, akitas: 3
+Sue 272: cars: 1, goldfish: 0, vizslas: 0
+Sue 273: goldfish: 10, samoyeds: 1, akitas: 2
+Sue 274: goldfish: 10, children: 2, pomeranians: 0
+Sue 275: children: 0, vizslas: 1, samoyeds: 6
+Sue 276: children: 1, vizslas: 3, samoyeds: 1
+Sue 277: perfumes: 4, cats: 6, children: 10
+Sue 278: pomeranians: 7, goldfish: 3, cars: 4
+Sue 279: perfumes: 5, goldfish: 9, trees: 7
+Sue 280: goldfish: 6, trees: 5, perfumes: 8
+Sue 281: cars: 2, akitas: 1, vizslas: 7
+Sue 282: vizslas: 4, akitas: 3, children: 8
+Sue 283: pomeranians: 8, akitas: 9, vizslas: 4
+Sue 284: samoyeds: 10, trees: 10, pomeranians: 2
+Sue 285: akitas: 9, perfumes: 7, goldfish: 6
+Sue 286: akitas: 2, vizslas: 7, goldfish: 10
+Sue 287: pomeranians: 8, cars: 6, samoyeds: 5
+Sue 288: pomeranians: 1, trees: 0, goldfish: 0
+Sue 289: trees: 10, samoyeds: 1, children: 0
+Sue 290: cats: 10, samoyeds: 6, trees: 0
+Sue 291: vizslas: 9, trees: 6, goldfish: 5
+Sue 292: cats: 4, perfumes: 8, cars: 3
+Sue 293: goldfish: 10, perfumes: 10, cats: 0
+Sue 294: cats: 7, trees: 6, akitas: 4
+Sue 295: vizslas: 8, cars: 1, akitas: 6
+Sue 296: vizslas: 5, akitas: 10, trees: 1
+Sue 297: pomeranians: 8, samoyeds: 5, vizslas: 4
+Sue 298: perfumes: 10, children: 5, vizslas: 2
+Sue 299: cars: 10, akitas: 7, cats: 5
+Sue 300: trees: 1, perfumes: 7, cars: 7
+Sue 301: cars: 9, vizslas: 1, perfumes: 3
+Sue 302: perfumes: 9, vizslas: 1, akitas: 5
+Sue 303: akitas: 9, trees: 1, goldfish: 10
+Sue 304: children: 10, vizslas: 6, pomeranians: 8
+Sue 305: trees: 3, goldfish: 6, cats: 9
+Sue 306: cars: 5, perfumes: 9, vizslas: 5
+Sue 307: children: 0, goldfish: 7, trees: 2
+Sue 308: trees: 9, samoyeds: 4, cars: 0
+Sue 309: cats: 8, vizslas: 2, perfumes: 3
+Sue 310: cars: 6, pomeranians: 6, vizslas: 6
+Sue 311: vizslas: 6, akitas: 7, cats: 10
+Sue 312: trees: 0, goldfish: 7, cars: 0
+Sue 313: perfumes: 5, akitas: 5, cars: 2
+Sue 314: akitas: 10, vizslas: 3, samoyeds: 8
+Sue 315: cars: 3, perfumes: 1, goldfish: 8
+Sue 316: pomeranians: 6, goldfish: 9, perfumes: 1
+Sue 317: goldfish: 4, akitas: 6, cars: 2
+Sue 318: perfumes: 8, vizslas: 8, akitas: 0
+Sue 319: akitas: 10, cars: 5, vizslas: 6
+Sue 320: vizslas: 4, akitas: 3, cats: 4
+Sue 321: goldfish: 4, akitas: 8, cars: 8
+Sue 322: pomeranians: 5, vizslas: 7, cats: 1
+Sue 323: perfumes: 1, trees: 6, goldfish: 0
+Sue 324: goldfish: 6, trees: 10, cars: 10
+Sue 325: akitas: 2, samoyeds: 6, trees: 9
+Sue 326: vizslas: 4, akitas: 7, cars: 9
+Sue 327: children: 3, perfumes: 4, cars: 1
+Sue 328: akitas: 9, perfumes: 6, cars: 10
+Sue 329: perfumes: 2, goldfish: 0, trees: 1
+Sue 330: vizslas: 10, pomeranians: 7, goldfish: 6
+Sue 331: trees: 3, vizslas: 8, cars: 3
+Sue 332: akitas: 2, cats: 1, goldfish: 8
+Sue 333: cars: 6, trees: 2, vizslas: 0
+Sue 334: samoyeds: 7, cars: 7, trees: 3
+Sue 335: cats: 7, children: 1, perfumes: 8
+Sue 336: akitas: 5, goldfish: 10, vizslas: 5
+Sue 337: cats: 3, vizslas: 0, akitas: 10
+Sue 338: perfumes: 8, cars: 1, trees: 8
+Sue 339: cars: 4, samoyeds: 8, children: 2
+Sue 340: goldfish: 9, pomeranians: 1, samoyeds: 1
+Sue 341: akitas: 3, trees: 0, goldfish: 2
+Sue 342: perfumes: 4, vizslas: 8, pomeranians: 9
+Sue 343: akitas: 4, cars: 5, goldfish: 4
+Sue 344: samoyeds: 5, cats: 4, trees: 0
+Sue 345: samoyeds: 4, cars: 8, akitas: 2
+Sue 346: akitas: 3, vizslas: 10, perfumes: 10
+Sue 347: goldfish: 10, akitas: 4, cars: 1
+Sue 348: perfumes: 10, cats: 4, vizslas: 5
+Sue 349: akitas: 2, vizslas: 4, cars: 7
+Sue 350: akitas: 5, vizslas: 5, cars: 6
+Sue 351: vizslas: 8, perfumes: 6, cars: 3
+Sue 352: cars: 10, vizslas: 0, goldfish: 10
+Sue 353: cars: 10, perfumes: 5, children: 7
+Sue 354: vizslas: 6, akitas: 3, samoyeds: 9
+Sue 355: akitas: 2, perfumes: 7, cars: 10
+Sue 356: cars: 10, perfumes: 7, children: 6
+Sue 357: akitas: 4, cars: 8, trees: 1
+Sue 358: trees: 2, cars: 1, goldfish: 2
+Sue 359: vizslas: 5, cars: 9, trees: 4
+Sue 360: perfumes: 4, akitas: 3, cars: 3
+Sue 361: children: 3, akitas: 2, cats: 5
+Sue 362: cars: 8, cats: 4, akitas: 10
+Sue 363: cats: 2, trees: 1, vizslas: 4
+Sue 364: vizslas: 2, pomeranians: 5, samoyeds: 9
+Sue 365: samoyeds: 2, akitas: 7, goldfish: 9
+Sue 366: goldfish: 8, trees: 7, cats: 2
+Sue 367: perfumes: 2, vizslas: 6, trees: 5
+Sue 368: cars: 5, samoyeds: 0, perfumes: 6
+Sue 369: samoyeds: 10, trees: 10, vizslas: 1
+Sue 370: trees: 2, vizslas: 3, cars: 4
+Sue 371: akitas: 6, pomeranians: 2, cats: 4
+Sue 372: trees: 2, perfumes: 3, goldfish: 9
+Sue 373: vizslas: 5, children: 0, pomeranians: 6
+Sue 374: trees: 1, vizslas: 8, perfumes: 10
+Sue 375: cars: 0, akitas: 6, children: 0
+Sue 376: akitas: 1, vizslas: 0, trees: 0
+Sue 377: samoyeds: 10, cats: 5, pomeranians: 0
+Sue 378: goldfish: 3, pomeranians: 7, cats: 7
+Sue 379: perfumes: 0, cats: 0, trees: 8
+Sue 380: perfumes: 4, samoyeds: 1, akitas: 7
+Sue 381: akitas: 4, pomeranians: 2, children: 4
+Sue 382: vizslas: 9, akitas: 4, trees: 10
+Sue 383: trees: 1, vizslas: 10, akitas: 6
+Sue 384: trees: 3, akitas: 8, goldfish: 3
+Sue 385: goldfish: 6, perfumes: 2, children: 9
+Sue 386: children: 10, akitas: 7, goldfish: 7
+Sue 387: goldfish: 3, vizslas: 10, perfumes: 5
+Sue 388: children: 4, trees: 0, cars: 2
+Sue 389: trees: 0, cats: 3, goldfish: 10
+Sue 390: samoyeds: 9, pomeranians: 0, cats: 6
+Sue 391: samoyeds: 10, trees: 3, akitas: 4
+Sue 392: akitas: 9, goldfish: 10, perfumes: 7
+Sue 393: goldfish: 6, cars: 2, akitas: 9
+Sue 394: trees: 4, goldfish: 9, vizslas: 7
+Sue 395: vizslas: 4, samoyeds: 1, goldfish: 6
+Sue 396: vizslas: 5, cats: 0, samoyeds: 1
+Sue 397: goldfish: 7, cats: 0, trees: 7
+Sue 398: cars: 10, akitas: 1, vizslas: 7
+Sue 399: samoyeds: 10, cats: 6, goldfish: 6
+Sue 400: cats: 6, samoyeds: 0, trees: 2
+Sue 401: trees: 1, children: 4, goldfish: 2
+Sue 402: cats: 8, vizslas: 4, children: 3
+Sue 403: cars: 9, perfumes: 8, pomeranians: 2
+Sue 404: goldfish: 8, trees: 2, cars: 5
+Sue 405: perfumes: 1, pomeranians: 5, vizslas: 5
+Sue 406: perfumes: 6, trees: 2, pomeranians: 6
+Sue 407: trees: 0, goldfish: 6, cars: 6
+Sue 408: trees: 0, samoyeds: 7, goldfish: 9
+Sue 409: samoyeds: 10, goldfish: 6, pomeranians: 0
+Sue 410: perfumes: 5, vizslas: 6, trees: 0
+Sue 411: goldfish: 2, trees: 2, pomeranians: 0
+Sue 412: pomeranians: 4, perfumes: 8, cats: 8
+Sue 413: vizslas: 4, cars: 5, akitas: 1
+Sue 414: perfumes: 2, trees: 8, goldfish: 7
+Sue 415: akitas: 3, trees: 1, perfumes: 3
+Sue 416: cars: 7, trees: 1, perfumes: 8
+Sue 417: cars: 5, goldfish: 5, trees: 1
+Sue 418: cars: 9, goldfish: 4, samoyeds: 2
+Sue 419: pomeranians: 8, akitas: 1, goldfish: 6
+Sue 420: cars: 0, cats: 0, children: 8
+Sue 421: akitas: 10, goldfish: 1, vizslas: 8
+Sue 422: children: 8, vizslas: 6, samoyeds: 10
+Sue 423: samoyeds: 3, goldfish: 10, vizslas: 8
+Sue 424: cars: 3, children: 7, goldfish: 4
+Sue 425: cars: 9, perfumes: 9, goldfish: 8
+Sue 426: akitas: 5, trees: 10, vizslas: 10
+Sue 427: vizslas: 10, cars: 3, akitas: 7
+Sue 428: cats: 6, perfumes: 5, goldfish: 10
+Sue 429: goldfish: 7, trees: 5, vizslas: 10
+Sue 430: perfumes: 3, trees: 7, cars: 3
+Sue 431: cars: 2, vizslas: 1, akitas: 6
+Sue 432: pomeranians: 8, perfumes: 5, cars: 3
+Sue 433: children: 8, cars: 0, perfumes: 7
+Sue 434: samoyeds: 0, vizslas: 9, akitas: 10
+Sue 435: akitas: 3, vizslas: 8, cats: 4
+Sue 436: goldfish: 5, trees: 8, samoyeds: 8
+Sue 437: cars: 10, samoyeds: 9, goldfish: 7
+Sue 438: samoyeds: 5, akitas: 7, perfumes: 9
+Sue 439: goldfish: 10, perfumes: 5, cars: 0
+Sue 440: pomeranians: 1, samoyeds: 9, children: 4
+Sue 441: vizslas: 4, perfumes: 2, cats: 5
+Sue 442: trees: 0, pomeranians: 3, cars: 7
+Sue 443: akitas: 0, cars: 2, vizslas: 10
+Sue 444: children: 1, akitas: 9, trees: 0
+Sue 445: cars: 5, perfumes: 7, goldfish: 9
+Sue 446: akitas: 0, perfumes: 1, vizslas: 2
+Sue 447: vizslas: 7, perfumes: 0, cars: 5
+Sue 448: vizslas: 6, goldfish: 10, trees: 0
+Sue 449: cars: 7, vizslas: 7, trees: 3
+Sue 450: pomeranians: 4, akitas: 4, vizslas: 8
+Sue 451: cats: 4, perfumes: 8, children: 3
+Sue 452: samoyeds: 8, akitas: 9, cars: 1
+Sue 453: cars: 8, akitas: 5, vizslas: 2
+Sue 454: vizslas: 9, perfumes: 4, akitas: 4
+Sue 455: akitas: 3, goldfish: 2, vizslas: 6
+Sue 456: cars: 4, perfumes: 5, goldfish: 10
+Sue 457: trees: 9, pomeranians: 4, goldfish: 10
+Sue 458: pomeranians: 1, perfumes: 9, children: 6
+Sue 459: samoyeds: 0, goldfish: 8, vizslas: 6
+Sue 460: cars: 10, goldfish: 8, samoyeds: 8
+Sue 461: akitas: 8, goldfish: 9, vizslas: 2
+Sue 462: cars: 1, vizslas: 2, akitas: 8
+Sue 463: goldfish: 2, akitas: 4, samoyeds: 10
+Sue 464: children: 5, perfumes: 5, cars: 5
+Sue 465: perfumes: 9, trees: 0, samoyeds: 6
+Sue 466: akitas: 5, goldfish: 3, cats: 6
+Sue 467: perfumes: 3, goldfish: 0, trees: 4
+Sue 468: goldfish: 2, children: 4, trees: 1
+Sue 469: cars: 0, perfumes: 8, children: 7
+Sue 470: vizslas: 8, cats: 5, samoyeds: 9
+Sue 471: pomeranians: 7, trees: 2, goldfish: 3
+Sue 472: goldfish: 8, akitas: 4, perfumes: 5
+Sue 473: perfumes: 2, pomeranians: 3, cars: 8
+Sue 474: samoyeds: 0, akitas: 7, pomeranians: 6
+Sue 475: vizslas: 7, perfumes: 1, trees: 6
+Sue 476: vizslas: 3, samoyeds: 1, perfumes: 10
+Sue 477: cars: 6, perfumes: 5, vizslas: 2
+Sue 478: pomeranians: 1, goldfish: 3, akitas: 7
+Sue 479: goldfish: 10, trees: 0, cars: 3
+Sue 480: cats: 3, akitas: 5, vizslas: 8
+Sue 481: pomeranians: 5, vizslas: 2, trees: 3
+Sue 482: cars: 8, samoyeds: 10, goldfish: 10
+Sue 483: pomeranians: 3, vizslas: 6, goldfish: 5
+Sue 484: perfumes: 7, vizslas: 4, akitas: 7
+Sue 485: goldfish: 1, trees: 0, perfumes: 10
+Sue 486: goldfish: 6, perfumes: 0, akitas: 10
+Sue 487: cats: 2, akitas: 10, trees: 1
+Sue 488: akitas: 1, goldfish: 3, cars: 7
+Sue 489: goldfish: 3, akitas: 6, vizslas: 6
+Sue 490: goldfish: 8, perfumes: 2, akitas: 2
+Sue 491: trees: 4, vizslas: 8, perfumes: 6
+Sue 492: cars: 9, perfumes: 3, cats: 0
+Sue 493: trees: 3, vizslas: 6, goldfish: 7
+Sue 494: trees: 8, samoyeds: 1, perfumes: 5
+Sue 495: children: 9, akitas: 8, vizslas: 4
+Sue 496: vizslas: 2, pomeranians: 1, perfumes: 7
+Sue 497: trees: 2, akitas: 4, vizslas: 6
+Sue 498: akitas: 8, pomeranians: 7, trees: 0
+Sue 499: perfumes: 6, goldfish: 3, vizslas: 7
+Sue 500: cars: 1, perfumes: 6, vizslas: 1
diff --git a/16/main.py b/16/main.py
new file mode 100755
index 0000000..ce630a2
--- /dev/null
+++ b/16/main.py
@@ -0,0 +1,66 @@
+#!/usr/bin/python
+
+
+
+# Machine's readings given in puzzle text
+readings = {
+ "children" : 3,
+ "cats" : 7,
+ "samoyeds" : 2,
+ "pomeranians" : 3,
+ "akitas" : 0,
+ "vizslas" : 0,
+ "goldfish" : 5,
+ "trees" : 3,
+ "cars" : 2,
+ "perfumes" : 1
+}
+
+
+
+def solve_partn(partn, lines):
+ # Parse input file into a list of dictionaries
+ aunts = []
+ for line in lines:
+ words = line.split(" ")
+
+ data = {}
+ for i in range(3):
+ key = words[2 + 2 * i].rstrip(":")
+ val = words[3 + 2 * i].rstrip(",")
+ data[key] = int(val)
+
+ aunts.append(data)
+
+ # Find the aunt who gave us the present
+ for i, a in enumerate(aunts):
+ is_target = True
+ for k in a.keys():
+ if partn == 1:
+ is_target &= a[k] == readings[k]
+ else: # partn == 2
+ if k == "cats" or k == "trees":
+ is_target &= a[k] > readings[k]
+ elif k == "pomeranians" or k == "goldfish":
+ is_target &= a[k] < readings[k]
+ else:
+ is_target &= a[k] == readings[k]
+ if is_target:
+ return i + 1
+
+ return -1 # Shouldn't happen
+
+
+
+def main():
+ # Read properties of all Aunts Sue from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_partn(1, lines)) # 213 for me
+ print("Part 2 solution:", solve_partn(2, lines)) # 323 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/17/input.txt b/17/input.txt
new file mode 100644
index 0000000..6b25a72
--- /dev/null
+++ b/17/input.txt
@@ -0,0 +1,20 @@
+33
+14
+18
+20
+45
+35
+16
+35
+1
+13
+18
+13
+50
+44
+48
+6
+24
+41
+30
+42
diff --git a/17/main.py b/17/main.py
new file mode 100755
index 0000000..d301334
--- /dev/null
+++ b/17/main.py
@@ -0,0 +1,52 @@
+#!/usr/bin/python
+
+
+
+def find_combos(sizes, taken, avail, remaining):
+ result = set()
+
+ # For each container we haven't used yet
+ for i in avail:
+ # If its capacity is less than what we need, recurse to find
+ # containers for the remaining liquid, which might not succeed.
+ if sizes[i] < remaining:
+ _taken = taken | {i}
+ _avail = avail - {i}
+ _remaining = remaining - sizes[i]
+ for c in find_combos(sizes, _taken, _avail, _remaining):
+ result |= {frozenset([i]) | c}
+ # If its capacity is exactly what we need, we've found an option
+ elif sizes[i] == remaining:
+ result |= {frozenset([i])}
+ # If its capacity is too large, this isn't an option; continue
+
+ return result
+
+
+
+def solve_partn(partn, combos):
+ if partn == 1:
+ options = combos
+ else: # partn == 2
+ lengths = map(len, combos)
+ min_len = min(lengths)
+ options = {c for c in combos if len(c) == min_len}
+ return len(options)
+
+
+
+def main():
+ # Read container volumes from the input text file
+ with open("input.txt", "r") as f:
+ sizes = list(map(int, f.read().splitlines()))
+
+ # Extremely expensive (takes several minutes), so do it once up front
+ combos = find_combos(sizes, set(), set(range(len(sizes))), 150)
+
+ print("Part 1 solution:", solve_partn(1, combos)) # 1304 for me
+ print("Part 2 solution:", solve_partn(2, combos)) # 18 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/17/test.py b/17/test.py
new file mode 100755
index 0000000..74943db
--- /dev/null
+++ b/17/test.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class Examples(unittest.TestCase):
+ def test_example1(self):
+ sizes = [20, 15, 10, 5, 5]
+ combos = main.find_combos(sizes, set(), set(range(len(sizes))), 25)
+ self.assertEqual(main.solve_partn(1, combos), 4)
+ self.assertEqual(main.solve_partn(2, combos), 3)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/18/input.txt b/18/input.txt
new file mode 100644
index 0000000..e91a2a7
--- /dev/null
+++ b/18/input.txt
@@ -0,0 +1,100 @@
+###.##..##.#..#.##...#..#.####..#.##.##.##..###...#....#...###..#..###..###.#.#.#..#.##..#...##.#..#
+.#...##.#####..##.......#..####.###.##.#..###.###.....#.#.####.##.###..##...###....#.##.....#.#.#.##
+.....#.#.....#..###..####..#.....##.#..###.####.#.######..##......#####.#.##.#########.###..#.##.#.#
+...###......#.#..###..#.#.....#.##..#.##..###...#.##.#..#..#.##.#..##......##.##.##.######...#....##
+.###.....#...#.#...####.#.###..#..####.#..#.##..####...##.#...#..###...###...####..##....####.##..#.
+..#....#...#.......#..###.###....#.##..#.....###.#.##.#....#.#....##.##..#.##.#..###.###.##.##..##.#
+##..#####.#.#....#.#...#.#.####..#....#..#....#.#..#.#####...#..##.#.....#.##..##.####......#.#.##..
+.#..##..#.#.###..##..##...#....##...#..#.#..##.##..###.####.....#.####.#.....##.#.##...#..####..#...
+#.#####.......#####...#...####.#.#.#....#.###.#.##.#####..#.###.#..##.##.#.##....#.##..#....####.#.#
+#.##...#####....##.#.#.....##......##.##...#.##.##...##...###.###.##.#.####.####.##..#.##.#.#.####..
+#.##.##....###.###.#..#..##.##.#..#.#..##..#.#...#.##........###..#...##.#.#.##.......##.....#...###
+###..#.#..##.##.#.#.#...#..#...##.##.#.########.......#.#...#....########..#.#.###..#.#..#.##..#####
+####.#.#...#.##.##..#.#...#....#..###..#.#.#.####.#.##.##.#..##..##..#..#####.####.##..########..##.
+.#.#...#..##.#..#..###.#..####.......##.#.#.#.##.#####..#..##...#.##...#..#....#..#..###..####.#....
+..#.#...#....##...#####..#..#...###.###.....#.###.#....#.#..##...#.##.##.####.#.#.#..#.##.#....#.#..
+#....###.####.##..#.#.###..###.##.##..#.#...###..#.##.#####.##.#######..#.#...##.#..........####.###
+#.#####.#......#.#......#.....##...##.#.#########.#......##..##..##.#..##.##..#....##...###...#.#...
+#..#..##..###.#.#.#.#.....###.#.####.##.##....#.#..##....#.#..#.####..###.##...#######.#####.##.#.#.
+..###.#........##.#...###..#.##..#.#....##.#......#..#.##..#.#..#.#..#.####.#####..###.##..#.##.#...
+##.###....#..##...#..#.#......##..#...#..#.####..#.##...##.####.#...#..###...#.#.#....###.##..#.#...
+..##.##.#.##..##.#..#.###...##..##..#....##..##...####.#..####.###...#.....#..#.##..##..###..#.#...#
+#.#....#.....#...##.#...####..#..##..##.####..##..##...####...#....##.#.#######..##.#......######.#.
+#.#...###.######.######..##..##....#.#......#......#.#.##.#.##.#.#.#...#...#....#.#.#.#..#.##..#...#
+####.###.#.#.##..#.##.#...#.##...#.##.##...#.....#.#..#.####.##..######.#..#.#..##....#.#.#..#.#.#.#
+..##......#.#...#.##.##..##..##..#..##..#########.#..###..###.##...#..##.#..#.#.#.######..#....#.#..
+..##.##.#...###.#...##..######.##.#..####..#..#.#.##.####.##.##.#...##....#...###.##.####..#....#.#.
+####...###..#.#.##.#.#....###..##.#.#..########..#...#.#...#.##....##.##...#.....#.#.....#.....#....
+.#.###############....#.##..###..#.####.#.##.##..#..#.#...###...##..##.##.#.....##...###.###.....#..
+.###..#..##.##..####.#.###.##.##..#..##....#.#......#......##.#...#.#...#..##.#.#...#...#.##..#.##..
+###.#.#.########.#.#..####.#..##.#.##.##.###.##..######...#..##.##.#..#.#...#.##..#####.....#.#.#..#
+.##.##..#.#...#####.#.#.###...##...####...#......#...#..####..#.##..........#..#.#..###....######.##
+..#####...#.#.#.#..#.##..#...#.#..#.##...##..##.##.#.##.#..#.#...#.......##.#...###.....#...#.#.#.##
+##.##.#..######.##...#.....#.###.#..##.#.#.#..####.#....##.#....####...##....#.#.##.#..###.##.##..##
+.###.##.#..#.###.####..#.##..####.#.#.##..###.#######.###.###...####........##....###.#...#.#.####.#
+........#..#.#..##..########..........#.##.#..##.#...#.....####....##..#..#.#####.###...#...#.##.###
+.....#..##.####...##.#####..######.##.#.###.####.##.##.#..##.##.######.##......#..#.####..##....#.##
+##...####....#.##.##.###....#.#...#.####..##.#.##.#.#...####.#.#.#.#...##.###...##...###...######.##
+.#....#.#.####...#.##.....##...###.#.#.##...##.#####....#.######.#.#....##..##...##....##.#.##.#.#.#
+.###..###.#.......#.#######..#.#.#.######....#.#####.#.....#.#########...#....##...##.####.#..#.....
+##.#..##..##.....#..##...#..##.##.#..#.#####.##.##.#.##.##...##.######.####..#.##..#####.##...##..#.
+#.###...##.#.#.#.##....#.#.##.##..#....#...#.#.........#..#..####..####.####..#.##.##.#....####..##.
+.#..######..#####.####.##.#.....#.#.#####..##..###.#.#.#..#.#...#.#######..##....##.##...#######..#.
+#...#....#.#.##..#####..#########..#.....#...##.#.#.###...#####..##...##...####.......#######.#..###
+.#......#...##.###..#....#...#.#.....#.#...##.#.#..#..###.##.###.#.##..##...#.##......#.###..#.#..##
+.#....####...###..#.....##..#...#.#.###.#.#.##...#.##.##.#.#.#..####..###.#.#.#.##.#.#...#..#...####
+......##.##.#...#####.##..#.###..#.#####..##.#..##.###......#...#...#..#......###.######...#.#.##..#
+###..#...#.##..###.#....##...#..#####.#.#..#.###...#####.#....##..####.#.##...#.#...##..#.#.#.#..#.#
+...##.#.##.##..#.#.#.###.#.#...#.....###.###.##...#.###.##...##..#..###.#..##.##..###.#....###..##..
+.##.#..###..###.##.##...#..#####...#.....#####.##..####...#.##.#.#..##.#.#.#....###.....#....##.....
+######.#..#.#..#....#.###...####.####.#.........#..##.#..##..##.....#..#.##.##...#...#####.#.##..#.#
+.##.###...####....#.####...#####..#..#...#..#.....###.#..#.###..#.###.#.......##.####..#.##.#...##..
+........#.#.##.#.....#####.###......##..#.##.#..#...####.#...#..###.#.#...##..#.#...#.####...#.#.###
+.#..#.##..##...######.###.##.#.#...#.#.#.#.##..##..##.#.##..#....#.##...#.##.##...##....##.###.##.#.
+##...#...#...###.#.#...#...#..###......##.#.#....##..##.#..##.#.######...#..##.#.##.#.#....#.##.##..
+...#..###.#....#...#.##..##.#.##.#..###.##..#.##..####.#########....#.....##.#.##.##..##.##.######.#
+#.##.#..##.......###...#.###....###.#..####..##.#####.##.###....##....#.###...####..#.#.#.##.....###
+.......#...#...##.#...##.#.#..#.##..##.#....###...##.#####...#.........#.......###.##.#.#.###....##.
+###.#.##.##.....#.#..#.#..####.####..#..###..........####.#.##...#######.###..#####..#.....#..###..#
+#...##.##..####.##.###.#.#######..###.#..#######..#.##.####...#..#.##.####..####.#.#.......####.#...
+...#.##..#..#..##........#.#..#..#.#....#.###.#.###..#.......###..#.....#....#..##.#...#.###...##.#.
+###.##..#.##.#.#####..#.##.####....#####..###.#.#..#...#...###.#.##..#.#.#.....#.####.#.#.#.#.#.#...
+..##..##..#..##.##.#...#..#....####....#...#..####..#.....######.###.####.#....##....##.#.#.###....#
+.#.#.#.##..####..#.....#.####.#....#.....#....#.##..#.#..#.#...#.#.#.#..#..#..##.#....####.......#..
+..##.##..###......#...#..##...#.###.####.#...#.####..#.#.#.....#.#...####...#.########.##.#.#.#..###
+#....#.##.....##.###.##.###..#.####.....####.##...#..##.###...###..###.#....####.#..#..#..#.#..##.#.
+.#.#.##....#.##......#.#..###.#....###....#......#.#.##.##.#########..##..#...#.####..#...####..#..#
+.#.#.......##.#.##.#...#...#.##.#..#.#.#.##....#..###.###.##.#.#...##.#..#..##....#..###.#...#.#.##.
+#.##.#....####...#..##..#.#.#.#.##.#...#####.#...#..#..#.####.####.#.#....#......##..##..###...#..##
+..##.###..##.####..#..#..##...###.#.#.#######.####...####......##.##..#...#.##...##....#..#..#.....#
+....#..#..#.#.####.#...##..#....####.#..####...#.#...###...#..#..##...#....##...#.....#.#..#.#.#...#
+...#.#.#.##..##.###..#.######....####.###...##...###.#...##.####..#.#..#.#..#.##.....#.#.#..##......
+.#.##.##.....##.#..###.###.##....#...###.#......#...##.###.#.##.##...###...###...#.######..#......#.
+###..#...#......#..##...#....##.#..###.##.####..##..##....####.#...#.#....##..#.#######..#.#.#####..
+##...#####..####..##....#.#.###.##.#..#.#..#.....###...###.#####.....#..##.#......#...#.###.##.##...
+...#.#.#..#.###..#.#.#....##.#.#..####.##.#.####.#.#.#...#....##....#.##.####..###.#.#...##.#..#..##
+#.#.#..#.##..##.##.#...##.#....#...###..##..#.#######.#.###..##......##.#..###.########.#.##..#.#.##
+######.###....##..#..#...####....#.#.#..#...#..######.#.#.##..##....##....##.##.##...#..#.####.#.#..
+#####.###..#..###......##...##.####.#.#.#.###.......##..##.####..##.####.#..#..####..#.####.#####...
+##.#.#.###..##.#.##.#.#.#.##.#...##........###.#.##..####....###.#.####.####.#.......##.##.##...##..
+#.#..###...#..##.....##.#..#.#..##..######.#####...###.#.......###...#..##..#..#..##.#.#....#..#..#.
+#.#..####.###..#...#...#...#.###..#.#.#.#.#.#.#..#....#.##.##.##..###..####.#..##..##.###.###....##.
+#..#.##.#####........#..#.##.#..##.#...#....#..#.##..###..##..##.##..#..##.#.#...#.#.##.#.##....#.#.
+.......##..#.....#..#.#.....#.##...####.###..####..#.#.#.#..#.....#....##...#..#.##..###.#.#....#...
+#...###########.##.....##...###.#.##.##..####.##...#.####.#####.#####.####...###.##...##..#.#.###..#
+....#.#.###.####.###...#...#.#..###.#.#.##...#..#.#.#..#.####..#..###.######.#.####.###...###.#.##.#
+.....#..#..########...#.#.#.#.#.#.#.#..###.##..####...##.#.#.#...##..#####.##.#...#.####.#######.##.
+.......#...#.#..#..#...#..#..##.....#.##....##.##...##..##.##...##...#.#..#.##.#.###.#.####.#.#..##.
+.####...#...#.#.#....##..........##.##.###.##.#.#..#.#.#......########.#...#.####.##.###..##...####.
+#.#.#...##.###..##..#..#.....####.#.....##.##.#..#.#.###.#..#######...##..#.#..#.#..############.###
+.##..####.#..#.....###..#..#.#.....#.#.#...##.##.#....#..#..###.#...#....#.#...####..#.....###.####.
+..#...#.###.###....##.#..#.##..####.##.#.##.##.##...###.####..#.#.#.##.#.#.#..###..##.##.##.##.#..##
+#...............##.....######.#.#####.##.#....#.#..#.##...#.##....#........##.##...#.##.##.#..#.##.#
+#..##..#.#.#.##.#..#.#.##.##...#...#..#.#.##..#.#...###...##...###..#####.#.#..#..#.#..#.#.##...##.#
+.#######.#.....##...#.#.####.######.#..#......#....##.#.#..#..###.#...###...#....#.#..#.##.#...#.#..
+#.###......##.#.##..#.###.###..####..##....#..###......##..##..#####.####....#...###.....###.#..#...
+###...#....###.#..#.###.##...###.##.......##.##.#.#.#....####....###..##.###...#..##....#.#.##..##..
+.##.......##.######.#.#..#..##....#####.###.#.##.....####....#......####....#.##.#.##..#.##...##.#.#
+.#.###...#.#.#.##.###..###...##..#.##.##..##..#.....###.#..#.##.##.####........##.#####.#.#....#...#
+##...##..#.##.#######.###.#.##.#####....##.....##.#.....#.#.##.#....#.##.#....##.#..#.###..#..#.#...
+.#..#.#.#.#...#.##...###.##.#.#...###.##...#.#..###....###.#.###...##..###..#..##.##....###...###.##
diff --git a/18/main.py b/18/main.py
new file mode 100755
index 0000000..e6bdf65
--- /dev/null
+++ b/18/main.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+
+import numpy as np
+
+
+
+def init_grid(lines):
+ dim = len(lines[0])
+ grid = np.zeros((dim + 2, dim + 2), dtype="bool")
+
+ for r, line in enumerate(lines):
+ for c, char in enumerate(line):
+ if char == "#":
+ grid[r + 1, c + 1] = True
+
+ return grid
+
+
+
+def solve_partn(partn, init, steps):
+ grid = init_grid(init)
+
+ # Part 2: some lights are stuck in the "on" state
+ if partn == 2:
+ grid[([1, 1, -2, -2], [1, -2, 1, -2])] = True
+
+ for i in range(steps):
+ new_grid = grid.copy()
+ for x in range(1, grid.shape[0] - 1):
+ for y in range(1, grid.shape[1] - 1):
+ neigh = np.sum(grid[x - 1 : x + 2, y - 1 : y + 2]) - grid[x, y]
+ if grid[x, y]:
+ if neigh < 2 or neigh > 3:
+ new_grid[x, y] = False
+ else: # not grid[x, y]
+ if neigh == 3:
+ new_grid[x, y] = True
+
+ if partn == 2:
+ new_grid[([1, 1, -2, -2], [1, -2, 1, -2])] = True
+
+ grid = new_grid
+
+ return np.count_nonzero(grid)
+
+
+
+def main():
+ # Read initial state from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_partn(1, lines, 100)) # 821 for me
+ print("Part 2 solution:", solve_partn(2, lines, 100)) # 886 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/18/test.py b/18/test.py
new file mode 100755
index 0000000..954e2b3
--- /dev/null
+++ b/18/test.py
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class Examples(unittest.TestCase):
+ def test_example1(self):
+ lines = [
+ ".#.#.#",
+ "...##.",
+ "#....#",
+ "..#...",
+ "#.#..#",
+ "####.."
+ ]
+ self.assertEqual(main.solve_partn(1, lines, 4), 4)
+ self.assertEqual(main.solve_partn(2, lines, 5), 17)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/19/input.txt b/19/input.txt
new file mode 100644
index 0000000..806774f
--- /dev/null
+++ b/19/input.txt
@@ -0,0 +1,45 @@
+Al => ThF
+Al => ThRnFAr
+B => BCa
+B => TiB
+B => TiRnFAr
+Ca => CaCa
+Ca => PB
+Ca => PRnFAr
+Ca => SiRnFYFAr
+Ca => SiRnMgAr
+Ca => SiTh
+F => CaF
+F => PMg
+F => SiAl
+H => CRnAlAr
+H => CRnFYFYFAr
+H => CRnFYMgAr
+H => CRnMgYFAr
+H => HCa
+H => NRnFYFAr
+H => NRnMgAr
+H => NTh
+H => OB
+H => ORnFAr
+Mg => BF
+Mg => TiMg
+N => CRnFAr
+N => HSi
+O => CRnFYFAr
+O => CRnMgAr
+O => HP
+O => NRnFAr
+O => OTi
+P => CaP
+P => PTi
+P => SiRnFAr
+Si => CaSi
+Th => ThCa
+Ti => BP
+Ti => TiTi
+e => HF
+e => NAl
+e => OMg
+
+CRnCaSiRnBSiRnFArTiBPTiTiBFArPBCaSiThSiRnTiBPBPMgArCaSiRnTiMgArCaSiThCaSiRnFArRnSiRnFArTiTiBFArCaCaSiRnSiThCaCaSiRnMgArFYSiRnFYCaFArSiThCaSiThPBPTiMgArCaPRnSiAlArPBCaCaSiRnFYSiThCaRnFArArCaCaSiRnPBSiRnFArMgYCaCaCaCaSiThCaCaSiAlArCaCaSiRnPBSiAlArBCaCaCaCaSiThCaPBSiThPBPBCaSiRnFYFArSiThCaSiRnFArBCaCaSiRnFYFArSiThCaPBSiThCaSiRnPMgArRnFArPTiBCaPRnFArCaCaCaCaSiRnCaCaSiRnFYFArFArBCaSiThFArThSiThSiRnTiRnPMgArFArCaSiThCaPBCaSiRnBFArCaCaPRnCaCaPMgArSiRnFYFArCaSiThRnPBPMgAr
diff --git a/19/main.py b/19/main.py
new file mode 100755
index 0000000..120a44a
--- /dev/null
+++ b/19/main.py
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+
+from random import shuffle
+
+
+
+def parse_input(lines):
+ rules = {}
+ for line in lines[0:-2]:
+ words = line.split()
+ k = words[0]
+ if k not in rules:
+ rules[k] = []
+ rules[k].append(words[2])
+
+ molecule = lines[-1]
+ return rules, molecule
+
+
+
+# Given a "parent" molecule, find all "child" molecules
+# that can be made from it with exactly one replacement.
+def find_children(rules, parent):
+ result = []
+ # For each replaceable string
+ for k in rules.keys():
+ # For each occurrence in the given molecule
+ p = 0
+ while p < len(parent):
+ i = parent.find(k, p)
+ if i != -1:
+ # For each replacement of the replaceable string
+ for r in rules[k]:
+ m = parent[0 : i] + r + parent[i + len(k) : ]
+ if m not in result:
+ result.append(m)
+ p = i + 1
+ else: # ran out of occurrences
+ break
+ return result
+
+
+
+# Given a "child" molecule, find all "parent" molecules
+# that can be produce it using exactly one replacement.
+def find_parents(rules, child):
+ result = []
+ # For each replaceable string
+ for k in rules.keys():
+ # For each replacement of the replaceable string
+ for r in rules[k]:
+ # For each occurrence in the given molecule
+ p = 0
+ while p < len(child):
+ i = child.find(r, p)
+ if i != -1:
+ m = child[0 : i] + k + child[i + len(r) : ]
+ if m not in result:
+ result.append(m)
+ p = i + 1
+ else: # ran out of occurrences
+ break
+ return result
+
+
+
+def solve_part1(lines):
+ rules, start = parse_input(lines)
+ result = find_children(rules, start)
+ return len(result)
+
+
+
+def solve_part2(lines):
+ rules, target = parse_input(lines)
+ molecules = [target]
+
+ # Compared to part 1, we do part 2 in reverse: from the target molecule,
+ # we work backwards through its history until we encounter "e"; because
+ # we go back one generation at a time, the first "e" must be the answer.
+ depth = 0
+ while "e" not in molecules:
+
+ # Find all ancestors of this generation; there could be HUGELY many
+ ancestors = []
+ for m in molecules:
+ parents = find_parents(rules, m)
+ for p in parents:
+ if p not in ancestors:
+ ancestors.append(p)
+
+ # Extremely greedy algorithm: only keep shortest candidates,
+ # because they're more likely to lie on the shortest path.
+ # However, this may give the wrong answer: the optimal path
+ # may involve some strategic locally non-optimal steps.
+ min_len = min(map(len, ancestors))
+ shortest = []
+ for p in ancestors:
+ if len(p) == min_len:
+ shortest.append(p)
+ ancestors = shortest
+
+ # Sledgehammer solution to keep the runtime under control,
+ # since it turns out the above greedy approach isn't enough.
+ # Fortunately, the puzzle input is well-chosen to allow this.
+ if len(ancestors) > 42:
+ shuffle(ancestors)
+ ancestors = ancestors[0 : 42]
+
+ molecules = ancestors
+ depth += 1
+
+ return depth
+
+
+
+def main():
+ # Read replacement rules and medicine molecules from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_part1(lines)) # 509 for me
+ print("Part 2 solution:", solve_part2(lines)) # 195 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/19/test.py b/19/test.py
new file mode 100755
index 0000000..f2fab39
--- /dev/null
+++ b/19/test.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class Examples(unittest.TestCase):
+ def test_example1(self):
+ lines = [
+ "e => H",
+ "e => O",
+ "H => HO",
+ "H => OH",
+ "O => HH",
+ "",
+ "HOH"
+ ]
+ self.assertEqual(main.solve_part1(lines), 4)
+ self.assertEqual(main.solve_part2(lines), 3)
+
+ def test_example2(self):
+ lines = [
+ "e => H",
+ "e => O",
+ "H => HO",
+ "H => OH",
+ "O => HH",
+ "",
+ "HOHOHO"
+ ]
+ self.assertEqual(main.solve_part1(lines), 7)
+ self.assertEqual(main.solve_part2(lines), 6)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/20/main.py b/20/main.py
new file mode 100755
index 0000000..c20cc5c
--- /dev/null
+++ b/20/main.py
@@ -0,0 +1,45 @@
+#!/usr/bin/python
+
+from math import sqrt
+
+
+
+def solve_partn(partn, target):
+ house = 1
+ while True:
+ # Find all divisors of the house number, because those
+ # determine which elves brings presents to which house.
+ # If I were smart, I'd cache the results and use them in
+ # both parts 1 and 2, but I can't be bothered to do that.
+ divs = [1, house]
+ for n in range(2, int(sqrt(house)) + 1):
+ if house % n == 0:
+ divs += [n, house / n]
+
+ # Give gifts to each house as described in the puzzle text
+ gifts = 0
+ for d in divs:
+ if partn == 1:
+ gifts += 10 * d
+ else: # partn == 2
+ if house / d <= 50:
+ gifts += 11 * d
+
+ if gifts >= target:
+ return house
+
+ house += 1
+
+
+
+def main():
+ # My personalized puzzle input
+ target = 34000000
+
+ print("Part 1 solution:", solve_partn(1, target)) # 786240 for me
+ print("Part 2 solution:", solve_partn(2, target)) # 831600 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/20/test.py b/20/test.py
new file mode 100755
index 0000000..15ac2f9
--- /dev/null
+++ b/20/test.py
@@ -0,0 +1,16 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ self.assertEqual(main.solve_partn(1, 150), 8)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/21/input.txt b/21/input.txt
new file mode 100644
index 0000000..e749051
--- /dev/null
+++ b/21/input.txt
@@ -0,0 +1,3 @@
+Hit Points: 103
+Damage: 9
+Armor: 2
diff --git a/21/main.py b/21/main.py
new file mode 100755
index 0000000..2c75798
--- /dev/null
+++ b/21/main.py
@@ -0,0 +1,155 @@
+#!/usr/bin/python
+
+from copy import copy
+
+
+
+class Entity:
+ pass
+
+class Item:
+ pass
+
+class Shop:
+ pass
+
+
+
+# Annoying boilerplate to initialize the shop with the items
+# given in the text. This is as compact as I could make it.
+def init_shop():
+ table_weapons = [
+ "Dagger 8 4 0",
+ "Shortsword 10 5 0",
+ "Warhammer 25 6 0",
+ "Longsword 40 7 0",
+ "Greataxe 74 8 0"
+ ]
+ table_armour = [
+ "Leather 13 0 1",
+ "Chainmail 31 0 2",
+ "Splintmail 53 0 3",
+ "Bandedmail 75 0 4",
+ "Platemail 102 0 5",
+ "NONE 0 0 0" # Because it's optional
+ ]
+ table_rings = [
+ "Damage+1 25 1 0",
+ "Damage+2 50 2 0",
+ "Damage+3 100 3 0",
+ "Defense+1 20 0 1",
+ "Defense+2 40 0 2",
+ "Defense+3 80 0 3",
+ "NONE 0 0 0" # Because it's optional
+ ]
+ tables = [
+ (table_weapons, "weapons"),
+ (table_armour, "armour"),
+ (table_rings, "rings")
+ ]
+
+ shop = Shop()
+ shop.weapons = []
+ shop.armour = []
+ shop.rings = []
+
+ for table, attr in tables:
+ for row in table:
+ fields = row.split()
+
+ item = Item()
+ item.name = fields[0]
+ item.cost = int(fields[1])
+ item.damage = int(fields[2])
+ item.armour = int(fields[3])
+
+ # Sometimes I love you, Python. But usually I don't.
+ shop.__dict__[attr].append(item)
+
+ return shop
+
+
+
+def will_win(player, boss):
+ turn = 0
+ while boss.health > 0 and player.health > 0:
+ # Player's turn to attack
+ if turn % 2 == 0:
+ boss.health -= max(1, player.damage - boss.armour)
+ # Boss' turn to attack
+ else: # turn % 2 == 1
+ player.health -= max(1, boss.damage - player.armour)
+ turn += 1
+
+ # Did the player win?
+ return boss.health <= 0
+
+
+
+def solve_partn(partn, player_stats, boss_stats):
+ shop = init_shop()
+
+ min_cost = 666
+ max_cost = 0
+ for w in shop.weapons:
+ for a in shop.armour:
+ for r1 in shop.rings:
+ for r2 in shop.rings:
+ # No more than one of each item
+ if r1 == r2:
+ continue
+
+ boss = copy( boss_stats)
+ player = copy(player_stats)
+
+ cost = w.cost + a.cost + r1.cost + r2.cost
+ player.damage += w.damage
+ player.damage += r1.damage
+ player.damage += r2.damage
+ player.armour += a.armour
+ player.armour += r1.armour
+ player.armour += r2.armour
+
+ # Part 1: find lowest cost where player wins
+ if will_win(player, boss):
+ if cost < min_cost:
+ min_cost = cost
+ # Part 2: find highest cost where player loses
+ else:
+ if cost > max_cost:
+ max_cost = cost
+
+ if partn == 1:
+ return min_cost
+ else: # partn == 2
+ return max_cost
+
+
+
+def main():
+ # Set up player's stats given in puzzle text
+ player = Entity()
+ player.health = 100
+ player.damage = 0
+ player.armour = 0
+
+ # Read boss' stats from input text file
+ boss = Entity()
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+ for i in range(len(lines)): # len(lines) = 3
+ words = lines[i].split()
+ if i == 0:
+ boss.health = int(words[-1])
+ if i == 1:
+ boss.damage = int(words[-1])
+ if i == 2:
+ boss.armour = int(words[-1])
+
+ print("Part 1 solution:", solve_partn(1, player, boss)) # 121 for me
+ print("Part 2 solution:", solve_partn(2, player, boss)) # 201 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/21/test.py b/21/test.py
new file mode 100755
index 0000000..4527f49
--- /dev/null
+++ b/21/test.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ player = main.Entity()
+ player.health = 8
+ player.damage = 0
+ player.armour = 0
+
+ boss = main.Entity()
+ boss.health = 12
+ boss.damage = 7
+ boss.armour = 2
+
+ result = main.solve_partn(1, player, boss)
+ self.assertEqual(result, 40 + 25)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/22/input.txt b/22/input.txt
new file mode 100644
index 0000000..7f98dd7
--- /dev/null
+++ b/22/input.txt
@@ -0,0 +1,2 @@
+Hit Points: 58
+Damage: 9
diff --git a/22/main.py b/22/main.py
new file mode 100755
index 0000000..9b7bb0b
--- /dev/null
+++ b/22/main.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python
+
+from copy import copy
+from random import randrange
+
+
+
+class Entity:
+ pass
+
+
+
+def solve_partn(partn, player_stats, boss_stats):
+ # We assign numbers 0-4 to the spells
+ mana_costs = [53, 73, 113, 173, 229]
+
+ least_mana = 666666
+ # 100K attempts turns out to be enough to reliably find the solution
+ for i in range(100000):
+ boss = copy( boss_stats)
+ player = copy(player_stats)
+
+ spent_mana = 0
+ # Timers for "Shield", "Poison" and "Recharge", respectively
+ timers = [0, 0, 0]
+
+ turn = 0
+ while boss.health > 0 and player.health > 0:
+ # Already spent too much; this can't be the solution, abort
+ if spent_mana > least_mana:
+ break
+
+ # Part 2: hard mode: player loses 1 HP at start of their turn
+ if partn == 2 and turn % 2 == 0:
+ player.health -= 1
+ if player.health <= 0:
+ break
+
+ # Apply status effects from past spells
+ # "Shield"
+ if timers[0] > 0:
+ timers[0] -= 1
+ if timers[0] == 0:
+ player.armour -= 7
+ # "Poison"
+ if timers[1] > 0:
+ boss.health -= 3
+ # Did the boss die before any actions were taken?
+ if boss.health <= 0:
+ break
+ timers[1] -= 1
+ # "Recharge"
+ if timers[2] > 0:
+ player.mana += 101
+ timers[2] -= 1
+
+ # Player's turn to cast a spell
+ if turn % 2 == 0:
+ # Can we afford to cast any spell? If not, we've lost; abort
+ if player.mana < min(mana_costs):
+ break
+
+ # Randomly select which spell to cast. Sounds inefficient, but
+ # works fine: we want to minimize the total mana spent, so we're
+ # looking for short sequences, which we're very unlikely to miss.
+ s = -1
+ while s == -1:
+ s = randrange(0, 5)
+ # Can we afford this spell?
+ if mana_costs[s] > player.mana:
+ s = -1
+ # Isn't this spell already active?
+ if s in [2, 3, 4] and timers[s - 2] > 0:
+ s = -1
+
+ player.mana -= mana_costs[s]
+ spent_mana += mana_costs[s]
+
+ # Apply effect of the spell we've selected
+ # "Magic Missile"
+ if s == 0:
+ boss.health -= 4
+ # "Drain"
+ if s == 1:
+ boss.health -= 2
+ player.health += 2
+ # "Shield"
+ if s == 2:
+ timers[0] = 6
+ player.armour += 7
+ # "Poison"
+ if s == 3:
+ timers[1] = 6
+ # "Recharge"
+ if s == 4:
+ timers[2] = 5
+
+ # Boss' turn to attack
+ else: # turn % 2 == 1
+ player.health -= max(1, boss.damage - player.armour)
+
+ turn += 1
+
+ # Did we stop because the player won?
+ if boss.health <= 0:
+ if spent_mana < least_mana:
+ least_mana = spent_mana
+
+ return least_mana
+
+
+
+def main():
+ # Set up player's stats given in puzzle text
+ player = Entity()
+ player.health = 50
+ player.armour = 0
+ player.mana = 500
+
+ # Read boss' stats from input text file
+ boss = Entity()
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+ for i in range(len(lines)): # len(lines) = 2
+ words = lines[i].split()
+ if i == 0:
+ boss.health = int(words[-1])
+ if i == 1:
+ boss.damage = int(words[-1])
+
+ print("Part 1 solution:", solve_partn(1, player, boss)) # 1269 for me
+ print("Part 2 solution:", solve_partn(2, player, boss)) # 1309 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/22/test.py b/22/test.py
new file mode 100755
index 0000000..8410d32
--- /dev/null
+++ b/22/test.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ player = main.Entity()
+ player.health = 10
+ player.armour = 0
+ player.mana = 250
+
+ boss = main.Entity()
+ boss.health = 13
+ boss.damage = 8
+
+ result = main.solve_partn(1, player, boss)
+ self.assertEqual(result, 173 + 53)
+
+ def test_example2(self):
+ player = main.Entity()
+ player.health = 10
+ player.armour = 0
+ player.mana = 250
+
+ boss = main.Entity()
+ boss.health = 14
+ boss.damage = 8
+
+ result = main.solve_partn(1, player, boss)
+ self.assertEqual(result, 229 + 113 + 73 + 173 + 53)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/23/input.txt b/23/input.txt
new file mode 100644
index 0000000..a2b735a
--- /dev/null
+++ b/23/input.txt
@@ -0,0 +1,47 @@
+jio a, +18
+inc a
+tpl a
+inc a
+tpl a
+tpl a
+tpl a
+inc a
+tpl a
+inc a
+tpl a
+inc a
+inc a
+tpl a
+tpl a
+tpl a
+inc a
+jmp +22
+tpl a
+inc a
+tpl a
+inc a
+inc a
+tpl a
+inc a
+tpl a
+inc a
+inc a
+tpl a
+tpl a
+inc a
+inc a
+tpl a
+inc a
+inc a
+tpl a
+inc a
+inc a
+tpl a
+jio a, +8
+inc b
+jie a, +4
+tpl a
+inc a
+jmp +2
+hlf a
+jmp -7
diff --git a/23/main.py b/23/main.py
new file mode 100755
index 0000000..bf84465
--- /dev/null
+++ b/23/main.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+
+
+
+def solve_partn(partn, program):
+ if partn == 1:
+ regs = { "a" : 0, "b" : 0 }
+ else: # partn == 2
+ regs = { "a" : 1, "b" : 0 }
+
+ # "Parse" the input once up front, so we don't waste time
+ # doing this each iteration of a loop in the given program.
+ tokens = []
+ for line in program:
+ words = line.split()
+ words[1] = words[1].rstrip(",")
+ tokens.append(words)
+
+ # Main program execution loop
+ i = 0 # instruction pointer
+ while i < len(tokens):
+ op = tokens[i][0]
+ d1 = tokens[i][1]
+
+ # The "i += 1" is repeated for consistency and clarity
+ if op == "hlf":
+ regs[d1] = int(regs[d1] / 2)
+ i += 1
+ elif op == "tpl":
+ regs[d1] = regs[d1] * 3
+ i += 1
+ elif op == "inc":
+ regs[d1] = regs[d1] + 1
+ i += 1
+ elif op == "jmp":
+ i += int(d1)
+ elif op == "jie":
+ if regs[d1] % 2 == 0:
+ d2 = tokens[i][2]
+ i += int(d2)
+ else:
+ i += 1
+ elif op == "jio":
+ if regs[d1] == 1:
+ d2 = tokens[i][2]
+ i += int(d2)
+ else:
+ i += 1
+
+ return regs["b"]
+
+
+
+def main():
+ # Read program's assembly code from input text file
+ with open("input.txt", "r") as f:
+ lines = f.read().splitlines()
+
+ print("Part 1 solution:", solve_partn(1, lines)) # 307 for me
+ print("Part 2 solution:", solve_partn(2, lines)) # 160 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/23/test.py b/23/test.py
new file mode 100755
index 0000000..19dad35
--- /dev/null
+++ b/23/test.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ prog = [
+ "inc b",
+ "jio b, +2",
+ "tpl b",
+ "inc b"
+ ]
+ self.assertEqual(main.solve_partn(1, prog), 2)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/24/input.txt b/24/input.txt
new file mode 100644
index 0000000..e204f7d
--- /dev/null
+++ b/24/input.txt
@@ -0,0 +1,28 @@
+1
+3
+5
+11
+13
+17
+19
+23
+29
+31
+37
+41
+43
+47
+53
+59
+67
+71
+73
+79
+83
+89
+97
+101
+103
+107
+109
+113
diff --git a/24/main.py b/24/main.py
new file mode 100755
index 0000000..61c97a6
--- /dev/null
+++ b/24/main.py
@@ -0,0 +1,86 @@
+#!/usr/bin/python
+
+from itertools import combinations
+
+
+
+def solve_partn(partn, masses):
+ if partn == 1:
+ num_groups = 3
+ else: # partn == 2
+ num_groups = 4
+
+ m_total = sum(masses)
+ m_group = int(m_total / num_groups)
+
+ # At first, it seems the easiest way is to generate all possible
+ # grouping of packages, and then just keep the ones that are valid,
+ # and find the best among the remaining configurations. However,
+ # the input file has 28 packages, so there are 3^28 = 23 trillion
+ # possible groupings, which is obviously too much to brute-force.
+
+ # Instead, we find the candidates for group 1: subsets of the packages
+ # whose masses add up to `m_group', with the smallest number of items.
+
+ # Step 1: Find all combinations of packages in a certain range
+ k_min = int((m_group - 1) / max(masses)) + 1
+ k_max = int(len(masses) / num_groups)
+ combos = []
+ for k in range(k_min, k_max + 1):
+ combos += combinations(masses, k)
+ # Step 2: Keep only combinations whose masses add up to `m_group'
+ group1 = []
+ for c in combos:
+ if sum(c) == m_group:
+ group1.append(c)
+ # Step 3: Keep only candidates with the smallest package count
+ min_len = min(map(len, group1))
+ smallest = {}
+ for g1 in group1:
+ if len(g1) == min_len:
+ smallest[g1] = []
+
+ # Verify for each group 1 candidate that the remaining packages can be
+ # divided into 2 more groups of mass `m_group'. This takes time and it
+ # turns out that no candidates get rejected, hence it's commented out.
+ #for i, g1 in enumerate(smallest):
+ # remaining = []
+ # for m in masses:
+ # if m not in g1:
+ # remaining.append(m)
+ # k_max = int(len(remaining) / (num_groups - 1))
+ # combos = []
+ # for k in range(k_min, k_max + 1):
+ # combos += combinations(remaining, k)
+ # for c in combos:
+ # if sum(c) == m_group:
+ # smallest[g1].append(c)
+ #valid = {}
+ #for g1 in smallest.keys():
+ # if len(smallest[g1]) > 0:
+ # valid[g1] = smallest[g1]
+
+ # Calculate quantum entanglement of each group 1 candidate
+ qentangs = []
+ for g1 in smallest.keys():
+ qe = 1
+ for m in g1:
+ qe *= m
+ qentangs.append(qe)
+
+ return min(qentangs)
+
+
+
+def main():
+ # Read package weights from input text file
+ with open("input.txt", "r") as f:
+ masses = list(map(int, f.read().splitlines()))
+
+ print("Part 1 solution:", solve_partn(1, masses)) # 10439961859 for me
+ print("Part 2 solution:", solve_partn(2, masses)) # 72050269 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/24/test.py b/24/test.py
new file mode 100755
index 0000000..2c2655c
--- /dev/null
+++ b/24/test.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class Examples(unittest.TestCase):
+ def test_example1(self):
+ masses = [1, 2, 3, 4, 5, 7, 8, 9, 10, 11]
+ self.assertEqual(main.solve_partn(1, masses), 99)
+ self.assertEqual(main.solve_partn(2, masses), 44)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/25/input.txt b/25/input.txt
new file mode 100644
index 0000000..c5c38e6
--- /dev/null
+++ b/25/input.txt
@@ -0,0 +1 @@
+To continue, please consult the code grid in the manual. Enter the code at row 2947, column 3029.
diff --git a/25/main.py b/25/main.py
new file mode 100755
index 0000000..48ca1f7
--- /dev/null
+++ b/25/main.py
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+
+
+
+def coord_list(until):
+ return result
+
+
+
+def solve_part1(until):
+ # Generate a list of table (row, col) coordinates in the order
+ # that are filled by the algorithm described in the puzzle text.
+ coords = []
+ r = 1
+ c = 0
+ while (r, c) != until:
+ if r == 1:
+ r = c + 1
+ c = 1
+ else:
+ r -= 1
+ c += 1
+ coords.append((r, c))
+
+ code = 20151125 # first code is given
+ for i in range(1, len(coords)):
+ code *= 252533
+ code %= 33554393
+
+ return code
+
+
+
+def main():
+ # Read target coordinate from input text file
+ with open("input.txt", "r") as f:
+ words = f.read().split()
+ row = int(words[-3].rstrip(","))
+ col = int(words[-1].rstrip("."))
+ coord = (row, col)
+
+ print("Part 1 solution:", solve_part1(coord)) # 19980801 for me
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/25/test.py b/25/test.py
new file mode 100755
index 0000000..5b34e3e
--- /dev/null
+++ b/25/test.py
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+
+import unittest
+
+import main
+
+
+
+class ExamplesPart1(unittest.TestCase):
+ def test_example1(self):
+ until = (1, 6)
+ self.assertEqual(main.solve_part1(until), 33511524)
+
+
+
+if __name__ == "__main__":
+ unittest.main()