From fb1d521d74d1d9222da4a67d9b92a3a9fb5fa49c Mon Sep 17 00:00:00 2001 From: Juris Date: Fri, 13 Dec 2024 08:54:48 +0200 Subject: [PATCH 1/4] 2024-13 --- python/ReadMe.md | 6 +- python/requirements.txt | 5 + scala2/src/main/resources/2024/13-test-00.txt | 16 +- scala2/src/main/resources/2024/13.txt | 1280 ++++++++++++++++- .../jurisk/adventofcode/y2024/Advent13.scala | 119 +- .../scala/jurisk/optimization/Optimizer.scala | 1 + .../adventofcode/y2024/Advent13Spec.scala | 11 +- 7 files changed, 1407 insertions(+), 31 deletions(-) create mode 100644 python/requirements.txt diff --git a/python/ReadMe.md b/python/ReadMe.md index 172cbd16..4338c0e4 100644 --- a/python/ReadMe.md +++ b/python/ReadMe.md @@ -3,11 +3,7 @@ Note - Python 3 is required. ``` -pip install z3-solver -pip install more-itertools -pip install numpy -pip install sympy -pip install pytest +pip install -r requirements.txt python y20AB/dayXY.py python -m pytest ``` diff --git a/python/requirements.txt b/python/requirements.txt new file mode 100644 index 00000000..ba89cca3 --- /dev/null +++ b/python/requirements.txt @@ -0,0 +1,5 @@ +z3-solver +more-itertools +numpy +sympy +pytest \ No newline at end of file diff --git a/scala2/src/main/resources/2024/13-test-00.txt b/scala2/src/main/resources/2024/13-test-00.txt index a8b6c947..444a287e 100644 --- a/scala2/src/main/resources/2024/13-test-00.txt +++ b/scala2/src/main/resources/2024/13-test-00.txt @@ -1 +1,15 @@ -noop \ No newline at end of file +Button A: X+94, Y+34 +Button B: X+22, Y+67 +Prize: X=8400, Y=5400 + +Button A: X+26, Y+66 +Button B: X+67, Y+21 +Prize: X=12748, Y=12176 + +Button A: X+17, Y+86 +Button B: X+84, Y+37 +Prize: X=7870, Y=6450 + +Button A: X+69, Y+23 +Button B: X+27, Y+71 +Prize: X=18641, Y=10279 \ No newline at end of file diff --git a/scala2/src/main/resources/2024/13.txt b/scala2/src/main/resources/2024/13.txt index a8b6c947..42ed3dd1 100644 --- a/scala2/src/main/resources/2024/13.txt +++ b/scala2/src/main/resources/2024/13.txt @@ -1 +1,1279 @@ -noop \ No newline at end of file +Button A: X+63, Y+14 +Button B: X+12, Y+37 +Prize: X=5921, Y=10432 + +Button A: X+48, Y+23 +Button B: X+38, Y+74 +Prize: X=1820, Y=1430 + +Button A: X+17, Y+64 +Button B: X+72, Y+31 +Prize: X=1801, Y=2437 + +Button A: X+76, Y+21 +Button B: X+20, Y+53 +Prize: X=4836, Y=1811 + +Button A: X+53, Y+18 +Button B: X+30, Y+54 +Prize: X=692, Y=17702 + +Button A: X+99, Y+33 +Button B: X+58, Y+70 +Prize: X=10829, Y=7967 + +Button A: X+15, Y+42 +Button B: X+33, Y+13 +Prize: X=8585, Y=18017 + +Button A: X+17, Y+70 +Button B: X+50, Y+14 +Prize: X=14945, Y=12142 + +Button A: X+80, Y+99 +Button B: X+81, Y+31 +Prize: X=10257, Y=5977 + +Button A: X+62, Y+27 +Button B: X+11, Y+34 +Prize: X=13026, Y=6898 + +Button A: X+12, Y+36 +Button B: X+62, Y+19 +Prize: X=2540, Y=4934 + +Button A: X+13, Y+65 +Button B: X+73, Y+66 +Prize: X=5014, Y=7728 + +Button A: X+60, Y+21 +Button B: X+21, Y+43 +Prize: X=16754, Y=1966 + +Button A: X+89, Y+33 +Button B: X+11, Y+54 +Prize: X=3645, Y=2949 + +Button A: X+56, Y+20 +Button B: X+24, Y+48 +Prize: X=1264, Y=11536 + +Button A: X+68, Y+21 +Button B: X+15, Y+42 +Prize: X=17409, Y=746 + +Button A: X+23, Y+66 +Button B: X+75, Y+32 +Prize: X=11223, Y=3354 + +Button A: X+19, Y+58 +Button B: X+47, Y+18 +Prize: X=6869, Y=7398 + +Button A: X+28, Y+47 +Button B: X+46, Y+18 +Prize: X=12204, Y=3852 + +Button A: X+25, Y+57 +Button B: X+43, Y+11 +Prize: X=3309, Y=15501 + +Button A: X+95, Y+55 +Button B: X+32, Y+79 +Prize: X=5845, Y=6710 + +Button A: X+18, Y+12 +Button B: X+43, Y+93 +Prize: X=2658, Y=4860 + +Button A: X+88, Y+12 +Button B: X+86, Y+83 +Prize: X=3684, Y=930 + +Button A: X+51, Y+18 +Button B: X+44, Y+75 +Prize: X=19780, Y=17984 + +Button A: X+59, Y+30 +Button B: X+19, Y+55 +Prize: X=13898, Y=19160 + +Button A: X+12, Y+51 +Button B: X+49, Y+15 +Prize: X=15071, Y=17144 + +Button A: X+22, Y+46 +Button B: X+70, Y+45 +Prize: X=10238, Y=3734 + +Button A: X+24, Y+47 +Button B: X+84, Y+35 +Prize: X=6444, Y=3684 + +Button A: X+32, Y+15 +Button B: X+24, Y+49 +Prize: X=18208, Y=2148 + +Button A: X+11, Y+67 +Button B: X+51, Y+11 +Prize: X=10173, Y=645 + +Button A: X+57, Y+28 +Button B: X+22, Y+41 +Prize: X=9124, Y=4647 + +Button A: X+13, Y+58 +Button B: X+51, Y+13 +Prize: X=9979, Y=12131 + +Button A: X+27, Y+95 +Button B: X+94, Y+60 +Prize: X=2096, Y=1960 + +Button A: X+43, Y+11 +Button B: X+36, Y+75 +Prize: X=16958, Y=1706 + +Button A: X+54, Y+35 +Button B: X+12, Y+85 +Prize: X=5958, Y=9190 + +Button A: X+40, Y+11 +Button B: X+19, Y+49 +Prize: X=12563, Y=11351 + +Button A: X+12, Y+65 +Button B: X+62, Y+24 +Prize: X=14148, Y=17166 + +Button A: X+77, Y+84 +Button B: X+82, Y+17 +Prize: X=9019, Y=4115 + +Button A: X+20, Y+11 +Button B: X+25, Y+56 +Prize: X=5480, Y=6240 + +Button A: X+14, Y+62 +Button B: X+40, Y+13 +Prize: X=3306, Y=1887 + +Button A: X+19, Y+49 +Button B: X+34, Y+13 +Prize: X=3547, Y=5488 + +Button A: X+85, Y+19 +Button B: X+14, Y+89 +Prize: X=5778, Y=2322 + +Button A: X+57, Y+82 +Button B: X+67, Y+31 +Prize: X=8692, Y=6358 + +Button A: X+76, Y+11 +Button B: X+20, Y+76 +Prize: X=16664, Y=5282 + +Button A: X+73, Y+14 +Button B: X+25, Y+29 +Prize: X=3114, Y=2340 + +Button A: X+91, Y+60 +Button B: X+12, Y+27 +Prize: X=2905, Y=3786 + +Button A: X+54, Y+12 +Button B: X+14, Y+80 +Prize: X=11888, Y=6320 + +Button A: X+51, Y+25 +Button B: X+49, Y+98 +Prize: X=6132, Y=10108 + +Button A: X+16, Y+67 +Button B: X+43, Y+15 +Prize: X=11996, Y=4341 + +Button A: X+33, Y+66 +Button B: X+36, Y+11 +Prize: X=16502, Y=13454 + +Button A: X+32, Y+42 +Button B: X+96, Y+37 +Prize: X=4960, Y=4196 + +Button A: X+50, Y+27 +Button B: X+13, Y+35 +Prize: X=5495, Y=3285 + +Button A: X+12, Y+39 +Button B: X+42, Y+13 +Prize: X=10070, Y=471 + +Button A: X+74, Y+52 +Button B: X+13, Y+36 +Prize: X=3061, Y=7244 + +Button A: X+41, Y+72 +Button B: X+43, Y+14 +Prize: X=7933, Y=19074 + +Button A: X+29, Y+58 +Button B: X+60, Y+17 +Prize: X=6839, Y=3481 + +Button A: X+62, Y+33 +Button B: X+16, Y+55 +Prize: X=18434, Y=17183 + +Button A: X+56, Y+57 +Button B: X+19, Y+78 +Prize: X=3999, Y=8118 + +Button A: X+13, Y+78 +Button B: X+93, Y+27 +Prize: X=2304, Y=2673 + +Button A: X+50, Y+20 +Button B: X+12, Y+43 +Prize: X=1330, Y=2930 + +Button A: X+40, Y+98 +Button B: X+46, Y+28 +Prize: X=4532, Y=7546 + +Button A: X+11, Y+16 +Button B: X+32, Y+15 +Prize: X=1004, Y=18942 + +Button A: X+40, Y+14 +Button B: X+31, Y+54 +Prize: X=12706, Y=17506 + +Button A: X+20, Y+37 +Button B: X+55, Y+19 +Prize: X=15140, Y=6215 + +Button A: X+95, Y+39 +Button B: X+13, Y+64 +Prize: X=5747, Y=7287 + +Button A: X+35, Y+68 +Button B: X+48, Y+20 +Prize: X=17403, Y=2672 + +Button A: X+14, Y+35 +Button B: X+60, Y+37 +Prize: X=6716, Y=5474 + +Button A: X+21, Y+44 +Button B: X+26, Y+11 +Prize: X=13336, Y=6502 + +Button A: X+12, Y+73 +Button B: X+49, Y+13 +Prize: X=8933, Y=11056 + +Button A: X+56, Y+79 +Button B: X+96, Y+42 +Prize: X=5384, Y=3391 + +Button A: X+73, Y+31 +Button B: X+19, Y+62 +Prize: X=10634, Y=16142 + +Button A: X+41, Y+64 +Button B: X+67, Y+26 +Prize: X=8227, Y=7184 + +Button A: X+23, Y+42 +Button B: X+29, Y+12 +Prize: X=15139, Y=4604 + +Button A: X+56, Y+30 +Button B: X+23, Y+49 +Prize: X=5897, Y=14529 + +Button A: X+15, Y+24 +Button B: X+45, Y+19 +Prize: X=4595, Y=5858 + +Button A: X+15, Y+44 +Button B: X+71, Y+46 +Prize: X=5683, Y=9788 + +Button A: X+30, Y+62 +Button B: X+47, Y+30 +Prize: X=1449, Y=1182 + +Button A: X+22, Y+50 +Button B: X+61, Y+23 +Prize: X=13134, Y=8314 + +Button A: X+14, Y+55 +Button B: X+71, Y+25 +Prize: X=1105, Y=12165 + +Button A: X+16, Y+38 +Button B: X+47, Y+27 +Prize: X=14274, Y=14866 + +Button A: X+72, Y+13 +Button B: X+18, Y+62 +Prize: X=16262, Y=5148 + +Button A: X+30, Y+72 +Button B: X+38, Y+15 +Prize: X=3466, Y=3594 + +Button A: X+27, Y+64 +Button B: X+62, Y+26 +Prize: X=8459, Y=16958 + +Button A: X+18, Y+50 +Button B: X+66, Y+35 +Prize: X=17492, Y=13125 + +Button A: X+94, Y+16 +Button B: X+98, Y+86 +Prize: X=5924, Y=2672 + +Button A: X+51, Y+12 +Button B: X+14, Y+26 +Prize: X=368, Y=4586 + +Button A: X+73, Y+24 +Button B: X+47, Y+99 +Prize: X=6932, Y=4869 + +Button A: X+41, Y+18 +Button B: X+12, Y+52 +Prize: X=3183, Y=15770 + +Button A: X+31, Y+14 +Button B: X+42, Y+73 +Prize: X=1830, Y=1745 + +Button A: X+56, Y+20 +Button B: X+18, Y+62 +Prize: X=16986, Y=15838 + +Button A: X+16, Y+35 +Button B: X+33, Y+23 +Prize: X=17558, Y=5165 + +Button A: X+84, Y+57 +Button B: X+11, Y+31 +Prize: X=8262, Y=3847 + +Button A: X+85, Y+12 +Button B: X+67, Y+81 +Prize: X=8788, Y=6177 + +Button A: X+46, Y+11 +Button B: X+31, Y+68 +Prize: X=18210, Y=8428 + +Button A: X+50, Y+12 +Button B: X+18, Y+76 +Prize: X=11006, Y=10004 + +Button A: X+14, Y+94 +Button B: X+59, Y+73 +Prize: X=1505, Y=3319 + +Button A: X+16, Y+25 +Button B: X+60, Y+14 +Prize: X=5384, Y=2511 + +Button A: X+40, Y+54 +Button B: X+34, Y+13 +Prize: X=420, Y=8904 + +Button A: X+39, Y+65 +Button B: X+40, Y+21 +Prize: X=14227, Y=9457 + +Button A: X+59, Y+56 +Button B: X+88, Y+15 +Prize: X=4145, Y=1947 + +Button A: X+14, Y+54 +Button B: X+66, Y+23 +Prize: X=11230, Y=7239 + +Button A: X+18, Y+53 +Button B: X+56, Y+19 +Prize: X=13534, Y=15652 + +Button A: X+46, Y+18 +Button B: X+16, Y+49 +Prize: X=13450, Y=1538 + +Button A: X+11, Y+49 +Button B: X+57, Y+20 +Prize: X=14617, Y=17561 + +Button A: X+20, Y+62 +Button B: X+91, Y+79 +Prize: X=4726, Y=5308 + +Button A: X+60, Y+39 +Button B: X+24, Y+49 +Prize: X=3008, Y=4805 + +Button A: X+72, Y+25 +Button B: X+17, Y+65 +Prize: X=2880, Y=12150 + +Button A: X+38, Y+84 +Button B: X+91, Y+29 +Prize: X=2180, Y=3786 + +Button A: X+29, Y+91 +Button B: X+30, Y+21 +Prize: X=1955, Y=5257 + +Button A: X+14, Y+25 +Button B: X+29, Y+17 +Prize: X=18496, Y=14119 + +Button A: X+16, Y+63 +Button B: X+75, Y+14 +Prize: X=3515, Y=12576 + +Button A: X+23, Y+11 +Button B: X+36, Y+58 +Prize: X=15711, Y=15311 + +Button A: X+75, Y+29 +Button B: X+12, Y+83 +Prize: X=3159, Y=5688 + +Button A: X+40, Y+26 +Button B: X+12, Y+32 +Prize: X=284, Y=12368 + +Button A: X+15, Y+60 +Button B: X+74, Y+12 +Prize: X=16442, Y=14336 + +Button A: X+89, Y+14 +Button B: X+69, Y+56 +Prize: X=7113, Y=4550 + +Button A: X+74, Y+20 +Button B: X+11, Y+51 +Prize: X=1498, Y=17164 + +Button A: X+55, Y+14 +Button B: X+22, Y+57 +Prize: X=14202, Y=3972 + +Button A: X+53, Y+16 +Button B: X+28, Y+69 +Prize: X=10197, Y=4823 + +Button A: X+11, Y+69 +Button B: X+74, Y+48 +Prize: X=4182, Y=6672 + +Button A: X+12, Y+28 +Button B: X+34, Y+19 +Prize: X=8522, Y=9575 + +Button A: X+35, Y+12 +Button B: X+24, Y+63 +Prize: X=15331, Y=5276 + +Button A: X+71, Y+28 +Button B: X+19, Y+39 +Prize: X=6737, Y=3350 + +Button A: X+43, Y+81 +Button B: X+46, Y+14 +Prize: X=8639, Y=15389 + +Button A: X+26, Y+35 +Button B: X+54, Y+21 +Prize: X=4168, Y=3388 + +Button A: X+58, Y+14 +Button B: X+23, Y+73 +Prize: X=16511, Y=10001 + +Button A: X+71, Y+16 +Button B: X+39, Y+98 +Prize: X=6042, Y=7874 + +Button A: X+69, Y+13 +Button B: X+12, Y+71 +Prize: X=14381, Y=9962 + +Button A: X+45, Y+20 +Button B: X+25, Y+64 +Prize: X=9955, Y=19248 + +Button A: X+20, Y+78 +Button B: X+57, Y+12 +Prize: X=886, Y=18314 + +Button A: X+31, Y+58 +Button B: X+59, Y+30 +Prize: X=6111, Y=18934 + +Button A: X+72, Y+49 +Button B: X+11, Y+31 +Prize: X=4216, Y=10597 + +Button A: X+79, Y+19 +Button B: X+17, Y+77 +Prize: X=7349, Y=3809 + +Button A: X+56, Y+21 +Button B: X+18, Y+92 +Prize: X=5464, Y=7505 + +Button A: X+98, Y+16 +Button B: X+48, Y+65 +Prize: X=10860, Y=5603 + +Button A: X+95, Y+15 +Button B: X+37, Y+36 +Prize: X=2821, Y=1893 + +Button A: X+12, Y+93 +Button B: X+64, Y+24 +Prize: X=3816, Y=4086 + +Button A: X+74, Y+26 +Button B: X+14, Y+46 +Prize: X=1548, Y=16972 + +Button A: X+20, Y+42 +Button B: X+39, Y+26 +Prize: X=8419, Y=3192 + +Button A: X+26, Y+62 +Button B: X+62, Y+31 +Prize: X=10956, Y=8392 + +Button A: X+25, Y+64 +Button B: X+35, Y+13 +Prize: X=930, Y=3332 + +Button A: X+17, Y+60 +Button B: X+58, Y+41 +Prize: X=2898, Y=6463 + +Button A: X+58, Y+86 +Button B: X+82, Y+36 +Prize: X=10222, Y=9936 + +Button A: X+29, Y+85 +Button B: X+95, Y+83 +Prize: X=9017, Y=8057 + +Button A: X+19, Y+67 +Button B: X+58, Y+18 +Prize: X=6909, Y=9797 + +Button A: X+35, Y+16 +Button B: X+41, Y+66 +Prize: X=6650, Y=13838 + +Button A: X+41, Y+12 +Button B: X+27, Y+70 +Prize: X=8502, Y=17916 + +Button A: X+42, Y+22 +Button B: X+31, Y+50 +Prize: X=9598, Y=1236 + +Button A: X+69, Y+21 +Button B: X+20, Y+69 +Prize: X=15843, Y=12128 + +Button A: X+21, Y+64 +Button B: X+44, Y+11 +Prize: X=9320, Y=445 + +Button A: X+52, Y+28 +Button B: X+37, Y+63 +Prize: X=13057, Y=9363 + +Button A: X+18, Y+61 +Button B: X+56, Y+21 +Prize: X=980, Y=10681 + +Button A: X+18, Y+30 +Button B: X+49, Y+25 +Prize: X=9663, Y=9675 + +Button A: X+60, Y+23 +Button B: X+25, Y+67 +Prize: X=19355, Y=7228 + +Button A: X+89, Y+13 +Button B: X+50, Y+69 +Prize: X=5953, Y=5250 + +Button A: X+34, Y+14 +Button B: X+26, Y+62 +Prize: X=15374, Y=9738 + +Button A: X+16, Y+85 +Button B: X+81, Y+46 +Prize: X=4306, Y=9809 + +Button A: X+19, Y+59 +Button B: X+31, Y+24 +Prize: X=2311, Y=4358 + +Button A: X+48, Y+70 +Button B: X+26, Y+12 +Prize: X=18182, Y=2026 + +Button A: X+53, Y+20 +Button B: X+56, Y+72 +Prize: X=6993, Y=5284 + +Button A: X+25, Y+80 +Button B: X+86, Y+22 +Prize: X=2657, Y=5464 + +Button A: X+16, Y+31 +Button B: X+83, Y+52 +Prize: X=4281, Y=4486 + +Button A: X+34, Y+25 +Button B: X+21, Y+56 +Prize: X=3677, Y=3961 + +Button A: X+23, Y+14 +Button B: X+22, Y+51 +Prize: X=5099, Y=14177 + +Button A: X+21, Y+21 +Button B: X+79, Y+12 +Prize: X=9164, Y=2799 + +Button A: X+88, Y+49 +Button B: X+29, Y+87 +Prize: X=9884, Y=12022 + +Button A: X+19, Y+79 +Button B: X+86, Y+45 +Prize: X=5530, Y=4551 + +Button A: X+95, Y+87 +Button B: X+82, Y+15 +Prize: X=5539, Y=3450 + +Button A: X+47, Y+13 +Button B: X+24, Y+42 +Prize: X=8476, Y=14658 + +Button A: X+40, Y+25 +Button B: X+16, Y+31 +Prize: X=2408, Y=14978 + +Button A: X+31, Y+83 +Button B: X+66, Y+14 +Prize: X=12489, Y=19145 + +Button A: X+21, Y+51 +Button B: X+43, Y+17 +Prize: X=19484, Y=11600 + +Button A: X+58, Y+22 +Button B: X+15, Y+52 +Prize: X=6102, Y=13788 + +Button A: X+62, Y+17 +Button B: X+24, Y+53 +Prize: X=16042, Y=5798 + +Button A: X+37, Y+21 +Button B: X+29, Y+54 +Prize: X=15497, Y=4808 + +Button A: X+22, Y+59 +Button B: X+99, Y+23 +Prize: X=5665, Y=5250 + +Button A: X+83, Y+14 +Button B: X+30, Y+64 +Prize: X=8493, Y=4910 + +Button A: X+28, Y+75 +Button B: X+68, Y+20 +Prize: X=5088, Y=2585 + +Button A: X+14, Y+39 +Button B: X+35, Y+22 +Prize: X=11491, Y=11926 + +Button A: X+52, Y+56 +Button B: X+64, Y+18 +Prize: X=6580, Y=4540 + +Button A: X+98, Y+11 +Button B: X+57, Y+76 +Prize: X=6791, Y=5704 + +Button A: X+88, Y+45 +Button B: X+40, Y+75 +Prize: X=8928, Y=8220 + +Button A: X+13, Y+33 +Button B: X+75, Y+50 +Prize: X=9076, Y=3791 + +Button A: X+75, Y+11 +Button B: X+13, Y+37 +Prize: X=7105, Y=1393 + +Button A: X+63, Y+43 +Button B: X+12, Y+35 +Prize: X=16619, Y=2191 + +Button A: X+59, Y+82 +Button B: X+85, Y+37 +Prize: X=3968, Y=3811 + +Button A: X+36, Y+91 +Button B: X+88, Y+48 +Prize: X=5932, Y=6447 + +Button A: X+24, Y+96 +Button B: X+83, Y+11 +Prize: X=9770, Y=8906 + +Button A: X+40, Y+13 +Button B: X+18, Y+27 +Prize: X=4810, Y=19642 + +Button A: X+25, Y+29 +Button B: X+69, Y+21 +Prize: X=7111, Y=4175 + +Button A: X+32, Y+60 +Button B: X+21, Y+12 +Prize: X=6652, Y=18176 + +Button A: X+71, Y+34 +Button B: X+15, Y+47 +Prize: X=18074, Y=14851 + +Button A: X+24, Y+55 +Button B: X+23, Y+13 +Prize: X=946, Y=8379 + +Button A: X+61, Y+25 +Button B: X+17, Y+39 +Prize: X=10946, Y=8580 + +Button A: X+14, Y+34 +Button B: X+66, Y+50 +Prize: X=16302, Y=602 + +Button A: X+36, Y+80 +Button B: X+67, Y+45 +Prize: X=7425, Y=6215 + +Button A: X+55, Y+88 +Button B: X+97, Y+24 +Prize: X=4421, Y=3400 + +Button A: X+43, Y+13 +Button B: X+22, Y+72 +Prize: X=17442, Y=4012 + +Button A: X+26, Y+57 +Button B: X+87, Y+42 +Prize: X=8063, Y=4737 + +Button A: X+63, Y+23 +Button B: X+45, Y+86 +Prize: X=6489, Y=7726 + +Button A: X+67, Y+73 +Button B: X+17, Y+82 +Prize: X=3866, Y=7640 + +Button A: X+34, Y+15 +Button B: X+22, Y+49 +Prize: X=4358, Y=2069 + +Button A: X+37, Y+18 +Button B: X+44, Y+78 +Prize: X=3863, Y=2898 + +Button A: X+25, Y+60 +Button B: X+57, Y+23 +Prize: X=15259, Y=12516 + +Button A: X+11, Y+32 +Button B: X+74, Y+30 +Prize: X=467, Y=3686 + +Button A: X+25, Y+86 +Button B: X+67, Y+63 +Prize: X=6940, Y=7963 + +Button A: X+11, Y+80 +Button B: X+75, Y+73 +Prize: X=6885, Y=10859 + +Button A: X+31, Y+50 +Button B: X+96, Y+31 +Prize: X=573, Y=305 + +Button A: X+53, Y+16 +Button B: X+37, Y+77 +Prize: X=15101, Y=16829 + +Button A: X+18, Y+78 +Button B: X+61, Y+12 +Prize: X=9178, Y=7268 + +Button A: X+43, Y+41 +Button B: X+11, Y+53 +Prize: X=3777, Y=6067 + +Button A: X+85, Y+28 +Button B: X+13, Y+44 +Prize: X=1662, Y=3288 + +Button A: X+27, Y+82 +Button B: X+65, Y+12 +Prize: X=6448, Y=14048 + +Button A: X+14, Y+92 +Button B: X+75, Y+84 +Prize: X=2876, Y=5816 + +Button A: X+14, Y+27 +Button B: X+52, Y+20 +Prize: X=13444, Y=17042 + +Button A: X+84, Y+25 +Button B: X+11, Y+60 +Prize: X=11642, Y=4160 + +Button A: X+45, Y+25 +Button B: X+33, Y+59 +Prize: X=9683, Y=10449 + +Button A: X+16, Y+61 +Button B: X+83, Y+50 +Prize: X=2160, Y=3972 + +Button A: X+64, Y+25 +Button B: X+48, Y+61 +Prize: X=5808, Y=6198 + +Button A: X+51, Y+55 +Button B: X+64, Y+16 +Prize: X=8113, Y=5197 + +Button A: X+36, Y+14 +Button B: X+16, Y+25 +Prize: X=12716, Y=12076 + +Button A: X+71, Y+21 +Button B: X+13, Y+70 +Prize: X=1999, Y=389 + +Button A: X+75, Y+14 +Button B: X+15, Y+83 +Prize: X=6365, Y=12329 + +Button A: X+59, Y+13 +Button B: X+23, Y+62 +Prize: X=11799, Y=8700 + +Button A: X+63, Y+12 +Button B: X+28, Y+84 +Prize: X=3756, Y=18080 + +Button A: X+83, Y+97 +Button B: X+93, Y+14 +Prize: X=6937, Y=4509 + +Button A: X+91, Y+52 +Button B: X+23, Y+54 +Prize: X=2740, Y=3200 + +Button A: X+98, Y+24 +Button B: X+12, Y+50 +Prize: X=2410, Y=3508 + +Button A: X+12, Y+48 +Button B: X+71, Y+56 +Prize: X=7260, Y=7152 + +Button A: X+81, Y+61 +Button B: X+12, Y+63 +Prize: X=5145, Y=6249 + +Button A: X+16, Y+53 +Button B: X+96, Y+21 +Prize: X=8048, Y=6463 + +Button A: X+33, Y+14 +Button B: X+15, Y+52 +Prize: X=14369, Y=9140 + +Button A: X+19, Y+59 +Button B: X+30, Y+11 +Prize: X=13981, Y=18382 + +Button A: X+15, Y+41 +Button B: X+62, Y+26 +Prize: X=918, Y=12146 + +Button A: X+60, Y+16 +Button B: X+17, Y+77 +Prize: X=16721, Y=8161 + +Button A: X+65, Y+30 +Button B: X+24, Y+49 +Prize: X=18662, Y=13672 + +Button A: X+19, Y+72 +Button B: X+72, Y+14 +Prize: X=8892, Y=19434 + +Button A: X+53, Y+26 +Button B: X+22, Y+57 +Prize: X=14006, Y=9613 + +Button A: X+64, Y+14 +Button B: X+14, Y+69 +Prize: X=6870, Y=9595 + +Button A: X+33, Y+12 +Button B: X+22, Y+64 +Prize: X=4763, Y=6884 + +Button A: X+11, Y+34 +Button B: X+66, Y+45 +Prize: X=1376, Y=6380 + +Button A: X+11, Y+49 +Button B: X+80, Y+18 +Prize: X=17079, Y=1417 + +Button A: X+88, Y+36 +Button B: X+17, Y+58 +Prize: X=6940, Y=3860 + +Button A: X+56, Y+18 +Button B: X+16, Y+71 +Prize: X=2160, Y=3872 + +Button A: X+73, Y+13 +Button B: X+19, Y+74 +Prize: X=5721, Y=13001 + +Button A: X+26, Y+62 +Button B: X+59, Y+14 +Prize: X=13427, Y=19772 + +Button A: X+62, Y+11 +Button B: X+20, Y+78 +Prize: X=9330, Y=17897 + +Button A: X+84, Y+23 +Button B: X+14, Y+73 +Prize: X=5254, Y=16308 + +Button A: X+35, Y+12 +Button B: X+20, Y+30 +Prize: X=10910, Y=1196 + +Button A: X+56, Y+25 +Button B: X+36, Y+72 +Prize: X=6600, Y=7980 + +Button A: X+18, Y+53 +Button B: X+71, Y+24 +Prize: X=15881, Y=16234 + +Button A: X+24, Y+81 +Button B: X+32, Y+21 +Prize: X=1792, Y=1698 + +Button A: X+57, Y+29 +Button B: X+22, Y+57 +Prize: X=3599, Y=12132 + +Button A: X+45, Y+22 +Button B: X+15, Y+43 +Prize: X=7250, Y=16657 + +Button A: X+11, Y+32 +Button B: X+76, Y+55 +Prize: X=6277, Y=8566 + +Button A: X+11, Y+38 +Button B: X+31, Y+20 +Prize: X=8663, Y=5068 + +Button A: X+13, Y+44 +Button B: X+79, Y+35 +Prize: X=16161, Y=12028 + +Button A: X+14, Y+86 +Button B: X+30, Y+33 +Prize: X=608, Y=1163 + +Button A: X+27, Y+18 +Button B: X+39, Y+96 +Prize: X=4887, Y=9558 + +Button A: X+29, Y+92 +Button B: X+96, Y+19 +Prize: X=9073, Y=2227 + +Button A: X+26, Y+69 +Button B: X+65, Y+14 +Prize: X=15993, Y=6605 + +Button A: X+44, Y+11 +Button B: X+14, Y+42 +Prize: X=8850, Y=15218 + +Button A: X+29, Y+55 +Button B: X+70, Y+39 +Prize: X=6877, Y=3948 + +Button A: X+14, Y+44 +Button B: X+66, Y+24 +Prize: X=11860, Y=13528 + +Button A: X+40, Y+18 +Button B: X+44, Y+73 +Prize: X=18372, Y=11267 + +Button A: X+75, Y+79 +Button B: X+12, Y+73 +Prize: X=6678, Y=8181 + +Button A: X+60, Y+92 +Button B: X+76, Y+28 +Prize: X=8196, Y=6724 + +Button A: X+11, Y+52 +Button B: X+47, Y+16 +Prize: X=19098, Y=9736 + +Button A: X+67, Y+70 +Button B: X+18, Y+83 +Prize: X=6057, Y=8575 + +Button A: X+82, Y+26 +Button B: X+14, Y+61 +Prize: X=18224, Y=11876 + +Button A: X+19, Y+60 +Button B: X+75, Y+32 +Prize: X=18161, Y=5480 + +Button A: X+43, Y+69 +Button B: X+52, Y+25 +Prize: X=17476, Y=4106 + +Button A: X+27, Y+95 +Button B: X+34, Y+32 +Prize: X=2126, Y=2836 + +Button A: X+40, Y+39 +Button B: X+14, Y+96 +Prize: X=1892, Y=6621 + +Button A: X+20, Y+71 +Button B: X+80, Y+40 +Prize: X=8920, Y=10438 + +Button A: X+40, Y+57 +Button B: X+33, Y+11 +Prize: X=6314, Y=568 + +Button A: X+99, Y+91 +Button B: X+15, Y+91 +Prize: X=4014, Y=11102 + +Button A: X+16, Y+22 +Button B: X+27, Y+12 +Prize: X=6729, Y=918 + +Button A: X+11, Y+67 +Button B: X+65, Y+16 +Prize: X=5652, Y=11917 + +Button A: X+14, Y+49 +Button B: X+81, Y+42 +Prize: X=17396, Y=17189 + +Button A: X+12, Y+31 +Button B: X+75, Y+53 +Prize: X=887, Y=4840 + +Button A: X+25, Y+63 +Button B: X+62, Y+15 +Prize: X=8820, Y=18605 + +Button A: X+43, Y+12 +Button B: X+18, Y+55 +Prize: X=10077, Y=19114 + +Button A: X+16, Y+27 +Button B: X+38, Y+12 +Prize: X=14666, Y=6686 + +Button A: X+65, Y+12 +Button B: X+30, Y+81 +Prize: X=5825, Y=10691 + +Button A: X+23, Y+65 +Button B: X+46, Y+14 +Prize: X=19098, Y=1810 + +Button A: X+32, Y+61 +Button B: X+55, Y+18 +Prize: X=9869, Y=745 + +Button A: X+19, Y+42 +Button B: X+97, Y+19 +Prize: X=5478, Y=4097 + +Button A: X+79, Y+25 +Button B: X+12, Y+63 +Prize: X=4014, Y=8007 + +Button A: X+24, Y+55 +Button B: X+57, Y+39 +Prize: X=2778, Y=3251 + +Button A: X+60, Y+14 +Button B: X+29, Y+77 +Prize: X=4471, Y=18743 + +Button A: X+44, Y+71 +Button B: X+45, Y+23 +Prize: X=4764, Y=16962 + +Button A: X+69, Y+44 +Button B: X+14, Y+37 +Prize: X=18647, Y=8826 + +Button A: X+15, Y+70 +Button B: X+58, Y+17 +Prize: X=2737, Y=10708 + +Button A: X+25, Y+48 +Button B: X+42, Y+20 +Prize: X=3308, Y=4896 + +Button A: X+16, Y+43 +Button B: X+70, Y+28 +Prize: X=10848, Y=15996 + +Button A: X+68, Y+74 +Button B: X+12, Y+68 +Prize: X=2632, Y=3084 + +Button A: X+67, Y+87 +Button B: X+91, Y+26 +Prize: X=6156, Y=5966 + +Button A: X+43, Y+88 +Button B: X+99, Y+11 +Prize: X=6953, Y=6182 + +Button A: X+66, Y+21 +Button B: X+17, Y+66 +Prize: X=6466, Y=5814 + +Button A: X+52, Y+24 +Button B: X+21, Y+43 +Prize: X=8244, Y=16996 + +Button A: X+46, Y+82 +Button B: X+45, Y+13 +Prize: X=9831, Y=14647 + +Button A: X+17, Y+30 +Button B: X+49, Y+23 +Prize: X=3553, Y=1954 + +Button A: X+56, Y+23 +Button B: X+16, Y+21 +Prize: X=5072, Y=2819 + +Button A: X+22, Y+98 +Button B: X+70, Y+75 +Prize: X=6382, Y=8773 + +Button A: X+32, Y+93 +Button B: X+68, Y+43 +Prize: X=7404, Y=6210 + +Button A: X+29, Y+75 +Button B: X+94, Y+38 +Prize: X=4690, Y=5566 + +Button A: X+54, Y+20 +Button B: X+39, Y+73 +Prize: X=16733, Y=11735 + +Button A: X+30, Y+15 +Button B: X+22, Y+35 +Prize: X=2952, Y=1980 + +Button A: X+20, Y+78 +Button B: X+86, Y+43 +Prize: X=7874, Y=4685 + +Button A: X+95, Y+22 +Button B: X+77, Y+92 +Prize: X=10628, Y=7208 + +Button A: X+77, Y+13 +Button B: X+36, Y+69 +Prize: X=2662, Y=3218 + +Button A: X+18, Y+49 +Button B: X+38, Y+22 +Prize: X=2140, Y=10210 + +Button A: X+22, Y+64 +Button B: X+68, Y+24 +Prize: X=14384, Y=1912 + +Button A: X+12, Y+34 +Button B: X+72, Y+37 +Prize: X=8864, Y=18895 + +Button A: X+50, Y+69 +Button B: X+67, Y+12 +Prize: X=7166, Y=2004 + +Button A: X+47, Y+31 +Button B: X+13, Y+75 +Prize: X=2386, Y=2238 + +Button A: X+24, Y+67 +Button B: X+57, Y+18 +Prize: X=17177, Y=13222 + +Button A: X+50, Y+22 +Button B: X+21, Y+38 +Prize: X=11997, Y=16114 + +Button A: X+61, Y+25 +Button B: X+29, Y+65 +Prize: X=845, Y=665 \ No newline at end of file diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala index 7628a042..25240816 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala @@ -1,37 +1,118 @@ package jurisk.adventofcode.y2024 +import cats.implicits.catsSyntaxOptionId +import jurisk.geometry.Coordinates2D +import jurisk.optimization.ImplicitConversions.{ + RichArithExprIntSort, + RichExpr, + RichLong, +} +import jurisk.optimization.Optimizer import jurisk.utils.FileInput._ import jurisk.utils.Parsing.StringOps +import scala.annotation.nowarn +import scala.util.Try + object Advent13 { - type Input = List[Command] + type Input = List[Machine] type N = Long + type C = Coordinates2D[Long] + + final case class Machine( + buttonA: C, + buttonB: C, + prize: C, + ) { + def solve: Option[N] = { + val results = for { + a <- 0L to 100 + b <- 0L to 100 + result = buttonA * a + buttonB * b + if result == prize + } yield 3 * a + b + + val result = results.minOption + println(s"$this $results $result") + result + } + + def solve2: Option[N] = Try { + solve2Q + }.toOption.flatten + + def solve2Q: Option[N] = { + println(s"Trying to solve $this") + // a * ax + b * bx = px + // a * ay + b * by = py + + implicit val optimizer: Optimizer = Optimizer.z3() + import optimizer._ + + val List(a, b, cost) = List("a", "b", "cost").map(labeledInt) + val (ax, bx, px) = + (buttonA.x.constant, buttonB.x.constant, prize.x.constant) + val (ay, by, py) = + (buttonA.y.constant, buttonB.y.constant, prize.y.constant) + + addConstraints( + a >= Zero, + b >= Zero, + a * ax + b * bx === px, + a * ay + b * by === py, + cost === 3.constant * a + b, + ) - sealed trait Command extends Product with Serializable - object Command { - case object Noop extends Command - final case class Something( - values: List[N] - ) extends Command - final case class Other(value: String) extends Command - - def parse(s: String): Command = - s match { - case "noop" => Noop - case s"something $rem" => Something(rem.extractLongList) - case s if s.nonEmpty => Other(s) - case _ => s.failedToParse + val _ = minimize(cost) + + @nowarn("cat=deprecation") + val m = checkAndGetModel() + debugPrint() + + val ar = m.getConstInterp(a).toString + val br = m.getConstInterp(b).toString + + (ar.toLong * 3 + br.toLong).some + +// println(m.toString) +// m.evaluate(cost, true).getString.toLong.some +// val List(ar, br) = runExternal("a", "b").map(resultToLong) +// val result = 3 * ar + br +// result.some + } + } + + object Machine { + def parse(s: String): Machine = { + def parseCoords(s: String, symbol: String): C = { + val Pattern = s"""X$symbol(\\d+), Y$symbol(\\d+)""".r + s match { + case Pattern(x, y) => Coordinates2D(x.toLong, y.toLong) + case _ => s.fail + } } + + val List(as, bs, ps) = s.split("\n").toList + val a = as.removePrefixUnsafe("Button A: ") + val b = bs.removePrefixUnsafe("Button B: ") + val p = ps.removePrefixUnsafe("Prize: ") + Machine(parseCoords(a, "\\+"), parseCoords(b, "\\+"), parseCoords(p, "=")) + } } def parse(input: String): Input = - input.parseLines(Command.parse) + input.parseSections(Machine.parse) def part1(data: Input): N = - 0 + data.flatMap(_.solve).sum - def part2(data: Input): N = - 0 + def part2(data: Input): N = { + val adjusted = data.map(m => + m.copy(prize = m.prize + Coordinates2D(10000000000000L, 10000000000000L)) + ) + + adjusted.flatMap(_.solve2).sum + } def parseFile(fileName: String): Input = parse(readFileText(fileName)) diff --git a/scala2/src/main/scala/jurisk/optimization/Optimizer.scala b/scala2/src/main/scala/jurisk/optimization/Optimizer.scala index 8ea3dee7..e6f81c87 100644 --- a/scala2/src/main/scala/jurisk/optimization/Optimizer.scala +++ b/scala2/src/main/scala/jurisk/optimization/Optimizer.scala @@ -43,6 +43,7 @@ trait Optimizer { def addConstraints(expressions: Expr[BoolSort]*): Unit + // Note - We were using `getConstInterp` to get the results from this @deprecated("Use `runExternal` instead", "2023-12-24") def checkAndGetModel(): Model diff --git a/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala b/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala index 27530555..1d824ed9 100644 --- a/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala +++ b/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala @@ -10,21 +10,22 @@ class Advent13Spec extends AnyFreeSpec { "part 1" - { "test" in { - part1(testData) shouldEqual 0 + part1(testData) shouldEqual 480 } "real" in { - part1(realData) shouldEqual 0 + part1(realData) shouldEqual 25751 } } "part 2" - { - "test" in { - part2(testData) shouldEqual 0 + "test #0" in { + val example2 = testData.head + example2.solve2 shouldEqual Some(80 * 3 + 40) } "real" in { - part2(realData) shouldEqual 0 + part2(realData) shouldEqual 108528956728655L } } } From f3d643572c7af37e255e2afcf232cf3aa8e1de6c Mon Sep 17 00:00:00 2001 From: Juris Date: Fri, 13 Dec 2024 09:00:34 +0200 Subject: [PATCH 2/4] 2024-13 --- .../jurisk/adventofcode/y2024/Advent13.scala | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala index 25240816..896f53cb 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala @@ -19,6 +19,14 @@ object Advent13 { type N = Long type C = Coordinates2D[Long] + sealed trait SolutionMode + private object SolutionMode { + case object InternalZ3 extends SolutionMode + case object ExternalZ3 extends SolutionMode + } + + private val SelectedMode: SolutionMode = SolutionMode.InternalZ3 + final case class Machine( buttonA: C, buttonB: C, @@ -32,9 +40,7 @@ object Advent13 { if result == prize } yield 3 * a + b - val result = results.minOption - println(s"$this $results $result") - result + results.minOption } def solve2: Option[N] = Try { @@ -45,6 +51,7 @@ object Advent13 { println(s"Trying to solve $this") // a * ax + b * bx = px // a * ay + b * by = py + // Minimize 3 * a + b implicit val optimizer: Optimizer = Optimizer.z3() import optimizer._ @@ -65,20 +72,20 @@ object Advent13 { val _ = minimize(cost) - @nowarn("cat=deprecation") - val m = checkAndGetModel() - debugPrint() - - val ar = m.getConstInterp(a).toString - val br = m.getConstInterp(b).toString - - (ar.toLong * 3 + br.toLong).some + val (ar, br) = SelectedMode match { + case SolutionMode.InternalZ3 => + @nowarn("cat=deprecation") + val m = checkAndGetModel() + val ar = m.getConstInterp(a).toString + val br = m.getConstInterp(b).toString + (ar.toLong, br.toLong) + + case SolutionMode.ExternalZ3 => + val List(ar, br) = runExternal("a", "b").map(resultToLong) + (ar, br) + } -// println(m.toString) -// m.evaluate(cost, true).getString.toLong.some -// val List(ar, br) = runExternal("a", "b").map(resultToLong) -// val result = 3 * ar + br -// result.some + (3 * ar + br).some } } From 790816d0fff8841c8b2a4f39dec930b0ee5f5bbf Mon Sep 17 00:00:00 2001 From: Juris Date: Fri, 13 Dec 2024 09:21:44 +0200 Subject: [PATCH 3/4] 2024-13 --- ReadMe.md | 3 ++ .../jurisk/adventofcode/y2018/Advent23.scala | 3 +- .../jurisk/adventofcode/y2022/Advent21.scala | 2 +- .../jurisk/adventofcode/y2023/Advent16.scala | 2 +- .../jurisk/adventofcode/y2023/Advent24.scala | 1 + .../jurisk/adventofcode/y2024/Advent13.scala | 50 +++++++++---------- .../scala/jurisk/optimization/Optimizer.scala | 31 +++++++----- .../main/scala/jurisk/process/Runner.scala | 9 ++-- .../adventofcode/y2024/Advent13Spec.scala | 7 ++- 9 files changed, 62 insertions(+), 46 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 3e2543bb..59f60127 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -6,6 +6,9 @@ | Year-Day | Task | Scala | Rust | Others | |----------|:-------------------------------------------------------------------------------|:-----------------------------------------------------------------------:|:----------------------------------------------:|:----------------------------------------------------------------------:| +| 2024-13 | [Claw Contraption](https://adventofcode.com/2024/day/13) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala) | [Rust](rust/y2024/src/bin/solution_2024_13.rs) | | +| 2024-12 | [Garden Groups](https://adventofcode.com/2024/day/12) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2024/Advent12.scala) | [Rust](rust/y2024/src/bin/solution_2024_12.rs) | | +| 2024-11 | [Plutonian Pebbles](https://adventofcode.com/2024/day/11) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2024/Advent11.scala) | [Rust](rust/y2024/src/bin/solution_2024_11.rs) | | | 2024-10 | [Hoof It](https://adventofcode.com/2024/day/10) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2024/Advent10.scala) | [Rust](rust/y2024/src/bin/solution_2024_10.rs) | | | 2024-09 | [Disk Fragmenter](https://adventofcode.com/2024/day/9) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2024/Advent09.scala) | [Rust](rust/y2024/src/bin/solution_2024_09.rs) | | | 2024-08 | [Resonant Collinearity](https://adventofcode.com/2024/day/8) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2024/Advent08.scala) | [Rust](rust/y2024/src/bin/solution_2024_08.rs) | | diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2018/Advent23.scala b/scala2/src/main/scala/jurisk/adventofcode/y2018/Advent23.scala index 604c3f83..832196e7 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2018/Advent23.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2018/Advent23.scala @@ -89,7 +89,8 @@ object Advent23 { val _ = maximize(nanobotsInRange) val _ = minimize(distanceFromOrigin) - val List(xc, yc, zc) = runExternal("x", "y", "z").map(resultToInt) + val List(xc, yc, zc) = + runExternal("x", "y", "z").getOrElse("Failed".fail).map(resultToInt) val found = Coords3D(xc, yc, zc) diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2022/Advent21.scala b/scala2/src/main/scala/jurisk/adventofcode/y2022/Advent21.scala index 4537cb77..b8d4aa32 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2022/Advent21.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2022/Advent21.scala @@ -184,7 +184,7 @@ object Advent21 { } } - val List(result) = runExternal(calculate) + val List(result) = runExternal(calculate).getOrElse("Failed".fail) resultToLong(result) } diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent16.scala b/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent16.scala index fc315844..dbef0f50 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent16.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent16.scala @@ -292,7 +292,7 @@ object Advent16 { Field2D.printCharField(debugField) } - val List(result) = runExternal("energized") + val List(result) = runExternal("energized").getOrElse("Failed".fail) resultToLong(result) } diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent24.scala b/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent24.scala index cca6577c..3532f35a 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent24.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent24.scala @@ -380,6 +380,7 @@ object Advent24 { val List(pxS, pyS, pzS, vxS, vyS, vzS) = o .runExternal("px", "py", "pz", "vx", "vy", "vz") + .getOrElse("Failed".fail) .map(r => resultToLong(r)) val result = PositionAndVelocity3D( diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala index 896f53cb..6043ea74 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala @@ -1,6 +1,5 @@ package jurisk.adventofcode.y2024 -import cats.implicits.catsSyntaxOptionId import jurisk.geometry.Coordinates2D import jurisk.optimization.ImplicitConversions.{ RichArithExprIntSort, @@ -12,7 +11,6 @@ import jurisk.utils.FileInput._ import jurisk.utils.Parsing.StringOps import scala.annotation.nowarn -import scala.util.Try object Advent13 { type Input = List[Machine] @@ -20,35 +18,35 @@ object Advent13 { type C = Coordinates2D[Long] sealed trait SolutionMode - private object SolutionMode { + object SolutionMode { case object InternalZ3 extends SolutionMode case object ExternalZ3 extends SolutionMode } - private val SelectedMode: SolutionMode = SolutionMode.InternalZ3 + private val CostA = 3 + private val CostB = 1 final case class Machine( buttonA: C, buttonB: C, prize: C, ) { - def solve: Option[N] = { + def bruteForceConstrained(limit: Int = 100): Option[N] = { val results = for { - a <- 0L to 100 - b <- 0L to 100 + a <- 0L to limit + b <- 0L to limit result = buttonA * a + buttonB * b if result == prize - } yield 3 * a + b + } yield CostA * a + CostB * b results.minOption } - def solve2: Option[N] = Try { - solve2Q - }.toOption.flatten + def solve( + solutionMode: SolutionMode = SolutionMode.InternalZ3 + ): Option[N] = { + println(s"Solving $this") - def solve2Q: Option[N] = { - println(s"Trying to solve $this") // a * ax + b * bx = px // a * ay + b * by = py // Minimize 3 * a + b @@ -67,29 +65,29 @@ object Advent13 { b >= Zero, a * ax + b * bx === px, a * ay + b * by === py, - cost === 3.constant * a + b, + cost === CostA.constant * a + CostB.constant * b, ) val _ = minimize(cost) - val (ar, br) = SelectedMode match { + solutionMode match { case SolutionMode.InternalZ3 => @nowarn("cat=deprecation") - val m = checkAndGetModel() - val ar = m.getConstInterp(a).toString - val br = m.getConstInterp(b).toString - (ar.toLong, br.toLong) + val modelResult = checkAndGetModel() + modelResult.map { m => + m.getConstInterp(cost).toString.toLong + } case SolutionMode.ExternalZ3 => - val List(ar, br) = runExternal("a", "b").map(resultToLong) - (ar, br) + runExternal("cost").map { m => + val List(cost) = m.map(resultToLong) + cost + } } - - (3 * ar + br).some } } - object Machine { + private object Machine { def parse(s: String): Machine = { def parseCoords(s: String, symbol: String): C = { val Pattern = s"""X$symbol(\\d+), Y$symbol(\\d+)""".r @@ -111,14 +109,14 @@ object Advent13 { input.parseSections(Machine.parse) def part1(data: Input): N = - data.flatMap(_.solve).sum + data.flatMap(_.bruteForceConstrained()).sum def part2(data: Input): N = { val adjusted = data.map(m => m.copy(prize = m.prize + Coordinates2D(10000000000000L, 10000000000000L)) ) - adjusted.flatMap(_.solve2).sum + adjusted.flatMap(_.solve()).sum } def parseFile(fileName: String): Input = diff --git a/scala2/src/main/scala/jurisk/optimization/Optimizer.scala b/scala2/src/main/scala/jurisk/optimization/Optimizer.scala index e6f81c87..562aec5e 100644 --- a/scala2/src/main/scala/jurisk/optimization/Optimizer.scala +++ b/scala2/src/main/scala/jurisk/optimization/Optimizer.scala @@ -1,6 +1,6 @@ package jurisk.optimization -import cats.implicits.catsSyntaxOptionId +import cats.implicits.{catsSyntaxOptionId, none} import com.microsoft.z3.ArithExpr import com.microsoft.z3.ArithSort import com.microsoft.z3.BoolExpr @@ -19,6 +19,7 @@ import com.microsoft.z3.Status import com.microsoft.z3.enumerations.Z3_lbool import jurisk.process.Runner import jurisk.utils.Parsing.StringOps +import mouse.all.booleanSyntaxMouse import org.scalatest.matchers.should.Matchers.convertToAnyShouldWrapper // The https://github.com/tudo-aqua/z3-turnkey distribution did not work on task 2023-24 while the same SMT-LIB program @@ -44,11 +45,11 @@ trait Optimizer { def addConstraints(expressions: Expr[BoolSort]*): Unit // Note - We were using `getConstInterp` to get the results from this - @deprecated("Use `runExternal` instead", "2023-12-24") - def checkAndGetModel(): Model + @deprecated("Prefer `runExternal` instead, as this one was glitching for some cases", "2023-12-24") + def checkAndGetModel(): Option[Model] // TODO: Make type-safe? - def runExternal(evaluate: String*): List[String] + def runExternal(evaluate: String*): Option[List[String]] def resultToInt(result: String): Int def resultToLong(result: String): Long @@ -164,13 +165,14 @@ private class Z3Optimizer(val context: Context, val optimize: Optimize) def addConstraints(constraints: Expr[BoolSort]*): Unit = optimize.Add(constraints: _*) - def checkAndGetModel(): Model = { + def checkAndGetModel(): Option[Model] = { val status = optimize.Check() - assert(status == Status.SATISFIABLE, "Model is not satisfiable") - optimize.getModel + (status == Status.SATISFIABLE).option( + optimize.getModel + ) } - def runExternal(evaluate: String*): List[String] = { + def runExternal(evaluate: String*): Option[List[String]] = { val debug = false val programStart = optimize.toString @@ -190,10 +192,15 @@ private class Z3Optimizer(val context: Context, val optimize: Optimize) if (debug) println(results) - val lines = results.splitLines - lines.head.trim shouldEqual "sat" - lines.tail.size shouldEqual evaluate.length - lines.tail + results match { + case Left(_) => + none + case Right(results) => + val lines = results.splitLines + lines.head.trim shouldEqual "sat" + lines.tail.size shouldEqual evaluate.length + lines.tail.some + } } def resultToInt(result: String): Int = diff --git a/scala2/src/main/scala/jurisk/process/Runner.scala b/scala2/src/main/scala/jurisk/process/Runner.scala index a00534d8..bebdeb12 100644 --- a/scala2/src/main/scala/jurisk/process/Runner.scala +++ b/scala2/src/main/scala/jurisk/process/Runner.scala @@ -1,5 +1,6 @@ package jurisk.process +import mouse.all.booleanSyntaxMouse import org.scalatest.matchers.should.Matchers.convertToAnyShouldWrapper import scala.io.Source @@ -7,7 +8,7 @@ import scala.sys.process.ProcessIO import scala.sys.process.stringSeqToProcess object Runner { - def runSync(command: String*)(input: String): String = { + def runSync(command: String*)(input: String): Either[String, String] = { val outputBuilder = new StringBuilder val io = new ProcessIO( in => { @@ -22,7 +23,9 @@ object Runner { _.close(), ) val process = command.run(io) - process.exitValue() shouldEqual 0 - outputBuilder.toString() + // Note - order is important + val exitValue = process.exitValue() + val result = outputBuilder.toString() + (exitValue == 0).either(result, result) } } diff --git a/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala b/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala index 1d824ed9..deb48d2c 100644 --- a/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala +++ b/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala @@ -1,6 +1,7 @@ package jurisk.adventofcode.y2024 import Advent13._ +import jurisk.adventofcode.y2024.Advent13.SolutionMode.{ExternalZ3, InternalZ3} import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers._ @@ -20,8 +21,10 @@ class Advent13Spec extends AnyFreeSpec { "part 2" - { "test #0" in { - val example2 = testData.head - example2.solve2 shouldEqual Some(80 * 3 + 40) + val example = testData.head + val expected = Some(80 * 3 + 40) + example.solve(InternalZ3) shouldEqual expected + example.solve(ExternalZ3) shouldEqual expected } "real" in { From 43e1672ad8f364b925153da77ff5101384ae772e Mon Sep 17 00:00:00 2001 From: Juris Date: Fri, 13 Dec 2024 20:30:53 +0200 Subject: [PATCH 4/4] 2024-13 --- rust/common/src/bool_ops.rs | 42 + rust/common/src/lib.rs | 1 + rust/common/src/math.rs | 45 + rust/common/src/parsing.rs | 10 + rust/y2024/resources/13-test-00.txt | 16 +- rust/y2024/resources/13.txt | 1280 ++++++++++++++++- rust/y2024/src/bin/solution_2024_13.rs | 137 ++ scala2/src/main/resources/2024/14-test-00.txt | 1 + scala2/src/main/resources/2024/14.txt | 1 + .../jurisk/adventofcode/y2024/Advent13.scala | 64 +- .../jurisk/adventofcode/y2024/Advent14.scala | 48 + .../jurisk/math/LinearEquationSystems.scala | 35 + .../adventofcode/y2024/Advent13Spec.scala | 11 +- .../adventofcode/y2024/Advent14Spec.scala | 30 + .../math/LinearEquationSystemsSpec.scala | 21 + 15 files changed, 1722 insertions(+), 20 deletions(-) create mode 100644 rust/common/src/bool_ops.rs create mode 100644 rust/y2024/src/bin/solution_2024_13.rs create mode 100644 scala2/src/main/resources/2024/14-test-00.txt create mode 100644 scala2/src/main/resources/2024/14.txt create mode 100644 scala2/src/main/scala/jurisk/adventofcode/y2024/Advent14.scala create mode 100644 scala2/src/main/scala/jurisk/math/LinearEquationSystems.scala create mode 100644 scala2/src/test/scala/jurisk/adventofcode/y2024/Advent14Spec.scala create mode 100644 scala2/src/test/scala/jurisk/math/LinearEquationSystemsSpec.scala diff --git a/rust/common/src/bool_ops.rs b/rust/common/src/bool_ops.rs new file mode 100644 index 00000000..9cfaab52 --- /dev/null +++ b/rust/common/src/bool_ops.rs @@ -0,0 +1,42 @@ +pub trait BoolOptionOps { + fn then_some_unit(self) -> Option<()>; + fn then_none(self) -> Option<()>; +} + +impl BoolOptionOps for bool { + fn then_some_unit(self) -> Option<()> { + if self { Some(()) } else { None } + } + + fn then_none(self) -> Option<()> { + if self { None } else { Some(()) } + } +} + +pub trait BoolResultOps { + #[expect(clippy::missing_errors_doc)] + fn then_ok_unit(self, error: FE) -> Result<(), E> + where + FE: FnOnce() -> E; + + #[expect(clippy::missing_errors_doc)] + fn then_err_unit(self, error: FE) -> Result<(), E> + where + FE: FnOnce() -> E; +} + +impl BoolResultOps for bool { + fn then_ok_unit(self, error: FE) -> Result<(), E> + where + FE: FnOnce() -> E, + { + if self { Ok(()) } else { Err(error()) } + } + + fn then_err_unit(self, error: FE) -> Result<(), E> + where + FE: FnOnce() -> E, + { + if self { Err(error()) } else { Ok(()) } + } +} diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs index 066775be..d8f5ae91 100644 --- a/rust/common/src/lib.rs +++ b/rust/common/src/lib.rs @@ -1,6 +1,7 @@ #![feature(step_trait)] pub mod area2d; +pub mod bool_ops; pub mod circular; pub mod coords2d; pub mod coords3d; diff --git a/rust/common/src/math.rs b/rust/common/src/math.rs index 823254fd..c3a0ffbf 100644 --- a/rust/common/src/math.rs +++ b/rust/common/src/math.rs @@ -1,3 +1,5 @@ +use num_traits::Num; + #[must_use] #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] pub fn factors(n: u32) -> Vec { @@ -61,3 +63,46 @@ pub fn gcd(a: i64, b: i64) -> i64 { pub fn lcm(a: i64, b: i64) -> i64 { a.abs() * b.abs() / gcd(a, b) } + +#[must_use] +pub fn solve_two_variable_integer_linear_equation_system( + a: N, + b: N, + c: N, + d: N, + e: N, + f: N, +) -> Option<(N, N)> { + let div = |dividend: N, divisor: N| -> Option { + (dividend % divisor == N::zero()).then(|| dividend / divisor) + }; + + let det = a * d - b * c; + if det == N::zero() { + None + } else { + let x = div(e * d - b * f, det); + let y = div(a * f - e * c, det); + match (x, y) { + (Some(x), Some(y)) => Some((x, y)), + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_solve_two_variable_integer_linear_equation_system() { + assert_eq!( + solve_two_variable_integer_linear_equation_system(2, 1, 3, -1, 15, 5), + Some((4, 7)) + ); + assert_eq!( + solve_two_variable_integer_linear_equation_system(1, 2, 1, 2, 5, 6), + None + ); + } +} diff --git a/rust/common/src/parsing.rs b/rust/common/src/parsing.rs index 59fcd2eb..7a03df0f 100644 --- a/rust/common/src/parsing.rs +++ b/rust/common/src/parsing.rs @@ -272,6 +272,16 @@ pub fn segments_separated_by_double_newline(input: &str) -> Vec { .collect() } +pub fn parse_segments_separated_by_double_newline(input: &str) -> Result, Error> +where + ::Err: Debug, +{ + segments_separated_by_double_newline(input) + .into_iter() + .map(|x| parse_str(&x)) + .collect() +} + /// # Errors /// /// Will return `Err` if parsing fails. diff --git a/rust/y2024/resources/13-test-00.txt b/rust/y2024/resources/13-test-00.txt index a8b6c947..444a287e 100644 --- a/rust/y2024/resources/13-test-00.txt +++ b/rust/y2024/resources/13-test-00.txt @@ -1 +1,15 @@ -noop \ No newline at end of file +Button A: X+94, Y+34 +Button B: X+22, Y+67 +Prize: X=8400, Y=5400 + +Button A: X+26, Y+66 +Button B: X+67, Y+21 +Prize: X=12748, Y=12176 + +Button A: X+17, Y+86 +Button B: X+84, Y+37 +Prize: X=7870, Y=6450 + +Button A: X+69, Y+23 +Button B: X+27, Y+71 +Prize: X=18641, Y=10279 \ No newline at end of file diff --git a/rust/y2024/resources/13.txt b/rust/y2024/resources/13.txt index a8b6c947..42ed3dd1 100644 --- a/rust/y2024/resources/13.txt +++ b/rust/y2024/resources/13.txt @@ -1 +1,1279 @@ -noop \ No newline at end of file +Button A: X+63, Y+14 +Button B: X+12, Y+37 +Prize: X=5921, Y=10432 + +Button A: X+48, Y+23 +Button B: X+38, Y+74 +Prize: X=1820, Y=1430 + +Button A: X+17, Y+64 +Button B: X+72, Y+31 +Prize: X=1801, Y=2437 + +Button A: X+76, Y+21 +Button B: X+20, Y+53 +Prize: X=4836, Y=1811 + +Button A: X+53, Y+18 +Button B: X+30, Y+54 +Prize: X=692, Y=17702 + +Button A: X+99, Y+33 +Button B: X+58, Y+70 +Prize: X=10829, Y=7967 + +Button A: X+15, Y+42 +Button B: X+33, Y+13 +Prize: X=8585, Y=18017 + +Button A: X+17, Y+70 +Button B: X+50, Y+14 +Prize: X=14945, Y=12142 + +Button A: X+80, Y+99 +Button B: X+81, Y+31 +Prize: X=10257, Y=5977 + +Button A: X+62, Y+27 +Button B: X+11, Y+34 +Prize: X=13026, Y=6898 + +Button A: X+12, Y+36 +Button B: X+62, Y+19 +Prize: X=2540, Y=4934 + +Button A: X+13, Y+65 +Button B: X+73, Y+66 +Prize: X=5014, Y=7728 + +Button A: X+60, Y+21 +Button B: X+21, Y+43 +Prize: X=16754, Y=1966 + +Button A: X+89, Y+33 +Button B: X+11, Y+54 +Prize: X=3645, Y=2949 + +Button A: X+56, Y+20 +Button B: X+24, Y+48 +Prize: X=1264, Y=11536 + +Button A: X+68, Y+21 +Button B: X+15, Y+42 +Prize: X=17409, Y=746 + +Button A: X+23, Y+66 +Button B: X+75, Y+32 +Prize: X=11223, Y=3354 + +Button A: X+19, Y+58 +Button B: X+47, Y+18 +Prize: X=6869, Y=7398 + +Button A: X+28, Y+47 +Button B: X+46, Y+18 +Prize: X=12204, Y=3852 + +Button A: X+25, Y+57 +Button B: X+43, Y+11 +Prize: X=3309, Y=15501 + +Button A: X+95, Y+55 +Button B: X+32, Y+79 +Prize: X=5845, Y=6710 + +Button A: X+18, Y+12 +Button B: X+43, Y+93 +Prize: X=2658, Y=4860 + +Button A: X+88, Y+12 +Button B: X+86, Y+83 +Prize: X=3684, Y=930 + +Button A: X+51, Y+18 +Button B: X+44, Y+75 +Prize: X=19780, Y=17984 + +Button A: X+59, Y+30 +Button B: X+19, Y+55 +Prize: X=13898, Y=19160 + +Button A: X+12, Y+51 +Button B: X+49, Y+15 +Prize: X=15071, Y=17144 + +Button A: X+22, Y+46 +Button B: X+70, Y+45 +Prize: X=10238, Y=3734 + +Button A: X+24, Y+47 +Button B: X+84, Y+35 +Prize: X=6444, Y=3684 + +Button A: X+32, Y+15 +Button B: X+24, Y+49 +Prize: X=18208, Y=2148 + +Button A: X+11, Y+67 +Button B: X+51, Y+11 +Prize: X=10173, Y=645 + +Button A: X+57, Y+28 +Button B: X+22, Y+41 +Prize: X=9124, Y=4647 + +Button A: X+13, Y+58 +Button B: X+51, Y+13 +Prize: X=9979, Y=12131 + +Button A: X+27, Y+95 +Button B: X+94, Y+60 +Prize: X=2096, Y=1960 + +Button A: X+43, Y+11 +Button B: X+36, Y+75 +Prize: X=16958, Y=1706 + +Button A: X+54, Y+35 +Button B: X+12, Y+85 +Prize: X=5958, Y=9190 + +Button A: X+40, Y+11 +Button B: X+19, Y+49 +Prize: X=12563, Y=11351 + +Button A: X+12, Y+65 +Button B: X+62, Y+24 +Prize: X=14148, Y=17166 + +Button A: X+77, Y+84 +Button B: X+82, Y+17 +Prize: X=9019, Y=4115 + +Button A: X+20, Y+11 +Button B: X+25, Y+56 +Prize: X=5480, Y=6240 + +Button A: X+14, Y+62 +Button B: X+40, Y+13 +Prize: X=3306, Y=1887 + +Button A: X+19, Y+49 +Button B: X+34, Y+13 +Prize: X=3547, Y=5488 + +Button A: X+85, Y+19 +Button B: X+14, Y+89 +Prize: X=5778, Y=2322 + +Button A: X+57, Y+82 +Button B: X+67, Y+31 +Prize: X=8692, Y=6358 + +Button A: X+76, Y+11 +Button B: X+20, Y+76 +Prize: X=16664, Y=5282 + +Button A: X+73, Y+14 +Button B: X+25, Y+29 +Prize: X=3114, Y=2340 + +Button A: X+91, Y+60 +Button B: X+12, Y+27 +Prize: X=2905, Y=3786 + +Button A: X+54, Y+12 +Button B: X+14, Y+80 +Prize: X=11888, Y=6320 + +Button A: X+51, Y+25 +Button B: X+49, Y+98 +Prize: X=6132, Y=10108 + +Button A: X+16, Y+67 +Button B: X+43, Y+15 +Prize: X=11996, Y=4341 + +Button A: X+33, Y+66 +Button B: X+36, Y+11 +Prize: X=16502, Y=13454 + +Button A: X+32, Y+42 +Button B: X+96, Y+37 +Prize: X=4960, Y=4196 + +Button A: X+50, Y+27 +Button B: X+13, Y+35 +Prize: X=5495, Y=3285 + +Button A: X+12, Y+39 +Button B: X+42, Y+13 +Prize: X=10070, Y=471 + +Button A: X+74, Y+52 +Button B: X+13, Y+36 +Prize: X=3061, Y=7244 + +Button A: X+41, Y+72 +Button B: X+43, Y+14 +Prize: X=7933, Y=19074 + +Button A: X+29, Y+58 +Button B: X+60, Y+17 +Prize: X=6839, Y=3481 + +Button A: X+62, Y+33 +Button B: X+16, Y+55 +Prize: X=18434, Y=17183 + +Button A: X+56, Y+57 +Button B: X+19, Y+78 +Prize: X=3999, Y=8118 + +Button A: X+13, Y+78 +Button B: X+93, Y+27 +Prize: X=2304, Y=2673 + +Button A: X+50, Y+20 +Button B: X+12, Y+43 +Prize: X=1330, Y=2930 + +Button A: X+40, Y+98 +Button B: X+46, Y+28 +Prize: X=4532, Y=7546 + +Button A: X+11, Y+16 +Button B: X+32, Y+15 +Prize: X=1004, Y=18942 + +Button A: X+40, Y+14 +Button B: X+31, Y+54 +Prize: X=12706, Y=17506 + +Button A: X+20, Y+37 +Button B: X+55, Y+19 +Prize: X=15140, Y=6215 + +Button A: X+95, Y+39 +Button B: X+13, Y+64 +Prize: X=5747, Y=7287 + +Button A: X+35, Y+68 +Button B: X+48, Y+20 +Prize: X=17403, Y=2672 + +Button A: X+14, Y+35 +Button B: X+60, Y+37 +Prize: X=6716, Y=5474 + +Button A: X+21, Y+44 +Button B: X+26, Y+11 +Prize: X=13336, Y=6502 + +Button A: X+12, Y+73 +Button B: X+49, Y+13 +Prize: X=8933, Y=11056 + +Button A: X+56, Y+79 +Button B: X+96, Y+42 +Prize: X=5384, Y=3391 + +Button A: X+73, Y+31 +Button B: X+19, Y+62 +Prize: X=10634, Y=16142 + +Button A: X+41, Y+64 +Button B: X+67, Y+26 +Prize: X=8227, Y=7184 + +Button A: X+23, Y+42 +Button B: X+29, Y+12 +Prize: X=15139, Y=4604 + +Button A: X+56, Y+30 +Button B: X+23, Y+49 +Prize: X=5897, Y=14529 + +Button A: X+15, Y+24 +Button B: X+45, Y+19 +Prize: X=4595, Y=5858 + +Button A: X+15, Y+44 +Button B: X+71, Y+46 +Prize: X=5683, Y=9788 + +Button A: X+30, Y+62 +Button B: X+47, Y+30 +Prize: X=1449, Y=1182 + +Button A: X+22, Y+50 +Button B: X+61, Y+23 +Prize: X=13134, Y=8314 + +Button A: X+14, Y+55 +Button B: X+71, Y+25 +Prize: X=1105, Y=12165 + +Button A: X+16, Y+38 +Button B: X+47, Y+27 +Prize: X=14274, Y=14866 + +Button A: X+72, Y+13 +Button B: X+18, Y+62 +Prize: X=16262, Y=5148 + +Button A: X+30, Y+72 +Button B: X+38, Y+15 +Prize: X=3466, Y=3594 + +Button A: X+27, Y+64 +Button B: X+62, Y+26 +Prize: X=8459, Y=16958 + +Button A: X+18, Y+50 +Button B: X+66, Y+35 +Prize: X=17492, Y=13125 + +Button A: X+94, Y+16 +Button B: X+98, Y+86 +Prize: X=5924, Y=2672 + +Button A: X+51, Y+12 +Button B: X+14, Y+26 +Prize: X=368, Y=4586 + +Button A: X+73, Y+24 +Button B: X+47, Y+99 +Prize: X=6932, Y=4869 + +Button A: X+41, Y+18 +Button B: X+12, Y+52 +Prize: X=3183, Y=15770 + +Button A: X+31, Y+14 +Button B: X+42, Y+73 +Prize: X=1830, Y=1745 + +Button A: X+56, Y+20 +Button B: X+18, Y+62 +Prize: X=16986, Y=15838 + +Button A: X+16, Y+35 +Button B: X+33, Y+23 +Prize: X=17558, Y=5165 + +Button A: X+84, Y+57 +Button B: X+11, Y+31 +Prize: X=8262, Y=3847 + +Button A: X+85, Y+12 +Button B: X+67, Y+81 +Prize: X=8788, Y=6177 + +Button A: X+46, Y+11 +Button B: X+31, Y+68 +Prize: X=18210, Y=8428 + +Button A: X+50, Y+12 +Button B: X+18, Y+76 +Prize: X=11006, Y=10004 + +Button A: X+14, Y+94 +Button B: X+59, Y+73 +Prize: X=1505, Y=3319 + +Button A: X+16, Y+25 +Button B: X+60, Y+14 +Prize: X=5384, Y=2511 + +Button A: X+40, Y+54 +Button B: X+34, Y+13 +Prize: X=420, Y=8904 + +Button A: X+39, Y+65 +Button B: X+40, Y+21 +Prize: X=14227, Y=9457 + +Button A: X+59, Y+56 +Button B: X+88, Y+15 +Prize: X=4145, Y=1947 + +Button A: X+14, Y+54 +Button B: X+66, Y+23 +Prize: X=11230, Y=7239 + +Button A: X+18, Y+53 +Button B: X+56, Y+19 +Prize: X=13534, Y=15652 + +Button A: X+46, Y+18 +Button B: X+16, Y+49 +Prize: X=13450, Y=1538 + +Button A: X+11, Y+49 +Button B: X+57, Y+20 +Prize: X=14617, Y=17561 + +Button A: X+20, Y+62 +Button B: X+91, Y+79 +Prize: X=4726, Y=5308 + +Button A: X+60, Y+39 +Button B: X+24, Y+49 +Prize: X=3008, Y=4805 + +Button A: X+72, Y+25 +Button B: X+17, Y+65 +Prize: X=2880, Y=12150 + +Button A: X+38, Y+84 +Button B: X+91, Y+29 +Prize: X=2180, Y=3786 + +Button A: X+29, Y+91 +Button B: X+30, Y+21 +Prize: X=1955, Y=5257 + +Button A: X+14, Y+25 +Button B: X+29, Y+17 +Prize: X=18496, Y=14119 + +Button A: X+16, Y+63 +Button B: X+75, Y+14 +Prize: X=3515, Y=12576 + +Button A: X+23, Y+11 +Button B: X+36, Y+58 +Prize: X=15711, Y=15311 + +Button A: X+75, Y+29 +Button B: X+12, Y+83 +Prize: X=3159, Y=5688 + +Button A: X+40, Y+26 +Button B: X+12, Y+32 +Prize: X=284, Y=12368 + +Button A: X+15, Y+60 +Button B: X+74, Y+12 +Prize: X=16442, Y=14336 + +Button A: X+89, Y+14 +Button B: X+69, Y+56 +Prize: X=7113, Y=4550 + +Button A: X+74, Y+20 +Button B: X+11, Y+51 +Prize: X=1498, Y=17164 + +Button A: X+55, Y+14 +Button B: X+22, Y+57 +Prize: X=14202, Y=3972 + +Button A: X+53, Y+16 +Button B: X+28, Y+69 +Prize: X=10197, Y=4823 + +Button A: X+11, Y+69 +Button B: X+74, Y+48 +Prize: X=4182, Y=6672 + +Button A: X+12, Y+28 +Button B: X+34, Y+19 +Prize: X=8522, Y=9575 + +Button A: X+35, Y+12 +Button B: X+24, Y+63 +Prize: X=15331, Y=5276 + +Button A: X+71, Y+28 +Button B: X+19, Y+39 +Prize: X=6737, Y=3350 + +Button A: X+43, Y+81 +Button B: X+46, Y+14 +Prize: X=8639, Y=15389 + +Button A: X+26, Y+35 +Button B: X+54, Y+21 +Prize: X=4168, Y=3388 + +Button A: X+58, Y+14 +Button B: X+23, Y+73 +Prize: X=16511, Y=10001 + +Button A: X+71, Y+16 +Button B: X+39, Y+98 +Prize: X=6042, Y=7874 + +Button A: X+69, Y+13 +Button B: X+12, Y+71 +Prize: X=14381, Y=9962 + +Button A: X+45, Y+20 +Button B: X+25, Y+64 +Prize: X=9955, Y=19248 + +Button A: X+20, Y+78 +Button B: X+57, Y+12 +Prize: X=886, Y=18314 + +Button A: X+31, Y+58 +Button B: X+59, Y+30 +Prize: X=6111, Y=18934 + +Button A: X+72, Y+49 +Button B: X+11, Y+31 +Prize: X=4216, Y=10597 + +Button A: X+79, Y+19 +Button B: X+17, Y+77 +Prize: X=7349, Y=3809 + +Button A: X+56, Y+21 +Button B: X+18, Y+92 +Prize: X=5464, Y=7505 + +Button A: X+98, Y+16 +Button B: X+48, Y+65 +Prize: X=10860, Y=5603 + +Button A: X+95, Y+15 +Button B: X+37, Y+36 +Prize: X=2821, Y=1893 + +Button A: X+12, Y+93 +Button B: X+64, Y+24 +Prize: X=3816, Y=4086 + +Button A: X+74, Y+26 +Button B: X+14, Y+46 +Prize: X=1548, Y=16972 + +Button A: X+20, Y+42 +Button B: X+39, Y+26 +Prize: X=8419, Y=3192 + +Button A: X+26, Y+62 +Button B: X+62, Y+31 +Prize: X=10956, Y=8392 + +Button A: X+25, Y+64 +Button B: X+35, Y+13 +Prize: X=930, Y=3332 + +Button A: X+17, Y+60 +Button B: X+58, Y+41 +Prize: X=2898, Y=6463 + +Button A: X+58, Y+86 +Button B: X+82, Y+36 +Prize: X=10222, Y=9936 + +Button A: X+29, Y+85 +Button B: X+95, Y+83 +Prize: X=9017, Y=8057 + +Button A: X+19, Y+67 +Button B: X+58, Y+18 +Prize: X=6909, Y=9797 + +Button A: X+35, Y+16 +Button B: X+41, Y+66 +Prize: X=6650, Y=13838 + +Button A: X+41, Y+12 +Button B: X+27, Y+70 +Prize: X=8502, Y=17916 + +Button A: X+42, Y+22 +Button B: X+31, Y+50 +Prize: X=9598, Y=1236 + +Button A: X+69, Y+21 +Button B: X+20, Y+69 +Prize: X=15843, Y=12128 + +Button A: X+21, Y+64 +Button B: X+44, Y+11 +Prize: X=9320, Y=445 + +Button A: X+52, Y+28 +Button B: X+37, Y+63 +Prize: X=13057, Y=9363 + +Button A: X+18, Y+61 +Button B: X+56, Y+21 +Prize: X=980, Y=10681 + +Button A: X+18, Y+30 +Button B: X+49, Y+25 +Prize: X=9663, Y=9675 + +Button A: X+60, Y+23 +Button B: X+25, Y+67 +Prize: X=19355, Y=7228 + +Button A: X+89, Y+13 +Button B: X+50, Y+69 +Prize: X=5953, Y=5250 + +Button A: X+34, Y+14 +Button B: X+26, Y+62 +Prize: X=15374, Y=9738 + +Button A: X+16, Y+85 +Button B: X+81, Y+46 +Prize: X=4306, Y=9809 + +Button A: X+19, Y+59 +Button B: X+31, Y+24 +Prize: X=2311, Y=4358 + +Button A: X+48, Y+70 +Button B: X+26, Y+12 +Prize: X=18182, Y=2026 + +Button A: X+53, Y+20 +Button B: X+56, Y+72 +Prize: X=6993, Y=5284 + +Button A: X+25, Y+80 +Button B: X+86, Y+22 +Prize: X=2657, Y=5464 + +Button A: X+16, Y+31 +Button B: X+83, Y+52 +Prize: X=4281, Y=4486 + +Button A: X+34, Y+25 +Button B: X+21, Y+56 +Prize: X=3677, Y=3961 + +Button A: X+23, Y+14 +Button B: X+22, Y+51 +Prize: X=5099, Y=14177 + +Button A: X+21, Y+21 +Button B: X+79, Y+12 +Prize: X=9164, Y=2799 + +Button A: X+88, Y+49 +Button B: X+29, Y+87 +Prize: X=9884, Y=12022 + +Button A: X+19, Y+79 +Button B: X+86, Y+45 +Prize: X=5530, Y=4551 + +Button A: X+95, Y+87 +Button B: X+82, Y+15 +Prize: X=5539, Y=3450 + +Button A: X+47, Y+13 +Button B: X+24, Y+42 +Prize: X=8476, Y=14658 + +Button A: X+40, Y+25 +Button B: X+16, Y+31 +Prize: X=2408, Y=14978 + +Button A: X+31, Y+83 +Button B: X+66, Y+14 +Prize: X=12489, Y=19145 + +Button A: X+21, Y+51 +Button B: X+43, Y+17 +Prize: X=19484, Y=11600 + +Button A: X+58, Y+22 +Button B: X+15, Y+52 +Prize: X=6102, Y=13788 + +Button A: X+62, Y+17 +Button B: X+24, Y+53 +Prize: X=16042, Y=5798 + +Button A: X+37, Y+21 +Button B: X+29, Y+54 +Prize: X=15497, Y=4808 + +Button A: X+22, Y+59 +Button B: X+99, Y+23 +Prize: X=5665, Y=5250 + +Button A: X+83, Y+14 +Button B: X+30, Y+64 +Prize: X=8493, Y=4910 + +Button A: X+28, Y+75 +Button B: X+68, Y+20 +Prize: X=5088, Y=2585 + +Button A: X+14, Y+39 +Button B: X+35, Y+22 +Prize: X=11491, Y=11926 + +Button A: X+52, Y+56 +Button B: X+64, Y+18 +Prize: X=6580, Y=4540 + +Button A: X+98, Y+11 +Button B: X+57, Y+76 +Prize: X=6791, Y=5704 + +Button A: X+88, Y+45 +Button B: X+40, Y+75 +Prize: X=8928, Y=8220 + +Button A: X+13, Y+33 +Button B: X+75, Y+50 +Prize: X=9076, Y=3791 + +Button A: X+75, Y+11 +Button B: X+13, Y+37 +Prize: X=7105, Y=1393 + +Button A: X+63, Y+43 +Button B: X+12, Y+35 +Prize: X=16619, Y=2191 + +Button A: X+59, Y+82 +Button B: X+85, Y+37 +Prize: X=3968, Y=3811 + +Button A: X+36, Y+91 +Button B: X+88, Y+48 +Prize: X=5932, Y=6447 + +Button A: X+24, Y+96 +Button B: X+83, Y+11 +Prize: X=9770, Y=8906 + +Button A: X+40, Y+13 +Button B: X+18, Y+27 +Prize: X=4810, Y=19642 + +Button A: X+25, Y+29 +Button B: X+69, Y+21 +Prize: X=7111, Y=4175 + +Button A: X+32, Y+60 +Button B: X+21, Y+12 +Prize: X=6652, Y=18176 + +Button A: X+71, Y+34 +Button B: X+15, Y+47 +Prize: X=18074, Y=14851 + +Button A: X+24, Y+55 +Button B: X+23, Y+13 +Prize: X=946, Y=8379 + +Button A: X+61, Y+25 +Button B: X+17, Y+39 +Prize: X=10946, Y=8580 + +Button A: X+14, Y+34 +Button B: X+66, Y+50 +Prize: X=16302, Y=602 + +Button A: X+36, Y+80 +Button B: X+67, Y+45 +Prize: X=7425, Y=6215 + +Button A: X+55, Y+88 +Button B: X+97, Y+24 +Prize: X=4421, Y=3400 + +Button A: X+43, Y+13 +Button B: X+22, Y+72 +Prize: X=17442, Y=4012 + +Button A: X+26, Y+57 +Button B: X+87, Y+42 +Prize: X=8063, Y=4737 + +Button A: X+63, Y+23 +Button B: X+45, Y+86 +Prize: X=6489, Y=7726 + +Button A: X+67, Y+73 +Button B: X+17, Y+82 +Prize: X=3866, Y=7640 + +Button A: X+34, Y+15 +Button B: X+22, Y+49 +Prize: X=4358, Y=2069 + +Button A: X+37, Y+18 +Button B: X+44, Y+78 +Prize: X=3863, Y=2898 + +Button A: X+25, Y+60 +Button B: X+57, Y+23 +Prize: X=15259, Y=12516 + +Button A: X+11, Y+32 +Button B: X+74, Y+30 +Prize: X=467, Y=3686 + +Button A: X+25, Y+86 +Button B: X+67, Y+63 +Prize: X=6940, Y=7963 + +Button A: X+11, Y+80 +Button B: X+75, Y+73 +Prize: X=6885, Y=10859 + +Button A: X+31, Y+50 +Button B: X+96, Y+31 +Prize: X=573, Y=305 + +Button A: X+53, Y+16 +Button B: X+37, Y+77 +Prize: X=15101, Y=16829 + +Button A: X+18, Y+78 +Button B: X+61, Y+12 +Prize: X=9178, Y=7268 + +Button A: X+43, Y+41 +Button B: X+11, Y+53 +Prize: X=3777, Y=6067 + +Button A: X+85, Y+28 +Button B: X+13, Y+44 +Prize: X=1662, Y=3288 + +Button A: X+27, Y+82 +Button B: X+65, Y+12 +Prize: X=6448, Y=14048 + +Button A: X+14, Y+92 +Button B: X+75, Y+84 +Prize: X=2876, Y=5816 + +Button A: X+14, Y+27 +Button B: X+52, Y+20 +Prize: X=13444, Y=17042 + +Button A: X+84, Y+25 +Button B: X+11, Y+60 +Prize: X=11642, Y=4160 + +Button A: X+45, Y+25 +Button B: X+33, Y+59 +Prize: X=9683, Y=10449 + +Button A: X+16, Y+61 +Button B: X+83, Y+50 +Prize: X=2160, Y=3972 + +Button A: X+64, Y+25 +Button B: X+48, Y+61 +Prize: X=5808, Y=6198 + +Button A: X+51, Y+55 +Button B: X+64, Y+16 +Prize: X=8113, Y=5197 + +Button A: X+36, Y+14 +Button B: X+16, Y+25 +Prize: X=12716, Y=12076 + +Button A: X+71, Y+21 +Button B: X+13, Y+70 +Prize: X=1999, Y=389 + +Button A: X+75, Y+14 +Button B: X+15, Y+83 +Prize: X=6365, Y=12329 + +Button A: X+59, Y+13 +Button B: X+23, Y+62 +Prize: X=11799, Y=8700 + +Button A: X+63, Y+12 +Button B: X+28, Y+84 +Prize: X=3756, Y=18080 + +Button A: X+83, Y+97 +Button B: X+93, Y+14 +Prize: X=6937, Y=4509 + +Button A: X+91, Y+52 +Button B: X+23, Y+54 +Prize: X=2740, Y=3200 + +Button A: X+98, Y+24 +Button B: X+12, Y+50 +Prize: X=2410, Y=3508 + +Button A: X+12, Y+48 +Button B: X+71, Y+56 +Prize: X=7260, Y=7152 + +Button A: X+81, Y+61 +Button B: X+12, Y+63 +Prize: X=5145, Y=6249 + +Button A: X+16, Y+53 +Button B: X+96, Y+21 +Prize: X=8048, Y=6463 + +Button A: X+33, Y+14 +Button B: X+15, Y+52 +Prize: X=14369, Y=9140 + +Button A: X+19, Y+59 +Button B: X+30, Y+11 +Prize: X=13981, Y=18382 + +Button A: X+15, Y+41 +Button B: X+62, Y+26 +Prize: X=918, Y=12146 + +Button A: X+60, Y+16 +Button B: X+17, Y+77 +Prize: X=16721, Y=8161 + +Button A: X+65, Y+30 +Button B: X+24, Y+49 +Prize: X=18662, Y=13672 + +Button A: X+19, Y+72 +Button B: X+72, Y+14 +Prize: X=8892, Y=19434 + +Button A: X+53, Y+26 +Button B: X+22, Y+57 +Prize: X=14006, Y=9613 + +Button A: X+64, Y+14 +Button B: X+14, Y+69 +Prize: X=6870, Y=9595 + +Button A: X+33, Y+12 +Button B: X+22, Y+64 +Prize: X=4763, Y=6884 + +Button A: X+11, Y+34 +Button B: X+66, Y+45 +Prize: X=1376, Y=6380 + +Button A: X+11, Y+49 +Button B: X+80, Y+18 +Prize: X=17079, Y=1417 + +Button A: X+88, Y+36 +Button B: X+17, Y+58 +Prize: X=6940, Y=3860 + +Button A: X+56, Y+18 +Button B: X+16, Y+71 +Prize: X=2160, Y=3872 + +Button A: X+73, Y+13 +Button B: X+19, Y+74 +Prize: X=5721, Y=13001 + +Button A: X+26, Y+62 +Button B: X+59, Y+14 +Prize: X=13427, Y=19772 + +Button A: X+62, Y+11 +Button B: X+20, Y+78 +Prize: X=9330, Y=17897 + +Button A: X+84, Y+23 +Button B: X+14, Y+73 +Prize: X=5254, Y=16308 + +Button A: X+35, Y+12 +Button B: X+20, Y+30 +Prize: X=10910, Y=1196 + +Button A: X+56, Y+25 +Button B: X+36, Y+72 +Prize: X=6600, Y=7980 + +Button A: X+18, Y+53 +Button B: X+71, Y+24 +Prize: X=15881, Y=16234 + +Button A: X+24, Y+81 +Button B: X+32, Y+21 +Prize: X=1792, Y=1698 + +Button A: X+57, Y+29 +Button B: X+22, Y+57 +Prize: X=3599, Y=12132 + +Button A: X+45, Y+22 +Button B: X+15, Y+43 +Prize: X=7250, Y=16657 + +Button A: X+11, Y+32 +Button B: X+76, Y+55 +Prize: X=6277, Y=8566 + +Button A: X+11, Y+38 +Button B: X+31, Y+20 +Prize: X=8663, Y=5068 + +Button A: X+13, Y+44 +Button B: X+79, Y+35 +Prize: X=16161, Y=12028 + +Button A: X+14, Y+86 +Button B: X+30, Y+33 +Prize: X=608, Y=1163 + +Button A: X+27, Y+18 +Button B: X+39, Y+96 +Prize: X=4887, Y=9558 + +Button A: X+29, Y+92 +Button B: X+96, Y+19 +Prize: X=9073, Y=2227 + +Button A: X+26, Y+69 +Button B: X+65, Y+14 +Prize: X=15993, Y=6605 + +Button A: X+44, Y+11 +Button B: X+14, Y+42 +Prize: X=8850, Y=15218 + +Button A: X+29, Y+55 +Button B: X+70, Y+39 +Prize: X=6877, Y=3948 + +Button A: X+14, Y+44 +Button B: X+66, Y+24 +Prize: X=11860, Y=13528 + +Button A: X+40, Y+18 +Button B: X+44, Y+73 +Prize: X=18372, Y=11267 + +Button A: X+75, Y+79 +Button B: X+12, Y+73 +Prize: X=6678, Y=8181 + +Button A: X+60, Y+92 +Button B: X+76, Y+28 +Prize: X=8196, Y=6724 + +Button A: X+11, Y+52 +Button B: X+47, Y+16 +Prize: X=19098, Y=9736 + +Button A: X+67, Y+70 +Button B: X+18, Y+83 +Prize: X=6057, Y=8575 + +Button A: X+82, Y+26 +Button B: X+14, Y+61 +Prize: X=18224, Y=11876 + +Button A: X+19, Y+60 +Button B: X+75, Y+32 +Prize: X=18161, Y=5480 + +Button A: X+43, Y+69 +Button B: X+52, Y+25 +Prize: X=17476, Y=4106 + +Button A: X+27, Y+95 +Button B: X+34, Y+32 +Prize: X=2126, Y=2836 + +Button A: X+40, Y+39 +Button B: X+14, Y+96 +Prize: X=1892, Y=6621 + +Button A: X+20, Y+71 +Button B: X+80, Y+40 +Prize: X=8920, Y=10438 + +Button A: X+40, Y+57 +Button B: X+33, Y+11 +Prize: X=6314, Y=568 + +Button A: X+99, Y+91 +Button B: X+15, Y+91 +Prize: X=4014, Y=11102 + +Button A: X+16, Y+22 +Button B: X+27, Y+12 +Prize: X=6729, Y=918 + +Button A: X+11, Y+67 +Button B: X+65, Y+16 +Prize: X=5652, Y=11917 + +Button A: X+14, Y+49 +Button B: X+81, Y+42 +Prize: X=17396, Y=17189 + +Button A: X+12, Y+31 +Button B: X+75, Y+53 +Prize: X=887, Y=4840 + +Button A: X+25, Y+63 +Button B: X+62, Y+15 +Prize: X=8820, Y=18605 + +Button A: X+43, Y+12 +Button B: X+18, Y+55 +Prize: X=10077, Y=19114 + +Button A: X+16, Y+27 +Button B: X+38, Y+12 +Prize: X=14666, Y=6686 + +Button A: X+65, Y+12 +Button B: X+30, Y+81 +Prize: X=5825, Y=10691 + +Button A: X+23, Y+65 +Button B: X+46, Y+14 +Prize: X=19098, Y=1810 + +Button A: X+32, Y+61 +Button B: X+55, Y+18 +Prize: X=9869, Y=745 + +Button A: X+19, Y+42 +Button B: X+97, Y+19 +Prize: X=5478, Y=4097 + +Button A: X+79, Y+25 +Button B: X+12, Y+63 +Prize: X=4014, Y=8007 + +Button A: X+24, Y+55 +Button B: X+57, Y+39 +Prize: X=2778, Y=3251 + +Button A: X+60, Y+14 +Button B: X+29, Y+77 +Prize: X=4471, Y=18743 + +Button A: X+44, Y+71 +Button B: X+45, Y+23 +Prize: X=4764, Y=16962 + +Button A: X+69, Y+44 +Button B: X+14, Y+37 +Prize: X=18647, Y=8826 + +Button A: X+15, Y+70 +Button B: X+58, Y+17 +Prize: X=2737, Y=10708 + +Button A: X+25, Y+48 +Button B: X+42, Y+20 +Prize: X=3308, Y=4896 + +Button A: X+16, Y+43 +Button B: X+70, Y+28 +Prize: X=10848, Y=15996 + +Button A: X+68, Y+74 +Button B: X+12, Y+68 +Prize: X=2632, Y=3084 + +Button A: X+67, Y+87 +Button B: X+91, Y+26 +Prize: X=6156, Y=5966 + +Button A: X+43, Y+88 +Button B: X+99, Y+11 +Prize: X=6953, Y=6182 + +Button A: X+66, Y+21 +Button B: X+17, Y+66 +Prize: X=6466, Y=5814 + +Button A: X+52, Y+24 +Button B: X+21, Y+43 +Prize: X=8244, Y=16996 + +Button A: X+46, Y+82 +Button B: X+45, Y+13 +Prize: X=9831, Y=14647 + +Button A: X+17, Y+30 +Button B: X+49, Y+23 +Prize: X=3553, Y=1954 + +Button A: X+56, Y+23 +Button B: X+16, Y+21 +Prize: X=5072, Y=2819 + +Button A: X+22, Y+98 +Button B: X+70, Y+75 +Prize: X=6382, Y=8773 + +Button A: X+32, Y+93 +Button B: X+68, Y+43 +Prize: X=7404, Y=6210 + +Button A: X+29, Y+75 +Button B: X+94, Y+38 +Prize: X=4690, Y=5566 + +Button A: X+54, Y+20 +Button B: X+39, Y+73 +Prize: X=16733, Y=11735 + +Button A: X+30, Y+15 +Button B: X+22, Y+35 +Prize: X=2952, Y=1980 + +Button A: X+20, Y+78 +Button B: X+86, Y+43 +Prize: X=7874, Y=4685 + +Button A: X+95, Y+22 +Button B: X+77, Y+92 +Prize: X=10628, Y=7208 + +Button A: X+77, Y+13 +Button B: X+36, Y+69 +Prize: X=2662, Y=3218 + +Button A: X+18, Y+49 +Button B: X+38, Y+22 +Prize: X=2140, Y=10210 + +Button A: X+22, Y+64 +Button B: X+68, Y+24 +Prize: X=14384, Y=1912 + +Button A: X+12, Y+34 +Button B: X+72, Y+37 +Prize: X=8864, Y=18895 + +Button A: X+50, Y+69 +Button B: X+67, Y+12 +Prize: X=7166, Y=2004 + +Button A: X+47, Y+31 +Button B: X+13, Y+75 +Prize: X=2386, Y=2238 + +Button A: X+24, Y+67 +Button B: X+57, Y+18 +Prize: X=17177, Y=13222 + +Button A: X+50, Y+22 +Button B: X+21, Y+38 +Prize: X=11997, Y=16114 + +Button A: X+61, Y+25 +Button B: X+29, Y+65 +Prize: X=845, Y=665 \ No newline at end of file diff --git a/rust/y2024/src/bin/solution_2024_13.rs b/rust/y2024/src/bin/solution_2024_13.rs new file mode 100644 index 00000000..96294ea5 --- /dev/null +++ b/rust/y2024/src/bin/solution_2024_13.rs @@ -0,0 +1,137 @@ +use std::str::FromStr; + +use advent_of_code_common::bool_ops::BoolResultOps; +use advent_of_code_common::coords2d::Coords2D; +use advent_of_code_common::math::solve_two_variable_integer_linear_equation_system; +use advent_of_code_common::parsing::{ + Error, parse_pair_unsafe, parse_segments_separated_by_double_newline, +}; + +const DATA: &str = include_str!("../../resources/13.txt"); + +type N = i64; +type Data = Vec; + +#[derive(Debug)] +struct Machine { + button_a: Coords2D, + button_b: Coords2D, + prize: Coords2D, +} + +impl Machine { + #[must_use] + pub fn solve(&self) -> Option { + solve_two_variable_integer_linear_equation_system( + self.button_a.x, + self.button_b.x, + self.button_a.y, + self.button_b.y, + self.prize.x, + self.prize.y, + ) + .map(|(a, b)| a * 3 + b) + } +} + +impl FromStr for Machine { + type Err = Error; + + fn from_str(s: &str) -> Result { + let lines: Vec<_> = s.lines().collect(); + (lines.len() == 3).then_ok_unit(|| format!("Invalid number of lines: {}", lines.len()))?; + let a = lines[0] + .strip_prefix("Button A: ") + .ok_or("Missing prefix 'Button A: '")?; + let b = lines[1] + .strip_prefix("Button B: ") + .ok_or("Missing prefix 'Button B: '")?; + let p = lines[2] + .strip_prefix("Prize: ") + .ok_or("Missing prefix 'Prize: '")?; + + let int_helper = |prefix: &str, s: &str| -> Result { + s.strip_prefix(prefix) + .ok_or(format!("Missing prefix '{}'", prefix))? + .parse() + .map_err(|e| format!("{e:?}")) + }; + let (ax, ay) = + parse_pair_unsafe(a, ", ", |x| int_helper("X+", x), |y| int_helper("Y+", y))?; + let (bx, by) = + parse_pair_unsafe(b, ", ", |x| int_helper("X+", x), |y| int_helper("Y+", y))?; + let (px, py) = + parse_pair_unsafe(p, ", ", |x| int_helper("X=", x), |y| int_helper("Y=", y))?; + + Ok(Machine { + button_a: Coords2D::new(ax, ay), + button_b: Coords2D::new(bx, by), + prize: Coords2D::new(px, py), + }) + } +} + +fn parse(input: &str) -> Result { + parse_segments_separated_by_double_newline(input) +} + +fn solve_1(data: &Data) -> N { + data.iter().flat_map(Machine::solve).sum() +} + +fn solve_2(data: &Data) -> N { + let adjusted = data + .iter() + .map(|m| { + Machine { + button_a: m.button_a, + button_b: m.button_b, + prize: m.prize + Coords2D::new(10000000000000, 10000000000000), + } + }) + .collect(); + + solve_1(&adjusted) +} + +fn main() -> Result<(), Error> { + let data = parse(DATA)?; + + let result_1 = solve_1(&data); + println!("Part 1: {result_1}"); + + let result_2 = solve_2(&data); + println!("Part 2: {result_2}"); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + const TEST_DATA: &str = include_str!("../../resources/13-test-00.txt"); + + fn test_data() -> Data { + parse(TEST_DATA).unwrap() + } + + fn real_data() -> Data { + parse(DATA).unwrap() + } + + #[test] + fn test_solve_1_test() { + assert_eq!(solve_1(&test_data()), 480); + } + + #[test] + fn test_solve_1_real() { + assert_eq!(solve_1(&real_data()), 25751); + } + + #[test] + fn test_solve_2_real() { + assert_eq!(solve_2(&real_data()), 108528956728655); + } +} diff --git a/scala2/src/main/resources/2024/14-test-00.txt b/scala2/src/main/resources/2024/14-test-00.txt new file mode 100644 index 00000000..a8b6c947 --- /dev/null +++ b/scala2/src/main/resources/2024/14-test-00.txt @@ -0,0 +1 @@ +noop \ No newline at end of file diff --git a/scala2/src/main/resources/2024/14.txt b/scala2/src/main/resources/2024/14.txt new file mode 100644 index 00000000..a8b6c947 --- /dev/null +++ b/scala2/src/main/resources/2024/14.txt @@ -0,0 +1 @@ +noop \ No newline at end of file diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala index 6043ea74..c0eb36ad 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent13.scala @@ -1,11 +1,15 @@ package jurisk.adventofcode.y2024 +import jurisk.adventofcode.y2024.Advent13.SolutionMode.BruteForce +import jurisk.adventofcode.y2024.Advent13.SolutionMode.LinearEquations +import jurisk.adventofcode.y2024.Advent13.SolutionMode.Z3 +import jurisk.adventofcode.y2024.Advent13.SolutionMode.Z3Mode.External +import jurisk.adventofcode.y2024.Advent13.SolutionMode.Z3Mode.Internal import jurisk.geometry.Coordinates2D -import jurisk.optimization.ImplicitConversions.{ - RichArithExprIntSort, - RichExpr, - RichLong, -} +import jurisk.math.LinearEquationSystems +import jurisk.optimization.ImplicitConversions.RichArithExprIntSort +import jurisk.optimization.ImplicitConversions.RichExpr +import jurisk.optimization.ImplicitConversions.RichLong import jurisk.optimization.Optimizer import jurisk.utils.FileInput._ import jurisk.utils.Parsing.StringOps @@ -19,8 +23,15 @@ object Advent13 { sealed trait SolutionMode object SolutionMode { - case object InternalZ3 extends SolutionMode - case object ExternalZ3 extends SolutionMode + final case class BruteForce(limit: Int) extends SolutionMode + final case class Z3(mode: Z3Mode) extends SolutionMode + case object LinearEquations extends SolutionMode + + sealed trait Z3Mode + object Z3Mode { + case object Internal extends Z3Mode + case object External extends Z3Mode + } } private val CostA = 3 @@ -31,7 +42,30 @@ object Advent13 { buttonB: C, prize: C, ) { - def bruteForceConstrained(limit: Int = 100): Option[N] = { + def solve( + solutionMode: SolutionMode + ): Option[N] = + solutionMode match { + case BruteForce(limit) => bruteForceConstrained(limit) + case Z3(mode) => solveZ3(mode) + case LinearEquations => linearEquations + } + + private def linearEquations: Option[N] = + LinearEquationSystems + .solveTwoVariablesInteger( + buttonA.x, + buttonB.x, + buttonA.y, + buttonB.y, + prize.x, + prize.y, + ) + .map { case (a, b) => + CostA * a + CostB * b + } + + private def bruteForceConstrained(limit: Int = 100): Option[N] = { val results = for { a <- 0L to limit b <- 0L to limit @@ -42,8 +76,8 @@ object Advent13 { results.minOption } - def solve( - solutionMode: SolutionMode = SolutionMode.InternalZ3 + private def solveZ3( + z3Mode: SolutionMode.Z3Mode ): Option[N] = { println(s"Solving $this") @@ -70,15 +104,15 @@ object Advent13 { val _ = minimize(cost) - solutionMode match { - case SolutionMode.InternalZ3 => + z3Mode match { + case Internal => @nowarn("cat=deprecation") val modelResult = checkAndGetModel() modelResult.map { m => m.getConstInterp(cost).toString.toLong } - case SolutionMode.ExternalZ3 => + case External => runExternal("cost").map { m => val List(cost) = m.map(resultToLong) cost @@ -109,14 +143,14 @@ object Advent13 { input.parseSections(Machine.parse) def part1(data: Input): N = - data.flatMap(_.bruteForceConstrained()).sum + data.flatMap(_.solve(BruteForce(100))).sum def part2(data: Input): N = { val adjusted = data.map(m => m.copy(prize = m.prize + Coordinates2D(10000000000000L, 10000000000000L)) ) - adjusted.flatMap(_.solve()).sum + adjusted.flatMap(_.solve(LinearEquations)).sum } def parseFile(fileName: String): Input = diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent14.scala b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent14.scala new file mode 100644 index 00000000..159aec27 --- /dev/null +++ b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent14.scala @@ -0,0 +1,48 @@ +package jurisk.adventofcode.y2024 + +import jurisk.utils.FileInput._ +import jurisk.utils.Parsing.StringOps + +object Advent14 { + type Input = List[Command] + type N = Long + + sealed trait Command extends Product with Serializable + object Command { + case object Noop extends Command + final case class Something( + values: List[N] + ) extends Command + final case class Other(value: String) extends Command + + def parse(s: String): Command = + s match { + case "noop" => Noop + case s"something $rem" => Something(rem.extractLongList) + case s if s.nonEmpty => Other(s) + case _ => s.failedToParse + } + } + + def parse(input: String): Input = + input.parseLines(Command.parse) + + def part1(data: Input): N = + 0 + + def part2(data: Input): N = + 0 + + def parseFile(fileName: String): Input = + parse(readFileText(fileName)) + + def fileName(suffix: String): String = + s"2024/14$suffix.txt" + + def main(args: Array[String]): Unit = { + val realData: Input = parseFile(fileName("")) + + println(s"Part 1: ${part1(realData)}") + println(s"Part 2: ${part2(realData)}") + } +} diff --git a/scala2/src/main/scala/jurisk/math/LinearEquationSystems.scala b/scala2/src/main/scala/jurisk/math/LinearEquationSystems.scala new file mode 100644 index 00000000..4ee461bb --- /dev/null +++ b/scala2/src/main/scala/jurisk/math/LinearEquationSystems.scala @@ -0,0 +1,35 @@ +package jurisk.math + +import mouse.all.booleanSyntaxMouse + +import scala.math.Integral.Implicits.infixIntegralOps + +object LinearEquationSystems { + // Solve a system of two linear equations with two variables, only integer solutions are supported: + // a * x + b * y = e + // c * x + d * y = f + def solveTwoVariablesInteger[N: Integral]( + a: N, + b: N, + c: N, + d: N, + e: N, + f: N, + ): Option[(N, N)] = { + val n = implicitly[Integral[N]] + + def div(dividend: N, divisor: N): Option[N] = + (dividend % divisor == n.zero).option(dividend / divisor) + + val det = a * d - b * c + if (det == n.zero) None + else { + val x = div(e * d - b * f, det) + val y = div(a * f - e * c, det) + (x, y) match { + case (Some(x), Some(y)) => Some((x, y)) + case _ => None + } + } + } +} diff --git a/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala b/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala index deb48d2c..72fb7b0d 100644 --- a/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala +++ b/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent13Spec.scala @@ -1,7 +1,11 @@ package jurisk.adventofcode.y2024 import Advent13._ -import jurisk.adventofcode.y2024.Advent13.SolutionMode.{ExternalZ3, InternalZ3} +import jurisk.adventofcode.y2024.Advent13.SolutionMode.{LinearEquations, Z3} +import jurisk.adventofcode.y2024.Advent13.SolutionMode.Z3Mode.{ + External, + Internal, +} import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers._ @@ -23,8 +27,9 @@ class Advent13Spec extends AnyFreeSpec { "test #0" in { val example = testData.head val expected = Some(80 * 3 + 40) - example.solve(InternalZ3) shouldEqual expected - example.solve(ExternalZ3) shouldEqual expected + example.solve(Z3(Internal)) shouldEqual expected + example.solve(Z3(External)) shouldEqual expected + example.solve(LinearEquations) shouldEqual expected } "real" in { diff --git a/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent14Spec.scala b/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent14Spec.scala new file mode 100644 index 00000000..36f15d2d --- /dev/null +++ b/scala2/src/test/scala/jurisk/adventofcode/y2024/Advent14Spec.scala @@ -0,0 +1,30 @@ +package jurisk.adventofcode.y2024 + +import Advent14._ +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.should.Matchers._ + +class Advent14Spec extends AnyFreeSpec { + private def testData = parseFile(fileName("-test-00")) + private def realData = parseFile(fileName("")) + + "part 1" - { + "test" in { + part1(testData) shouldEqual 0 + } + + "real" in { + part1(realData) shouldEqual 0 + } + } + + "part 2" - { + "test" in { + part2(testData) shouldEqual 0 + } + + "real" in { + part2(realData) shouldEqual 0 + } + } +} diff --git a/scala2/src/test/scala/jurisk/math/LinearEquationSystemsSpec.scala b/scala2/src/test/scala/jurisk/math/LinearEquationSystemsSpec.scala new file mode 100644 index 00000000..c2f0fd1f --- /dev/null +++ b/scala2/src/test/scala/jurisk/math/LinearEquationSystemsSpec.scala @@ -0,0 +1,21 @@ +package jurisk.math + +import org.scalatest.freespec.AnyFreeSpec + +class LinearEquationSystemsSpec extends AnyFreeSpec { + "solveTwoVariablesInteger" - { + "should solve a system of two linear equations with two variables" in { + assert( + LinearEquationSystems.solveTwoVariablesInteger(2, 1, 3, -1, 15, + 5) === Some((4, 7)) + ) + } + + "should return None if the system of two linear equations with two variables has no solution" in { + assert( + LinearEquationSystems.solveTwoVariablesInteger(1, 2, 1, 2, 5, + 6) === None + ) + } + } +}