Compare commits
171 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
77b24dd148 | ||
|
b9341bdecc | ||
|
ae5527b72d | ||
|
96f139fe10 | ||
|
683cac334c | ||
|
146d025d41 | ||
|
954ef1e6ce | ||
|
24580fdfd8 | ||
|
7c0a124a5d | ||
|
11e32ddfda | ||
|
4dcdab9931 | ||
|
f965eea33a | ||
|
c3c73ee517 | ||
|
8308940674 | ||
|
bc06f86fdc | ||
|
2c25b33bcc | ||
|
8651884ca6 | ||
|
7447c7b536 | ||
|
3e8d796b2e | ||
|
fcd4b47951 | ||
|
b15131bf1e | ||
|
2c5c51e05f | ||
|
51275dd539 | ||
|
f1ae1c598f | ||
|
91ba8ec86f | ||
|
323f810fcd | ||
|
8969ea895f | ||
|
67f7eef636 | ||
|
4f8b50577a | ||
|
67e41503c9 | ||
|
30e0bb3665 | ||
|
291c627c79 | ||
|
89306f4a04 | ||
|
721d69e766 | ||
|
356fd35b08 | ||
|
92bd85e1dd | ||
|
22129048e7 | ||
|
781e4cd6e1 | ||
|
46558672e8 | ||
|
3c544c559b | ||
|
4367a5183a | ||
|
03e4e75978 | ||
|
98eb515c19 | ||
|
dd3f332870 | ||
|
9d7ef94fa6 | ||
ce315b8778 | |||
|
ab4e3e199c | ||
|
2c1a0b919b | ||
|
cd6f97cd7e | ||
|
5312755f32 | ||
|
55cb5ed745 | ||
|
f0d8e156a9 | ||
|
c19279fad3 | ||
0d50b44c37 | |||
|
b32d46b641 | ||
|
5c43eb2c73 | ||
|
540fe37b9d | ||
|
2a4f923552 | ||
|
4821db89cc | ||
|
d1733a5888 | ||
|
dd8458fa96 | ||
|
850c66cd8d | ||
|
2597235d0c | ||
|
db9a3b3ed3 | ||
|
31b0e9f195 | ||
|
5b07e73382 | ||
|
c1732baa0d | ||
|
de96ab0e25 | ||
|
cd58b7861b | ||
|
8d2f61fa65 | ||
|
3d7dd37c11 | ||
|
f373528b06 | ||
|
3fe9555cb1 | ||
|
f94e2bd831 | ||
|
685f1e56d7 | ||
|
ea0c9e7812 | ||
|
52cb793d06 | ||
|
4a2a63b0b0 | ||
|
9f96abbd43 | ||
|
57bf025622 | ||
|
bcadb68189 | ||
|
d7d7837c1f | ||
|
82fab771ab | ||
|
85fff24cc1 | ||
|
9326d6c76c | ||
|
eefb3ceb44 | ||
|
2959387bcd | ||
|
41b07cfe83 | ||
|
981e745eb0 | ||
|
8760e47283 | ||
|
1db3ab9090 | ||
|
9a1769e200 | ||
|
8c150c0bb1 | ||
|
69929b28dd | ||
f41b50c9e0 | |||
|
d6b99454d2 | ||
|
1fc3c1632d | ||
|
9141029557 | ||
|
9d5b57fd56 | ||
|
0756cf74c4 | ||
|
bd5727c758 | ||
|
0ebd823656 | ||
|
7c6c9e5995 | ||
|
f4cd8318b0 | ||
|
e06f3da2bd | ||
|
fb8a911d4d | ||
|
4e1c71b221 | ||
|
0f8a272b71 | ||
|
508c8cdc42 | ||
|
ee55c807ef | ||
|
10a5b92740 | ||
|
d3eacd48aa | ||
|
53f05058f3 | ||
|
c0ea724d4c | ||
|
57c15270dc | ||
|
7533dd0b11 | ||
|
dd80b30e26 | ||
|
0508d95e33 | ||
|
f2cc3e4d16 | ||
|
9526c383f3 | ||
|
dd88a1838d | ||
|
72ffd399b3 | ||
|
020ad7c6d1 | ||
|
1fc4d6531d | ||
|
d8bb659e78 | ||
|
6ade69ac35 | ||
|
ed7149e5f4 | ||
|
e2df3c4825 | ||
|
266703cdc0 | ||
|
f635ea3c97 | ||
|
304aeb16bd | ||
|
ed00c72e59 | ||
|
835458e0f3 | ||
|
3b9e9a2c8f | ||
|
fadb2a71c2 | ||
|
91434051c6 | ||
|
c1161f6c1f | ||
|
3b4efce02f | ||
|
2aaf55b72f | ||
|
d493856d20 | ||
|
67647c7923 | ||
|
3c61e5cb7f | ||
|
37e0e1ef06 | ||
|
725e18d480 | ||
|
3d383527e4 | ||
|
48e8dff52b | ||
|
0b53406b52 | ||
|
232d019b40 | ||
|
35190adcdf | ||
|
d4199b2810 | ||
|
971c1b0dda | ||
|
4fe1f521b7 | ||
|
38b1d86514 | ||
|
4899583b15 | ||
|
792951afa8 | ||
|
5004aa3376 | ||
|
9ba338a4f1 | ||
|
8795d7a276 | ||
|
89adfb151a | ||
|
9fd8a5866a | ||
|
7564fac345 | ||
|
6bd27e4bca | ||
|
26cfe60f16 | ||
|
32a1220072 | ||
|
9852aea94b | ||
|
f76767ca6d | ||
|
1b60725e9c | ||
|
b8cb2cb0b9 | ||
|
228f7501bb | ||
|
f4ef0a2666 | ||
|
60e68ed31c |
518
poetry.lock
generated
518
poetry.lock
generated
@ -1,4 +1,15 @@
|
|||||||
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "absl-py"
|
||||||
|
version = "2.1.0"
|
||||||
|
description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "absl-py-2.1.0.tar.gz", hash = "sha256:7820790efbb316739cde8b4e19357243fc3608a152024288513dd968d7d959ff"},
|
||||||
|
{file = "absl_py-2.1.0-py3-none-any.whl", hash = "sha256:526a04eadab8b4ee719ce68f204172ead1027549089702d99b9059f129ff1308"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "appnope"
|
name = "appnope"
|
||||||
@ -133,6 +144,30 @@ traitlets = ">=4"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
test = ["pytest"]
|
test = ["pytest"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cplex"
|
||||||
|
version = "22.1.1.2"
|
||||||
|
description = "A Python interface to the CPLEX Callable Library, Community Edition."
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "cplex-22.1.1.2-cp310-cp310-macosx_10_6_x86_64.whl", hash = "sha256:d74a91f6e9c6f4ad4c7c69f7ab937ea9e91178a556f6f105c87eef9e434ea42e"},
|
||||||
|
{file = "cplex-22.1.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b066b5aa01fcf7cb471ad41920b3fecdd87dc95686e9a7031ff470873f0db10f"},
|
||||||
|
{file = "cplex-22.1.1.2-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:a7230032928b1a657c384d3f49c04d8b80d0ab8134a2f4c0b26ff50e71ec767a"},
|
||||||
|
{file = "cplex-22.1.1.2-cp310-cp310-manylinux2014_ppc64le.whl", hash = "sha256:dd81e8ee7a7f1fb5769bcbb1349b084b37c495cbd0db1a095d774f97d790ee3c"},
|
||||||
|
{file = "cplex-22.1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:68b33bb9ff84be3442a6f71000e7214781c6aa8674143a9aa79cb9a84e697dfd"},
|
||||||
|
{file = "cplex-22.1.1.2-cp311-cp311-macosx_10_6_x86_64.whl", hash = "sha256:cda2f59af50d6c3d6476b2e38aba1e947f9bd72591d71961a9d53b5582062ba9"},
|
||||||
|
{file = "cplex-22.1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0f69a539ed50994e26e32c3d2b203eb5f112d1ba64400241614e1a91c0933974"},
|
||||||
|
{file = "cplex-22.1.1.2-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:2a0f6984980779e6878a6cded52ee08806bae49af6bd209c7740549417e69e96"},
|
||||||
|
{file = "cplex-22.1.1.2-cp311-cp311-manylinux2014_ppc64le.whl", hash = "sha256:0ac0005414a09facbeaa976a89b3153d4ed15f23a89bf5d283f65d4e951f63be"},
|
||||||
|
{file = "cplex-22.1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:46550cac476d74cc95dc3abf6f9bfe08c9fd61d889e20f2028f754b9fe503b88"},
|
||||||
|
{file = "cplex-22.1.1.2-cp39-cp39-macosx_10_6_x86_64.whl", hash = "sha256:21f6fd1ad4876a7775e64fe8a1fb43f6bb7a010c5e099abdb8c01887a8cc1d84"},
|
||||||
|
{file = "cplex-22.1.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d6acbf74c3fe32f2138a98730d1ebe3fa275c8c3fdcd8b1f68d312bfe9ef6899"},
|
||||||
|
{file = "cplex-22.1.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3211f9c84f44c6317ea1e83a6c2ff1cfdc08532f421987b7faa8c07a018dfae5"},
|
||||||
|
{file = "cplex-22.1.1.2-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:648ad8c1c83ea30b0802c571a0dbf7fac23dcb9dc121ea0768c1794313aec2a7"},
|
||||||
|
{file = "cplex-22.1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:285b26008a77942c6c9c29bff91e1658c1beed2aa520e1a8b26137d81abf81dc"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "debugpy"
|
name = "debugpy"
|
||||||
version = "1.8.9"
|
version = "1.8.9"
|
||||||
@ -179,6 +214,19 @@ files = [
|
|||||||
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "docplex"
|
||||||
|
version = "2.28.240"
|
||||||
|
description = "The IBM Decision Optimization CPLEX Modeling for Python"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "docplex-2.28.240.tar.gz", hash = "sha256:c0de407e33f8709bb4cd91b6efeb96fd88bfecbdce2caec51afb79253bde6ff5"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
six = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "exceptiongroup"
|
name = "exceptiongroup"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
@ -207,6 +255,50 @@ files = [
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
|
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imageio"
|
||||||
|
version = "2.36.1"
|
||||||
|
description = "Library for reading and writing a wide range of image, video, scientific, and volumetric data formats."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
files = [
|
||||||
|
{file = "imageio-2.36.1-py3-none-any.whl", hash = "sha256:20abd2cae58e55ca1af8a8dcf43293336a59adf0391f1917bf8518633cfc2cdf"},
|
||||||
|
{file = "imageio-2.36.1.tar.gz", hash = "sha256:e4e1d231f47f9a9e16100b0f7ce1a86e8856fb4d1c0fa2c4365a316f1746be62"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
numpy = "*"
|
||||||
|
pillow = ">=8.3.2"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
all-plugins = ["astropy", "av", "imageio-ffmpeg", "numpy (>2)", "pillow-heif", "psutil", "rawpy", "tifffile"]
|
||||||
|
all-plugins-pypy = ["av", "imageio-ffmpeg", "pillow-heif", "psutil", "tifffile"]
|
||||||
|
build = ["wheel"]
|
||||||
|
dev = ["black", "flake8", "fsspec[github]", "pytest", "pytest-cov"]
|
||||||
|
docs = ["numpydoc", "pydata-sphinx-theme", "sphinx (<6)"]
|
||||||
|
ffmpeg = ["imageio-ffmpeg", "psutil"]
|
||||||
|
fits = ["astropy"]
|
||||||
|
full = ["astropy", "av", "black", "flake8", "fsspec[github]", "gdal", "imageio-ffmpeg", "itk", "numpy (>2)", "numpydoc", "pillow-heif", "psutil", "pydata-sphinx-theme", "pytest", "pytest-cov", "rawpy", "sphinx (<6)", "tifffile", "wheel"]
|
||||||
|
gdal = ["gdal"]
|
||||||
|
itk = ["itk"]
|
||||||
|
linting = ["black", "flake8"]
|
||||||
|
pillow-heif = ["pillow-heif"]
|
||||||
|
pyav = ["av"]
|
||||||
|
rawpy = ["numpy (>2)", "rawpy"]
|
||||||
|
test = ["fsspec[github]", "pytest", "pytest-cov"]
|
||||||
|
tifffile = ["tifffile"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "immutabledict"
|
||||||
|
version = "4.2.1"
|
||||||
|
description = "Immutable wrapper around dictionaries (a fork of frozendict)"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "immutabledict-4.2.1-py3-none-any.whl", hash = "sha256:c56a26ced38c236f79e74af3ccce53772827cef5c3bce7cab33ff2060f756373"},
|
||||||
|
{file = "immutabledict-4.2.1.tar.gz", hash = "sha256:d91017248981c72eb66c8ff9834e99c2f53562346f23e7f51e7a5ebcf66a3bcc"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipykernel"
|
name = "ipykernel"
|
||||||
version = "6.29.5"
|
version = "6.29.5"
|
||||||
@ -430,68 +522,133 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "numpy"
|
name = "numpy"
|
||||||
version = "2.1.3"
|
version = "2.2.0"
|
||||||
description = "Fundamental package for array computing in Python"
|
description = "Fundamental package for array computing in Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10"
|
python-versions = ">=3.10"
|
||||||
files = [
|
files = [
|
||||||
{file = "numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff"},
|
{file = "numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa"},
|
||||||
{file = "numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5"},
|
{file = "numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219"},
|
||||||
{file = "numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1"},
|
{file = "numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e"},
|
||||||
{file = "numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd"},
|
{file = "numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9"},
|
||||||
{file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3"},
|
{file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3"},
|
||||||
{file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098"},
|
{file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83"},
|
||||||
{file = "numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c"},
|
{file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a"},
|
||||||
{file = "numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4"},
|
{file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31"},
|
||||||
{file = "numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23"},
|
{file = "numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661"},
|
||||||
{file = "numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0"},
|
{file = "numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4"},
|
||||||
{file = "numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d"},
|
{file = "numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6"},
|
||||||
{file = "numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41"},
|
{file = "numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90"},
|
||||||
{file = "numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9"},
|
{file = "numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608"},
|
||||||
{file = "numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09"},
|
{file = "numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da"},
|
||||||
{file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a"},
|
{file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74"},
|
||||||
{file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b"},
|
{file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e"},
|
||||||
{file = "numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee"},
|
{file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b"},
|
||||||
{file = "numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0"},
|
{file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d"},
|
||||||
{file = "numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9"},
|
{file = "numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410"},
|
||||||
{file = "numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2"},
|
{file = "numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73"},
|
||||||
{file = "numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e"},
|
{file = "numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3"},
|
||||||
{file = "numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958"},
|
{file = "numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e"},
|
||||||
{file = "numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8"},
|
{file = "numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67"},
|
||||||
{file = "numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564"},
|
{file = "numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e"},
|
||||||
{file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512"},
|
{file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038"},
|
||||||
{file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b"},
|
{file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03"},
|
||||||
{file = "numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc"},
|
{file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a"},
|
||||||
{file = "numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0"},
|
{file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef"},
|
||||||
{file = "numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9"},
|
{file = "numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1"},
|
||||||
{file = "numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a"},
|
{file = "numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f"},
|
{file = "numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598"},
|
{file = "numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57"},
|
{file = "numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe"},
|
{file = "numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43"},
|
{file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56"},
|
{file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a"},
|
{file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef"},
|
{file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f"},
|
{file = "numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed"},
|
{file = "numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f"},
|
{file = "numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4"},
|
{file = "numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e"},
|
{file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0"},
|
{file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408"},
|
{file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6"},
|
{file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f"},
|
{file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17"},
|
{file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48"},
|
{file = "numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066"},
|
||||||
{file = "numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4"},
|
{file = "numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881"},
|
||||||
{file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f"},
|
{file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773"},
|
||||||
{file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4"},
|
{file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e"},
|
||||||
{file = "numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d"},
|
{file = "numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7"},
|
||||||
{file = "numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb"},
|
{file = "numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221"},
|
||||||
{file = "numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"},
|
{file = "numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opencv-python"
|
||||||
|
version = "4.10.0.84"
|
||||||
|
description = "Wrapper package for OpenCV python bindings."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "opencv-python-4.10.0.84.tar.gz", hash = "sha256:72d234e4582e9658ffea8e9cae5b63d488ad06994ef12d81dc303b17472f3526"},
|
||||||
|
{file = "opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc182f8f4cda51b45f01c64e4cbedfc2f00aff799debebc305d8d0210c43f251"},
|
||||||
|
{file = "opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:71e575744f1d23f79741450254660442785f45a0797212852ee5199ef12eed98"},
|
||||||
|
{file = "opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09a332b50488e2dda866a6c5573ee192fe3583239fb26ff2f7f9ceb0bc119ea6"},
|
||||||
|
{file = "opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ace140fc6d647fbe1c692bcb2abce768973491222c067c131d80957c595b71f"},
|
||||||
|
{file = "opencv_python-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:2db02bb7e50b703f0a2d50c50ced72e95c574e1e5a0bb35a8a86d0b35c98c236"},
|
||||||
|
{file = "opencv_python-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:32dbbd94c26f611dc5cc6979e6b7aa1f55a64d6b463cc1dcd3c95505a63e48fe"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
numpy = [
|
||||||
|
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||||
|
{version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""},
|
||||||
|
{version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""},
|
||||||
|
{version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ortools"
|
||||||
|
version = "9.11.4210"
|
||||||
|
description = "Google OR-Tools python libraries and modules"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "ortools-9.11.4210-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:127f20f03ce04c28f979eac635d1cacdc01597c9e035a1981070506294d7db9c"},
|
||||||
|
{file = "ortools-9.11.4210-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:250c62ba9e5fcaf18ada449bc0128c71bb0dbea83ddec5559cc506cff920235c"},
|
||||||
|
{file = "ortools-9.11.4210-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c15cefc7bc71aa2f70bee157aaa7e51ed8cb74c3edd499d15b6f5cd79cdf5b"},
|
||||||
|
{file = "ortools-9.11.4210-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c3693f0cb07ee0b5d36c4817bcfe05a745e3a613798a2ed62eb998d7fff979c"},
|
||||||
|
{file = "ortools-9.11.4210-cp310-cp310-win_amd64.whl", hash = "sha256:6b9d4ae6c7e9efac7cbef8a6289e97e238c2f0a8ef587b3e56b71af14c2f04e6"},
|
||||||
|
{file = "ortools-9.11.4210-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:0f902caa1576d737714f6a4fa165db62469bce82115e250409607197b3b6b434"},
|
||||||
|
{file = "ortools-9.11.4210-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6f3e2869396dc6d8ee2d11b65d6f88f6386bb3ad64212c0ad7a6d32ddcb48ca"},
|
||||||
|
{file = "ortools-9.11.4210-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a56c5844ff927ce3c5428159cdd01b7fdbe243e8062bf1dfdb2e0eb305a55a30"},
|
||||||
|
{file = "ortools-9.11.4210-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5a17a37aeaa7d149e2fe8c8dfd5f09630ae28ad734a109ad55536605f8059df"},
|
||||||
|
{file = "ortools-9.11.4210-cp311-cp311-win_amd64.whl", hash = "sha256:d9b858f0273e19f81555428d54d407428d0a70a8cb5df2c320935bb735f2c6bb"},
|
||||||
|
{file = "ortools-9.11.4210-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:079bea08c6341dcfe3fb9586eb6edec6ae80f4ed16ed366fd7a46ef4b5709009"},
|
||||||
|
{file = "ortools-9.11.4210-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f55124f9d1afa6434d0d6de07c6a4eb836f29b00b3413d27138634d5d79b606"},
|
||||||
|
{file = "ortools-9.11.4210-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f48e3d4053a169440608d881c1abd2a706db885d9b0af85bf45b444a1fec244"},
|
||||||
|
{file = "ortools-9.11.4210-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03ecd32e5d760d48e59832ef6bf724f8cac95e4e40db72a7fb912abf7adcf931"},
|
||||||
|
{file = "ortools-9.11.4210-cp312-cp312-win_amd64.whl", hash = "sha256:bc1b6e4cc0a121ef888481a99194765e6df72d4d3da81f928543171a2bac8cbb"},
|
||||||
|
{file = "ortools-9.11.4210-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:a503291dc12dc44da48567c5e1f79c77cd054fb86176f2c99d2860bc5a57e03d"},
|
||||||
|
{file = "ortools-9.11.4210-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2fd0aa0b4edfb3088f086bc05d42a381cc3d03da4b8ad18ce18ba213ab2c719f"},
|
||||||
|
{file = "ortools-9.11.4210-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c242738a49279c3a58b4611a64dd24634f1638f4dbb435163e3f9308e7c84c9"},
|
||||||
|
{file = "ortools-9.11.4210-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db9186bbc7a30538f277e704e9e77ff52bc75aa2c17095a125b2dc212a75cf8e"},
|
||||||
|
{file = "ortools-9.11.4210-cp38-cp38-win_amd64.whl", hash = "sha256:afcca4919e1095a79af0375276c5377a2d75794ebd592c4cc841d9979e0526e7"},
|
||||||
|
{file = "ortools-9.11.4210-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:a2828e91960c4e4fcf27bc6e200e2cd61ce1c42d4a3b95481a842a58c8315345"},
|
||||||
|
{file = "ortools-9.11.4210-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8d7d1a105f105502cf2f785816b09b796e5845fd47efac0dc0e3c0476b4c961a"},
|
||||||
|
{file = "ortools-9.11.4210-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f250641d7b822be25237fb78aed0878b07e8afdefddb700bafcc52f32ad520a"},
|
||||||
|
{file = "ortools-9.11.4210-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd2c0b319f4c0999360ab45a85d5764838c9dd0fd33437d12e32b2c07cbe04e4"},
|
||||||
|
{file = "ortools-9.11.4210-cp39-cp39-win_amd64.whl", hash = "sha256:219ffa56e8e4f52561586cea3dd55eb0f5d174a84c83a819d71debad766338e3"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
absl-py = ">=2.0.0"
|
||||||
|
immutabledict = ">=3.0.0"
|
||||||
|
numpy = ">=1.13.3"
|
||||||
|
pandas = ">=2.0.0"
|
||||||
|
protobuf = ">=5.26.1,<5.27"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "24.2"
|
version = "24.2"
|
||||||
@ -556,9 +713,9 @@ files = [
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
numpy = [
|
numpy = [
|
||||||
{version = ">=1.22.4", markers = "python_version < \"3.11\""},
|
|
||||||
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
|
|
||||||
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||||
|
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
|
||||||
|
{version = ">=1.22.4", markers = "python_version < \"3.11\""},
|
||||||
]
|
]
|
||||||
python-dateutil = ">=2.8.2"
|
python-dateutil = ">=2.8.2"
|
||||||
pytz = ">=2020.1"
|
pytz = ">=2020.1"
|
||||||
@ -640,6 +797,98 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
ptyprocess = ">=0.5"
|
ptyprocess = ">=0.5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pillow"
|
||||||
|
version = "11.0.0"
|
||||||
|
description = "Python Imaging Library (Fork)"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
files = [
|
||||||
|
{file = "pillow-11.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947"},
|
||||||
|
{file = "pillow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba"},
|
||||||
|
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086"},
|
||||||
|
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9"},
|
||||||
|
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488"},
|
||||||
|
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f"},
|
||||||
|
{file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb"},
|
||||||
|
{file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97"},
|
||||||
|
{file = "pillow-11.0.0-cp310-cp310-win32.whl", hash = "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50"},
|
||||||
|
{file = "pillow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c"},
|
||||||
|
{file = "pillow-11.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1"},
|
||||||
|
{file = "pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc"},
|
||||||
|
{file = "pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a"},
|
||||||
|
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3"},
|
||||||
|
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5"},
|
||||||
|
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b"},
|
||||||
|
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa"},
|
||||||
|
{file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306"},
|
||||||
|
{file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9"},
|
||||||
|
{file = "pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5"},
|
||||||
|
{file = "pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291"},
|
||||||
|
{file = "pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9"},
|
||||||
|
{file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"},
|
||||||
|
{file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"},
|
||||||
|
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4"},
|
||||||
|
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f"},
|
||||||
|
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9"},
|
||||||
|
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7"},
|
||||||
|
{file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6"},
|
||||||
|
{file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc"},
|
||||||
|
{file = "pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6"},
|
||||||
|
{file = "pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47"},
|
||||||
|
{file = "pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904"},
|
||||||
|
{file = "pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3"},
|
||||||
|
{file = "pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba"},
|
||||||
|
{file = "pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a"},
|
||||||
|
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916"},
|
||||||
|
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d"},
|
||||||
|
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7"},
|
||||||
|
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e"},
|
||||||
|
{file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f"},
|
||||||
|
{file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae"},
|
||||||
|
{file = "pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4"},
|
||||||
|
{file = "pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd"},
|
||||||
|
{file = "pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd"},
|
||||||
|
{file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2"},
|
||||||
|
{file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2"},
|
||||||
|
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b"},
|
||||||
|
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2"},
|
||||||
|
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830"},
|
||||||
|
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734"},
|
||||||
|
{file = "pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316"},
|
||||||
|
{file = "pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06"},
|
||||||
|
{file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273"},
|
||||||
|
{file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790"},
|
||||||
|
{file = "pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944"},
|
||||||
|
{file = "pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
|
||||||
|
fpx = ["olefile"]
|
||||||
|
mic = ["olefile"]
|
||||||
|
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
|
||||||
|
typing = ["typing-extensions"]
|
||||||
|
xmp = ["defusedxml"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "platformdirs"
|
name = "platformdirs"
|
||||||
version = "4.3.6"
|
version = "4.3.6"
|
||||||
@ -689,6 +938,26 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
wcwidth = "*"
|
wcwidth = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "protobuf"
|
||||||
|
version = "5.26.1"
|
||||||
|
description = ""
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "protobuf-5.26.1-cp310-abi3-win32.whl", hash = "sha256:3c388ea6ddfe735f8cf69e3f7dc7611e73107b60bdfcf5d0f024c3ccd3794e23"},
|
||||||
|
{file = "protobuf-5.26.1-cp310-abi3-win_amd64.whl", hash = "sha256:e6039957449cb918f331d32ffafa8eb9255769c96aa0560d9a5bf0b4e00a2a33"},
|
||||||
|
{file = "protobuf-5.26.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:38aa5f535721d5bb99861166c445c4105c4e285c765fbb2ac10f116e32dcd46d"},
|
||||||
|
{file = "protobuf-5.26.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:fbfe61e7ee8c1860855696e3ac6cfd1b01af5498facc6834fcc345c9684fb2ca"},
|
||||||
|
{file = "protobuf-5.26.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:f7417703f841167e5a27d48be13389d52ad705ec09eade63dfc3180a959215d7"},
|
||||||
|
{file = "protobuf-5.26.1-cp38-cp38-win32.whl", hash = "sha256:d693d2504ca96750d92d9de8a103102dd648fda04540495535f0fec7577ed8fc"},
|
||||||
|
{file = "protobuf-5.26.1-cp38-cp38-win_amd64.whl", hash = "sha256:9b557c317ebe6836835ec4ef74ec3e994ad0894ea424314ad3552bc6e8835b4e"},
|
||||||
|
{file = "protobuf-5.26.1-cp39-cp39-win32.whl", hash = "sha256:b9ba3ca83c2e31219ffbeb9d76b63aad35a3eb1544170c55336993d7a18ae72c"},
|
||||||
|
{file = "protobuf-5.26.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ee014c2c87582e101d6b54260af03b6596728505c79f17c8586e7523aaa8f8c"},
|
||||||
|
{file = "protobuf-5.26.1-py3-none-any.whl", hash = "sha256:da612f2720c0183417194eeaa2523215c4fcc1a1949772dc65f05047e08d5932"},
|
||||||
|
{file = "protobuf-5.26.1.tar.gz", hash = "sha256:8ca2a1d97c290ec7b16e4e5dff2e5ae150cc1582f55b5ab300d45cb0dfa90e51"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psutil"
|
name = "psutil"
|
||||||
version = "6.1.0"
|
version = "6.1.0"
|
||||||
@ -755,6 +1024,19 @@ files = [
|
|||||||
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
|
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pygifsicle"
|
||||||
|
version = "1.1.0"
|
||||||
|
description = "Python package wrapping the gifsicle library for editing and optimizing gifs."
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "pygifsicle-1.1.0.tar.gz", hash = "sha256:dcef433520ace4c1136dfc7060e77042142a3dbd6bdb6a19bd9149ef5cbe7441"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
test = ["pytest", "pytest-cov", "touch", "validate_version_code"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pygments"
|
name = "pygments"
|
||||||
version = "2.18.0"
|
version = "2.18.0"
|
||||||
@ -771,13 +1053,13 @@ windows-terminal = ["colorama (>=0.4.6)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyright"
|
name = "pyright"
|
||||||
version = "1.1.389"
|
version = "1.1.390"
|
||||||
description = "Command line wrapper for pyright"
|
description = "Command line wrapper for pyright"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "pyright-1.1.389-py3-none-any.whl", hash = "sha256:41e9620bba9254406dc1f621a88ceab5a88af4c826feb4f614d95691ed243a60"},
|
{file = "pyright-1.1.390-py3-none-any.whl", hash = "sha256:ecebfba5b6b50af7c1a44c2ba144ba2ab542c227eb49bc1f16984ff714e0e110"},
|
||||||
{file = "pyright-1.1.389.tar.gz", hash = "sha256:716bf8cc174ab8b4dcf6828c3298cac05c5ed775dda9910106a5dcfe4c7fe220"},
|
{file = "pyright-1.1.390.tar.gz", hash = "sha256:aad7f160c49e0fbf8209507a15e17b781f63a86a1facb69ca877c71ef2e9538d"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -1026,90 +1308,40 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""}
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.8.1"
|
version = "0.8.3"
|
||||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "ruff-0.8.1-py3-none-linux_armv6l.whl", hash = "sha256:fae0805bd514066f20309f6742f6ee7904a773eb9e6c17c45d6b1600ca65c9b5"},
|
{file = "ruff-0.8.3-py3-none-linux_armv6l.whl", hash = "sha256:8d5d273ffffff0acd3db5bf626d4b131aa5a5ada1276126231c4174543ce20d6"},
|
||||||
{file = "ruff-0.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8a4f7385c2285c30f34b200ca5511fcc865f17578383db154e098150ce0a087"},
|
{file = "ruff-0.8.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e4d66a21de39f15c9757d00c50c8cdd20ac84f55684ca56def7891a025d7e939"},
|
||||||
{file = "ruff-0.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd054486da0c53e41e0086e1730eb77d1f698154f910e0cd9e0d64274979a209"},
|
{file = "ruff-0.8.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c356e770811858bd20832af696ff6c7e884701115094f427b64b25093d6d932d"},
|
||||||
{file = "ruff-0.8.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2029b8c22da147c50ae577e621a5bfbc5d1fed75d86af53643d7a7aee1d23871"},
|
{file = "ruff-0.8.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c0a60a825e3e177116c84009d5ebaa90cf40dfab56e1358d1df4e29a9a14b13"},
|
||||||
{file = "ruff-0.8.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2666520828dee7dfc7e47ee4ea0d928f40de72056d929a7c5292d95071d881d1"},
|
{file = "ruff-0.8.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fb782f4db39501210ac093c79c3de581d306624575eddd7e4e13747e61ba18"},
|
||||||
{file = "ruff-0.8.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:333c57013ef8c97a53892aa56042831c372e0bb1785ab7026187b7abd0135ad5"},
|
{file = "ruff-0.8.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f26bc76a133ecb09a38b7868737eded6941b70a6d34ef53a4027e83913b6502"},
|
||||||
{file = "ruff-0.8.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:288326162804f34088ac007139488dcb43de590a5ccfec3166396530b58fb89d"},
|
{file = "ruff-0.8.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:01b14b2f72a37390c1b13477c1c02d53184f728be2f3ffc3ace5b44e9e87b90d"},
|
||||||
{file = "ruff-0.8.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b12c39b9448632284561cbf4191aa1b005882acbc81900ffa9f9f471c8ff7e26"},
|
{file = "ruff-0.8.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53babd6e63e31f4e96ec95ea0d962298f9f0d9cc5990a1bbb023a6baf2503a82"},
|
||||||
{file = "ruff-0.8.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:364e6674450cbac8e998f7b30639040c99d81dfb5bbc6dfad69bc7a8f916b3d1"},
|
{file = "ruff-0.8.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ae441ce4cf925b7f363d33cd6570c51435972d697e3e58928973994e56e1452"},
|
||||||
{file = "ruff-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22346f845fec132aa39cd29acb94451d030c10874408dbf776af3aaeb53284c"},
|
{file = "ruff-0.8.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7c65bc0cadce32255e93c57d57ecc2cca23149edd52714c0c5d6fa11ec328cd"},
|
||||||
{file = "ruff-0.8.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2f2f7a7e7648a2bfe6ead4e0a16745db956da0e3a231ad443d2a66a105c04fa"},
|
{file = "ruff-0.8.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5be450bb18f23f0edc5a4e5585c17a56ba88920d598f04a06bd9fd76d324cb20"},
|
||||||
{file = "ruff-0.8.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:adf314fc458374c25c5c4a4a9270c3e8a6a807b1bec018cfa2813d6546215540"},
|
{file = "ruff-0.8.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8faeae3827eaa77f5721f09b9472a18c749139c891dbc17f45e72d8f2ca1f8fc"},
|
||||||
{file = "ruff-0.8.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a885d68342a231b5ba4d30b8c6e1b1ee3a65cf37e3d29b3c74069cdf1ee1e3c9"},
|
{file = "ruff-0.8.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:db503486e1cf074b9808403991663e4277f5c664d3fe237ee0d994d1305bb060"},
|
||||||
{file = "ruff-0.8.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d2c16e3508c8cc73e96aa5127d0df8913d2290098f776416a4b157657bee44c5"},
|
{file = "ruff-0.8.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6567be9fb62fbd7a099209257fef4ad2c3153b60579818b31a23c886ed4147ea"},
|
||||||
{file = "ruff-0.8.1-py3-none-win32.whl", hash = "sha256:93335cd7c0eaedb44882d75a7acb7df4b77cd7cd0d2255c93b28791716e81790"},
|
{file = "ruff-0.8.3-py3-none-win32.whl", hash = "sha256:19048f2f878f3ee4583fc6cb23fb636e48c2635e30fb2022b3a1cd293402f964"},
|
||||||
{file = "ruff-0.8.1-py3-none-win_amd64.whl", hash = "sha256:2954cdbe8dfd8ab359d4a30cd971b589d335a44d444b6ca2cb3d1da21b75e4b6"},
|
{file = "ruff-0.8.3-py3-none-win_amd64.whl", hash = "sha256:f7df94f57d7418fa7c3ffb650757e0c2b96cf2501a0b192c18e4fb5571dfada9"},
|
||||||
{file = "ruff-0.8.1-py3-none-win_arm64.whl", hash = "sha256:55873cc1a473e5ac129d15eccb3c008c096b94809d693fc7053f588b67822737"},
|
{file = "ruff-0.8.3-py3-none-win_arm64.whl", hash = "sha256:fe2756edf68ea79707c8d68b78ca9a58ed9af22e430430491ee03e718b5e4936"},
|
||||||
{file = "ruff-0.8.1.tar.gz", hash = "sha256:3583db9a6450364ed5ca3f3b4225958b24f78178908d5c4bc0f46251ccca898f"},
|
{file = "ruff-0.8.3.tar.gz", hash = "sha256:5e7558304353b84279042fc584a4f4cb8a07ae79b2bf3da1a7551d960b5626d3"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scipy"
|
|
||||||
version = "1.14.1"
|
|
||||||
description = "Fundamental algorithms for scientific computing in Python"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.10"
|
|
||||||
files = [
|
|
||||||
{file = "scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389"},
|
|
||||||
{file = "scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3"},
|
|
||||||
{file = "scipy-1.14.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0"},
|
|
||||||
{file = "scipy-1.14.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3"},
|
|
||||||
{file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d"},
|
|
||||||
{file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69"},
|
|
||||||
{file = "scipy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad"},
|
|
||||||
{file = "scipy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5"},
|
|
||||||
{file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"},
|
|
||||||
{file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"},
|
|
||||||
{file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"},
|
|
||||||
{file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"},
|
|
||||||
{file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"},
|
|
||||||
{file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"},
|
|
||||||
{file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"},
|
|
||||||
{file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"},
|
|
||||||
{file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"},
|
|
||||||
{file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"},
|
|
||||||
{file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"},
|
|
||||||
{file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"},
|
|
||||||
{file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"},
|
|
||||||
{file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"},
|
|
||||||
{file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"},
|
|
||||||
{file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"},
|
|
||||||
{file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"},
|
|
||||||
{file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"},
|
|
||||||
{file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"},
|
|
||||||
{file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"},
|
|
||||||
{file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"},
|
|
||||||
{file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"},
|
|
||||||
{file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"},
|
|
||||||
{file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"},
|
|
||||||
{file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
numpy = ">=1.23.5,<2.3"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"]
|
|
||||||
doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<=7.3.7)", "sphinx-design (>=0.4.0)"]
|
|
||||||
test = ["Cython", "array-api-strict (>=2.0)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.16.0"
|
version = "1.17.0"
|
||||||
description = "Python 2 and 3 compatibility utilities"
|
description = "Python 2 and 3 compatibility utilities"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
|
||||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1245,6 +1477,20 @@ files = [
|
|||||||
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
|
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
|
||||||
test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
|
test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "types-networkx"
|
||||||
|
version = "3.4.2.20241115"
|
||||||
|
description = "Typing stubs for networkx"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "types-networkx-3.4.2.20241115.tar.gz", hash = "sha256:d669b650cf6c6c9ec879a825449eb04a5c10742f3109177e1683f57ee49e0f59"},
|
||||||
|
{file = "types_networkx-3.4.2.20241115-py3-none-any.whl", hash = "sha256:f0c382924d6614e06bf0b1ca0b837b8f33faa58982bc086ea762efaf39aa98dd"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
numpy = ">=1.20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "4.12.2"
|
version = "4.12.2"
|
||||||
@ -1281,4 +1527,4 @@ files = [
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "b643261f91a781d77735e05f6d2ac1002867600c2df6393a9d1a15f5e1189109"
|
content-hash = "5b57bccd8dc65a9acecbe187939bae625ef6a259f4188c6587907245bcfa604f"
|
||||||
|
@ -12,10 +12,12 @@ python = "^3.10"
|
|||||||
numpy = "^2.1.3"
|
numpy = "^2.1.3"
|
||||||
tqdm = "^4.67.1"
|
tqdm = "^4.67.1"
|
||||||
parse = "^1.20.2"
|
parse = "^1.20.2"
|
||||||
scipy = "^1.14.1"
|
|
||||||
sympy = "^1.13.3"
|
sympy = "^1.13.3"
|
||||||
networkx = "^3.4.2"
|
networkx = "^3.4.2"
|
||||||
pandas = "^2.2.3"
|
pillow = "^11.0.0"
|
||||||
|
imageio = "^2.36.1"
|
||||||
|
pygifsicle = "^1.1.0"
|
||||||
|
opencv-python = "^4.10.0.84"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
pyright = "^1.1.389"
|
pyright = "^1.1.389"
|
||||||
@ -23,6 +25,14 @@ ruff = "^0.8.1"
|
|||||||
poethepoet = "^0.31.1"
|
poethepoet = "^0.31.1"
|
||||||
ipykernel = "^6.29.5"
|
ipykernel = "^6.29.5"
|
||||||
networkx-stubs = "^0.0.1"
|
networkx-stubs = "^0.0.1"
|
||||||
|
types-networkx = "^3.4.2.20241115"
|
||||||
|
|
||||||
|
[tool.poetry.group.cplex.dependencies]
|
||||||
|
docplex = "^2.28.240"
|
||||||
|
cplex = "^22.1.1.2"
|
||||||
|
|
||||||
|
[tool.poetry.group.ortools.dependencies]
|
||||||
|
ortools = "^9.11.4210"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
holt59-aoc = "holt59.aoc.__main__:main"
|
holt59-aoc = "holt59.aoc.__main__:main"
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
floor = 0
|
floor = 0
|
||||||
floors = [(floor := floor + (1 if c == "(" else -1)) for c in line]
|
floors = [(floor := floor + (1 if c == "(" else -1)) for c in input]
|
||||||
|
|
||||||
|
yield floors[-1]
|
||||||
print(f"answer 1 is {floors[-1]}")
|
yield floors.index(-1)
|
||||||
print(f"answer 2 is {floors.index(-1)}")
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# see http://www.se16.info/js/lands2.htm for the explanation of 'atoms' (or elements)
|
# see http://www.se16.info/js/lands2.htm for the explanation of 'atoms' (or elements)
|
||||||
#
|
#
|
||||||
@ -9,7 +9,7 @@ line = sys.stdin.read().strip()
|
|||||||
# CodeGolf answer https://codegolf.stackexchange.com/a/8479/42148
|
# CodeGolf answer https://codegolf.stackexchange.com/a/8479/42148
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
atoms = [
|
ATOMS: list[tuple[str, tuple[int, ...]]] = [
|
||||||
("22", (0, )), # 0
|
("22", (0, )), # 0
|
||||||
("13112221133211322112211213322112", (71, 90, 0, 19, 2, )), # 1
|
("13112221133211322112211213322112", (71, 90, 0, 19, 2, )), # 1
|
||||||
("312211322212221121123222112", (1, )), # 2
|
("312211322212221121123222112", (1, )), # 2
|
||||||
@ -105,7 +105,7 @@ atoms = [
|
|||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
starters = [
|
STARTERS = [
|
||||||
"1",
|
"1",
|
||||||
"11",
|
"11",
|
||||||
"21",
|
"21",
|
||||||
@ -122,27 +122,26 @@ def look_and_say_length(s: str, n: int) -> int:
|
|||||||
if n == 0:
|
if n == 0:
|
||||||
return len(s)
|
return len(s)
|
||||||
|
|
||||||
if s in starters:
|
if s in STARTERS:
|
||||||
return look_and_say_length(
|
return look_and_say_length(
|
||||||
"".join(f"{len(list(g))}{k}" for k, g in itertools.groupby(s)), n - 1
|
"".join(f"{len(list(g))}{k}" for k, g in itertools.groupby(s)), n - 1
|
||||||
)
|
)
|
||||||
|
|
||||||
counts = {i: 0 for i in range(len(atoms))}
|
counts = {i: 0 for i in range(len(ATOMS))}
|
||||||
idx = next(i for i, (a, _) in enumerate(atoms) if s == a)
|
idx = next(i for i, (a, _) in enumerate(ATOMS) if s == a)
|
||||||
counts[idx] = 1
|
counts[idx] = 1
|
||||||
|
|
||||||
for _ in range(n):
|
for _ in range(n):
|
||||||
c2 = {i: 0 for i in range(len(atoms))}
|
c2 = {i: 0 for i in range(len(ATOMS))}
|
||||||
for i in counts:
|
for i in counts:
|
||||||
for j in atoms[i][1]:
|
for j in ATOMS[i][1]:
|
||||||
c2[j] += counts[i]
|
c2[j] += counts[i]
|
||||||
counts = c2
|
counts = c2
|
||||||
|
|
||||||
return sum(counts[i] * len(a[0]) for i, a in enumerate(atoms))
|
return sum(counts[i] * len(a[0]) for i, a in enumerate(ATOMS))
|
||||||
|
|
||||||
|
|
||||||
answer_1 = look_and_say_length(line, 40)
|
class Solver(BaseSolver):
|
||||||
print(f"answer 1 is {answer_1}")
|
def solve(self, input: str) -> Iterator[Any] | None:
|
||||||
|
yield look_and_say_length(input, 40)
|
||||||
answer_2 = look_and_say_length(line, 50)
|
yield look_and_say_length(input, 50)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def is_valid(p: str) -> bool:
|
def is_valid(p: str) -> bool:
|
||||||
@ -40,10 +42,8 @@ def find_next_password(p: str) -> str:
|
|||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
answer_1 = find_next_password(line)
|
answer_1 = find_next_password(input)
|
||||||
print(f"answer 1 is {answer_1}")
|
yield answer_1
|
||||||
|
yield find_next_password(increment(answer_1))
|
||||||
answer_2 = find_next_password(increment(answer_1))
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import sys
|
from typing import Any, Iterator, TypeAlias
|
||||||
from typing import TypeAlias
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
JsonObject: TypeAlias = dict[str, "JsonObject"] | list["JsonObject"] | int | str
|
JsonObject: TypeAlias = dict[str, "JsonObject"] | list["JsonObject"] | int | str
|
||||||
|
|
||||||
@ -18,10 +19,9 @@ def json_sum(value: JsonObject, ignore: str | None = None) -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
data: JsonObject = json.load(sys.stdin)
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
data: JsonObject = json.loads(input)
|
||||||
|
|
||||||
answer_1 = json_sum(data)
|
yield json_sum(data)
|
||||||
print(f"answer 1 is {answer_1}")
|
yield json_sum(data, "red")
|
||||||
|
|
||||||
answer_2 = json_sum(data, "red")
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Literal, cast
|
from typing import Any, Iterator, Literal, cast
|
||||||
|
|
||||||
import parse # type: ignore
|
import parse # type: ignore
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def max_change_in_happiness(happiness: dict[str, dict[str, int]]) -> int:
|
def max_change_in_happiness(happiness: dict[str, dict[str, int]]) -> int:
|
||||||
guests = list(happiness)
|
guests = list(happiness)
|
||||||
@ -17,7 +18,9 @@ def max_change_in_happiness(happiness: dict[str, dict[str, int]]) -> int:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
happiness: dict[str, dict[str, int]] = defaultdict(dict)
|
happiness: dict[str, dict[str, int]] = defaultdict(dict)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
@ -29,13 +32,9 @@ for line in lines:
|
|||||||
)
|
)
|
||||||
happiness[u1][u2] = hap if gain_or_loose == "gain" else -hap
|
happiness[u1][u2] = hap if gain_or_loose == "gain" else -hap
|
||||||
|
|
||||||
|
yield max_change_in_happiness(happiness)
|
||||||
answer_1 = max_change_in_happiness(happiness)
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
for guest in list(happiness):
|
for guest in list(happiness):
|
||||||
happiness["me"][guest] = 0
|
happiness["me"][guest] = 0
|
||||||
happiness[guest]["me"] = 0
|
happiness[guest]["me"] = 0
|
||||||
|
|
||||||
answer_2 = max_change_in_happiness(happiness)
|
yield max_change_in_happiness(happiness)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import sys
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Literal, cast
|
from typing import Any, Iterator, Literal, cast
|
||||||
|
|
||||||
import parse # type: ignore
|
import parse # type: ignore
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Reindeer:
|
class Reindeer:
|
||||||
@ -13,7 +14,9 @@ class Reindeer:
|
|||||||
rest_time: int
|
rest_time: int
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
reindeers: list[Reindeer] = []
|
reindeers: list[Reindeer] = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
@ -26,7 +29,9 @@ for line in lines:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
reindeers.append(
|
reindeers.append(
|
||||||
Reindeer(name=reindeer, speed=speed, fly_time=speed_time, rest_time=rest_time)
|
Reindeer(
|
||||||
|
name=reindeer, speed=speed, fly_time=speed_time, rest_time=rest_time
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
target = 1000 if len(reindeers) <= 2 else 2503
|
target = 1000 if len(reindeers) <= 2 else 2503
|
||||||
@ -37,7 +42,7 @@ states: dict[Reindeer, tuple[Literal["resting", "flying"], int]] = {
|
|||||||
distances: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
distances: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
||||||
points: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
points: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
||||||
|
|
||||||
for time in range(target):
|
for time in self.progress.wrap(range(target)):
|
||||||
for reindeer in reindeers:
|
for reindeer in reindeers:
|
||||||
if states[reindeer][0] == "flying":
|
if states[reindeer][0] == "flying":
|
||||||
distances[reindeer] += reindeer.speed
|
distances[reindeer] += reindeer.speed
|
||||||
@ -54,9 +59,5 @@ for time in range(target):
|
|||||||
else:
|
else:
|
||||||
states[reindeer] = ("resting", time + reindeer.rest_time)
|
states[reindeer] = ("resting", time + reindeer.rest_time)
|
||||||
|
|
||||||
|
yield max(distances.values())
|
||||||
answer_1 = max(distances.values())
|
yield max(points.values()) - 1
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = max(points.values()) - 1
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import math
|
import math
|
||||||
import sys
|
from typing import Any, Iterator, Sequence, cast
|
||||||
from typing import Sequence, cast
|
|
||||||
|
|
||||||
import parse # type: ignore
|
import parse # type: ignore
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def score(ingredients: list[list[int]], teaspoons: Sequence[int]) -> int:
|
def score(ingredients: list[list[int]], teaspoons: Sequence[int]) -> int:
|
||||||
return math.prod(
|
return math.prod(
|
||||||
@ -18,7 +19,9 @@ def score(ingredients: list[list[int]], teaspoons: Sequence[int]) -> int:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
ingredients: list[list[int]] = []
|
ingredients: list[list[int]] = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
@ -49,9 +52,5 @@ for a in range(total_teaspoons + 1):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
yield max(scores)
|
||||||
answer_1 = max(scores)
|
yield max(score for score, calory in zip(scores, calories) if calory == 500)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = max(score for score, calory in zip(scores, calories) if calory == 500)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import operator as op
|
import operator as op
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Callable
|
from typing import Any, Callable, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
MFCSAM: dict[str, int] = {
|
MFCSAM: dict[str, int] = {
|
||||||
"children": 3,
|
"children": 3,
|
||||||
@ -17,18 +18,10 @@ MFCSAM: dict[str, int] = {
|
|||||||
"perfumes": 1,
|
"perfumes": 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = sys.stdin.readlines()
|
|
||||||
|
|
||||||
aunts: list[dict[str, int]] = [
|
def match(
|
||||||
{
|
aunts: list[dict[str, int]], operators: dict[str, Callable[[int, int], bool]]
|
||||||
match[1]: int(match[2])
|
) -> int:
|
||||||
for match in re.findall(R"((?P<compound>[^:, ]+): (?P<quantity>\d+))", line)
|
|
||||||
}
|
|
||||||
for line in lines
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def match(operators: dict[str, Callable[[int, int], bool]]) -> int:
|
|
||||||
return next(
|
return next(
|
||||||
i
|
i
|
||||||
for i, aunt in enumerate(aunts, start=1)
|
for i, aunt in enumerate(aunts, start=1)
|
||||||
@ -36,16 +29,29 @@ def match(operators: dict[str, Callable[[int, int], bool]]) -> int:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
answer_1 = match(defaultdict(lambda: op.eq))
|
class Solver(BaseSolver):
|
||||||
print(f"answer 1 is {answer_1}")
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
answer_2 = match(
|
aunts: list[dict[str, int]] = [
|
||||||
|
{
|
||||||
|
match[1]: int(match[2])
|
||||||
|
for match in re.findall(
|
||||||
|
R"((?P<compound>[^:, ]+): (?P<quantity>\d+))", line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for line in lines
|
||||||
|
]
|
||||||
|
|
||||||
|
yield match(aunts, defaultdict(lambda: op.eq))
|
||||||
|
|
||||||
|
yield match(
|
||||||
|
aunts,
|
||||||
defaultdict(
|
defaultdict(
|
||||||
lambda: op.eq,
|
lambda: op.eq,
|
||||||
trees=op.gt,
|
trees=op.gt,
|
||||||
cats=op.gt,
|
cats=op.gt,
|
||||||
pomeranians=op.lt,
|
pomeranians=op.lt,
|
||||||
goldfish=op.lt,
|
goldfish=op.lt,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
from typing import Iterator
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def iter_combinations(value: int, containers: list[int]) -> Iterator[tuple[int, ...]]:
|
def iter_combinations(value: int, containers: list[int]) -> Iterator[tuple[int, ...]]:
|
||||||
@ -16,15 +17,18 @@ def iter_combinations(value: int, containers: list[int]) -> Iterator[tuple[int,
|
|||||||
yield (containers[i],) + combination
|
yield (containers[i],) + combination
|
||||||
|
|
||||||
|
|
||||||
containers = [int(c) for c in sys.stdin.read().split()]
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
containers = [int(c) for c in input.split()]
|
||||||
total = 25 if len(containers) <= 5 else 150
|
total = 25 if len(containers) <= 5 else 150
|
||||||
|
|
||||||
combinations = [combination for combination in iter_combinations(total, containers)]
|
combinations = [
|
||||||
|
combination for combination in iter_combinations(total, containers)
|
||||||
|
]
|
||||||
|
|
||||||
answer_1 = len(combinations)
|
yield len(combinations)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
min_containers = min(len(combination) for combination in combinations)
|
min_containers = min(len(combination) for combination in combinations)
|
||||||
|
yield sum(
|
||||||
answer_2 = sum(1 for combination in combinations if len(combination) == min_containers)
|
1 for combination in combinations if len(combination) == min_containers
|
||||||
print(f"answer 2 is {answer_2}")
|
)
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
|
||||||
grid0 = np.array([[c == "#" for c in line] for line in sys.stdin.read().splitlines()])
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
grid0 = np.array([[c == "#" for c in line] for line in input.splitlines()])
|
||||||
|
|
||||||
# add an always off circle around
|
# add an always off circle around
|
||||||
grid0 = np.concatenate(
|
grid0 = np.concatenate(
|
||||||
@ -34,7 +39,6 @@ iis, jjs = iis.flatten(), jjs.flatten()
|
|||||||
ins = iis[:, None] + np.array(moves)[:, 0]
|
ins = iis[:, None] + np.array(moves)[:, 0]
|
||||||
jns = jjs[:, None] + np.array(moves)[:, 1]
|
jns = jjs[:, None] + np.array(moves)[:, 1]
|
||||||
|
|
||||||
|
|
||||||
def game_of_life(grid: NDArray[np.bool_]) -> NDArray[np.bool_]:
|
def game_of_life(grid: NDArray[np.bool_]) -> NDArray[np.bool_]:
|
||||||
neighbors_on = grid[ins, jns].sum(axis=1)
|
neighbors_on = grid[ins, jns].sum(axis=1)
|
||||||
cells_on = grid[iis, jjs]
|
cells_on = grid[iis, jjs]
|
||||||
@ -44,15 +48,12 @@ def game_of_life(grid: NDArray[np.bool_]) -> NDArray[np.bool_]:
|
|||||||
|
|
||||||
return grid
|
return grid
|
||||||
|
|
||||||
|
|
||||||
grid = grid0
|
grid = grid0
|
||||||
n_steps = 4 if len(grid) < 10 else 100
|
n_steps = 4 if len(grid) < 10 else 100
|
||||||
for _ in range(n_steps):
|
for _ in range(n_steps):
|
||||||
grid = game_of_life(grid)
|
grid = game_of_life(grid)
|
||||||
|
|
||||||
answer_1 = grid.sum()
|
yield grid.sum()
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
|
|
||||||
n_steps = 5 if len(grid) < 10 else 100
|
n_steps = 5 if len(grid) < 10 else 100
|
||||||
grid = grid0
|
grid = grid0
|
||||||
@ -62,5 +63,4 @@ for _ in range(n_steps):
|
|||||||
|
|
||||||
grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True
|
grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True
|
||||||
|
|
||||||
answer_2 = sum(cell for line in grid for cell in line)
|
yield sum(cell for line in grid for cell in line)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
replacements_s, molecule = sys.stdin.read().split("\n\n")
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
replacements_s, molecule = input.split("\n\n")
|
||||||
|
|
||||||
REPLACEMENTS: dict[str, list[str]] = defaultdict(list)
|
REPLACEMENTS: dict[str, list[str]] = defaultdict(list)
|
||||||
for replacement_s in replacements_s.splitlines():
|
for replacement_s in replacements_s.splitlines():
|
||||||
@ -17,8 +22,7 @@ generated = [
|
|||||||
if molecule[i:].startswith(symbol)
|
if molecule[i:].startswith(symbol)
|
||||||
]
|
]
|
||||||
|
|
||||||
answer_1 = len(set(generated))
|
yield len(set(generated))
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
inversion: dict[str, str] = {
|
inversion: dict[str, str] = {
|
||||||
replacement: symbol
|
replacement: symbol
|
||||||
@ -48,9 +52,6 @@ while molecule != "e":
|
|||||||
m2 += molecule[i]
|
m2 += molecule[i]
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# print(m2)
|
|
||||||
molecule = m2
|
molecule = m2
|
||||||
|
|
||||||
|
yield count
|
||||||
answer_2 = count
|
|
||||||
print(f"answer 2 is {count}")
|
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
length, width, height = np.array(
|
length, width, height = np.array(
|
||||||
[[int(c) for c in line.split("x")] for line in lines]
|
[[int(c) for c in line.split("x")] for line in input.splitlines()]
|
||||||
).T
|
).T
|
||||||
|
|
||||||
lw, wh, hl = (length * width, width * height, height * length)
|
lw, wh, hl = (length * width, width * height, height * length)
|
||||||
|
|
||||||
answer_1 = np.sum(2 * (lw + wh + hl) + np.min(np.stack([lw, wh, hl]), axis=0))
|
yield np.sum(2 * (lw + wh + hl) + np.min(np.stack([lw, wh, hl]), axis=0))
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = np.sum(
|
yield np.sum(
|
||||||
length * width * height
|
length * width * height
|
||||||
+ 2 * np.min(np.stack([length + width, length + height, height + width]), axis=0)
|
+ 2
|
||||||
|
* np.min(
|
||||||
|
np.stack([length + width, length + height, height + width]), axis=0
|
||||||
|
)
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
target = int(sys.stdin.read())
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def presents(n: int, elf: int, max: int = target) -> int:
|
def presents(n: int, elf: int, max: int) -> int:
|
||||||
count = 0
|
count = 0
|
||||||
k = 1
|
k = 1
|
||||||
while k * k < n:
|
while k * k < n:
|
||||||
@ -21,8 +21,9 @@ def presents(n: int, elf: int, max: int = target) -> int:
|
|||||||
return count
|
return count
|
||||||
|
|
||||||
|
|
||||||
answer_1 = next(n for n in itertools.count(1) if presents(n, 10) >= target)
|
class Solver(BaseSolver):
|
||||||
print(f"answer 1 is {answer_1}")
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
target = int(input)
|
||||||
|
|
||||||
answer_2 = next(n for n in itertools.count(1) if presents(n, 11, 50) >= target)
|
yield next(n for n in itertools.count(1) if presents(n, 10, target) >= target)
|
||||||
print(f"answer 2 is {answer_2}")
|
yield next(n for n in itertools.count(1) if presents(n, 11, 50) >= target)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from typing import TypeAlias
|
from typing import Any, Iterator, TypeAlias
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
Modifier: TypeAlias = tuple[str, int, int, int]
|
Modifier: TypeAlias = tuple[str, int, int, int]
|
||||||
|
|
||||||
@ -33,7 +34,9 @@ RINGS: list[Modifier] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
player_hp = 100
|
player_hp = 100
|
||||||
|
|
||||||
@ -41,7 +44,6 @@ boss_attack = int(lines[1].split(":")[1].strip())
|
|||||||
boss_armor = int(lines[2].split(":")[1].strip())
|
boss_armor = int(lines[2].split(":")[1].strip())
|
||||||
boss_hp = int(lines[0].split(":")[1].strip())
|
boss_hp = int(lines[0].split(":")[1].strip())
|
||||||
|
|
||||||
|
|
||||||
min_cost, max_cost = 1_000_000, 0
|
min_cost, max_cost = 1_000_000, 0
|
||||||
for equipments in itertools.product(WEAPONS, ARMORS, RINGS, RINGS):
|
for equipments in itertools.product(WEAPONS, ARMORS, RINGS, RINGS):
|
||||||
if equipments[-1][0] != "" and equipments[-2] == equipments[-1]:
|
if equipments[-1][0] != "" and equipments[-2] == equipments[-1]:
|
||||||
@ -58,9 +60,5 @@ for equipments in itertools.product(WEAPONS, ARMORS, RINGS, RINGS):
|
|||||||
else:
|
else:
|
||||||
max_cost = max(cost, max_cost)
|
max_cost = max(cost, max_cost)
|
||||||
|
|
||||||
|
yield min_cost
|
||||||
answer_1 = min_cost
|
yield max_cost
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = max_cost
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import heapq
|
import heapq
|
||||||
import sys
|
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||||
from typing import Literal, TypeAlias, cast
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
PlayerType: TypeAlias = Literal["player", "boss"]
|
PlayerType: TypeAlias = Literal["player", "boss"]
|
||||||
SpellType: TypeAlias = Literal["magic missile", "drain", "shield", "poison", "recharge"]
|
SpellType: TypeAlias = Literal["magic missile", "drain", "shield", "poison", "recharge"]
|
||||||
@ -62,17 +63,6 @@ def play(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
visited.add((player, player_hp, player_mana, player_armor, boss_hp, buffs))
|
visited.add((player, player_hp, player_mana, player_armor, boss_hp, buffs))
|
||||||
|
|
||||||
if hard_mode and player == "player":
|
|
||||||
player_hp = max(0, player_hp - 1)
|
|
||||||
|
|
||||||
if player_hp == 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if boss_hp == 0:
|
|
||||||
winning_node = spells
|
|
||||||
continue
|
|
||||||
|
|
||||||
new_buffs: list[tuple[BuffType, int]] = []
|
new_buffs: list[tuple[BuffType, int]] = []
|
||||||
for buff, length in buffs:
|
for buff, length in buffs:
|
||||||
length = length - 1
|
length = length - 1
|
||||||
@ -88,6 +78,16 @@ def play(
|
|||||||
if length > 0:
|
if length > 0:
|
||||||
new_buffs.append((buff, length))
|
new_buffs.append((buff, length))
|
||||||
|
|
||||||
|
if hard_mode and player == "player":
|
||||||
|
player_hp = player_hp - 1
|
||||||
|
|
||||||
|
if player_hp <= 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if boss_hp <= 0:
|
||||||
|
winning_node = spells
|
||||||
|
continue
|
||||||
|
|
||||||
buffs = tuple(new_buffs)
|
buffs = tuple(new_buffs)
|
||||||
|
|
||||||
if player == "boss":
|
if player == "boss":
|
||||||
@ -155,7 +155,9 @@ def play(
|
|||||||
return winning_node
|
return winning_node
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
player_hp = 50
|
player_hp = 50
|
||||||
player_mana = 500
|
player_mana = 500
|
||||||
@ -164,14 +166,16 @@ player_armor = 0
|
|||||||
boss_hp = int(lines[0].split(":")[1].strip())
|
boss_hp = int(lines[0].split(":")[1].strip())
|
||||||
boss_attack = int(lines[1].split(":")[1].strip())
|
boss_attack = int(lines[1].split(":")[1].strip())
|
||||||
|
|
||||||
answer_1 = sum(
|
yield sum(
|
||||||
c
|
c
|
||||||
for _, c in play(player_hp, player_mana, player_armor, boss_hp, boss_attack, False)
|
for _, c in play(
|
||||||
|
player_hp, player_mana, player_armor, boss_hp, boss_attack, False
|
||||||
|
)
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# 1242 (not working)
|
yield sum(
|
||||||
answer_2 = sum(
|
c
|
||||||
c for _, c in play(player_hp, player_mana, player_armor, boss_hp, boss_attack, True)
|
for _, c in play(
|
||||||
|
player_hp, player_mana, player_armor, boss_hp, boss_attack, True
|
||||||
|
)
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
107
src/holt59/aoc/2015/day23.py
Normal file
107
src/holt59/aoc/2015/day23.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import inspect
|
||||||
|
from typing import Any, Callable, Final, Iterator, Mapping
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Instruction:
|
||||||
|
def __init__(self, fn: Callable[..., None]):
|
||||||
|
self._fn = fn
|
||||||
|
|
||||||
|
args = inspect.getfullargspec(fn)
|
||||||
|
|
||||||
|
self._argtypes = [args.annotations[arg] for arg in args.args[1:]]
|
||||||
|
|
||||||
|
def __call__(self, args: tuple[str, ...]):
|
||||||
|
self._fn(
|
||||||
|
*(argtype(arg) for arg, argtype in zip(args, self._argtypes, strict=True))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Machine:
|
||||||
|
def __init__(
|
||||||
|
self, instructions: list[str], registers: dict[str, int] = {"a": 0, "b": 1}
|
||||||
|
):
|
||||||
|
self.instructions: Final = [
|
||||||
|
(part[0], tuple(arg.strip() for arg in " ".join(part[1:]).split(",")))
|
||||||
|
for instruction in instructions
|
||||||
|
if (part := instruction.split())
|
||||||
|
]
|
||||||
|
|
||||||
|
self._fns = {
|
||||||
|
name: Instruction(getattr(self, name))
|
||||||
|
for name in ("hlf", "tpl", "inc", "jmp", "jie", "jio")
|
||||||
|
}
|
||||||
|
|
||||||
|
self._registers = registers.copy()
|
||||||
|
self._ip = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def registers(self) -> Mapping[str, int]:
|
||||||
|
return self._registers
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ip(self) -> int:
|
||||||
|
return self._ip
|
||||||
|
|
||||||
|
def reset(self, registers: dict[str, int] = {"a": 0, "b": 0}):
|
||||||
|
self._registers = registers.copy()
|
||||||
|
self._ip = 0
|
||||||
|
|
||||||
|
def hlf(self, register: str):
|
||||||
|
self._registers[register] //= 2
|
||||||
|
self._ip += 1
|
||||||
|
|
||||||
|
def tpl(self, register: str):
|
||||||
|
self._registers[register] *= 3
|
||||||
|
self._ip += 1
|
||||||
|
|
||||||
|
def inc(self, register: str):
|
||||||
|
self._registers[register] += 1
|
||||||
|
self._ip += 1
|
||||||
|
|
||||||
|
def jmp(self, offset: int):
|
||||||
|
self._ip += offset
|
||||||
|
assert 0 <= self._ip < len(self.instructions)
|
||||||
|
|
||||||
|
def jie(self, register: str, offset: int):
|
||||||
|
if self._registers[register] % 2 == 0:
|
||||||
|
self._ip += offset
|
||||||
|
else:
|
||||||
|
self._ip += 1
|
||||||
|
|
||||||
|
def jio(self, register: str, offset: int):
|
||||||
|
if self._registers[register] == 1:
|
||||||
|
self._ip += offset
|
||||||
|
else:
|
||||||
|
self._ip += 1
|
||||||
|
|
||||||
|
def _exec(self) -> bool:
|
||||||
|
# execute next instruction
|
||||||
|
if self._ip >= len(self.instructions):
|
||||||
|
return False
|
||||||
|
|
||||||
|
ins, args = self.instructions[self._ip]
|
||||||
|
|
||||||
|
if ins not in self._fns:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._fns[ins](args)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while self._exec():
|
||||||
|
...
|
||||||
|
return self.registers
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
machine = Machine(input.splitlines())
|
||||||
|
|
||||||
|
registers = machine.run()
|
||||||
|
yield registers["b"]
|
||||||
|
|
||||||
|
machine.reset({"a": 1, "b": 0})
|
||||||
|
registers = machine.run()
|
||||||
|
yield registers["b"]
|
88
src/holt59/aoc/2015/day24.py
Normal file
88
src/holt59/aoc/2015/day24.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
from typing import Any, Iterator, TypeAlias
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
TupleOfInts: TypeAlias = tuple[int, ...]
|
||||||
|
|
||||||
|
|
||||||
|
def check_n_groups(
|
||||||
|
target: int, groups: tuple[TupleOfInts, ...], numbers: TupleOfInts
|
||||||
|
) -> bool:
|
||||||
|
n_groups = len(groups)
|
||||||
|
groups_s = tuple(sum(group) for group in groups)
|
||||||
|
|
||||||
|
if all(target == group_s for group_s in groups_s):
|
||||||
|
return not numbers
|
||||||
|
|
||||||
|
if not numbers:
|
||||||
|
return False
|
||||||
|
|
||||||
|
head, *tail_l = numbers
|
||||||
|
tail, tail_s = tuple(tail_l), sum(tail_l)
|
||||||
|
|
||||||
|
return any(
|
||||||
|
groups_s[i] + head <= target
|
||||||
|
and sum(groups_s[j] for j in range(len(groups)) if i != j) + tail_s
|
||||||
|
>= (n_groups - 1) * target
|
||||||
|
and check_n_groups(
|
||||||
|
target, groups[:i] + ((groups[i] + (head,)),) + groups[i + 1 :], tail
|
||||||
|
)
|
||||||
|
for i in range(len(groups))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def enumerate_single_subset(
|
||||||
|
target: int, numbers: TupleOfInts
|
||||||
|
) -> Iterator[tuple[int, TupleOfInts, TupleOfInts]]:
|
||||||
|
"""
|
||||||
|
Enumerate subset of numbers whose sum equals target.
|
||||||
|
|
||||||
|
Subset are enumerated in increasing order of length, then product (quantum value).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
target: Target for the sum of the subset.
|
||||||
|
numbers: Tuple of integers to find the subset from.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A generator (quantum, subset, remaining) where subset if the subset of numbers
|
||||||
|
whose sum equals target, quantum the product of the subset, and remaining the
|
||||||
|
remaining numbers.
|
||||||
|
"""
|
||||||
|
groups: list[tuple[int, TupleOfInts, TupleOfInts]] = [(1, (), numbers)]
|
||||||
|
|
||||||
|
for _ in range(len(numbers)):
|
||||||
|
new_groups: list[tuple[int, TupleOfInts, TupleOfInts]] = []
|
||||||
|
|
||||||
|
for g_quantum, group, remaining in groups:
|
||||||
|
sg = sum(group)
|
||||||
|
for i in range(len(remaining)):
|
||||||
|
if group and remaining[i] <= group[-1]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
uv = remaining[:i] + remaining[i + 1 :]
|
||||||
|
kv = g_quantum * remaining[i], group + (remaining[i],), uv
|
||||||
|
|
||||||
|
if sg + remaining[i] == target:
|
||||||
|
yield kv
|
||||||
|
elif sg + remaining[i] < target:
|
||||||
|
new_groups.append(kv)
|
||||||
|
|
||||||
|
groups = new_groups
|
||||||
|
|
||||||
|
|
||||||
|
def find_min_quantum(numbers: tuple[int, ...], n_groups: int):
|
||||||
|
return next(
|
||||||
|
g_quantum
|
||||||
|
for g_quantum, group_1v2, group_234v2 in enumerate_single_subset(
|
||||||
|
sum(numbers) // n_groups, numbers
|
||||||
|
)
|
||||||
|
if check_n_groups(sum(group_1v2), ((),) * (n_groups - 1), group_234v2)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
numbers = tuple(map(int, input.split()))
|
||||||
|
|
||||||
|
yield find_min_quantum(numbers, 3)
|
||||||
|
yield find_min_quantum(numbers, 4)
|
16
src/holt59/aoc/2015/day25.py
Normal file
16
src/holt59/aoc/2015/day25.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import re
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
from ..tools.math import pow_mod
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
m = re.search(r"row\s*([0-9]+)\s*,\s*column\s*([0-9]+)", input)
|
||||||
|
assert m is not None
|
||||||
|
|
||||||
|
row, col = int(m.group(1)), int(m.group(2))
|
||||||
|
n = (row * (row - 1)) // 2 + col * (col + 1) // 2 + (row - 1) * (col - 1)
|
||||||
|
|
||||||
|
yield (20151125 * pow_mod(252533, n - 1, 33554393)) % 33554393
|
@ -1,7 +1,7 @@
|
|||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def process(directions: str) -> dict[tuple[int, int], int]:
|
def process(directions: str) -> dict[tuple[int, int], int]:
|
||||||
@ -27,8 +27,7 @@ def process(directions: str) -> dict[tuple[int, int], int]:
|
|||||||
return counts
|
return counts
|
||||||
|
|
||||||
|
|
||||||
answer_1 = len(process(line))
|
class Solver(BaseSolver):
|
||||||
print(f"answer 1 is {answer_1}")
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
yield len(process(input))
|
||||||
answer_2 = len(process(line[::2]) | process(line[1::2]))
|
yield len(process(input[::2]) | process(input[1::2]))
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import itertools
|
import itertools
|
||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
it = iter(itertools.count(1))
|
it = iter(itertools.count(1))
|
||||||
answer_1 = next(
|
yield next(
|
||||||
i for i in it if hashlib.md5(f"{line}{i}".encode()).hexdigest().startswith("00000")
|
i
|
||||||
|
for i in it
|
||||||
|
if hashlib.md5(f"{input}{i}".encode()).hexdigest().startswith("00000")
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
yield next(
|
||||||
|
i
|
||||||
answer_2 = next(
|
for i in it
|
||||||
i for i in it if hashlib.md5(f"{line}{i}".encode()).hexdigest().startswith("000000")
|
if hashlib.md5(f"{input}{i}".encode()).hexdigest().startswith("000000")
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
VOWELS = "aeiou"
|
VOWELS = "aeiou"
|
||||||
FORBIDDEN = {"ab", "cd", "pq", "xy"}
|
FORBIDDEN = {"ab", "cd", "pq", "xy"}
|
||||||
@ -27,10 +29,8 @@ def is_nice_2(s: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
answer_1 = sum(map(is_nice_1, lines))
|
lines = input.splitlines()
|
||||||
print(f"answer 1 is {answer_1}")
|
yield sum(map(is_nice_1, lines))
|
||||||
|
yield sum(map(is_nice_2, lines))
|
||||||
answer_2 = sum(map(is_nice_2, lines))
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import sys
|
from typing import Any, Iterator, Literal, cast
|
||||||
from typing import Literal, cast
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import parse # type: ignore
|
import parse # type: ignore
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
lights_1 = np.zeros((1000, 1000), dtype=bool)
|
lights_1 = np.zeros((1000, 1000), dtype=bool)
|
||||||
lights_2 = np.zeros((1000, 1000), dtype=int)
|
lights_2 = np.zeros((1000, 1000), dtype=int)
|
||||||
for line in lines:
|
for line in input.splitlines():
|
||||||
action, sx, sy, ex, ey = cast(
|
action, sx, sy, ex, ey = cast(
|
||||||
tuple[Literal["turn on", "turn off", "toggle"], int, int, int, int],
|
tuple[Literal["turn on", "turn off", "toggle"], int, int, int, int],
|
||||||
parse.parse("{} {:d},{:d} through {:d},{:d}", line), # type: ignore
|
parse.parse("{} {:d},{:d} through {:d},{:d}", line), # type: ignore
|
||||||
@ -26,8 +28,5 @@ for line in lines:
|
|||||||
lights_1[sx:ex, sy:ey] = ~lights_1[sx:ex, sy:ey]
|
lights_1[sx:ex, sy:ey] = ~lights_1[sx:ex, sy:ey]
|
||||||
lights_2[sx:ex, sy:ey] += 2
|
lights_2[sx:ex, sy:ey] += 2
|
||||||
|
|
||||||
answer_1 = lights_1.sum()
|
yield lights_1.sum()
|
||||||
print(f"answer 1 is {answer_1}")
|
yield lights_2.sum()
|
||||||
|
|
||||||
answer_2 = lights_2.sum()
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import logging
|
|
||||||
import operator
|
import operator
|
||||||
import os
|
from typing import Any, Callable, Iterator
|
||||||
import sys
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
|
||||||
|
|
||||||
OPERATORS = {
|
OPERATORS = {
|
||||||
"AND": operator.and_,
|
"AND": operator.and_,
|
||||||
@ -36,7 +32,22 @@ def value_of(key: str) -> tuple[str, Callable[[dict[str, int]], int]]:
|
|||||||
return key, lambda values: values[key]
|
return key, lambda values: values[key]
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
def process(
|
||||||
|
signals: Signals,
|
||||||
|
values: dict[str, int],
|
||||||
|
) -> dict[str, int]:
|
||||||
|
while signals:
|
||||||
|
signal = next(s for s in signals if all(p in values for p in signals[s][0]))
|
||||||
|
_, deps, command = signals[signal]
|
||||||
|
values[signal] = command(deps[0](values), deps[1](values)) % 65536
|
||||||
|
del signals[signal]
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any] | None:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
signals: Signals = {}
|
signals: Signals = {}
|
||||||
values: dict[str, int] = {"": 0}
|
values: dict[str, int] = {"": 0}
|
||||||
@ -77,25 +88,9 @@ for line in lines:
|
|||||||
|
|
||||||
signals[signal] = ((lhs_s, rhs_s), (lhs_fn, rhs_fn), op)
|
signals[signal] = ((lhs_s, rhs_s), (lhs_fn, rhs_fn), op)
|
||||||
|
|
||||||
|
|
||||||
def process(
|
|
||||||
signals: Signals,
|
|
||||||
values: dict[str, int],
|
|
||||||
) -> dict[str, int]:
|
|
||||||
while signals:
|
|
||||||
signal = next(s for s in signals if all(p in values for p in signals[s][0]))
|
|
||||||
_, deps, command = signals[signal]
|
|
||||||
values[signal] = command(deps[0](values), deps[1](values)) % 65536
|
|
||||||
del signals[signal]
|
|
||||||
|
|
||||||
return values
|
|
||||||
|
|
||||||
|
|
||||||
values_1 = process(signals.copy(), values.copy())
|
values_1 = process(signals.copy(), values.copy())
|
||||||
logging.info("\n" + "\n".join(f"{k}: {values_1[k]}" for k in sorted(values_1)))
|
for k in sorted(values_1):
|
||||||
answer_1 = values_1["a"]
|
self.logger.info(f"{k}: {values_1[k]}")
|
||||||
print(f"answer 1 is {answer_1}")
|
yield values_1["a"]
|
||||||
|
|
||||||
values_2 = process(signals.copy(), values | {"b": values_1["a"]})
|
yield process(signals.copy(), values | {"b": values_1["a"]})["a"]
|
||||||
answer_2 = values_2["a"]
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import logging
|
from typing import Any, Iterator
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
answer_1 = sum(
|
yield sum(
|
||||||
# left and right quotes (not in memory)
|
# left and right quotes (not in memory)
|
||||||
2
|
2
|
||||||
# each \\ adds one character in the literals (compared to memory)
|
# each \\ adds one character in the literals (compared to memory)
|
||||||
@ -21,9 +20,8 @@ answer_1 = sum(
|
|||||||
+ 3 * (line.count(R"\x") - line.count(R"\\x") + line.count(R"\\\x"))
|
+ 3 * (line.count(R"\x") - line.count(R"\\x") + line.count(R"\\\x"))
|
||||||
for line in lines
|
for line in lines
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = sum(
|
yield sum(
|
||||||
# needs to wrap in quotes (2 characters)
|
# needs to wrap in quotes (2 characters)
|
||||||
2
|
2
|
||||||
# needs to escape every \ with an extra \
|
# needs to escape every \ with an extra \
|
||||||
@ -32,4 +30,3 @@ answer_2 = sum(
|
|||||||
+ line.count('"')
|
+ line.count('"')
|
||||||
for line in lines
|
for line in lines
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import cast
|
from typing import Any, Iterator, cast
|
||||||
|
|
||||||
import parse # type: ignore
|
import parse # type: ignore
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
distances: dict[str, dict[str, int]] = defaultdict(dict)
|
distances: dict[str, dict[str, int]] = defaultdict(dict)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
@ -20,8 +24,5 @@ distance_of_routes = {
|
|||||||
for route in map(tuple, itertools.permutations(distances))
|
for route in map(tuple, itertools.permutations(distances))
|
||||||
}
|
}
|
||||||
|
|
||||||
answer_1 = min(distance_of_routes.values())
|
yield min(distance_of_routes.values())
|
||||||
print(f"answer 1 is {answer_1}")
|
yield max(distance_of_routes.values())
|
||||||
|
|
||||||
answer_2 = max(distance_of_routes.values())
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
values = [int(line) for line in lines]
|
values = [int(line) for line in lines]
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = sum(v2 > v1 for v1, v2 in zip(values[:-1], values[1:]))
|
yield sum(v2 > v1 for v1, v2 in zip(values[:-1], values[1:]))
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
runnings = [sum(values[i : i + 3]) for i in range(len(values) - 2)]
|
runnings = [sum(values[i : i + 3]) for i in range(len(values) - 2)]
|
||||||
answer_2 = sum(v2 > v1 for v1, v2 in zip(runnings[:-1], runnings[1:]))
|
yield sum(v2 > v1 for v1, v2 in zip(runnings[:-1], runnings[1:]))
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,47 @@
|
|||||||
import sys
|
from functools import reduce
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
BRACKETS = {"{": "}", "[": "]", "<": ">", "(": ")"}
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
CORRUPT_SCORES = {")": 3, "]": 57, "}": 1197, ">": 25137}
|
||||||
answer_2 = ...
|
COMPLETE_SCORES = {")": 1, "]": 2, "}": 3, ">": 4}
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
|
||||||
|
def corrupted_or_incomplete(line: str) -> tuple[bool, str]:
|
||||||
|
opens: list[str] = []
|
||||||
|
|
||||||
|
for c in line:
|
||||||
|
if c in BRACKETS:
|
||||||
|
opens.append(c)
|
||||||
|
elif BRACKETS[opens[-1]] != c:
|
||||||
|
return True, c
|
||||||
|
else:
|
||||||
|
opens.pop()
|
||||||
|
|
||||||
|
return (False, "".join(opens))
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
|
answer_1: int = 0
|
||||||
|
incomplete_scores: list[int] = []
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
c, r = corrupted_or_incomplete(line)
|
||||||
|
if c:
|
||||||
|
answer_1 += CORRUPT_SCORES[r]
|
||||||
|
else:
|
||||||
|
incomplete_scores.append(
|
||||||
|
reduce(
|
||||||
|
lambda s, c: s * 5 + COMPLETE_SCORES[BRACKETS[c]],
|
||||||
|
reversed(r),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
yield answer_1
|
||||||
|
yield sorted(incomplete_scores)[len(incomplete_scores) // 2]
|
||||||
|
@ -1,11 +1,66 @@
|
|||||||
import sys
|
import itertools as it
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
def do_step(values: list[list[int]]) -> tuple[list[list[int]], set[tuple[int, int]]]:
|
||||||
answer_2 = ...
|
values = [[c + 1 for c in r] for r in values]
|
||||||
print(f"answer 2 is {answer_2}")
|
flashed: set[tuple[int, int]] = set()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
found = False
|
||||||
|
|
||||||
|
for i_row, row in enumerate(values):
|
||||||
|
for i_col, col in enumerate(row):
|
||||||
|
if col <= 9 or (i_row, i_col) in flashed:
|
||||||
|
continue
|
||||||
|
|
||||||
|
found = True
|
||||||
|
flashed.add((i_row, i_col))
|
||||||
|
for dr, dc in it.product((-1, 0, 1), repeat=2):
|
||||||
|
if 0 <= i_row + dr < len(values) and 0 <= i_col + dc < len(
|
||||||
|
values[0]
|
||||||
|
):
|
||||||
|
values[i_row + dr][i_col + dc] += 1
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
break
|
||||||
|
|
||||||
|
for i, j in flashed:
|
||||||
|
values[i][j] = 0
|
||||||
|
|
||||||
|
return values, flashed
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def print_grid(self, values: list[list[int]], flashed: set[tuple[int, int]]):
|
||||||
|
for i_row, row in enumerate(values):
|
||||||
|
s_row = ""
|
||||||
|
for i_col, col in enumerate(row):
|
||||||
|
if (i_row, i_col) in flashed:
|
||||||
|
s_row += f"\033[0;31m{col}\033[0;00m"
|
||||||
|
else:
|
||||||
|
s_row += str(col)
|
||||||
|
self.logger.info(s_row)
|
||||||
|
self.logger.info("")
|
||||||
|
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
values_0 = [[int(c) for c in r] for r in input.splitlines()]
|
||||||
|
|
||||||
|
values = values_0
|
||||||
|
total_flashed: int = 0
|
||||||
|
for _ in range(100):
|
||||||
|
values, flashed = do_step(values)
|
||||||
|
total_flashed += len(flashed)
|
||||||
|
|
||||||
|
yield total_flashed
|
||||||
|
|
||||||
|
n_cells = len(values) * len(values[0])
|
||||||
|
flashed: set[tuple[int, int]] = set()
|
||||||
|
values, step = values_0, 0
|
||||||
|
while len(flashed) != n_cells:
|
||||||
|
values, flashed = do_step(values)
|
||||||
|
step += 1
|
||||||
|
|
||||||
|
yield step
|
||||||
|
@ -1,11 +1,64 @@
|
|||||||
import sys
|
import string
|
||||||
|
from collections import defaultdict
|
||||||
|
from functools import cache
|
||||||
|
from typing import Any, Iterator, Mapping, Sequence
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
@cache
|
||||||
answer_2 = ...
|
def is_small(node: str):
|
||||||
print(f"answer 2 is {answer_2}")
|
return all(c in string.ascii_lowercase for c in node)
|
||||||
|
|
||||||
|
|
||||||
|
def enumerate_paths(
|
||||||
|
neighbors: Mapping[str, Sequence[str]],
|
||||||
|
duplicate_smalls: int = 0,
|
||||||
|
start: str = "start",
|
||||||
|
current: tuple[str, ...] = ("start",),
|
||||||
|
) -> Iterator[tuple[str, ...]]:
|
||||||
|
if start == "end":
|
||||||
|
yield current
|
||||||
|
|
||||||
|
for neighbor in neighbors[start]:
|
||||||
|
if not is_small(neighbor):
|
||||||
|
yield from enumerate_paths(
|
||||||
|
neighbors, duplicate_smalls, neighbor, current + (neighbor,)
|
||||||
|
)
|
||||||
|
elif neighbor not in current:
|
||||||
|
yield from enumerate_paths(
|
||||||
|
neighbors, duplicate_smalls, neighbor, current + (neighbor,)
|
||||||
|
)
|
||||||
|
elif duplicate_smalls > 0:
|
||||||
|
yield from enumerate_paths(
|
||||||
|
neighbors, duplicate_smalls - 1, neighbor, current + (neighbor,)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
neighbors: dict[str, list[str]] = defaultdict(list)
|
||||||
|
|
||||||
|
for row in input.splitlines():
|
||||||
|
a, b = row.split("-")
|
||||||
|
if a != "end" and b != "start":
|
||||||
|
neighbors[a].append(b)
|
||||||
|
if b != "end" and a != "start":
|
||||||
|
neighbors[b].append(a)
|
||||||
|
|
||||||
|
if self.files:
|
||||||
|
graph = "graph {\n"
|
||||||
|
for node, neighbors_of in neighbors.items():
|
||||||
|
graph += (
|
||||||
|
" ".join(
|
||||||
|
f"{node} -- {neighbor};"
|
||||||
|
for neighbor in neighbors_of
|
||||||
|
if node <= neighbor or node == "start" or neighbor == "end"
|
||||||
|
)
|
||||||
|
+ "\n"
|
||||||
|
)
|
||||||
|
graph += "}\n"
|
||||||
|
self.files.create("graph.dot", graph.encode(), False)
|
||||||
|
|
||||||
|
yield len(list(enumerate_paths(neighbors)))
|
||||||
|
yield len(list(enumerate_paths(neighbors, 1)))
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import sys
|
|
||||||
from math import prod
|
from math import prod
|
||||||
from typing import Literal, TypeAlias, cast
|
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
Command: TypeAlias = Literal["forward", "up", "down"]
|
Command: TypeAlias = Literal["forward", "up", "down"]
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
commands: list[tuple[Command, int]] = [
|
commands: list[tuple[Command, int]] = [
|
||||||
(cast(Command, (p := line.split())[0]), int(p[1])) for line in lines
|
(cast(Command, (p := line.split())[0]), int(p[1])) for line in lines
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def depth_and_position(use_aim: bool):
|
def depth_and_position(use_aim: bool):
|
||||||
aim, pos, depth = 0, 0, 0
|
aim, pos, depth = 0, 0, 0
|
||||||
for command, value in commands:
|
for command, value in commands:
|
||||||
@ -31,11 +34,5 @@ def depth_and_position(use_aim: bool):
|
|||||||
|
|
||||||
return depth, pos
|
return depth, pos
|
||||||
|
|
||||||
|
yield prod(depth_and_position(False))
|
||||||
# part 1
|
yield prod(depth_and_position(True))
|
||||||
answer_1 = prod(depth_and_position(False))
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
|
||||||
answer_2 = prod(depth_and_position(True))
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
# part 1
|
|
||||||
answer_1 = ...
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
class Solver(BaseSolver):
|
||||||
answer_2 = ...
|
def solve(self, input: str) -> Iterator[Any]: ...
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import sys
|
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from typing import Literal
|
from typing import Any, Iterator, Literal
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def generator_rating(
|
def generator_rating(
|
||||||
@ -20,20 +21,23 @@ def generator_rating(
|
|||||||
return values[0]
|
return values[0]
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
most_and_least_common = [
|
most_and_least_common = [
|
||||||
tuple(Counter(line[col] for line in lines).most_common(2)[m][0] for m in range(2))
|
tuple(
|
||||||
|
Counter(line[col] for line in lines).most_common(2)[m][0]
|
||||||
|
for m in range(2)
|
||||||
|
)
|
||||||
for col in range(len(lines[0]))
|
for col in range(len(lines[0]))
|
||||||
]
|
]
|
||||||
gamma_rate = int("".join(most for most, _ in most_and_least_common), base=2)
|
gamma_rate = int("".join(most for most, _ in most_and_least_common), base=2)
|
||||||
epsilon_rate = int("".join(least for _, least in most_and_least_common), base=2)
|
epsilon_rate = int("".join(least for _, least in most_and_least_common), base=2)
|
||||||
print(f"answer 1 is {gamma_rate * epsilon_rate}")
|
yield gamma_rate * epsilon_rate
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
oxygen_generator_rating = int(generator_rating(lines, True, "1"), base=2)
|
oxygen_generator_rating = int(generator_rating(lines, True, "1"), base=2)
|
||||||
co2_scrubber_rating = int(generator_rating(lines, False, "0"), base=2)
|
co2_scrubber_rating = int(generator_rating(lines, False, "0"), base=2)
|
||||||
answer_2 = oxygen_generator_rating * co2_scrubber_rating
|
yield oxygen_generator_rating * co2_scrubber_rating
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
numbers = [int(c) for c in lines[0].split(",")]
|
numbers = [int(c) for c in lines[0].split(",")]
|
||||||
|
|
||||||
@ -26,7 +31,9 @@ for round, number in enumerate(numbers):
|
|||||||
if winning_rounds[index][0] > 0:
|
if winning_rounds[index][0] > 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if np.any(np.all(marked[index], axis=0) | np.all(marked[index], axis=1)):
|
if np.any(
|
||||||
|
np.all(marked[index], axis=0) | np.all(marked[index], axis=1)
|
||||||
|
):
|
||||||
winning_rounds[index] = (
|
winning_rounds[index] = (
|
||||||
round,
|
round,
|
||||||
number * int(np.sum(boards[index][~marked[index]])),
|
number * int(np.sum(boards[index][~marked[index]])),
|
||||||
@ -38,8 +45,8 @@ for round, number in enumerate(numbers):
|
|||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
(_, score) = min(winning_rounds, key=lambda w: w[0])
|
(_, score) = min(winning_rounds, key=lambda w: w[0])
|
||||||
print(f"answer 1 is {score}")
|
yield score
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
(_, score) = max(winning_rounds, key=lambda w: w[0])
|
(_, score) = max(winning_rounds, key=lambda w: w[0])
|
||||||
print(f"answer 2 is {score}")
|
yield score
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
lines: list[str] = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
sections: list[tuple[tuple[int, int], tuple[int, int]]] = [
|
sections: list[tuple[tuple[int, int], tuple[int, int]]] = [
|
||||||
(
|
(
|
||||||
@ -20,10 +25,8 @@ sections: list[tuple[tuple[int, int], tuple[int, int]]] = [
|
|||||||
|
|
||||||
np_sections = np.array(sections).reshape(-1, 4)
|
np_sections = np.array(sections).reshape(-1, 4)
|
||||||
|
|
||||||
x_min, x_max, y_min, y_max = (
|
x_max, y_max = (
|
||||||
min(np_sections[:, 0].min(), np_sections[:, 2].min()),
|
|
||||||
max(np_sections[:, 0].max(), np_sections[:, 2].max()),
|
max(np_sections[:, 0].max(), np_sections[:, 2].max()),
|
||||||
min(np_sections[:, 1].min(), np_sections[:, 3].min()),
|
|
||||||
max(np_sections[:, 1].max(), np_sections[:, 3].max()),
|
max(np_sections[:, 1].max(), np_sections[:, 3].max()),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -41,8 +44,5 @@ for (x1, y1), (x2, y2) in sections:
|
|||||||
for i, j in zip(y_rng, x_rng):
|
for i, j in zip(y_rng, x_rng):
|
||||||
counts_2[i, j] += 1
|
counts_2[i, j] += 1
|
||||||
|
|
||||||
answer_1 = (counts_1 >= 2).sum()
|
yield (counts_1 >= 2).sum()
|
||||||
print(f"answer 1 is {answer_1}")
|
yield (counts_2 >= 2).sum()
|
||||||
|
|
||||||
answer_2 = (counts_2 >= 2).sum()
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
values = [int(c) for c in sys.stdin.read().strip().split(",")]
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
values = [int(c) for c in input.split(",")]
|
||||||
|
|
||||||
days = 256
|
days = 256
|
||||||
lanterns = {day: 0 for day in range(days)}
|
lanterns = {day: 0 for day in range(days)}
|
||||||
@ -12,10 +17,5 @@ for day in range(days):
|
|||||||
for day2 in range(day + 9, days, 7):
|
for day2 in range(day + 9, days, 7):
|
||||||
lanterns[day2] += lanterns[day]
|
lanterns[day2] += lanterns[day]
|
||||||
|
|
||||||
# part 1
|
yield sum(v for k, v in lanterns.items() if k < 80) + len(values)
|
||||||
answer_1 = sum(v for k, v in lanterns.items() if k < 80) + len(values)
|
yield sum(lanterns.values()) + len(values)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
|
||||||
answer_2 = sum(lanterns.values()) + len(values)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
positions = [int(c) for c in sys.stdin.read().strip().split(",")]
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
positions = [int(c) for c in input.split(",")]
|
||||||
|
|
||||||
min_position, max_position = min(positions), max(positions)
|
min_position, max_position = min(positions), max(positions)
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = min(
|
yield min(
|
||||||
sum(abs(p - position) for p in positions)
|
sum(abs(p - position) for p in positions)
|
||||||
for position in range(min_position, max_position + 1)
|
for position in range(min_position, max_position + 1)
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
answer_2 = min(
|
yield min(
|
||||||
sum(abs(p - position) * (abs(p - position) + 1) // 2 for p in positions)
|
sum(abs(p - position) * (abs(p - position) + 1) // 2 for p in positions)
|
||||||
for position in range(min_position, max_position + 1)
|
for position in range(min_position, max_position + 1)
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import os
|
from typing import Any, Iterator
|
||||||
import sys
|
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
|
|
||||||
digits = {
|
digits = {
|
||||||
"abcefg": 0,
|
"abcefg": 0,
|
||||||
@ -17,14 +16,18 @@ digits = {
|
|||||||
"abcdfg": 9,
|
"abcdfg": 9,
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
lengths = {len(k) for k, v in digits.items() if v in (1, 4, 7, 8)}
|
lengths = {len(k) for k, v in digits.items() if v in (1, 4, 7, 8)}
|
||||||
answer_1 = sum(
|
yield sum(
|
||||||
len(p) in lengths for line in lines for p in line.split("|")[1].strip().split()
|
len(p) in lengths
|
||||||
|
for line in lines
|
||||||
|
for p in line.split("|")[1].strip().split()
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
values: list[int] = []
|
values: list[int] = []
|
||||||
@ -49,7 +52,9 @@ for line in lines:
|
|||||||
bd = [u for u in per_length[4][0] if u not in cf]
|
bd = [u for u in per_length[4][0] if u not in cf]
|
||||||
|
|
||||||
# the 3 digits of length 5 have a, d and g in common
|
# the 3 digits of length 5 have a, d and g in common
|
||||||
adg = [u for u in per_length[5][0] if all(u in pe for pe in per_length[5][1:])]
|
adg = [
|
||||||
|
u for u in per_length[5][0] if all(u in pe for pe in per_length[5][1:])
|
||||||
|
]
|
||||||
|
|
||||||
# we can remove a
|
# we can remove a
|
||||||
dg = [u for u in adg if u != a]
|
dg = [u for u in adg if u != a]
|
||||||
@ -77,11 +82,8 @@ for line in lines:
|
|||||||
digit = "".join(sorted(mapping[c] for c in number))
|
digit = "".join(sorted(mapping[c] for c in number))
|
||||||
value = 10 * value + digits[digit]
|
value = 10 * value + digits[digit]
|
||||||
|
|
||||||
if VERBOSE:
|
self.logger.info(f"value for '{line}' is {value}")
|
||||||
print(value)
|
|
||||||
|
|
||||||
values.append(value)
|
values.append(value)
|
||||||
|
|
||||||
|
yield sum(values)
|
||||||
answer_2 = sum(values)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import sys
|
|
||||||
from math import prod
|
from math import prod
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
values = [[int(c) for c in row] for row in sys.stdin.read().splitlines()]
|
from ..base import BaseSolver
|
||||||
n_rows, n_cols = len(values), len(values[0])
|
|
||||||
|
|
||||||
|
|
||||||
def neighbors(point: tuple[int, int]):
|
def neighbors(point: tuple[int, int], n_rows: int, n_cols: int):
|
||||||
i, j = point
|
i, j = point
|
||||||
for di, dj in ((-1, 0), (+1, 0), (0, -1), (0, +1)):
|
for di, dj in ((-1, 0), (+1, 0), (0, -1), (0, +1)):
|
||||||
if 0 <= i + di < n_rows and 0 <= j + dj < n_cols:
|
if 0 <= i + di < n_rows and 0 <= j + dj < n_cols:
|
||||||
yield (i + di, j + dj)
|
yield (i + di, j + dj)
|
||||||
|
|
||||||
|
|
||||||
def basin(start: tuple[int, int]) -> set[tuple[int, int]]:
|
def basin(values: list[list[int]], start: tuple[int, int]) -> set[tuple[int, int]]:
|
||||||
|
n_rows, n_cols = len(values), len(values[0])
|
||||||
visited: set[tuple[int, int]] = set()
|
visited: set[tuple[int, int]] = set()
|
||||||
queue = [start]
|
queue = [start]
|
||||||
|
|
||||||
@ -23,22 +23,25 @@ def basin(start: tuple[int, int]) -> set[tuple[int, int]]:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
visited.add((i, j))
|
visited.add((i, j))
|
||||||
queue.extend(neighbors((i, j)))
|
queue.extend(neighbors((i, j), n_rows, n_cols))
|
||||||
|
|
||||||
return visited
|
return visited
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
values = [[int(c) for c in row] for row in input.splitlines()]
|
||||||
|
n_rows, n_cols = len(values), len(values[0])
|
||||||
|
|
||||||
low_points = [
|
low_points = [
|
||||||
(i, j)
|
(i, j)
|
||||||
for i in range(n_rows)
|
for i in range(n_rows)
|
||||||
for j in range(n_cols)
|
for j in range(n_cols)
|
||||||
if all(values[ti][tj] > values[i][j] for ti, tj in neighbors((i, j)))
|
if all(
|
||||||
|
values[ti][tj] > values[i][j]
|
||||||
|
for ti, tj in neighbors((i, j), n_rows, n_cols)
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
# part 1
|
yield sum(values[i][j] + 1 for i, j in low_points)
|
||||||
answer_1 = sum(values[i][j] + 1 for i, j in low_points)
|
yield prod(sorted(len(basin(values, point)) for point in low_points)[-3:])
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
|
||||||
answer_2 = prod(sorted(len(basin(point)) for point in low_points)[-3:])
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
blocks = sys.stdin.read().split("\n\n")
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
blocks = input.split("\n\n")
|
||||||
values = sorted(sum(map(int, block.split())) for block in blocks)
|
values = sorted(sum(map(int, block.split())) for block in blocks)
|
||||||
|
|
||||||
print(f"answer 1 is {values[-1]}")
|
yield values[-1]
|
||||||
print(f"answer 2 is {sum(values[-3:])}")
|
yield sum(values[-3:])
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
cycle = 1
|
|
||||||
x = 1
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = [line.strip() for line in input.splitlines()]
|
||||||
|
|
||||||
|
cycle, x = 1, 1
|
||||||
values = {cycle: x}
|
values = {cycle: x}
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
@ -23,16 +26,18 @@ for line in lines:
|
|||||||
values[cycle] = x
|
values[cycle] = x
|
||||||
|
|
||||||
answer_1 = sum(c * values[c] for c in range(20, max(values.keys()) + 1, 40))
|
answer_1 = sum(c * values[c] for c in range(20, max(values.keys()) + 1, 40))
|
||||||
print(f"answer 1 is {answer_1}")
|
yield answer_1
|
||||||
|
|
||||||
|
yield (
|
||||||
for i in range(6):
|
"\n"
|
||||||
for j in range(40):
|
+ "\n".join(
|
||||||
v = values[1 + i * 40 + j]
|
"".join(
|
||||||
|
"#"
|
||||||
if j >= v - 1 and j <= v + 1:
|
if j >= (v := values[1 + i * 40 + j]) - 1 and j <= v + 1
|
||||||
print("#", end="")
|
else "."
|
||||||
else:
|
for j in range(40)
|
||||||
print(".", end="")
|
)
|
||||||
|
for i in range(6)
|
||||||
print()
|
)
|
||||||
|
+ "\n"
|
||||||
|
)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import copy
|
import copy
|
||||||
import sys
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from typing import Callable, Final, Mapping, Sequence
|
from typing import Any, Callable, Final, Iterator, Mapping, Sequence
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
class Monkey:
|
class Monkey:
|
||||||
@ -119,13 +120,14 @@ def monkey_business(inspects: dict[Monkey, int]) -> int:
|
|||||||
return sorted_levels[-2] * sorted_levels[-1]
|
return sorted_levels[-2] * sorted_levels[-1]
|
||||||
|
|
||||||
|
|
||||||
monkeys = [parse_monkey(block.splitlines()) for block in sys.stdin.read().split("\n\n")]
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
monkeys = [parse_monkey(block.splitlines()) for block in input.split("\n\n")]
|
||||||
|
|
||||||
# case 1: we simply divide the worry by 3 after applying the monkey worry operation
|
# case 1: we simply divide the worry by 3 after applying the monkey worry operation
|
||||||
answer_1 = monkey_business(
|
yield monkey_business(
|
||||||
run(copy.deepcopy(monkeys), 20, me_worry_fn=lambda w: w // 3)
|
run(copy.deepcopy(monkeys), 20, me_worry_fn=lambda w: w // 3)
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# case 2: to keep reasonable level values, we can use a modulo operation, we need to
|
# case 2: to keep reasonable level values, we can use a modulo operation, we need to
|
||||||
# use the product of all "divisible by" test so that the test remains valid
|
# use the product of all "divisible by" test so that the test remains valid
|
||||||
@ -136,7 +138,10 @@ print(f"answer 1 is {answer_1}")
|
|||||||
# we use the product of all test value
|
# we use the product of all test value
|
||||||
#
|
#
|
||||||
total_test_value = reduce(lambda w, m: w * m.test_value, monkeys, 1)
|
total_test_value = reduce(lambda w, m: w * m.test_value, monkeys, 1)
|
||||||
answer_2 = monkey_business(
|
yield monkey_business(
|
||||||
run(copy.deepcopy(monkeys), 10_000, me_worry_fn=lambda w: w % total_test_value)
|
run(
|
||||||
|
copy.deepcopy(monkeys),
|
||||||
|
10_000,
|
||||||
|
me_worry_fn=lambda w: w % total_test_value,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import heapq
|
import heapq
|
||||||
import sys
|
from typing import Any, Callable, Iterator, TypeVar
|
||||||
from typing import Callable, Iterator, TypeVar
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
Node = TypeVar("Node")
|
Node = TypeVar("Node")
|
||||||
|
|
||||||
@ -68,30 +69,6 @@ def make_path(parents: dict[Node, Node], start: Node, end: Node) -> list[Node] |
|
|||||||
return list(reversed(path))
|
return list(reversed(path))
|
||||||
|
|
||||||
|
|
||||||
def print_path(path: list[tuple[int, int]], n_rows: int, n_cols: int) -> None:
|
|
||||||
end = path[-1]
|
|
||||||
|
|
||||||
graph = [["." for _c in range(n_cols)] for _r in range(n_rows)]
|
|
||||||
graph[end[0]][end[1]] = "E"
|
|
||||||
|
|
||||||
for i in range(0, len(path) - 1):
|
|
||||||
cr, cc = path[i]
|
|
||||||
nr, nc = path[i + 1]
|
|
||||||
|
|
||||||
if cr == nr and nc == cc - 1:
|
|
||||||
graph[cr][cc] = "<"
|
|
||||||
elif cr == nr and nc == cc + 1:
|
|
||||||
graph[cr][cc] = ">"
|
|
||||||
elif cr == nr - 1 and nc == cc:
|
|
||||||
graph[cr][cc] = "v"
|
|
||||||
elif cr == nr + 1 and nc == cc:
|
|
||||||
graph[cr][cc] = "^"
|
|
||||||
else:
|
|
||||||
assert False, "{} -> {} infeasible".format(path[i], path[i + 1])
|
|
||||||
|
|
||||||
print("\n".join("".join(row) for row in graph))
|
|
||||||
|
|
||||||
|
|
||||||
def neighbors(
|
def neighbors(
|
||||||
grid: list[list[int]], node: tuple[int, int], up: bool
|
grid: list[list[int]], node: tuple[int, int], up: bool
|
||||||
) -> Iterator[tuple[int, int]]:
|
) -> Iterator[tuple[int, int]]:
|
||||||
@ -118,7 +95,42 @@ def neighbors(
|
|||||||
|
|
||||||
# === main code ===
|
# === main code ===
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def print_path(
|
||||||
|
self, name: str, path: list[tuple[int, int]], n_rows: int, n_cols: int
|
||||||
|
) -> None:
|
||||||
|
if not self.files:
|
||||||
|
return
|
||||||
|
|
||||||
|
end = path[-1]
|
||||||
|
|
||||||
|
graph = [["." for _c in range(n_cols)] for _r in range(n_rows)]
|
||||||
|
graph[end[0]][end[1]] = "E"
|
||||||
|
|
||||||
|
for i in range(0, len(path) - 1):
|
||||||
|
cr, cc = path[i]
|
||||||
|
nr, nc = path[i + 1]
|
||||||
|
|
||||||
|
if cr == nr and nc == cc - 1:
|
||||||
|
graph[cr][cc] = "<"
|
||||||
|
elif cr == nr and nc == cc + 1:
|
||||||
|
graph[cr][cc] = ">"
|
||||||
|
elif cr == nr - 1 and nc == cc:
|
||||||
|
graph[cr][cc] = "v"
|
||||||
|
elif cr == nr + 1 and nc == cc:
|
||||||
|
graph[cr][cc] = "^"
|
||||||
|
else:
|
||||||
|
assert False, "{} -> {} infeasible".format(path[i], path[i + 1])
|
||||||
|
|
||||||
|
self.files.create(
|
||||||
|
f"graph_{name}.txt",
|
||||||
|
"\n".join("".join(row) for row in graph).encode(),
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
grid = [[ord(cell) - ord("a") for cell in line] for line in lines]
|
grid = [[ord(cell) - ord("a") for cell in line] for line in lines]
|
||||||
|
|
||||||
@ -145,19 +157,20 @@ assert end is not None
|
|||||||
grid[start[0]][start[1]] = 0
|
grid[start[0]][start[1]] = 0
|
||||||
grid[end[0]][end[1]] = ord("z") - ord("a")
|
grid[end[0]][end[1]] = ord("z") - ord("a")
|
||||||
|
|
||||||
|
|
||||||
lengths_1, parents_1 = dijkstra(
|
lengths_1, parents_1 = dijkstra(
|
||||||
start=start, neighbors=lambda n: neighbors(grid, n, True), cost=lambda lhs, rhs: 1
|
start=start,
|
||||||
|
neighbors=lambda n: neighbors(grid, n, True),
|
||||||
|
cost=lambda lhs, rhs: 1,
|
||||||
)
|
)
|
||||||
path_1 = make_path(parents_1, start, end)
|
path_1 = make_path(parents_1, start, end)
|
||||||
assert path_1 is not None
|
assert path_1 is not None
|
||||||
|
|
||||||
print_path(path_1, n_rows=len(grid), n_cols=len(grid[0]))
|
self.print_path("answer1", path_1, n_rows=len(grid), n_cols=len(grid[0]))
|
||||||
|
yield lengths_1[end] - 1
|
||||||
|
|
||||||
print(f"answer 1 is {lengths_1[end] - 1}")
|
lengths_2, _ = dijkstra(
|
||||||
|
start=end,
|
||||||
lengths_2, parents_2 = dijkstra(
|
neighbors=lambda n: neighbors(grid, n, False),
|
||||||
start=end, neighbors=lambda n: neighbors(grid, n, False), cost=lambda lhs, rhs: 1
|
cost=lambda lhs, rhs: 1,
|
||||||
)
|
)
|
||||||
answer_2 = min(lengths_2.get(start, float("inf")) for start in start_s)
|
yield min(lengths_2.get(start, float("inf")) for start in start_s)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import json
|
import json
|
||||||
import sys
|
|
||||||
from functools import cmp_to_key
|
from functools import cmp_to_key
|
||||||
from typing import TypeAlias, cast
|
from typing import Any, Iterator, TypeAlias, cast
|
||||||
|
|
||||||
blocks = sys.stdin.read().strip().split("\n\n")
|
from ..base import BaseSolver
|
||||||
|
|
||||||
pairs = [tuple(json.loads(p) for p in block.split("\n")) for block in blocks]
|
|
||||||
|
|
||||||
Packet: TypeAlias = list[int | list["Packet"]]
|
Packet: TypeAlias = list[int | list["Packet"]]
|
||||||
|
|
||||||
@ -28,8 +25,12 @@ def compare(lhs: Packet, rhs: Packet) -> int:
|
|||||||
return len(rhs) - len(lhs)
|
return len(rhs) - len(lhs)
|
||||||
|
|
||||||
|
|
||||||
answer_1 = sum(i + 1 for i, (lhs, rhs) in enumerate(pairs) if compare(lhs, rhs) > 0)
|
class Solver(BaseSolver):
|
||||||
print(f"answer_1 is {answer_1}")
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
blocks = input.split("\n\n")
|
||||||
|
pairs = [tuple(json.loads(p) for p in block.split("\n")) for block in blocks]
|
||||||
|
|
||||||
|
yield sum(i + 1 for i, (lhs, rhs) in enumerate(pairs) if compare(lhs, rhs) > 0)
|
||||||
|
|
||||||
dividers = [[[2]], [[6]]]
|
dividers = [[[2]], [[6]]]
|
||||||
|
|
||||||
@ -38,4 +39,4 @@ packets.extend(dividers)
|
|||||||
packets = list(reversed(sorted(packets, key=cmp_to_key(compare))))
|
packets = list(reversed(sorted(packets, key=cmp_to_key(compare))))
|
||||||
|
|
||||||
d_index = [packets.index(d) + 1 for d in dividers]
|
d_index = [packets.index(d) + 1 for d in dividers]
|
||||||
print(f"answer 2 is {d_index[0] * d_index[1]}")
|
yield d_index[0] * d_index[1]
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import sys
|
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from typing import Callable, cast
|
from typing import Any, Callable, Iterator, cast
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
class Cell(Enum):
|
class Cell(Enum):
|
||||||
@ -12,26 +13,6 @@ class Cell(Enum):
|
|||||||
return {Cell.AIR: ".", Cell.ROCK: "#", Cell.SAND: "O"}[self]
|
return {Cell.AIR: ".", Cell.ROCK: "#", Cell.SAND: "O"}[self]
|
||||||
|
|
||||||
|
|
||||||
def print_blocks(blocks: dict[tuple[int, int], Cell]):
|
|
||||||
"""
|
|
||||||
Print the given set of blocks on a grid.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
blocks: Set of blocks to print.
|
|
||||||
"""
|
|
||||||
x_min, y_min, x_max, y_max = (
|
|
||||||
min(x for x, _ in blocks),
|
|
||||||
0,
|
|
||||||
max(x for x, _ in blocks),
|
|
||||||
max(y for _, y in blocks),
|
|
||||||
)
|
|
||||||
|
|
||||||
for y in range(y_min, y_max + 1):
|
|
||||||
print(
|
|
||||||
"".join(str(blocks.get((x, y), Cell.AIR)) for x in range(x_min, x_max + 1))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def flow(
|
def flow(
|
||||||
blocks: dict[tuple[int, int], Cell],
|
blocks: dict[tuple[int, int], Cell],
|
||||||
stop_fn: Callable[[int, int], bool],
|
stop_fn: Callable[[int, int], bool],
|
||||||
@ -84,19 +65,51 @@ def flow(
|
|||||||
|
|
||||||
# === inputs ===
|
# === inputs ===
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def print_blocks(self, name: str, blocks: dict[tuple[int, int], Cell]):
|
||||||
|
"""
|
||||||
|
Print the given set of blocks on a grid.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
blocks: Set of blocks to print.
|
||||||
|
"""
|
||||||
|
if not self.files:
|
||||||
|
return
|
||||||
|
|
||||||
|
x_min, y_min, x_max, y_max = (
|
||||||
|
min(x for x, _ in blocks),
|
||||||
|
0,
|
||||||
|
max(x for x, _ in blocks),
|
||||||
|
max(y for _, y in blocks),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.files.create(
|
||||||
|
f"blocks_{name}.txt",
|
||||||
|
"\n".join(
|
||||||
|
"".join(
|
||||||
|
str(blocks.get((x, y), Cell.AIR)) for x in range(x_min, x_max + 1)
|
||||||
|
)
|
||||||
|
for y in range(y_min, y_max + 1)
|
||||||
|
).encode(),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = [line.strip() for line in input.splitlines()]
|
||||||
|
|
||||||
paths: list[list[tuple[int, int]]] = []
|
paths: list[list[tuple[int, int]]] = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
parts = line.split(" -> ")
|
parts = line.split(" -> ")
|
||||||
paths.append(
|
paths.append(
|
||||||
[
|
[
|
||||||
cast(tuple[int, int], tuple(int(c.strip()) for c in part.split(",")))
|
cast(
|
||||||
|
tuple[int, int], tuple(int(c.strip()) for c in part.split(","))
|
||||||
|
)
|
||||||
for part in parts
|
for part in parts
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
blocks: dict[tuple[int, int], Cell] = {}
|
blocks: dict[tuple[int, int], Cell] = {}
|
||||||
for path in paths:
|
for path in paths:
|
||||||
for start, end in zip(path[:-1], path[1:]):
|
for start, end in zip(path[:-1], path[1:]):
|
||||||
@ -109,24 +122,17 @@ for path in paths:
|
|||||||
for y in range(y_start, y_end):
|
for y in range(y_start, y_end):
|
||||||
blocks[x, y] = Cell.ROCK
|
blocks[x, y] = Cell.ROCK
|
||||||
|
|
||||||
print_blocks(blocks)
|
self.print_blocks("start", blocks)
|
||||||
print()
|
|
||||||
|
|
||||||
x_min, y_min, x_max, y_max = (
|
y_max = max(y for _, y in blocks)
|
||||||
min(x for x, _ in blocks),
|
|
||||||
0,
|
|
||||||
max(x for x, _ in blocks),
|
|
||||||
max(y for _, y in blocks),
|
|
||||||
)
|
|
||||||
|
|
||||||
# === part 1 ===
|
# === part 1 ===
|
||||||
|
|
||||||
blocks_1 = flow(
|
blocks_1 = flow(
|
||||||
blocks.copy(), stop_fn=lambda x, y: y > y_max, fill_fn=lambda x, y: Cell.AIR
|
blocks.copy(), stop_fn=lambda x, y: y > y_max, fill_fn=lambda x, y: Cell.AIR
|
||||||
)
|
)
|
||||||
print_blocks(blocks_1)
|
self.print_blocks("part1", blocks_1)
|
||||||
print(f"answer 1 is {sum(v == Cell.SAND for v in blocks_1.values())}")
|
yield sum(v == Cell.SAND for v in blocks_1.values())
|
||||||
print()
|
|
||||||
|
|
||||||
# === part 2 ===
|
# === part 2 ===
|
||||||
|
|
||||||
@ -136,5 +142,5 @@ blocks_2 = flow(
|
|||||||
fill_fn=lambda x, y: Cell.AIR if y < y_max + 2 else Cell.ROCK,
|
fill_fn=lambda x, y: Cell.AIR if y < y_max + 2 else Cell.ROCK,
|
||||||
)
|
)
|
||||||
blocks_2[500, 0] = Cell.SAND
|
blocks_2[500, 0] = Cell.SAND
|
||||||
print_blocks(blocks_2)
|
self.print_blocks("part2", blocks_2)
|
||||||
print(f"answer 2 is {sum(v == Cell.SAND for v in blocks_2.values())}")
|
yield sum(v == Cell.SAND for v in blocks_2.values())
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
import sys
|
import itertools as it
|
||||||
from typing import Any
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import parse # type: ignore
|
import parse # type: ignore
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
def part1(sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], row: int) -> int:
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def part1(
|
||||||
|
self, sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], row: int
|
||||||
|
) -> int:
|
||||||
no_beacons_row_l: list[NDArray[np.floating[Any]]] = []
|
no_beacons_row_l: list[NDArray[np.floating[Any]]] = []
|
||||||
|
|
||||||
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
||||||
@ -16,17 +21,14 @@ def part1(sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], row: int) ->
|
|||||||
no_beacons_row_l.append(sx + np.arange(0, d - abs(sy - row) + 1)) # type: ignore
|
no_beacons_row_l.append(sx + np.arange(0, d - abs(sy - row) + 1)) # type: ignore
|
||||||
|
|
||||||
beacons_at_row = set(bx for (bx, by) in sensor_to_beacon.values() if by == row)
|
beacons_at_row = set(bx for (bx, by) in sensor_to_beacon.values() if by == row)
|
||||||
no_beacons_row = set(np.concatenate(no_beacons_row_l)).difference(beacons_at_row) # type: ignore
|
no_beacons_row = set(it.chain(*no_beacons_row_l)).difference(beacons_at_row) # type: ignore
|
||||||
|
|
||||||
return len(no_beacons_row)
|
return len(no_beacons_row)
|
||||||
|
|
||||||
|
|
||||||
def part2_intervals(
|
def part2_intervals(
|
||||||
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], xy_max: int
|
self, sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], xy_max: int
|
||||||
) -> tuple[int, int, int]:
|
) -> tuple[int, int, int]:
|
||||||
from tqdm import trange
|
for y in self.progress.wrap(range(xy_max + 1)):
|
||||||
|
|
||||||
for y in trange(xy_max + 1):
|
|
||||||
its: list[tuple[int, int]] = []
|
its: list[tuple[int, int]] = []
|
||||||
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
||||||
d = abs(sx - bx) + abs(sy - by)
|
d = abs(sx - bx) + abs(sy - by)
|
||||||
@ -46,9 +48,8 @@ def part2_intervals(
|
|||||||
|
|
||||||
return (0, 0, 0)
|
return (0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
def part2_cplex(
|
def part2_cplex(
|
||||||
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], xy_max: int
|
self, sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], xy_max: int
|
||||||
) -> tuple[int, int, int]:
|
) -> tuple[int, int, int]:
|
||||||
from docplex.mp.model import Model
|
from docplex.mp.model import Model
|
||||||
|
|
||||||
@ -58,7 +59,10 @@ def part2_cplex(
|
|||||||
|
|
||||||
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
||||||
d = abs(sx - bx) + abs(sy - by)
|
d = abs(sx - bx) + abs(sy - by)
|
||||||
m.add_constraint(m.abs(x - sx) + m.abs(y - sy) >= d + 1, ctname=f"ct_{sx}_{sy}") # type: ignore
|
m.add_constraint(
|
||||||
|
m.abs(x - sx) + m.abs(y - sy) >= d + 1, # type: ignore
|
||||||
|
ctname=f"ct_{sx}_{sy}",
|
||||||
|
)
|
||||||
|
|
||||||
m.set_objective("min", x + y)
|
m.set_objective("min", x + y)
|
||||||
|
|
||||||
@ -69,8 +73,8 @@ def part2_cplex(
|
|||||||
vy = int(s.get_value(y))
|
vy = int(s.get_value(y))
|
||||||
return vx, vy, 4_000_000 * vx + vy
|
return vx, vy, 4_000_000 * vx + vy
|
||||||
|
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = input.splitlines()
|
||||||
|
|
||||||
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]] = {}
|
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]] = {}
|
||||||
|
|
||||||
@ -83,8 +87,9 @@ for line in lines:
|
|||||||
xy_max = 4_000_000 if max(sensor_to_beacon) > (1_000, 0) else 20
|
xy_max = 4_000_000 if max(sensor_to_beacon) > (1_000, 0) else 20
|
||||||
row = 2_000_000 if max(sensor_to_beacon) > (1_000, 0) else 10
|
row = 2_000_000 if max(sensor_to_beacon) > (1_000, 0) else 10
|
||||||
|
|
||||||
print(f"answer 1 is {part1(sensor_to_beacon, row)}")
|
yield self.part1(sensor_to_beacon, row)
|
||||||
|
|
||||||
# x, y, a2 = part2_cplex(sensor_to_beacon, xy_max)
|
# x, y, a2 = part2_cplex(sensor_to_beacon, xy_max)
|
||||||
x, y, a2 = part2_intervals(sensor_to_beacon, xy_max)
|
x, y, a2 = self.part2_intervals(sensor_to_beacon, xy_max)
|
||||||
print(f"answer 2 is {a2} (x={x}, y={y})")
|
self.logger.info(f"answer 2 is {a2} (x={x}, y={y})")
|
||||||
|
yield a2
|
||||||
|
@ -3,11 +3,10 @@ from __future__ import annotations
|
|||||||
import heapq
|
import heapq
|
||||||
import itertools
|
import itertools
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import FrozenSet, NamedTuple
|
from typing import Any, FrozenSet, Iterator, NamedTuple
|
||||||
|
|
||||||
from tqdm import tqdm
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
class Pipe(NamedTuple):
|
class Pipe(NamedTuple):
|
||||||
@ -36,8 +35,8 @@ def breadth_first_search(pipes: dict[str, Pipe], pipe: Pipe) -> dict[Pipe, int]:
|
|||||||
Runs a BFS from the given pipe and return the shortest distance (in term of hops)
|
Runs a BFS from the given pipe and return the shortest distance (in term of hops)
|
||||||
to all other pipes.
|
to all other pipes.
|
||||||
"""
|
"""
|
||||||
queue = [(0, pipe_1)]
|
queue = [(0, pipe)]
|
||||||
visited = set()
|
visited: set[Pipe] = set()
|
||||||
distances: dict[Pipe, int] = {}
|
distances: dict[Pipe, int] = {}
|
||||||
|
|
||||||
while len(distances) < len(pipes):
|
while len(distances) < len(pipes):
|
||||||
@ -61,7 +60,12 @@ def update_with_better(
|
|||||||
node_at_times[flowing] = max(node_at_times[flowing], flow)
|
node_at_times[flowing] = max(node_at_times[flowing], flow)
|
||||||
|
|
||||||
|
|
||||||
|
# === MAIN ===
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
def part_1(
|
def part_1(
|
||||||
|
self,
|
||||||
start_pipe: Pipe,
|
start_pipe: Pipe,
|
||||||
max_time: int,
|
max_time: int,
|
||||||
distances: dict[tuple[Pipe, Pipe], int],
|
distances: dict[tuple[Pipe, Pipe], int],
|
||||||
@ -98,15 +102,17 @@ def part_1(
|
|||||||
for flow in nodes_of_pipe.values()
|
for flow in nodes_of_pipe.values()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def part_2(
|
def part_2(
|
||||||
|
self,
|
||||||
start_pipe: Pipe,
|
start_pipe: Pipe,
|
||||||
max_time: int,
|
max_time: int,
|
||||||
distances: dict[tuple[Pipe, Pipe], int],
|
distances: dict[tuple[Pipe, Pipe], int],
|
||||||
relevant_pipes: FrozenSet[Pipe],
|
relevant_pipes: FrozenSet[Pipe],
|
||||||
):
|
):
|
||||||
def compute(pipes_for_me: FrozenSet[Pipe]) -> int:
|
def compute(pipes_for_me: FrozenSet[Pipe]) -> int:
|
||||||
return part_1(start_pipe, max_time, distances, pipes_for_me) + part_1(
|
return self.part_1(
|
||||||
|
start_pipe, max_time, distances, pipes_for_me
|
||||||
|
) + self.part_1(
|
||||||
start_pipe, max_time, distances, relevant_pipes - pipes_for_me
|
start_pipe, max_time, distances, relevant_pipes - pipes_for_me
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -116,14 +122,10 @@ def part_2(
|
|||||||
for relevant_pipes_1 in itertools.combinations(relevant_pipes, r)
|
for relevant_pipes_1 in itertools.combinations(relevant_pipes, r)
|
||||||
]
|
]
|
||||||
|
|
||||||
return max(compute(comb) for comb in tqdm(combs))
|
return max(compute(comb) for comb in self.progress.wrap(combs))
|
||||||
|
|
||||||
|
|
||||||
# === MAIN ===
|
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = [line.strip() for line in input.splitlines()]
|
||||||
|
|
||||||
pipes: dict[str, Pipe] = {}
|
pipes: dict[str, Pipe] = {}
|
||||||
for line in lines:
|
for line in lines:
|
||||||
@ -150,9 +152,8 @@ for pipe_1 in pipes.values():
|
|||||||
# valves with flow
|
# valves with flow
|
||||||
relevant_pipes = frozenset(pipe for pipe in pipes.values() if pipe.flow > 0)
|
relevant_pipes = frozenset(pipe for pipe in pipes.values() if pipe.flow > 0)
|
||||||
|
|
||||||
|
|
||||||
# 1651, 1653
|
# 1651, 1653
|
||||||
print(part_1(pipes["AA"], 30, distances, relevant_pipes))
|
yield self.part_1(pipes["AA"], 30, distances, relevant_pipes)
|
||||||
|
|
||||||
# 1707, 2223
|
# 1707, 2223
|
||||||
print(part_2(pipes["AA"], 26, distances, relevant_pipes))
|
yield self.part_2(pipes["AA"], 26, distances, relevant_pipes)
|
||||||
|
@ -1,23 +1,16 @@
|
|||||||
import sys
|
from typing import Any, Iterator, Sequence, TypeAlias, TypeVar
|
||||||
from typing import Sequence, TypeVar
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from numpy.typing import NDArray
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
Tower: TypeAlias = NDArray[np.bool]
|
||||||
def print_tower(tower: np.ndarray, out: str = "#"):
|
|
||||||
print("-" * (tower.shape[1] + 2))
|
|
||||||
non_empty = False
|
|
||||||
for row in reversed(range(1, tower.shape[0])):
|
|
||||||
if not non_empty and not tower[row, :].any():
|
|
||||||
continue
|
|
||||||
non_empty = True
|
|
||||||
print("|" + "".join(out if c else "." for c in tower[row, :]) + "|")
|
|
||||||
print("+" + "-" * tower.shape[1] + "+")
|
|
||||||
|
|
||||||
|
|
||||||
def tower_height(tower: np.ndarray) -> int:
|
def tower_height(tower: Tower) -> int:
|
||||||
return int(tower.shape[0] - tower[::-1, :].argmax(axis=0).min() - 1)
|
return int(tower.shape[0] - tower[::-1, :].argmax(axis=0).min() - 1)
|
||||||
|
|
||||||
|
|
||||||
@ -45,8 +38,8 @@ def build_tower(
|
|||||||
n_rocks: int,
|
n_rocks: int,
|
||||||
jets: str,
|
jets: str,
|
||||||
early_stop: bool = False,
|
early_stop: bool = False,
|
||||||
init: np.ndarray = np.ones(WIDTH, dtype=bool),
|
init: Tower = np.ones(WIDTH, dtype=bool),
|
||||||
) -> tuple[np.ndarray, int, int, dict[int, int]]:
|
) -> tuple[Tower, int, int, dict[int, int]]:
|
||||||
tower = EMPTY_BLOCKS.copy()
|
tower = EMPTY_BLOCKS.copy()
|
||||||
tower[0, :] = init
|
tower[0, :] = init
|
||||||
|
|
||||||
@ -95,14 +88,13 @@ def build_tower(
|
|||||||
return tower, rock_count, done_at.get((i_rock, i_jet), -1), heights
|
return tower, rock_count, done_at.get((i_rock, i_jet), -1), heights
|
||||||
|
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
tower, *_ = build_tower(2022, line)
|
tower, *_ = build_tower(2022, input)
|
||||||
answer_1 = tower_height(tower)
|
yield tower_height(tower)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
TOTAL_ROCKS = 1_000_000_000_000
|
TOTAL_ROCKS = 1_000_000_000_000
|
||||||
tower_1, n_rocks_1, prev_1, heights_1 = build_tower(TOTAL_ROCKS, line, True)
|
_tower_1, n_rocks_1, prev_1, heights_1 = build_tower(TOTAL_ROCKS, input, True)
|
||||||
assert prev_1 > 0
|
assert prev_1 > 0
|
||||||
|
|
||||||
# 2767 1513
|
# 2767 1513
|
||||||
@ -116,5 +108,4 @@ remaining_height = (
|
|||||||
heights_1[prev_1 + remaining_rocks % n_repeat_rocks] - heights_1[prev_1]
|
heights_1[prev_1 + remaining_rocks % n_repeat_rocks] - heights_1[prev_1]
|
||||||
)
|
)
|
||||||
|
|
||||||
answer_2 = base_height + (n_repeat_towers + 1) * repeat_height + remaining_height
|
yield base_height + (n_repeat_towers + 1) * repeat_height + remaining_height
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
xyz = np.asarray(
|
xyz = np.asarray(
|
||||||
[
|
[
|
||||||
tuple(int(x) for x in row.split(",")) # type: ignore
|
tuple(int(x) for x in row.split(",")) # type: ignore
|
||||||
for row in sys.stdin.read().splitlines()
|
for row in input.splitlines()
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,14 +19,14 @@ xyz = xyz - xyz.min(axis=0) + 1
|
|||||||
cubes = np.zeros(xyz.max(axis=0) + 3, dtype=bool)
|
cubes = np.zeros(xyz.max(axis=0) + 3, dtype=bool)
|
||||||
cubes[xyz[:, 0], xyz[:, 1], xyz[:, 2]] = True
|
cubes[xyz[:, 0], xyz[:, 1], xyz[:, 2]] = True
|
||||||
|
|
||||||
n_dims = len(cubes.shape)
|
|
||||||
|
|
||||||
faces = [(-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1)]
|
faces = [(-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1)]
|
||||||
|
|
||||||
answer_1 = sum(
|
yield sum(
|
||||||
1 for x, y, z in xyz for dx, dy, dz in faces if not cubes[x + dx, y + dy, z + dz]
|
1
|
||||||
|
for x, y, z in xyz
|
||||||
|
for dx, dy, dz in faces
|
||||||
|
if not cubes[x + dx, y + dy, z + dz]
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
visited = np.zeros_like(cubes, dtype=bool)
|
visited = np.zeros_like(cubes, dtype=bool)
|
||||||
queue = [(0, 0, 0)]
|
queue = [(0, 0, 0)]
|
||||||
@ -37,7 +42,9 @@ while queue:
|
|||||||
|
|
||||||
for dx, dy, dz in faces:
|
for dx, dy, dz in faces:
|
||||||
nx, ny, nz = x + dx, y + dy, z + dz
|
nx, ny, nz = x + dx, y + dy, z + dz
|
||||||
if not all(n >= 0 and n < cubes.shape[i] for i, n in enumerate((nx, ny, nz))):
|
if not all(
|
||||||
|
n >= 0 and n < cubes.shape[i] for i, n in enumerate((nx, ny, nz))
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if visited[nx, ny, nz]:
|
if visited[nx, ny, nz]:
|
||||||
@ -47,4 +54,5 @@ while queue:
|
|||||||
n_faces += 1
|
n_faces += 1
|
||||||
else:
|
else:
|
||||||
queue.append((nx, ny, nz))
|
queue.append((nx, ny, nz))
|
||||||
print(f"answer 2 is {n_faces}")
|
|
||||||
|
yield n_faces
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import sys
|
from typing import Any, Iterator, Literal
|
||||||
from typing import Any, Literal
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import parse # pyright: ignore[reportMissingTypeStubs]
|
import parse # pyright: ignore[reportMissingTypeStubs]
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
Reagent = Literal["ore", "clay", "obsidian", "geode"]
|
Reagent = Literal["ore", "clay", "obsidian", "geode"]
|
||||||
REAGENTS: tuple[Reagent, ...] = (
|
REAGENTS: tuple[Reagent, ...] = (
|
||||||
"ore",
|
"ore",
|
||||||
@ -62,29 +63,6 @@ def dominates(lhs: State, rhs: State):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
|
||||||
blueprints: list[dict[Reagent, IntOfReagent]] = []
|
|
||||||
for line in lines:
|
|
||||||
r: list[int] = parse.parse( # type: ignore
|
|
||||||
"Blueprint {}: "
|
|
||||||
"Each ore robot costs {:d} ore. "
|
|
||||||
"Each clay robot costs {:d} ore. "
|
|
||||||
"Each obsidian robot costs {:d} ore and {:d} clay. "
|
|
||||||
"Each geode robot costs {:d} ore and {:d} obsidian.",
|
|
||||||
line,
|
|
||||||
)
|
|
||||||
|
|
||||||
blueprints.append(
|
|
||||||
{
|
|
||||||
"ore": {"ore": r[1]},
|
|
||||||
"clay": {"ore": r[2]},
|
|
||||||
"obsidian": {"ore": r[3], "clay": r[4]},
|
|
||||||
"geode": {"ore": r[5], "obsidian": r[6]},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def run(blueprint: dict[Reagent, dict[Reagent, int]], max_time: int) -> int:
|
def run(blueprint: dict[Reagent, dict[Reagent, int]], max_time: int) -> int:
|
||||||
# since we can only build one robot per time, we do not need more than X robots
|
# since we can only build one robot per time, we do not need more than X robots
|
||||||
# of type K where X is the maximum number of K required among all robots, e.g.,
|
# of type K where X is the maximum number of K required among all robots, e.g.,
|
||||||
@ -173,11 +151,31 @@ def run(blueprint: dict[Reagent, dict[Reagent, int]], max_time: int) -> int:
|
|||||||
return max(state.reagents["geode"] for state in state_after_t[max_time])
|
return max(state.reagents["geode"] for state in state_after_t[max_time])
|
||||||
|
|
||||||
|
|
||||||
answer_1 = sum(
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
blueprints: list[dict[Reagent, IntOfReagent]] = []
|
||||||
|
for line in input.splitlines():
|
||||||
|
r: list[int] = parse.parse( # type: ignore
|
||||||
|
"Blueprint {}: "
|
||||||
|
"Each ore robot costs {:d} ore. "
|
||||||
|
"Each clay robot costs {:d} ore. "
|
||||||
|
"Each obsidian robot costs {:d} ore and {:d} clay. "
|
||||||
|
"Each geode robot costs {:d} ore and {:d} obsidian.",
|
||||||
|
line,
|
||||||
|
)
|
||||||
|
|
||||||
|
blueprints.append(
|
||||||
|
{
|
||||||
|
"ore": {"ore": r[1]},
|
||||||
|
"clay": {"ore": r[2]},
|
||||||
|
"obsidian": {"ore": r[3], "clay": r[4]},
|
||||||
|
"geode": {"ore": r[5], "obsidian": r[6]},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
yield sum(
|
||||||
(i_blueprint + 1) * run(blueprint, 24)
|
(i_blueprint + 1) * run(blueprint, 24)
|
||||||
for i_blueprint, blueprint in enumerate(blueprints)
|
for i_blueprint, blueprint in enumerate(blueprints)
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = run(blueprints[0], 32) * run(blueprints[1], 32) * run(blueprints[2], 32)
|
yield (run(blueprints[0], 32) * run(blueprints[1], 32) * run(blueprints[2], 32))
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def score_1(ux: int, vx: int) -> int:
|
def score_1(ux: int, vx: int) -> int:
|
||||||
@ -33,7 +35,9 @@ def score_2(ux: int, vx: int) -> int:
|
|||||||
return (ux + vx - 1) % 3 + 1 + vx * 3
|
return (ux + vx - 1) % 3 + 1 + vx * 3
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.readlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
# the solution relies on replacing rock / paper / scissor by values 0 / 1 / 2 and using
|
# the solution relies on replacing rock / paper / scissor by values 0 / 1 / 2 and using
|
||||||
# modulo-3 arithmetic
|
# modulo-3 arithmetic
|
||||||
@ -47,7 +51,7 @@ lines = sys.stdin.readlines()
|
|||||||
values = [(ord(row[0]) - ord("A"), ord(row[2]) - ord("X")) for row in lines]
|
values = [(ord(row[0]) - ord("A"), ord(row[2]) - ord("X")) for row in lines]
|
||||||
|
|
||||||
# part 1 - 13526
|
# part 1 - 13526
|
||||||
print(f"answer 1 is {sum(score_1(*v) for v in values)}")
|
yield sum(score_1(*v) for v in values)
|
||||||
|
|
||||||
# part 2 - 14204
|
# part 2 - 14204
|
||||||
print(f"answer 2 is {sum(score_2(*v) for v in values)}")
|
yield sum(score_2(*v) for v in values)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
class Number:
|
class Number:
|
||||||
@ -65,10 +67,9 @@ def decrypt(numbers: list[Number], key: int, rounds: int) -> int:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
numbers = [Number(int(x)) for i, x in enumerate(sys.stdin.readlines())]
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
numbers = [Number(int(x)) for x in input.splitlines()]
|
||||||
|
|
||||||
answer_1 = decrypt(numbers, 1, 1)
|
yield decrypt(numbers, 1, 1)
|
||||||
print(f"answer 1 is {answer_1}")
|
yield decrypt(numbers, 811589153, 10)
|
||||||
|
|
||||||
answer_2 = decrypt(numbers, 811589153, 10)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import operator
|
import operator
|
||||||
import sys
|
from typing import Any, Callable, Iterator
|
||||||
from typing import Callable
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def compute(monkeys: dict[str, int | tuple[str, str, str]], monkey: str) -> int:
|
def compute(monkeys: dict[str, int | tuple[str, str, str]], monkey: str) -> int:
|
||||||
@ -77,7 +78,9 @@ def invert(
|
|||||||
return monkeys
|
return monkeys
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = [line.strip() for line in input.splitlines()]
|
||||||
|
|
||||||
monkeys: dict[str, int | tuple[str, str, str]] = {}
|
monkeys: dict[str, int | tuple[str, str, str]] = {}
|
||||||
|
|
||||||
@ -96,12 +99,10 @@ for line in lines:
|
|||||||
|
|
||||||
op_monkeys.add(name)
|
op_monkeys.add(name)
|
||||||
|
|
||||||
|
yield compute(monkeys.copy(), "root")
|
||||||
answer_1 = compute(monkeys.copy(), "root")
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# assume the second operand of 'root' can be computed, and the first one depends on
|
# assume the second operand of 'root' can be computed, and the first one depends on
|
||||||
# humn, which is the case is my input and the test input
|
# humn, which is the case is my input and the test input
|
||||||
|
assert isinstance(monkeys["root"], tuple)
|
||||||
p1, _, p2 = monkeys["root"] # type: ignore
|
p1, _, p2 = monkeys["root"] # type: ignore
|
||||||
answer_2 = compute(invert(monkeys, "humn", compute(monkeys.copy(), p2)), "humn")
|
yield compute(invert(monkeys, "humn", compute(monkeys.copy(), p2)), "humn")
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import re
|
import re
|
||||||
import sys
|
from typing import Any, Callable, Iterator
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
VOID, EMPTY, WALL = 0, 1, 2
|
VOID, EMPTY, WALL = 0, 1, 2
|
||||||
TILE_FROM_CHAR = {" ": VOID, ".": EMPTY, "#": WALL}
|
TILE_FROM_CHAR = {" ": VOID, ".": EMPTY, "#": WALL}
|
||||||
|
|
||||||
SCORES = {"E": 0, "S": 1, "W": 2, "N": 3}
|
SCORES = {"E": 0, "S": 1, "W": 2, "N": 3}
|
||||||
|
|
||||||
|
|
||||||
board_map_s, direction_s = sys.stdin.read().split("\n\n")
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
board_map_s, direction_s = input.split("\n\n")
|
||||||
|
|
||||||
# board
|
# board
|
||||||
board_lines = board_map_s.splitlines()
|
board_lines = board_map_s.splitlines()
|
||||||
@ -23,16 +26,19 @@ board = np.array(
|
|||||||
)
|
)
|
||||||
|
|
||||||
directions = [
|
directions = [
|
||||||
int(p1) if p2 else p1 for p1, p2 in re.findall(R"(([0-9])+|L|R)", direction_s)
|
int(p1) if p2 else p1
|
||||||
|
for p1, p2 in re.findall(R"(([0-9])+|L|R)", direction_s)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# find on each row and column the first and last non-void
|
# find on each row and column the first and last non-void
|
||||||
row_first_non_void = np.argmax(board != VOID, axis=1)
|
row_first_non_void = np.argmax(board != VOID, axis=1)
|
||||||
row_last_non_void = board.shape[1] - np.argmax(board[:, ::-1] != VOID, axis=1) - 1
|
row_last_non_void = (
|
||||||
|
board.shape[1] - np.argmax(board[:, ::-1] != VOID, axis=1) - 1
|
||||||
|
)
|
||||||
col_first_non_void = np.argmax(board != VOID, axis=0)
|
col_first_non_void = np.argmax(board != VOID, axis=0)
|
||||||
col_last_non_void = board.shape[0] - np.argmax(board[::-1, :] != VOID, axis=0) - 1
|
col_last_non_void = (
|
||||||
|
board.shape[0] - np.argmax(board[::-1, :] != VOID, axis=0) - 1
|
||||||
|
)
|
||||||
|
|
||||||
faces = np.zeros_like(board)
|
faces = np.zeros_like(board)
|
||||||
size = np.gcd(board.shape[0], board.shape[1])
|
size = np.gcd(board.shape[0], board.shape[1])
|
||||||
@ -103,7 +109,6 @@ else:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def wrap_part_1(y0: int, x0: int, r0: str) -> tuple[int, int, str]:
|
def wrap_part_1(y0: int, x0: int, r0: str) -> tuple[int, int, str]:
|
||||||
if r0 == "E":
|
if r0 == "E":
|
||||||
return y0, row_first_non_void[y0], r0
|
return y0, row_first_non_void[y0], r0
|
||||||
@ -116,14 +121,14 @@ def wrap_part_1(y0: int, x0: int, r0: str) -> tuple[int, int, str]:
|
|||||||
|
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
|
||||||
def wrap_part_2(y0: int, x0: int, r0: str) -> tuple[int, int, str]:
|
def wrap_part_2(y0: int, x0: int, r0: str) -> tuple[int, int, str]:
|
||||||
cube = faces[y0, x0]
|
cube = faces[y0, x0]
|
||||||
assert r0 in faces_wrap[cube]
|
assert r0 in faces_wrap[cube]
|
||||||
return faces_wrap[cube][r0](y0, x0)
|
return faces_wrap[cube][r0](y0, x0)
|
||||||
|
|
||||||
|
def run(
|
||||||
def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int, str]:
|
wrap: Callable[[int, int, str], tuple[int, int, str]],
|
||||||
|
) -> tuple[int, int, str]:
|
||||||
y0 = 0
|
y0 = 0
|
||||||
x0 = np.where(board[0] == EMPTY)[0][0]
|
x0 = np.where(board[0] == EMPTY)[0][0]
|
||||||
r0 = "E"
|
r0 = "E"
|
||||||
@ -132,7 +137,9 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
|
|||||||
if isinstance(direction, int):
|
if isinstance(direction, int):
|
||||||
while direction > 0:
|
while direction > 0:
|
||||||
if r0 == "E":
|
if r0 == "E":
|
||||||
xi = np.where(board[y0, x0 + 1 : x0 + direction + 1] == WALL)[0]
|
xi = np.where(
|
||||||
|
board[y0, x0 + 1 : x0 + direction + 1] == WALL
|
||||||
|
)[0]
|
||||||
if len(xi):
|
if len(xi):
|
||||||
x0 = x0 + xi[0]
|
x0 = x0 + xi[0]
|
||||||
direction = 0
|
direction = 0
|
||||||
@ -148,10 +155,14 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
|
|||||||
x0 = row_last_non_void[y0]
|
x0 = row_last_non_void[y0]
|
||||||
direction = 0
|
direction = 0
|
||||||
else:
|
else:
|
||||||
direction = direction - (row_last_non_void[y0] - x0) - 1
|
direction = (
|
||||||
|
direction - (row_last_non_void[y0] - x0) - 1
|
||||||
|
)
|
||||||
y0, x0, r0 = y0_t, x0_t, r0_t
|
y0, x0, r0 = y0_t, x0_t, r0_t
|
||||||
elif r0 == "S":
|
elif r0 == "S":
|
||||||
yi = np.where(board[y0 + 1 : y0 + direction + 1, x0] == WALL)[0]
|
yi = np.where(
|
||||||
|
board[y0 + 1 : y0 + direction + 1, x0] == WALL
|
||||||
|
)[0]
|
||||||
if len(yi):
|
if len(yi):
|
||||||
y0 = y0 + yi[0]
|
y0 = y0 + yi[0]
|
||||||
direction = 0
|
direction = 0
|
||||||
@ -167,7 +178,9 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
|
|||||||
y0 = col_last_non_void[x0]
|
y0 = col_last_non_void[x0]
|
||||||
direction = 0
|
direction = 0
|
||||||
else:
|
else:
|
||||||
direction = direction - (col_last_non_void[x0] - y0) - 1
|
direction = (
|
||||||
|
direction - (col_last_non_void[x0] - y0) - 1
|
||||||
|
)
|
||||||
y0, x0, r0 = y0_t, x0_t, r0_t
|
y0, x0, r0 = y0_t, x0_t, r0_t
|
||||||
elif r0 == "W":
|
elif r0 == "W":
|
||||||
left = max(x0 - direction - 1, 0)
|
left = max(x0 - direction - 1, 0)
|
||||||
@ -175,7 +188,10 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
|
|||||||
if len(xi):
|
if len(xi):
|
||||||
x0 = left + xi[-1] + 1
|
x0 = left + xi[-1] + 1
|
||||||
direction = 0
|
direction = 0
|
||||||
elif x0 - direction >= 0 and board[y0, x0 - direction] == EMPTY:
|
elif (
|
||||||
|
x0 - direction >= 0
|
||||||
|
and board[y0, x0 - direction] == EMPTY
|
||||||
|
):
|
||||||
x0 = x0 - direction
|
x0 = x0 - direction
|
||||||
direction = 0
|
direction = 0
|
||||||
else:
|
else:
|
||||||
@ -184,7 +200,9 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
|
|||||||
x0 = row_first_non_void[y0]
|
x0 = row_first_non_void[y0]
|
||||||
direction = 0
|
direction = 0
|
||||||
else:
|
else:
|
||||||
direction = direction - (x0 - row_first_non_void[y0]) - 1
|
direction = (
|
||||||
|
direction - (x0 - row_first_non_void[y0]) - 1
|
||||||
|
)
|
||||||
y0, x0, r0 = y0_t, x0_t, r0_t
|
y0, x0, r0 = y0_t, x0_t, r0_t
|
||||||
elif r0 == "N":
|
elif r0 == "N":
|
||||||
top = max(y0 - direction - 1, 0)
|
top = max(y0 - direction - 1, 0)
|
||||||
@ -192,7 +210,10 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
|
|||||||
if len(yi):
|
if len(yi):
|
||||||
y0 = top + yi[-1] + 1
|
y0 = top + yi[-1] + 1
|
||||||
direction = 0
|
direction = 0
|
||||||
elif y0 - direction >= 0 and board[y0 - direction, x0] == EMPTY:
|
elif (
|
||||||
|
y0 - direction >= 0
|
||||||
|
and board[y0 - direction, x0] == EMPTY
|
||||||
|
):
|
||||||
y0 = y0 - direction
|
y0 = y0 - direction
|
||||||
direction = 0
|
direction = 0
|
||||||
else:
|
else:
|
||||||
@ -201,7 +222,9 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
|
|||||||
y0 = col_first_non_void[x0]
|
y0 = col_first_non_void[x0]
|
||||||
direction = 0
|
direction = 0
|
||||||
else:
|
else:
|
||||||
direction = direction - (y0 - col_first_non_void[x0]) - 1
|
direction = (
|
||||||
|
direction - (y0 - col_first_non_void[x0]) - 1
|
||||||
|
)
|
||||||
y0, x0, r0 = y0_t, x0_t, r0_t
|
y0, x0, r0 = y0_t, x0_t, r0_t
|
||||||
else:
|
else:
|
||||||
r0 = {
|
r0 = {
|
||||||
@ -213,11 +236,8 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
|
|||||||
|
|
||||||
return y0, x0, r0
|
return y0, x0, r0
|
||||||
|
|
||||||
|
|
||||||
y1, x1, r1 = run(wrap_part_1)
|
y1, x1, r1 = run(wrap_part_1)
|
||||||
answer_1 = 1000 * (1 + y1) + 4 * (1 + x1) + SCORES[r1]
|
yield 1000 * (1 + y1) + 4 * (1 + x1) + SCORES[r1]
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
y2, x2, r2 = run(wrap_part_2)
|
y2, x2, r2 = run(wrap_part_2)
|
||||||
answer_2 = 1000 * (1 + y2) + 4 * (1 + x2) + SCORES[r2]
|
yield 1000 * (1 + y2) + 4 * (1 + x2) + SCORES[r2]
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
Directions = list[
|
Directions = list[
|
||||||
tuple[
|
tuple[
|
||||||
@ -18,22 +20,10 @@ DIRECTIONS: Directions = [
|
|||||||
|
|
||||||
|
|
||||||
def min_max_yx(positions: set[tuple[int, int]]) -> tuple[int, int, int, int]:
|
def min_max_yx(positions: set[tuple[int, int]]) -> tuple[int, int, int, int]:
|
||||||
ys, xs = {y for y, x in positions}, {x for y, x in positions}
|
ys, xs = {y for y, _x in positions}, {x for _y, x in positions}
|
||||||
return min(ys), min(xs), max(ys), max(xs)
|
return min(ys), min(xs), max(ys), max(xs)
|
||||||
|
|
||||||
|
|
||||||
def print_positions(positions: set[tuple[int, int]]):
|
|
||||||
min_y, min_x, max_y, max_x = min_max_yx(positions)
|
|
||||||
print(
|
|
||||||
"\n".join(
|
|
||||||
"".join(
|
|
||||||
"#" if (y, x) in positions else "." for x in range(min_x - 1, max_x + 2)
|
|
||||||
)
|
|
||||||
for y in range(min_y - 1, max_y + 2)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def round(
|
def round(
|
||||||
positions: set[tuple[int, int]],
|
positions: set[tuple[int, int]],
|
||||||
directions: Directions,
|
directions: Directions,
|
||||||
@ -69,9 +59,11 @@ def round(
|
|||||||
directions.append(directions.pop(0))
|
directions.append(directions.pop(0))
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
POSITIONS = {
|
POSITIONS = {
|
||||||
(i, j)
|
(i, j)
|
||||||
for i, row in enumerate(sys.stdin.read().splitlines())
|
for i, row in enumerate(input.splitlines())
|
||||||
for j, col in enumerate(row)
|
for j, col in enumerate(row)
|
||||||
if col == "#"
|
if col == "#"
|
||||||
}
|
}
|
||||||
@ -79,14 +71,15 @@ POSITIONS = {
|
|||||||
# === part 1 ===
|
# === part 1 ===
|
||||||
|
|
||||||
p1, d1 = POSITIONS.copy(), DIRECTIONS.copy()
|
p1, d1 = POSITIONS.copy(), DIRECTIONS.copy()
|
||||||
for r in range(10):
|
for _ in range(10):
|
||||||
round(p1, d1)
|
round(p1, d1)
|
||||||
|
|
||||||
min_y, min_x, max_y, max_x = min_max_yx(p1)
|
min_y, min_x, max_y, max_x = min_max_yx(p1)
|
||||||
answer_1 = sum(
|
yield sum(
|
||||||
(y, x) not in p1 for y in range(min_y, max_y + 1) for x in range(min_x, max_x + 1)
|
(y, x) not in p1
|
||||||
|
for y in range(min_y, max_y + 1)
|
||||||
|
for x in range(min_x, max_x + 1)
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# === part 2 ===
|
# === part 2 ===
|
||||||
|
|
||||||
@ -100,4 +93,4 @@ while True:
|
|||||||
if backup == p2:
|
if backup == p2:
|
||||||
break
|
break
|
||||||
|
|
||||||
print(f"answer 2 is {answer_2}")
|
yield answer_2
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
import heapq
|
import heapq
|
||||||
import math
|
import math
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = [line.strip() for line in input.splitlines()]
|
||||||
|
|
||||||
winds = {
|
winds = {
|
||||||
(i - 1, j - 1, lines[i][j])
|
(i - 1, j - 1, lines[i][j])
|
||||||
@ -15,8 +20,12 @@ winds = {
|
|||||||
n_rows, n_cols = len(lines) - 2, len(lines[0]) - 2
|
n_rows, n_cols = len(lines) - 2, len(lines[0]) - 2
|
||||||
CYCLE = math.lcm(n_rows, n_cols)
|
CYCLE = math.lcm(n_rows, n_cols)
|
||||||
|
|
||||||
east_winds = [{j for j in range(n_cols) if (i, j, ">") in winds} for i in range(n_rows)]
|
east_winds = [
|
||||||
west_winds = [{j for j in range(n_cols) if (i, j, "<") in winds} for i in range(n_rows)]
|
{j for j in range(n_cols) if (i, j, ">") in winds} for i in range(n_rows)
|
||||||
|
]
|
||||||
|
west_winds = [
|
||||||
|
{j for j in range(n_cols) if (i, j, "<") in winds} for i in range(n_rows)
|
||||||
|
]
|
||||||
north_winds = [
|
north_winds = [
|
||||||
{i for i in range(n_rows) if (i, j, "^") in winds} for j in range(n_cols)
|
{i for i in range(n_rows) if (i, j, "^") in winds} for j in range(n_cols)
|
||||||
]
|
]
|
||||||
@ -24,13 +33,14 @@ south_winds = [
|
|||||||
{i for i in range(n_rows) if (i, j, "v") in winds} for j in range(n_cols)
|
{i for i in range(n_rows) if (i, j, "v") in winds} for j in range(n_cols)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def run(start: tuple[int, int], start_cycle: int, end: tuple[int, int]):
|
def run(start: tuple[int, int], start_cycle: int, end: tuple[int, int]):
|
||||||
def heuristic(y: int, x: int) -> int:
|
def heuristic(y: int, x: int) -> int:
|
||||||
return abs(end[0] - y) + abs(end[1] - x)
|
return abs(end[0] - y) + abs(end[1] - x)
|
||||||
|
|
||||||
# (distance + heuristic, distance, (start_pos, cycle))
|
# (distance + heuristic, distance, (start_pos, cycle))
|
||||||
queue = [(heuristic(start[0], start[1]), 0, ((start[0], start[1]), start_cycle))]
|
queue = [
|
||||||
|
(heuristic(start[0], start[1]), 0, ((start[0], start[1]), start_cycle))
|
||||||
|
]
|
||||||
visited: set[tuple[tuple[int, int], int]] = set()
|
visited: set[tuple[tuple[int, int], int]] = set()
|
||||||
distances: dict[tuple[int, int], dict[int, int]] = defaultdict(lambda: {})
|
distances: dict[tuple[int, int], dict[int, int]] = defaultdict(lambda: {})
|
||||||
|
|
||||||
@ -54,13 +64,17 @@ def run(start: tuple[int, int], start_cycle: int, end: tuple[int, int]):
|
|||||||
n_cycle = (cycle + 1) % CYCLE
|
n_cycle = (cycle + 1) % CYCLE
|
||||||
|
|
||||||
if (ty, tx) == end:
|
if (ty, tx) == end:
|
||||||
heapq.heappush(queue, (distance + 1, distance + 1, ((ty, tx), n_cycle)))
|
heapq.heappush(
|
||||||
|
queue, (distance + 1, distance + 1, ((ty, tx), n_cycle))
|
||||||
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
if ((ty, tx), n_cycle) in visited:
|
if ((ty, tx), n_cycle) in visited:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if (ty, tx) != start and (ty < 0 or tx < 0 or ty >= n_rows or tx >= n_cols):
|
if (ty, tx) != start and (
|
||||||
|
ty < 0 or tx < 0 or ty >= n_rows or tx >= n_cols
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if (ty, tx) != start:
|
if (ty, tx) != start:
|
||||||
@ -75,12 +89,17 @@ def run(start: tuple[int, int], start_cycle: int, end: tuple[int, int]):
|
|||||||
|
|
||||||
heapq.heappush(
|
heapq.heappush(
|
||||||
queue,
|
queue,
|
||||||
((heuristic(ty, tx) + distance + 1, distance + 1, ((ty, tx), n_cycle))),
|
(
|
||||||
|
(
|
||||||
|
heuristic(ty, tx) + distance + 1,
|
||||||
|
distance + 1,
|
||||||
|
((ty, tx), n_cycle),
|
||||||
|
)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
return distances, next(iter(distances[end].values()))
|
return distances, next(iter(distances[end].values()))
|
||||||
|
|
||||||
|
|
||||||
start = (
|
start = (
|
||||||
-1,
|
-1,
|
||||||
next(j for j in range(1, len(lines[0]) - 1) if lines[0][j] == ".") - 1,
|
next(j for j in range(1, len(lines[0]) - 1) if lines[0][j] == ".") - 1,
|
||||||
@ -91,8 +110,8 @@ end = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
distances_1, forward_1 = run(start, 0, end)
|
distances_1, forward_1 = run(start, 0, end)
|
||||||
print(f"answer 1 is {forward_1}")
|
yield forward_1
|
||||||
|
|
||||||
distances_2, return_1 = run(end, next(iter(distances_1[end].keys())), start)
|
distances_2, return_1 = run(end, next(iter(distances_1[end].keys())), start)
|
||||||
distances_3, forward_2 = run(start, next(iter(distances_2[start].keys())), end)
|
_distances_3, forward_2 = run(start, next(iter(distances_2[start].keys())), end)
|
||||||
print(f"answer 2 is {forward_1 + return_1 + forward_2}")
|
yield forward_1 + return_1 + forward_2
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = [line.strip() for line in input.splitlines()]
|
||||||
|
|
||||||
coeffs = {"2": 2, "1": 1, "0": 0, "-": -1, "=": -2}
|
coeffs = {"2": 2, "1": 1, "0": 0, "-": -1, "=": -2}
|
||||||
|
|
||||||
|
|
||||||
def snafu2number(number: str) -> int:
|
def snafu2number(number: str) -> int:
|
||||||
value = 0
|
value = 0
|
||||||
for c in number:
|
for c in number:
|
||||||
@ -12,7 +16,6 @@ def snafu2number(number: str) -> int:
|
|||||||
value += coeffs[c]
|
value += coeffs[c]
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def number2snafu(number: int) -> str:
|
def number2snafu(number: int) -> str:
|
||||||
values = ["0", "1", "2", "=", "-"]
|
values = ["0", "1", "2", "=", "-"]
|
||||||
res = ""
|
res = ""
|
||||||
@ -22,6 +25,4 @@ def number2snafu(number: int) -> str:
|
|||||||
number = number // 5 + int(mod >= 3)
|
number = number // 5 + int(mod >= 3)
|
||||||
return "".join(reversed(res))
|
return "".join(reversed(res))
|
||||||
|
|
||||||
|
yield number2snafu(sum(map(snafu2number, lines)))
|
||||||
answer_1 = number2snafu(sum(map(snafu2number, lines)))
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
@ -1,23 +1,28 @@
|
|||||||
import string
|
import string
|
||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = [line.strip() for line in sys.stdin.readlines()]
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = [line.strip() for line in input.splitlines()]
|
||||||
|
|
||||||
# extract content of each part
|
# extract content of each part
|
||||||
parts = [(set(line[: len(line) // 2]), set(line[len(line) // 2 :])) for line in lines]
|
parts = [
|
||||||
|
(set(line[: len(line) // 2]), set(line[len(line) // 2 :])) for line in lines
|
||||||
|
]
|
||||||
|
|
||||||
# priorities
|
# priorities
|
||||||
priorities = {c: i + 1 for i, c in enumerate(string.ascii_letters)}
|
priorities = {c: i + 1 for i, c in enumerate(string.ascii_letters)}
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
part1 = sum(priorities[c] for p1, p2 in parts for c in p1.intersection(p2))
|
yield sum(priorities[c] for p1, p2 in parts for c in p1.intersection(p2))
|
||||||
print(f"answer 1 is {part1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
n_per_group = 3
|
n_per_group = 3
|
||||||
part2 = sum(
|
yield sum(
|
||||||
priorities[c]
|
priorities[c]
|
||||||
for i in range(0, len(lines), n_per_group)
|
for i in range(0, len(lines), n_per_group)
|
||||||
for c in set(lines[i]).intersection(*lines[i + 1 : i + n_per_group])
|
for c in set(lines[i]).intersection(*lines[i + 1 : i + n_per_group])
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {part2}")
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = [line.strip() for line in sys.stdin.readlines()]
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def make_range(value: str) -> set[int]:
|
def make_range(value: str) -> set[int]:
|
||||||
@ -8,10 +8,13 @@ def make_range(value: str) -> set[int]:
|
|||||||
return set(range(int(parts[0]), int(parts[1]) + 1))
|
return set(range(int(parts[0]), int(parts[1]) + 1))
|
||||||
|
|
||||||
|
|
||||||
sections = [tuple(make_range(part) for part in line.split(",")) for line in lines]
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = [line.strip() for line in input.splitlines()]
|
||||||
|
|
||||||
answer_1 = sum(s1.issubset(s2) or s2.issubset(s1) for s1, s2 in sections)
|
sections = [
|
||||||
print(f"answer 1 is {answer_1}")
|
tuple(make_range(part) for part in line.split(",")) for line in lines
|
||||||
|
]
|
||||||
|
|
||||||
answer_2 = sum(bool(s1.intersection(s2)) for s1, s2 in sections)
|
yield sum(s1.issubset(s2) or s2.issubset(s1) for s1, s2 in sections)
|
||||||
print(f"answer 1 is {answer_2}")
|
yield sum(bool(s1.intersection(s2)) for s1, s2 in sections)
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import copy
|
import copy
|
||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
blocks_s, moves_s = (part.splitlines() for part in sys.stdin.read().split("\n\n"))
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
blocks_s, moves_s = (part.splitlines() for part in input.split("\n\n"))
|
||||||
|
|
||||||
blocks: dict[str, list[str]] = {stack: [] for stack in blocks_s[-1].split()}
|
blocks: dict[str, list[str]] = {stack: [] for stack in blocks_s[-1].split()}
|
||||||
|
|
||||||
@ -34,8 +39,5 @@ for move in moves_s:
|
|||||||
blocks_2[to_].extend(blocks_2[from_][-count:])
|
blocks_2[to_].extend(blocks_2[from_][-count:])
|
||||||
del blocks_2[from_][-count:]
|
del blocks_2[from_][-count:]
|
||||||
|
|
||||||
answer_1 = "".join(s[-1] for s in blocks_1.values())
|
yield "".join(s[-1] for s in blocks_1.values())
|
||||||
print(f"answer 1 is {answer_1}")
|
yield "".join(s[-1] for s in blocks_2.values())
|
||||||
|
|
||||||
answer_2 = "".join(s[-1] for s in blocks_2.values())
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def index_of_first_n_differents(data: str, n: int) -> int:
|
def index_of_first_n_differents(data: str, n: int) -> int:
|
||||||
@ -8,8 +10,7 @@ def index_of_first_n_differents(data: str, n: int) -> int:
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
|
||||||
data = sys.stdin.read().strip()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
yield index_of_first_n_differents(input, 4)
|
||||||
print(f"answer 1 is {index_of_first_n_differents(data, 4)}")
|
yield index_of_first_n_differents(input, 14)
|
||||||
print(f"answer 2 is {index_of_first_n_differents(data, 14)}")
|
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import sys
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = [line.strip() for line in input.splitlines()]
|
||||||
|
|
||||||
# we are going to use Path to create path and go up/down in the file tree since it
|
# we are going to use Path to create path and go up/down in the file tree since it
|
||||||
# implements everything we need
|
# implements everything we need
|
||||||
@ -53,7 +58,6 @@ for line in lines[1:]:
|
|||||||
trees[cur_path].append(path)
|
trees[cur_path].append(path)
|
||||||
sizes[path] = size
|
sizes[path] = size
|
||||||
|
|
||||||
|
|
||||||
def compute_size(path: Path) -> int:
|
def compute_size(path: Path) -> int:
|
||||||
size = sizes[path]
|
size = sizes[path]
|
||||||
|
|
||||||
@ -62,12 +66,10 @@ def compute_size(path: Path) -> int:
|
|||||||
|
|
||||||
return sum(compute_size(sub) for sub in trees[path])
|
return sum(compute_size(sub) for sub in trees[path])
|
||||||
|
|
||||||
|
|
||||||
acc_sizes = {path: compute_size(path) for path in trees}
|
acc_sizes = {path: compute_size(path) for path in trees}
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = sum(size for size in acc_sizes.values() if size <= 100_000)
|
yield sum(size for size in acc_sizes.values() if size <= 100_000)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
total_space = 70_000_000
|
total_space = 70_000_000
|
||||||
@ -76,5 +78,4 @@ free_space = total_space - acc_sizes[base_path]
|
|||||||
|
|
||||||
to_free_space = update_space - free_space
|
to_free_space = update_space - free_space
|
||||||
|
|
||||||
answer_2 = min(size for size in acc_sizes.values() if size >= to_free_space)
|
yield min(size for size in acc_sizes.values() if size >= to_free_space)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = [line.strip() for line in input.splitlines()]
|
||||||
|
|
||||||
trees = np.array([[int(x) for x in row] for row in lines])
|
trees = np.array([[int(x) for x in row] for row in lines])
|
||||||
|
|
||||||
@ -22,9 +27,7 @@ highest_trees[1:-1, 1:-1] = [
|
|||||||
for i in range(1, trees.shape[0] - 1)
|
for i in range(1, trees.shape[0] - 1)
|
||||||
]
|
]
|
||||||
|
|
||||||
answer_1 = (highest_trees.min(axis=2) < trees).sum()
|
yield (highest_trees.min(axis=2) < trees).sum()
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
|
|
||||||
def viewing_distance(row_of_trees: NDArray[np.int_], value: int) -> int:
|
def viewing_distance(row_of_trees: NDArray[np.int_], value: int) -> int:
|
||||||
w = np.where(row_of_trees >= value)[0]
|
w = np.where(row_of_trees >= value)[0]
|
||||||
@ -34,7 +37,6 @@ def viewing_distance(row_of_trees: NDArray[np.int_], value: int) -> int:
|
|||||||
|
|
||||||
return w[0] + 1
|
return w[0] + 1
|
||||||
|
|
||||||
|
|
||||||
# answer 2
|
# answer 2
|
||||||
v_distances = np.zeros(trees.shape + (4,), dtype=int)
|
v_distances = np.zeros(trees.shape + (4,), dtype=int)
|
||||||
v_distances[1:-1, 1:-1, :] = [
|
v_distances[1:-1, 1:-1, :] = [
|
||||||
@ -49,5 +51,4 @@ v_distances[1:-1, 1:-1, :] = [
|
|||||||
]
|
]
|
||||||
for i in range(1, trees.shape[0] - 1)
|
for i in range(1, trees.shape[0] - 1)
|
||||||
]
|
]
|
||||||
answer_2 = np.prod(v_distances, axis=2).max()
|
yield np.prod(v_distances, axis=2).max()
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import sys
|
import itertools as it
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def move(head: tuple[int, int], command: str) -> tuple[int, int]:
|
def move(head: tuple[int, int], command: str) -> tuple[int, int]:
|
||||||
h_col, h_row = head
|
h_col, h_row = head
|
||||||
@ -43,17 +46,14 @@ def run(commands: list[str], n_blocks: int) -> list[tuple[int, int]]:
|
|||||||
return visited
|
return visited
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = [line.strip() for line in input.splitlines()]
|
||||||
|
|
||||||
# flatten the commands
|
# flatten the commands
|
||||||
commands: list[str] = []
|
commands = list(
|
||||||
for line in lines:
|
it.chain(*(p[0] * int(p[1]) for line in lines if (p := line.split())))
|
||||||
d, c = line.split()
|
)
|
||||||
commands.extend(d * int(c))
|
|
||||||
|
|
||||||
|
yield len(set(run(commands, n_blocks=2)))
|
||||||
visited_1 = run(commands, n_blocks=2)
|
yield len(set(run(commands, n_blocks=10)))
|
||||||
print(f"answer 1 is {len(set(visited_1))}")
|
|
||||||
|
|
||||||
visited_2 = run(commands, n_blocks=10)
|
|
||||||
print(f"answer 2 is {len(set(visited_2))}")
|
|
||||||
|
@ -1,27 +1,9 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
lookups_1 = {str(d): d for d in range(1, 10)}
|
|
||||||
lookups_2 = lookups_1 | {
|
|
||||||
d: i + 1
|
|
||||||
for i, d in enumerate(
|
|
||||||
(
|
|
||||||
"one",
|
|
||||||
"two",
|
|
||||||
"three",
|
|
||||||
"four",
|
|
||||||
"five",
|
|
||||||
"six",
|
|
||||||
"seven",
|
|
||||||
"eight",
|
|
||||||
"nine",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def find_values(lookups: dict[str, int]) -> list[int]:
|
def find_values(lines: list[str], lookups: dict[str, int]) -> list[int]:
|
||||||
values: list[int] = []
|
values: list[int] = []
|
||||||
|
|
||||||
for line in filter(bool, lines):
|
for line in filter(bool, lines):
|
||||||
@ -41,5 +23,27 @@ def find_values(lookups: dict[str, int]) -> list[int]:
|
|||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
print(f"answer 1 is {sum(find_values(lookups_1))}")
|
class Solver(BaseSolver):
|
||||||
print(f"answer 2 is {sum(find_values(lookups_2))}")
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lookups_1 = {str(d): d for d in range(1, 10)}
|
||||||
|
lookups_2 = lookups_1 | {
|
||||||
|
d: i + 1
|
||||||
|
for i, d in enumerate(
|
||||||
|
(
|
||||||
|
"one",
|
||||||
|
"two",
|
||||||
|
"three",
|
||||||
|
"four",
|
||||||
|
"five",
|
||||||
|
"six",
|
||||||
|
"seven",
|
||||||
|
"eight",
|
||||||
|
"nine",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
|
yield sum(find_values(lines, lookups_1))
|
||||||
|
yield sum(find_values(lines, lookups_2))
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import os
|
from typing import Any, Iterator, Literal, cast
|
||||||
import sys
|
|
||||||
from typing import Literal, cast
|
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
|
|
||||||
Symbol = Literal["|", "-", "L", "J", "7", "F", ".", "S"]
|
Symbol = Literal["|", "-", "L", "J", "7", "F", ".", "S"]
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
lines: list[list[Symbol]] = [
|
lines: list[list[Symbol]] = [
|
||||||
[cast(Symbol, symbol) for symbol in line] for line in sys.stdin.read().splitlines()
|
[cast(Symbol, symbol) for symbol in line] for line in input.splitlines()
|
||||||
]
|
]
|
||||||
|
|
||||||
# find starting point
|
# find starting point
|
||||||
@ -51,8 +52,7 @@ while True:
|
|||||||
|
|
||||||
loop.append((i, j))
|
loop.append((i, j))
|
||||||
|
|
||||||
answer_1 = len(loop) // 2
|
yield len(loop) // 2
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
|
|
||||||
@ -83,18 +83,17 @@ for i in range(len(lines)):
|
|||||||
if (i, j) in loop_s and lines[i][j] in "|LJ":
|
if (i, j) in loop_s and lines[i][j] in "|LJ":
|
||||||
cnt += 1
|
cnt += 1
|
||||||
|
|
||||||
if VERBOSE:
|
if self.files:
|
||||||
for i in range(len(lines)):
|
rows = [["." for _j in range(len(lines[0]))] for _i in range(len(lines))]
|
||||||
for j in range(len(lines[0])):
|
rows[si][sj] = "\033[91mS\033[0m"
|
||||||
if (i, j) == (si, sj):
|
|
||||||
print("\033[91mS\033[0m", end="")
|
|
||||||
elif (i, j) in loop:
|
|
||||||
print(lines[i][j], end="")
|
|
||||||
elif (i, j) in inside:
|
|
||||||
print("\033[92mI\033[0m", end="")
|
|
||||||
else:
|
|
||||||
print(".", end="")
|
|
||||||
print()
|
|
||||||
|
|
||||||
answer_2 = len(inside)
|
for i, j in loop:
|
||||||
print(f"answer 2 is {answer_2}")
|
rows[i][j] = lines[i][j]
|
||||||
|
for i, j in inside:
|
||||||
|
rows[i][j] = "\033[92mI\033[0m"
|
||||||
|
|
||||||
|
self.files.create(
|
||||||
|
"output.txt", "\n".join("".join(row) for row in rows).encode(), True
|
||||||
|
)
|
||||||
|
|
||||||
|
yield len(inside)
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
data = np.array([[c == "#" for c in line] for line in lines])
|
data = np.array([[c == "#" for c in line] for line in lines])
|
||||||
|
|
||||||
@ -11,7 +16,6 @@ columns = {c for c in range(data.shape[1]) if not data[:, c].any()}
|
|||||||
|
|
||||||
galaxies_y, galaxies_x = np.where(data) # type: ignore
|
galaxies_y, galaxies_x = np.where(data) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def compute_total_distance(expansion: int) -> int:
|
def compute_total_distance(expansion: int) -> int:
|
||||||
distances: list[int] = []
|
distances: list[int] = []
|
||||||
for g1 in range(len(galaxies_y)):
|
for g1 in range(len(galaxies_y)):
|
||||||
@ -31,11 +35,8 @@ def compute_total_distance(expansion: int) -> int:
|
|||||||
distances.append(dx + dy)
|
distances.append(dx + dy)
|
||||||
return sum(distances)
|
return sum(distances)
|
||||||
|
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = compute_total_distance(2)
|
yield compute_total_distance(2)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
answer_2 = compute_total_distance(1000000)
|
yield compute_total_distance(1000000)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from typing import Iterable
|
from typing import Any, Iterable, Iterator
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
@lru_cache
|
@lru_cache
|
||||||
@ -77,31 +75,29 @@ def compute_possible_arrangements(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def compute_all_possible_arrangements(lines: Iterable[str], repeat: int) -> int:
|
class Solver(BaseSolver):
|
||||||
|
def compute_all_possible_arrangements(
|
||||||
|
self, lines: Iterable[str], repeat: int
|
||||||
|
) -> int:
|
||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
if VERBOSE:
|
for i_line, line in enumerate(lines):
|
||||||
from tqdm import tqdm
|
self.logger.info(f"processing line {i_line}: {line}...")
|
||||||
|
|
||||||
lines = tqdm(lines)
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
parts = line.split(" ")
|
parts = line.split(" ")
|
||||||
count += compute_possible_arrangements(
|
count += compute_possible_arrangements(
|
||||||
tuple(filter(len, "?".join(parts[0] for _ in range(repeat)).split("."))),
|
tuple(
|
||||||
|
filter(len, "?".join(parts[0] for _ in range(repeat)).split("."))
|
||||||
|
),
|
||||||
tuple(int(c) for c in parts[1].split(",")) * repeat,
|
tuple(int(c) for c in parts[1].split(",")) * repeat,
|
||||||
)
|
)
|
||||||
|
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = input.splitlines()
|
||||||
|
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = compute_all_possible_arrangements(lines, 1)
|
yield self.compute_all_possible_arrangements(lines, 1)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
answer_2 = compute_all_possible_arrangements(lines, 5)
|
yield self.compute_all_possible_arrangements(lines, 5)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import sys
|
from typing import Any, Callable, Iterator, Literal
|
||||||
from typing import Callable, Literal
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def split(block: list[str], axis: Literal[0, 1], count: int) -> int:
|
def split(block: list[str], axis: Literal[0, 1], count: int) -> int:
|
||||||
@ -25,19 +26,18 @@ def split(block: list[str], axis: Literal[0, 1], count: int) -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
blocks = [block.splitlines() for block in sys.stdin.read().split("\n\n")]
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
blocks = [block.splitlines() for block in input.split("\n\n")]
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = sum(
|
yield sum(
|
||||||
split(block, axis=1, count=0) + 100 * split(block, axis=0, count=0)
|
split(block, axis=1, count=0) + 100 * split(block, axis=0, count=0)
|
||||||
for block in blocks
|
for block in blocks
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
answer_2 = sum(
|
yield sum(
|
||||||
split(block, axis=1, count=1) + 100 * split(block, axis=0, count=1)
|
split(block, axis=1, count=1) + 100 * split(block, axis=0, count=1)
|
||||||
for block in blocks
|
for block in blocks
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import sys
|
from typing import Any, Iterator, TypeAlias
|
||||||
from typing import TypeAlias
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
RockGrid: TypeAlias = list[list[str]]
|
RockGrid: TypeAlias = list[list[str]]
|
||||||
|
|
||||||
rocks0 = [list(line) for line in sys.stdin.read().splitlines()]
|
|
||||||
|
|
||||||
|
|
||||||
def slide_rocks_top(rocks: RockGrid) -> RockGrid:
|
def slide_rocks_top(rocks: RockGrid) -> RockGrid:
|
||||||
top = [0 if c == "." else 1 for c in rocks[0]]
|
top = [0 if c == "." else 1 for c in rocks[0]]
|
||||||
@ -34,13 +33,17 @@ def cycle(rocks: RockGrid) -> RockGrid:
|
|||||||
return rocks
|
return rocks
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
rocks0 = [list(line) for line in input.splitlines()]
|
||||||
|
|
||||||
rocks = slide_rocks_top([[c for c in r] for r in rocks0])
|
rocks = slide_rocks_top([[c for c in r] for r in rocks0])
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = sum(
|
yield sum(
|
||||||
(len(rocks) - i) * sum(1 for c in row if c == "O") for i, row in enumerate(rocks)
|
(len(rocks) - i) * sum(1 for c in row if c == "O")
|
||||||
|
for i, row in enumerate(rocks)
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
rocks = rocks0
|
rocks = rocks0
|
||||||
@ -61,8 +64,7 @@ cycle_length = i_cycle - cycle_start
|
|||||||
|
|
||||||
ci = cycle_start + (N - cycle_start) % cycle_length - 1
|
ci = cycle_start + (N - cycle_start) % cycle_length - 1
|
||||||
|
|
||||||
answer_2 = sum(
|
yield sum(
|
||||||
(len(rocks) - i) * sum(1 for c in row if c == "O")
|
(len(rocks) - i) * sum(1 for c in row if c == "O")
|
||||||
for i, row in enumerate(cycles[ci])
|
for i, row in enumerate(cycles[ci])
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import sys
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
steps = sys.stdin.read().strip().split(",")
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def _hash(s: str) -> int:
|
def _hash(s: str) -> int:
|
||||||
return reduce(lambda v, u: ((v + ord(u)) * 17) % 256, s, 0)
|
return reduce(lambda v, u: ((v + ord(u)) * 17) % 256, s, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
steps = input.split(",")
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = sum(map(_hash, steps))
|
yield sum(map(_hash, steps))
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
boxes: list[dict[str, int]] = [{} for _ in range(256)]
|
boxes: list[dict[str, int]] = [{} for _ in range(256)]
|
||||||
@ -23,9 +26,8 @@ for step in steps:
|
|||||||
label = step[:-1]
|
label = step[:-1]
|
||||||
boxes[_hash(label)].pop(label, None)
|
boxes[_hash(label)].pop(label, None)
|
||||||
|
|
||||||
answer_2 = sum(
|
yield sum(
|
||||||
i_box * i_lens * length
|
i_box * i_lens * length
|
||||||
for i_box, box in enumerate(boxes, start=1)
|
for i_box, box in enumerate(boxes, start=1)
|
||||||
for i_lens, length in enumerate(box.values(), start=1)
|
for i_lens, length in enumerate(box.values(), start=1)
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import os
|
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||||
import sys
|
|
||||||
from typing import Literal, TypeAlias, cast
|
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
|
|
||||||
CellType: TypeAlias = Literal[".", "|", "-", "\\", "/"]
|
CellType: TypeAlias = Literal[".", "|", "-", "\\", "/"]
|
||||||
Direction: TypeAlias = Literal["R", "L", "U", "D"]
|
Direction: TypeAlias = Literal["R", "L", "U", "D"]
|
||||||
@ -78,19 +76,25 @@ def propagate(
|
|||||||
return beams
|
return beams
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
layout: list[list[CellType]] = [
|
layout: list[list[CellType]] = [
|
||||||
[cast(CellType, col) for col in row] for row in sys.stdin.read().splitlines()
|
[cast(CellType, col) for col in row] for row in input.splitlines()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
beams = propagate(layout, (0, 0), "R")
|
beams = propagate(layout, (0, 0), "R")
|
||||||
|
|
||||||
if VERBOSE:
|
if self.files:
|
||||||
print("\n".join(["".join("#" if col else "." for col in row) for row in beams]))
|
self.files.create(
|
||||||
|
"beams.txt",
|
||||||
|
"\n".join(
|
||||||
|
"".join("#" if col else "." for col in row) for row in beams
|
||||||
|
).encode(),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = sum(sum(map(bool, row)) for row in beams)
|
yield sum(sum(map(bool, row)) for row in beams)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
n_rows, n_cols = len(layout), len(layout[0])
|
n_rows, n_cols = len(layout), len(layout[0])
|
||||||
@ -103,8 +107,7 @@ for col in range(n_cols):
|
|||||||
cases.append(((0, col), "D"))
|
cases.append(((0, col), "D"))
|
||||||
cases.append(((n_rows - 1, col), "U"))
|
cases.append(((n_rows - 1, col), "U"))
|
||||||
|
|
||||||
answer_2 = max(
|
yield max(
|
||||||
sum(sum(map(bool, row)) for row in propagate(layout, start, direction))
|
sum(sum(map(bool, row)) for row in propagate(layout, start, direction))
|
||||||
for start, direction in cases
|
for start, direction in cases
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import heapq
|
import heapq
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Literal, TypeAlias
|
from typing import Any, Iterator, Literal, TypeAlias
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
|
|
||||||
Direction: TypeAlias = Literal[">", "<", "^", "v"]
|
Direction: TypeAlias = Literal[">", "<", "^", "v"]
|
||||||
|
|
||||||
@ -32,11 +30,17 @@ MAPPINGS: dict[Direction, tuple[int, int, Direction]] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
def print_shortest_path(
|
def print_shortest_path(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
grid: list[list[int]],
|
grid: list[list[int]],
|
||||||
target: tuple[int, int],
|
target: tuple[int, int],
|
||||||
per_cell: dict[tuple[int, int], list[tuple[Label, int]]],
|
per_cell: dict[tuple[int, int], list[tuple[Label, int]]],
|
||||||
):
|
):
|
||||||
|
if not self.files:
|
||||||
|
return
|
||||||
|
|
||||||
assert len(per_cell[target]) == 1
|
assert len(per_cell[target]) == 1
|
||||||
label = per_cell[target][0][0]
|
label = per_cell[target][0][0]
|
||||||
|
|
||||||
@ -66,16 +70,19 @@ def print_shortest_path(
|
|||||||
if (r, c) != (prev_label.row, prev_label.col):
|
if (r, c) != (prev_label.row, prev_label.col):
|
||||||
p_grid[r][c] = f"\033[93m{grid[r][c]}\033[0m"
|
p_grid[r][c] = f"\033[93m{grid[r][c]}\033[0m"
|
||||||
|
|
||||||
p_grid[label.row][label.col] = f"\033[91m{grid[label.row][label.col]}\033[0m"
|
p_grid[label.row][label.col] = (
|
||||||
|
f"\033[91m{grid[label.row][label.col]}\033[0m"
|
||||||
|
)
|
||||||
|
|
||||||
prev_label = label
|
prev_label = label
|
||||||
|
|
||||||
p_grid[0][0] = f"\033[92m{grid[0][0]}\033[0m"
|
p_grid[0][0] = f"\033[92m{grid[0][0]}\033[0m"
|
||||||
|
|
||||||
print("\n".join("".join(row) for row in p_grid))
|
self.files.create(
|
||||||
|
name, "\n".join("".join(row) for row in p_grid).encode(), True
|
||||||
|
)
|
||||||
|
|
||||||
|
def shortest_many_paths(self, grid: list[list[int]]) -> dict[tuple[int, int], int]:
|
||||||
def shortest_many_paths(grid: list[list[int]]) -> dict[tuple[int, int], int]:
|
|
||||||
n_rows, n_cols = len(grid), len(grid[0])
|
n_rows, n_cols = len(grid), len(grid[0])
|
||||||
|
|
||||||
visited: dict[tuple[int, int], tuple[Label, int]] = {}
|
visited: dict[tuple[int, int], tuple[Label, int]] = {}
|
||||||
@ -125,8 +132,9 @@ def shortest_many_paths(grid: list[list[int]]) -> dict[tuple[int, int], int]:
|
|||||||
|
|
||||||
return {(r, c): visited[r, c][1] for r in range(n_rows) for c in range(n_cols)}
|
return {(r, c): visited[r, c][1] for r in range(n_rows) for c in range(n_cols)}
|
||||||
|
|
||||||
|
|
||||||
def shortest_path(
|
def shortest_path(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
grid: list[list[int]],
|
grid: list[list[int]],
|
||||||
min_straight: int,
|
min_straight: int,
|
||||||
max_straight: int,
|
max_straight: int,
|
||||||
@ -215,19 +223,16 @@ def shortest_path(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if VERBOSE:
|
self.print_shortest_path(f"shortest-path_{name}.txt", grid, target, per_cell)
|
||||||
print_shortest_path(grid, target, per_cell)
|
|
||||||
|
|
||||||
return per_cell[target][0][1]
|
return per_cell[target][0][1]
|
||||||
|
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
data = [[int(c) for c in r] for r in sys.stdin.read().splitlines()]
|
data = [[int(c) for c in r] for r in input.splitlines()]
|
||||||
estimates = shortest_many_paths(data)
|
estimates = self.shortest_many_paths(data)
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = shortest_path(data, 1, 3, lower_bounds=estimates)
|
yield self.shortest_path("answer_1", data, 1, 3, lower_bounds=estimates)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
answer_2 = shortest_path(data, 4, 10, lower_bounds=estimates)
|
yield self.shortest_path("answer_2", data, 4, 10, lower_bounds=estimates)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import sys
|
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||||
from typing import Literal, TypeAlias, cast
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
Direction: TypeAlias = Literal["R", "L", "U", "D"]
|
Direction: TypeAlias = Literal["R", "L", "U", "D"]
|
||||||
|
|
||||||
@ -33,17 +34,19 @@ def polygon(values: list[tuple[Direction, int]]) -> tuple[list[tuple[int, int]],
|
|||||||
return corners, perimeter
|
return corners, perimeter
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = area(
|
yield area(
|
||||||
*polygon([(cast(Direction, (p := line.split())[0]), int(p[1])) for line in lines])
|
*polygon(
|
||||||
|
[(cast(Direction, (p := line.split())[0]), int(p[1])) for line in lines]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
answer_2 = area(
|
yield area(
|
||||||
*polygon(
|
*polygon(
|
||||||
[
|
[
|
||||||
(DIRECTIONS[int((h := line.split()[-1])[-2])], int(h[2:-2], 16))
|
(DIRECTIONS[int((h := line.split()[-1])[-2])], int(h[2:-2], 16))
|
||||||
@ -51,4 +54,3 @@ answer_2 = area(
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
import logging
|
|
||||||
import operator
|
import operator
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from math import prod
|
from math import prod
|
||||||
from typing import Literal, TypeAlias, cast
|
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
|
||||||
|
|
||||||
Category: TypeAlias = Literal["x", "m", "a", "s"]
|
Category: TypeAlias = Literal["x", "m", "a", "s"]
|
||||||
Part: TypeAlias = dict[Category, int]
|
Part: TypeAlias = dict[Category, int]
|
||||||
@ -22,7 +17,8 @@ Check: TypeAlias = tuple[Category, Literal["<", ">"], int] | None
|
|||||||
Workflow: TypeAlias = list[tuple[Check, str]]
|
Workflow: TypeAlias = list[tuple[Check, str]]
|
||||||
|
|
||||||
|
|
||||||
def accept(workflows: dict[str, Workflow], part: Part) -> bool:
|
class Solver(BaseSolver):
|
||||||
|
def accept(self, workflows: dict[str, Workflow], part: Part) -> bool:
|
||||||
workflow = "in"
|
workflow = "in"
|
||||||
decision: bool | None = None
|
decision: bool | None = None
|
||||||
|
|
||||||
@ -42,8 +38,7 @@ def accept(workflows: dict[str, Workflow], part: Part) -> bool:
|
|||||||
|
|
||||||
return decision
|
return decision
|
||||||
|
|
||||||
|
def propagate(self, workflows: dict[str, Workflow], start: PartWithBounds) -> int:
|
||||||
def propagate(workflows: dict[str, Workflow], start: PartWithBounds) -> int:
|
|
||||||
def _fmt(meta: PartWithBounds) -> str:
|
def _fmt(meta: PartWithBounds) -> str:
|
||||||
return "{" + ", ".join(f"{k}={v}" for k, v in meta.items()) + "}"
|
return "{" + ", ".join(f"{k}={v}" for k, v in meta.items()) + "}"
|
||||||
|
|
||||||
@ -52,13 +47,13 @@ def propagate(workflows: dict[str, Workflow], start: PartWithBounds) -> int:
|
|||||||
) -> int:
|
) -> int:
|
||||||
count = 0
|
count = 0
|
||||||
if target in workflows:
|
if target in workflows:
|
||||||
logging.info(f" transfer to {target}")
|
self.logger.info(f" transfer to {target}")
|
||||||
queue.append((meta, target))
|
queue.append((meta, target))
|
||||||
elif target == "A":
|
elif target == "A":
|
||||||
count = prod((high - low + 1) for low, high in meta.values())
|
count = prod((high - low + 1) for low, high in meta.values())
|
||||||
logging.info(f" accepted ({count})")
|
self.logger.info(f" accepted ({count})")
|
||||||
else:
|
else:
|
||||||
logging.info(" rejected")
|
self.logger.info(" rejected")
|
||||||
return count
|
return count
|
||||||
|
|
||||||
accepted = 0
|
accepted = 0
|
||||||
@ -69,24 +64,26 @@ def propagate(workflows: dict[str, Workflow], start: PartWithBounds) -> int:
|
|||||||
while queue:
|
while queue:
|
||||||
n_iterations += 1
|
n_iterations += 1
|
||||||
meta, workflow = queue.pop()
|
meta, workflow = queue.pop()
|
||||||
logging.info(f"{workflow}: {_fmt(meta)}")
|
self.logger.info(f"{workflow}: {_fmt(meta)}")
|
||||||
for check, target in workflows[workflow]:
|
for check, target in workflows[workflow]:
|
||||||
if check is None:
|
if check is None:
|
||||||
logging.info(" end-of-workflow")
|
self.logger.info(" end-of-workflow")
|
||||||
accepted += transfer_or_accept(target, meta, queue)
|
accepted += transfer_or_accept(target, meta, queue)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
category, sense, value = check
|
category, sense, value = check
|
||||||
bounds, op = meta[category], OPERATORS[sense]
|
bounds, op = meta[category], OPERATORS[sense]
|
||||||
|
|
||||||
logging.info(f" checking {_fmt(meta)} against {category} {sense} {value}")
|
self.logger.info(
|
||||||
|
f" checking {_fmt(meta)} against {category} {sense} {value}"
|
||||||
|
)
|
||||||
|
|
||||||
if not op(bounds[0], value) and not op(bounds[1], value):
|
if not op(bounds[0], value) and not op(bounds[1], value):
|
||||||
logging.info(" reject, always false")
|
self.logger.info(" reject, always false")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if op(meta[category][0], value) and op(meta[category][1], value):
|
if op(meta[category][0], value) and op(meta[category][1], value):
|
||||||
logging.info(" accept, always true")
|
self.logger.info(" accept, always true")
|
||||||
accepted += transfer_or_accept(target, meta, queue)
|
accepted += transfer_or_accept(target, meta, queue)
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -96,15 +93,15 @@ def propagate(workflows: dict[str, Workflow], start: PartWithBounds) -> int:
|
|||||||
meta[category], meta2[category] = (value, high), (low, value - 1)
|
meta[category], meta2[category] = (value, high), (low, value - 1)
|
||||||
else:
|
else:
|
||||||
meta[category], meta2[category] = (low, value), (value + 1, high)
|
meta[category], meta2[category] = (low, value), (value + 1, high)
|
||||||
logging.info(f" split {_fmt(meta2)} ({target}), {_fmt(meta)}")
|
self.logger.info(f" split {_fmt(meta2)} ({target}), {_fmt(meta)}")
|
||||||
|
|
||||||
accepted += transfer_or_accept(target, meta2, queue)
|
accepted += transfer_or_accept(target, meta2, queue)
|
||||||
|
|
||||||
logging.info(f"run took {n_iterations} iterations")
|
self.logger.info(f"run took {n_iterations} iterations")
|
||||||
return accepted
|
return accepted
|
||||||
|
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
workflows_s, parts_s = sys.stdin.read().strip().split("\n\n")
|
workflows_s, parts_s = input.split("\n\n")
|
||||||
|
|
||||||
workflows: dict[str, Workflow] = {}
|
workflows: dict[str, Workflow] = {}
|
||||||
for workflow_s in workflows_s.split("\n"):
|
for workflow_s in workflows_s.split("\n"):
|
||||||
@ -129,12 +126,9 @@ parts: list[Part] = [
|
|||||||
{cast(Category, s[0]): int(s[2:]) for s in part_s[1:-1].split(",")}
|
{cast(Category, s[0]): int(s[2:]) for s in part_s[1:-1].split(",")}
|
||||||
for part_s in parts_s.split("\n")
|
for part_s in parts_s.split("\n")
|
||||||
]
|
]
|
||||||
answer_1 = sum(sum(part.values()) for part in parts if accept(workflows, part))
|
yield sum(sum(part.values()) for part in parts if self.accept(workflows, part))
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
answer_2 = propagate(
|
yield self.propagate(
|
||||||
workflows, {cast(Category, c): (1, 4000) for c in ["x", "m", "a", "s"]}
|
workflows, {cast(Category, c): (1, 4000) for c in ["x", "m", "a", "s"]}
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import math
|
import math
|
||||||
import sys
|
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||||
from typing import Literal, TypeAlias, cast
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
CubeType: TypeAlias = Literal["red", "blue", "green"]
|
CubeType: TypeAlias = Literal["red", "blue", "green"]
|
||||||
|
|
||||||
MAX_CUBES: dict[CubeType, int] = {"red": 12, "green": 13, "blue": 14}
|
MAX_CUBES: dict[CubeType, int] = {"red": 12, "green": 13, "blue": 14}
|
||||||
|
|
||||||
# parse games
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
games: dict[int, list[dict[CubeType, int]]] = {}
|
games: dict[int, list[dict[CubeType, int]]] = {}
|
||||||
for line in filter(bool, lines):
|
for line in filter(bool, lines):
|
||||||
id_part, sets_part = line.split(":")
|
id_part, sets_part = line.split(":")
|
||||||
@ -21,8 +24,7 @@ for line in filter(bool, lines):
|
|||||||
for cube_set_s in sets_part.strip().split(";")
|
for cube_set_s in sets_part.strip().split(";")
|
||||||
]
|
]
|
||||||
|
|
||||||
# part 1
|
yield sum(
|
||||||
answer_1 = sum(
|
|
||||||
id
|
id
|
||||||
for id, set_of_cubes in games.items()
|
for id, set_of_cubes in games.items()
|
||||||
if all(
|
if all(
|
||||||
@ -31,13 +33,11 @@ answer_1 = sum(
|
|||||||
for cube, n_cubes in cube_set.items()
|
for cube, n_cubes in cube_set.items()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
yield sum(
|
||||||
answer_2 = sum(
|
|
||||||
math.prod(
|
math.prod(
|
||||||
max(cube_set.get(cube, 0) for cube_set in set_of_cubes) for cube in MAX_CUBES
|
max(cube_set.get(cube, 0) for cube_set in set_of_cubes)
|
||||||
|
for cube in MAX_CUBES
|
||||||
)
|
)
|
||||||
for set_of_cubes in games.values()
|
for set_of_cubes in games.values()
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,55 +1,41 @@
|
|||||||
import logging
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from math import lcm
|
from math import lcm
|
||||||
from typing import Literal, TypeAlias
|
from typing import Any, Iterator, Literal, TypeAlias
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
|
||||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
ModuleType: TypeAlias = Literal["broadcaster", "conjunction", "flip-flop"]
|
ModuleType: TypeAlias = Literal["broadcaster", "conjunction", "flip-flop"]
|
||||||
PulseType: TypeAlias = Literal["high", "low"]
|
PulseType: TypeAlias = Literal["high", "low"]
|
||||||
|
|
||||||
modules: dict[str, tuple[ModuleType, list[str]]] = {}
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
_modules: dict[str, tuple[ModuleType, list[str]]]
|
||||||
|
|
||||||
for line in lines:
|
def _process(
|
||||||
name, outputs_s = line.split(" -> ")
|
self,
|
||||||
outputs = outputs_s.split(", ")
|
|
||||||
if name == "broadcaster":
|
|
||||||
modules["broadcaster"] = ("broadcaster", outputs)
|
|
||||||
else:
|
|
||||||
modules[name[1:]] = (
|
|
||||||
"conjunction" if name.startswith("&") else "flip-flop",
|
|
||||||
outputs,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def process(
|
|
||||||
start: tuple[str, str, PulseType],
|
start: tuple[str, str, PulseType],
|
||||||
flip_flop_states: dict[str, Literal["on", "off"]],
|
flip_flop_states: dict[str, Literal["on", "off"]],
|
||||||
conjunction_states: dict[str, dict[str, PulseType]],
|
conjunction_states: dict[str, dict[str, PulseType]],
|
||||||
) -> tuple[dict[PulseType, int], dict[str, dict[PulseType, int]]]:
|
) -> tuple[dict[PulseType, int], dict[str, dict[PulseType, int]]]:
|
||||||
pulses: list[tuple[str, str, PulseType]] = [start]
|
pulses: list[tuple[str, str, PulseType]] = [start]
|
||||||
counts: dict[PulseType, int] = {"low": 0, "high": 0}
|
counts: dict[PulseType, int] = {"low": 0, "high": 0}
|
||||||
inputs: dict[str, dict[PulseType, int]] = defaultdict(lambda: {"low": 0, "high": 0})
|
inputs: dict[str, dict[PulseType, int]] = defaultdict(
|
||||||
|
lambda: {"low": 0, "high": 0}
|
||||||
|
)
|
||||||
|
|
||||||
logging.info("starting process... ")
|
self.logger.info("starting process... ")
|
||||||
|
|
||||||
while pulses:
|
while pulses:
|
||||||
input, name, pulse = pulses.pop(0)
|
input, name, pulse = pulses.pop(0)
|
||||||
logging.info(f"{input} -{pulse}-> {name}")
|
self.logger.info(f"{input} -{pulse}-> {name}")
|
||||||
counts[pulse] += 1
|
counts[pulse] += 1
|
||||||
|
|
||||||
inputs[name][pulse] += 1
|
inputs[name][pulse] += 1
|
||||||
|
|
||||||
if name not in modules:
|
if name not in self._modules:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
type, outputs = modules[name]
|
type, outputs = self._modules[name]
|
||||||
|
|
||||||
if type == "broadcaster":
|
if type == "broadcaster":
|
||||||
...
|
...
|
||||||
@ -77,41 +63,63 @@ def process(
|
|||||||
|
|
||||||
return counts, inputs
|
return counts, inputs
|
||||||
|
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
self._modules = {}
|
||||||
|
|
||||||
with open("./day20.dot", "w") as fp:
|
lines = input.splitlines()
|
||||||
fp.write("digraph G {\n")
|
|
||||||
fp.write("rx [shape=circle, color=red, style=filled];\n")
|
for line in lines:
|
||||||
for name, (type, outputs) in modules.items():
|
name, outputs_s = line.split(" -> ")
|
||||||
|
outputs = outputs_s.split(", ")
|
||||||
|
if name == "broadcaster":
|
||||||
|
self._modules["broadcaster"] = ("broadcaster", outputs)
|
||||||
|
else:
|
||||||
|
self._modules[name[1:]] = (
|
||||||
|
"conjunction" if name.startswith("&") else "flip-flop",
|
||||||
|
outputs,
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.files:
|
||||||
|
contents = "digraph G {\n"
|
||||||
|
contents += "rx [shape=circle, color=red, style=filled];\n"
|
||||||
|
for name, (type, outputs) in self._modules.items():
|
||||||
if type == "conjunction":
|
if type == "conjunction":
|
||||||
shape = "diamond"
|
shape = "diamond"
|
||||||
elif type == "flip-flop":
|
elif type == "flip-flop":
|
||||||
shape = "box"
|
shape = "box"
|
||||||
else:
|
else:
|
||||||
shape = "circle"
|
shape = "circle"
|
||||||
fp.write(f"{name} [shape={shape}];\n")
|
contents += f"{name} [shape={shape}];\n"
|
||||||
for name, (type, outputs) in modules.items():
|
for name, (type, outputs) in self._modules.items():
|
||||||
for output in outputs:
|
for output in outputs:
|
||||||
fp.write(f"{name} -> {output};\n")
|
contents += f"{name} -> {output};\n"
|
||||||
fp.write("}\n")
|
contents += "}\n"
|
||||||
|
|
||||||
|
self.files.create("day20.dot", contents.encode(), False)
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
flip_flop_states: dict[str, Literal["on", "off"]] = {
|
flip_flop_states: dict[str, Literal["on", "off"]] = {
|
||||||
name: "off" for name, (type, _) in modules.items() if type == "flip-flop"
|
name: "off"
|
||||||
|
for name, (type, _) in self._modules.items()
|
||||||
|
if type == "flip-flop"
|
||||||
}
|
}
|
||||||
conjunction_states: dict[str, dict[str, PulseType]] = {
|
conjunction_states: dict[str, dict[str, PulseType]] = {
|
||||||
name: {input: "low" for input, (_, outputs) in modules.items() if name in outputs}
|
name: {
|
||||||
for name, (type, _) in modules.items()
|
input: "low"
|
||||||
|
for input, (_, outputs) in self._modules.items()
|
||||||
|
if name in outputs
|
||||||
|
}
|
||||||
|
for name, (type, _) in self._modules.items()
|
||||||
if type == "conjunction"
|
if type == "conjunction"
|
||||||
}
|
}
|
||||||
counts: dict[PulseType, int] = {"low": 0, "high": 0}
|
counts: dict[PulseType, int] = {"low": 0, "high": 0}
|
||||||
for _ in range(1000):
|
for _ in range(1000):
|
||||||
result, _ = process(
|
result, _ = self._process(
|
||||||
("button", "broadcaster", "low"), flip_flop_states, conjunction_states
|
("button", "broadcaster", "low"), flip_flop_states, conjunction_states
|
||||||
)
|
)
|
||||||
for pulse in ("low", "high"):
|
for pulse in ("low", "high"):
|
||||||
counts[pulse] += result[pulse]
|
counts[pulse] += result[pulse]
|
||||||
answer_1 = counts["low"] * counts["high"]
|
yield counts["low"] * counts["high"]
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
|
|
||||||
@ -124,23 +132,27 @@ for name in conjunction_states:
|
|||||||
conjunction_states[name][input] = "low"
|
conjunction_states[name][input] = "low"
|
||||||
|
|
||||||
# find the conjunction connected to rx
|
# find the conjunction connected to rx
|
||||||
to_rx = [name for name, (_, outputs) in modules.items() if "rx" in outputs]
|
to_rx = [
|
||||||
|
name for name, (_, outputs) in self._modules.items() if "rx" in outputs
|
||||||
|
]
|
||||||
assert len(to_rx) == 1, "cannot handle multiple module inputs for rx"
|
assert len(to_rx) == 1, "cannot handle multiple module inputs for rx"
|
||||||
assert (
|
assert (
|
||||||
modules[to_rx[0]][0] == "conjunction"
|
self._modules[to_rx[0]][0] == "conjunction"
|
||||||
), "can only handle conjunction as input to rx"
|
), "can only handle conjunction as input to rx"
|
||||||
|
|
||||||
to_rx_inputs = [name for name, (_, outputs) in modules.items() if to_rx[0] in outputs]
|
to_rx_inputs = [
|
||||||
|
name for name, (_, outputs) in self._modules.items() if to_rx[0] in outputs
|
||||||
|
]
|
||||||
assert all(
|
assert all(
|
||||||
modules[i][0] == "conjunction" and len(modules[i][1]) == 1 for i in to_rx_inputs
|
self._modules[i][0] == "conjunction" and len(self._modules[i][1]) == 1
|
||||||
|
for i in to_rx_inputs
|
||||||
), "can only handle inversion as second-order inputs to rx"
|
), "can only handle inversion as second-order inputs to rx"
|
||||||
|
|
||||||
|
|
||||||
count = 1
|
count = 1
|
||||||
cycles: dict[str, int] = {}
|
cycles: dict[str, int] = {}
|
||||||
second: dict[str, int] = {}
|
second: dict[str, int] = {}
|
||||||
while len(second) != len(to_rx_inputs):
|
while len(second) != len(to_rx_inputs):
|
||||||
_, inputs = process(
|
_, inputs = self._process(
|
||||||
("button", "broadcaster", "low"), flip_flop_states, conjunction_states
|
("button", "broadcaster", "low"), flip_flop_states, conjunction_states
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -157,5 +169,4 @@ assert all(
|
|||||||
second[k] == cycles[k] * 2 for k in to_rx_inputs
|
second[k] == cycles[k] * 2 for k in to_rx_inputs
|
||||||
), "cannot only handle cycles starting at the beginning"
|
), "cannot only handle cycles starting at the beginning"
|
||||||
|
|
||||||
answer_2 = lcm(*cycles.values())
|
yield lcm(*cycles.values())
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import logging
|
from typing import Any, Iterator
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
|
||||||
|
|
||||||
|
|
||||||
def reachable(
|
def reachable(
|
||||||
@ -21,25 +18,29 @@ def reachable(
|
|||||||
return tiles
|
return tiles
|
||||||
|
|
||||||
|
|
||||||
map = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
map = input.splitlines()
|
||||||
start = next(
|
start = next(
|
||||||
(i, j) for i in range(len(map)) for j in range(len(map[i])) if map[i][j] == "S"
|
(i, j)
|
||||||
|
for i in range(len(map))
|
||||||
|
for j in range(len(map[i]))
|
||||||
|
if map[i][j] == "S"
|
||||||
)
|
)
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = len(reachable(map, {start}, 6 if len(map) < 20 else 64))
|
yield len(reachable(map, {start}, 6 if len(map) < 20 else 64))
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
|
|
||||||
# the initial map is a square and contains an empty rhombus whose diameter is the size
|
# the initial map is a square and contains an empty rhombus whose diameter is
|
||||||
# of the map, and has only empty cells around the middle row and column
|
# the size of the map, and has only empty cells around the middle row and column
|
||||||
#
|
#
|
||||||
# after ~n/2 steps, the first map is filled with a rhombus, after that we get a bigger
|
# after ~n/2 steps, the first map is filled with a rhombus, after that we get a
|
||||||
# rhombus every n steps
|
# bigger rhombus every n steps
|
||||||
#
|
#
|
||||||
# we are going to find the number of cells reached for the initial rhombus, n steps
|
# we are going to find the number of cells reached for the initial rhombus, n
|
||||||
# after and n * 2 steps after
|
# steps after and n * 2 steps after
|
||||||
#
|
#
|
||||||
cycle = len(map)
|
cycle = len(map)
|
||||||
rhombus = (len(map) - 3) // 2 + 1
|
rhombus = (len(map) - 3) // 2 + 1
|
||||||
@ -49,7 +50,7 @@ values.append(len(tiles := reachable(map, {start}, rhombus)))
|
|||||||
values.append(len(tiles := reachable(map, tiles, cycle)))
|
values.append(len(tiles := reachable(map, tiles, cycle)))
|
||||||
values.append(len(tiles := reachable(map, tiles, cycle)))
|
values.append(len(tiles := reachable(map, tiles, cycle)))
|
||||||
|
|
||||||
if logging.root.getEffectiveLevel() == logging.INFO:
|
if self.files:
|
||||||
n_rows, n_cols = len(map), len(map[0])
|
n_rows, n_cols = len(map), len(map[0])
|
||||||
|
|
||||||
rows = [
|
rows = [
|
||||||
@ -65,10 +66,11 @@ if logging.root.getEffectiveLevel() == logging.INFO:
|
|||||||
if (i // cycle) % 2 == (j // cycle) % 2:
|
if (i // cycle) % 2 == (j // cycle) % 2:
|
||||||
rows[i][j] = f"\033[91m{rows[i][j]}\033[0m"
|
rows[i][j] = f"\033[91m{rows[i][j]}\033[0m"
|
||||||
|
|
||||||
print("\n".join("".join(row) for row in rows))
|
self.files.create(
|
||||||
|
"cycle.txt", "\n".join("".join(row) for row in rows).encode(), True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.info(f"values to fit: {values}")
|
||||||
logging.info(f"values to fit: {values}")
|
|
||||||
|
|
||||||
# version 1:
|
# version 1:
|
||||||
#
|
#
|
||||||
@ -100,31 +102,31 @@ logging.info(f"values to fit: {values}")
|
|||||||
# depending on the number of cycles, either A or B will be in the center
|
# depending on the number of cycles, either A or B will be in the center
|
||||||
#
|
#
|
||||||
|
|
||||||
counts = [
|
# counts = [
|
||||||
[
|
# [
|
||||||
sum(
|
# sum(
|
||||||
(i, j) in tiles
|
# (i, j) in tiles
|
||||||
for i in range(ci * cycle, (ci + 1) * cycle)
|
# for i in range(ci * cycle, (ci + 1) * cycle)
|
||||||
for j in range(cj * cycle, (cj + 1) * cycle)
|
# for j in range(cj * cycle, (cj + 1) * cycle)
|
||||||
)
|
# )
|
||||||
for cj in range(-2, 3)
|
# for cj in range(-2, 3)
|
||||||
]
|
# ]
|
||||||
for ci in range(-2, 3)
|
# for ci in range(-2, 3)
|
||||||
]
|
# ]
|
||||||
|
|
||||||
radius = (26501365 - rhombus) // cycle - 1
|
# radius = (26501365 - rhombus) // cycle - 1
|
||||||
A = counts[2][2] if radius % 2 == 0 else counts[2][1]
|
# A = counts[2][2] if radius % 2 == 0 else counts[2][1]
|
||||||
B = counts[2][2] if radius % 2 == 1 else counts[2][1]
|
# B = counts[2][2] if radius % 2 == 1 else counts[2][1]
|
||||||
answer_2 = (
|
# answer_2 = (
|
||||||
(radius + 1) * A
|
# (radius + 1) * A
|
||||||
+ radius * B
|
# + radius * B
|
||||||
+ 2 * radius * (radius + 1) // 2 * A
|
# + 2 * radius * (radius + 1) // 2 * A
|
||||||
+ 2 * radius * (radius - 1) // 2 * B
|
# + 2 * radius * (radius - 1) // 2 * B
|
||||||
+ sum(counts[i][j] for i, j in ((0, 2), (-1, 2), (2, 0), (2, -1)))
|
# + sum(counts[i][j] for i, j in ((0, 2), (-1, 2), (2, 0), (2, -1)))
|
||||||
+ sum(counts[i][j] for i, j in ((0, 1), (0, 3), (-1, 1), (-1, 3))) * (radius + 1)
|
# + sum(counts[i][j] for i, j in ((0, 1), (0, 3), (-1, 1), (-1, 3)))
|
||||||
+ sum(counts[i][j] for i, j in ((1, 1), (1, 3), (-2, 1), (-2, 3))) * radius
|
# * (radius + 1)
|
||||||
)
|
# + sum(counts[i][j] for i, j in ((1, 1), (1, 3), (-2, 1), (-2, 3))) * radius
|
||||||
print(f"answer 2 (v1) is {answer_2}")
|
# )
|
||||||
|
|
||||||
# version 2: fitting a polynomial
|
# version 2: fitting a polynomial
|
||||||
#
|
#
|
||||||
@ -145,5 +147,4 @@ y1, y2, y3 = values
|
|||||||
a, b, c = (y1 + y3) // 2 - y2, 2 * y2 - (3 * y1 + y3) // 2, y1
|
a, b, c = (y1 + y3) // 2 - y2, 2 * y2 - (3 * y1 + y3) // 2, y1
|
||||||
|
|
||||||
n = (26501365 - rhombus) // cycle
|
n = (26501365 - rhombus) // cycle
|
||||||
answer_2 = a * n * n + b * n + c
|
yield a * n * n + b * n + c
|
||||||
print(f"answer 2 (v2) is {answer_2}")
|
|
||||||
|
@ -1,23 +1,20 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import string
|
import string
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
def _name(i: int) -> str:
|
def _name(i: int) -> str:
|
||||||
if len(lines) < 26:
|
if len(lines) < 26:
|
||||||
return string.ascii_uppercase[i]
|
return string.ascii_uppercase[i]
|
||||||
return f"B{i:04d}"
|
return f"B{i:04d}"
|
||||||
|
|
||||||
|
|
||||||
def build_supports(
|
def build_supports(
|
||||||
bricks: list[tuple[tuple[int, int, int], tuple[int, int, int]]],
|
bricks: list[tuple[tuple[int, int, int], tuple[int, int, int]]],
|
||||||
) -> tuple[dict[int, set[int]], dict[int, set[int]]]:
|
) -> tuple[dict[int, set[int]], dict[int, set[int]]]:
|
||||||
@ -42,7 +39,9 @@ def build_supports(
|
|||||||
|
|
||||||
# 2. compute the bricks that supports any brick
|
# 2. compute the bricks that supports any brick
|
||||||
supported_by: dict[int, set[int]] = {}
|
supported_by: dict[int, set[int]] = {}
|
||||||
supports: dict[int, set[int]] = {i_brick: set() for i_brick in range(len(bricks))}
|
supports: dict[int, set[int]] = {
|
||||||
|
i_brick: set() for i_brick in range(len(bricks))
|
||||||
|
}
|
||||||
for i_brick, ((sx, sy, sz), (ex, ey, ez)) in enumerate(bricks):
|
for i_brick, ((sx, sy, sz), (ex, ey, ez)) in enumerate(bricks):
|
||||||
name = _name(i_brick)
|
name = _name(i_brick)
|
||||||
|
|
||||||
@ -51,7 +50,7 @@ def build_supports(
|
|||||||
for x, y in itertools.product(range(sx, ex + 1), range(sy, ey + 1))
|
for x, y in itertools.product(range(sx, ex + 1), range(sy, ey + 1))
|
||||||
if (v := levels[x, y, sz - 1]) != -1
|
if (v := levels[x, y, sz - 1]) != -1
|
||||||
}
|
}
|
||||||
logging.info(
|
self.logger.info(
|
||||||
f"{name} supported by {', '.join(map(_name, supported_by[i_brick]))}"
|
f"{name} supported by {', '.join(map(_name, supported_by[i_brick]))}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -60,7 +59,6 @@ def build_supports(
|
|||||||
|
|
||||||
return supported_by, supports
|
return supported_by, supports
|
||||||
|
|
||||||
|
|
||||||
bricks: list[tuple[tuple[int, int, int], tuple[int, int, int]]] = []
|
bricks: list[tuple[tuple[int, int, int], tuple[int, int, int]]] = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
bricks.append(
|
bricks.append(
|
||||||
@ -75,11 +73,10 @@ bricks = sorted(bricks, key=lambda b: b[0][-1])
|
|||||||
supported_by, supports = build_supports(bricks)
|
supported_by, supports = build_supports(bricks)
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = len(bricks) - sum(
|
yield len(bricks) - sum(
|
||||||
any(len(supported_by[supported]) == 1 for supported in supports_to)
|
any(len(supported_by[supported]) == 1 for supported in supports_to)
|
||||||
for supports_to in supports.values()
|
for supports_to in supports.values()
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
falling_in_chain: dict[int, set[int]] = {}
|
falling_in_chain: dict[int, set[int]] = {}
|
||||||
@ -100,12 +97,13 @@ for i_brick in range(len(bricks)):
|
|||||||
|
|
||||||
for d_brick in to_disintegrate:
|
for d_brick in to_disintegrate:
|
||||||
for supported in supports[d_brick]:
|
for supported in supports[d_brick]:
|
||||||
supported_by_copy[supported] = supported_by_copy[supported] - {d_brick}
|
supported_by_copy[supported] = supported_by_copy[supported] - {
|
||||||
|
d_brick
|
||||||
|
}
|
||||||
|
|
||||||
if not supported_by_copy[supported]:
|
if not supported_by_copy[supported]:
|
||||||
to_disintegrate_v.add(supported)
|
to_disintegrate_v.add(supported)
|
||||||
|
|
||||||
to_disintegrate = to_disintegrate_v
|
to_disintegrate = to_disintegrate_v
|
||||||
|
|
||||||
answer_2 = sum(len(falling) for falling in falling_in_chain.values())
|
yield sum(len(falling) for falling in falling_in_chain.values())
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import logging
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Literal, Sequence, TypeAlias, cast
|
from typing import Any, Iterator, Literal, Sequence, TypeAlias, cast
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
|
||||||
|
|
||||||
DirectionType: TypeAlias = Literal[">", "<", "^", "v", ".", "#"]
|
DirectionType: TypeAlias = Literal[">", "<", "^", "v", ".", "#"]
|
||||||
|
|
||||||
@ -35,6 +31,7 @@ def neighbors(
|
|||||||
Compute neighbors of the given node, ignoring the given set of nodes and considering
|
Compute neighbors of the given node, ignoring the given set of nodes and considering
|
||||||
that you can go uphill on slopes.
|
that you can go uphill on slopes.
|
||||||
"""
|
"""
|
||||||
|
n_rows, n_cols = len(grid), len(grid[0])
|
||||||
i, j = node
|
i, j = node
|
||||||
|
|
||||||
for di, dj in Neighbors[grid[i][j]]:
|
for di, dj in Neighbors[grid[i][j]]:
|
||||||
@ -103,7 +100,9 @@ def compute_direct_links(
|
|||||||
return direct
|
return direct
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
def longest_path_length(
|
def longest_path_length(
|
||||||
|
self,
|
||||||
links: dict[tuple[int, int], list[tuple[tuple[int, int], int]]],
|
links: dict[tuple[int, int], list[tuple[tuple[int, int], int]]],
|
||||||
start: tuple[int, int],
|
start: tuple[int, int],
|
||||||
target: tuple[int, int],
|
target: tuple[int, int],
|
||||||
@ -129,29 +128,29 @@ def longest_path_length(
|
|||||||
if reach not in path
|
if reach not in path
|
||||||
)
|
)
|
||||||
|
|
||||||
logging.info(f"processed {nodes} nodes")
|
self.logger.info(f"processed {nodes} nodes")
|
||||||
|
|
||||||
return max_distance
|
return max_distance
|
||||||
|
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = cast(list[Sequence[DirectionType]], input.splitlines())
|
||||||
|
|
||||||
lines = cast(list[Sequence[DirectionType]], sys.stdin.read().splitlines())
|
|
||||||
n_rows, n_cols = len(lines), len(lines[0])
|
|
||||||
start = (0, 1)
|
start = (0, 1)
|
||||||
target = (len(lines) - 1, len(lines[0]) - 2)
|
target = (len(lines) - 1, len(lines[0]) - 2)
|
||||||
|
|
||||||
|
|
||||||
direct_links: dict[tuple[int, int], list[tuple[tuple[int, int], int]]] = {
|
direct_links: dict[tuple[int, int], list[tuple[tuple[int, int], int]]] = {
|
||||||
start: [reachable(lines, start, target)]
|
start: [reachable(lines, start, target)]
|
||||||
}
|
}
|
||||||
direct_links.update(compute_direct_links(lines, direct_links[start][0][0], target))
|
direct_links.update(
|
||||||
|
compute_direct_links(lines, direct_links[start][0][0], target)
|
||||||
|
)
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = longest_path_length(direct_links, start, target)
|
yield self.longest_path_length(direct_links, start, target)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
reverse_links: dict[tuple[int, int], list[tuple[tuple[int, int], int]]] = defaultdict(
|
reverse_links: dict[tuple[int, int], list[tuple[tuple[int, int], int]]] = (
|
||||||
list
|
defaultdict(list)
|
||||||
)
|
)
|
||||||
for origin, links in direct_links.items():
|
for origin, links in direct_links.items():
|
||||||
for destination, distance in links:
|
for destination, distance in links:
|
||||||
@ -163,5 +162,4 @@ links = {
|
|||||||
for k in direct_links.keys() | reverse_links.keys()
|
for k in direct_links.keys() | reverse_links.keys()
|
||||||
}
|
}
|
||||||
|
|
||||||
answer_2 = longest_path_length(links, start, target)
|
yield self.longest_path_length(links, start, target)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from sympy import solve, symbols
|
from sympy import solve, symbols
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
positions = np.array(
|
positions = np.array(
|
||||||
[[int(c) for c in line.split("@")[0].strip().split(", ")] for line in lines]
|
[[int(c) for c in line.split("@")[0].strip().split(", ")] for line in lines]
|
||||||
@ -13,7 +18,9 @@ velocities = np.array(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
low, high = [7, 27] if len(positions) <= 10 else [200000000000000, 400000000000000]
|
low, high = (
|
||||||
|
[7, 27] if len(positions) <= 10 else [200000000000000, 400000000000000]
|
||||||
|
)
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
for i1, (p1, v1) in enumerate(zip(positions, velocities)):
|
for i1, (p1, v1) in enumerate(zip(positions, velocities)):
|
||||||
@ -31,9 +38,7 @@ for i1, (p1, v1) in enumerate(zip(positions, velocities)):
|
|||||||
c = p + np.expand_dims(t, 1) * r
|
c = p + np.expand_dims(t, 1) * r
|
||||||
count += np.all((low <= c) & (c <= high), axis=1).sum()
|
count += np.all((low <= c) & (c <= high), axis=1).sum()
|
||||||
|
|
||||||
|
yield count
|
||||||
answer_1 = count
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
# equation
|
# equation
|
||||||
@ -54,10 +59,11 @@ x, y, z, vx, vy, vz, *ts = symbols(
|
|||||||
)
|
)
|
||||||
equations = []
|
equations = []
|
||||||
for i1, ti in zip(range(n), ts):
|
for i1, ti in zip(range(n), ts):
|
||||||
for p, d, pi, di in zip((x, y, z), (vx, vy, vz), positions[i1], velocities[i1]):
|
for p, d, pi, di in zip(
|
||||||
|
(x, y, z), (vx, vy, vz), positions[i1], velocities[i1]
|
||||||
|
):
|
||||||
equations.append(p + ti * d - pi - ti * di)
|
equations.append(p + ti * d - pi - ti * di)
|
||||||
|
print(equations)
|
||||||
|
|
||||||
r = solve(equations, [x, y, z, vx, vy, vz] + list(ts), dict=True)[0]
|
r = solve(equations, [x, y, z, vx, vy, vz] + list(ts), dict=True)[0]
|
||||||
|
yield r[x] + r[y] + r[z]
|
||||||
answer_2 = r[x] + r[y] + r[z]
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import sys
|
# pyright: reportUnknownMemberType=false
|
||||||
|
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
components = {
|
components = {
|
||||||
(p := line.split(": "))[0]: p[1].split() for line in sys.stdin.read().splitlines()
|
(p := line.split(": "))[0]: p[1].split() for line in input.splitlines()
|
||||||
}
|
}
|
||||||
|
|
||||||
targets = {t for c in components for t in components[c] if t not in components}
|
graph: "nx.Graph[str]" = nx.Graph()
|
||||||
|
|
||||||
graph = nx.Graph()
|
|
||||||
graph.add_edges_from((u, v) for u, vs in components.items() for v in vs)
|
graph.add_edges_from((u, v) for u, vs in components.items() for v in vs)
|
||||||
|
|
||||||
cut = nx.minimum_edge_cut(graph)
|
cut = nx.minimum_edge_cut(graph)
|
||||||
@ -17,9 +22,4 @@ graph.remove_edges_from(cut)
|
|||||||
c1, c2 = nx.connected_components(graph)
|
c1, c2 = nx.connected_components(graph)
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = len(c1) * len(c2)
|
yield len(c1) * len(c2)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
|
||||||
answer_2 = ...
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import string
|
import string
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
NOT_A_SYMBOL = "." + string.digits
|
NOT_A_SYMBOL = "." + string.digits
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
values: list[int] = []
|
values: list[int] = []
|
||||||
gears: dict[tuple[int, int], list[int]] = defaultdict(list)
|
gears: dict[tuple[int, int], list[int]] = defaultdict(list)
|
||||||
@ -44,10 +49,5 @@ for i, line in enumerate(lines):
|
|||||||
# continue starting from the end of the number
|
# continue starting from the end of the number
|
||||||
j = k
|
j = k
|
||||||
|
|
||||||
# part 1
|
yield sum(values)
|
||||||
answer_1 = sum(values)
|
yield sum(v1 * v2 for v1, v2 in filter(lambda vs: len(vs) == 2, gears.values()))
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
|
||||||
answer_2 = sum(v1 * v2 for v1, v2 in filter(lambda vs: len(vs) == 2, gears.values()))
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import sys
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@ -9,7 +11,9 @@ class Card:
|
|||||||
values: list[int]
|
values: list[int]
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
cards: list[Card] = []
|
cards: list[Card] = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
@ -26,8 +30,7 @@ for line in lines:
|
|||||||
winnings = [sum(1 for n in card.values if n in card.numbers) for card in cards]
|
winnings = [sum(1 for n in card.values if n in card.numbers) for card in cards]
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = sum(2 ** (winning - 1) for winning in winnings if winning > 0)
|
yield sum(2 ** (winning - 1) for winning in winnings if winning > 0)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
card2cards = {i: list(range(i + 1, i + w + 1)) for i, w in enumerate(winnings)}
|
card2cards = {i: list(range(i + 1, i + w + 1)) for i, w in enumerate(winnings)}
|
||||||
@ -38,4 +41,4 @@ for i in range(len(cards)):
|
|||||||
for j in card2cards[i]:
|
for j in card2cards[i]:
|
||||||
card2values[j] += card2values[i]
|
card2values[j] += card2values[i]
|
||||||
|
|
||||||
print(f"answer 2 is {sum(card2values.values())}")
|
yield sum(card2values.values())
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import sys
|
from typing import Any, Iterator, Sequence
|
||||||
from typing import Sequence
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
MAP_ORDER = [
|
MAP_ORDER = [
|
||||||
"seed",
|
"seed",
|
||||||
@ -12,55 +13,6 @@ MAP_ORDER = [
|
|||||||
"location",
|
"location",
|
||||||
]
|
]
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
|
||||||
# mappings from one category to another, each list contains
|
|
||||||
# ranges stored as (source, target, length), ordered by start and
|
|
||||||
# completed to have no "hole"
|
|
||||||
maps: dict[tuple[str, str], list[tuple[int, int, int]]] = {}
|
|
||||||
|
|
||||||
# parsing
|
|
||||||
index = 2
|
|
||||||
while index < len(lines):
|
|
||||||
p1, _, p2 = lines[index].split()[0].split("-")
|
|
||||||
|
|
||||||
# extract the existing ranges from the file - we store as (source, target, length)
|
|
||||||
# whereas the file is in order (target, source, length)
|
|
||||||
index += 1
|
|
||||||
values: list[tuple[int, int, int]] = []
|
|
||||||
while index < len(lines) and lines[index]:
|
|
||||||
n1, n2, n3 = lines[index].split()
|
|
||||||
values.append((int(n2), int(n1), int(n3)))
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
# sort by source value
|
|
||||||
values.sort()
|
|
||||||
|
|
||||||
# add a 'fake' interval starting at 0 if missing
|
|
||||||
if values[0][0] != 0:
|
|
||||||
values.insert(0, (0, 0, values[0][0]))
|
|
||||||
|
|
||||||
# fill gaps between intervals
|
|
||||||
for i in range(len(values) - 1):
|
|
||||||
next_start = values[i + 1][0]
|
|
||||||
end = values[i][0] + values[i][2]
|
|
||||||
if next_start != end:
|
|
||||||
values.insert(
|
|
||||||
i + 1,
|
|
||||||
(end, end, next_start - end),
|
|
||||||
)
|
|
||||||
|
|
||||||
# add an interval covering values up to at least 2**32 at the end
|
|
||||||
last_start, _, last_length = values[-1]
|
|
||||||
values.append((last_start + last_length, last_start + last_length, 2**32))
|
|
||||||
|
|
||||||
assert all(v1[0] + v1[2] == v2[0] for v1, v2 in zip(values[:-1], values[1:]))
|
|
||||||
assert values[0][0] == 0
|
|
||||||
assert values[-1][0] + values[-1][-1] >= 2**32
|
|
||||||
|
|
||||||
maps[p1, p2] = values
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
|
|
||||||
def find_range(
|
def find_range(
|
||||||
values: tuple[int, int], map: list[tuple[int, int, int]]
|
values: tuple[int, int], map: list[tuple[int, int, int]]
|
||||||
@ -111,19 +63,71 @@ def find_range(
|
|||||||
return ranges
|
return ranges
|
||||||
|
|
||||||
|
|
||||||
def find_location_ranges(seeds: Sequence[tuple[int, int]]) -> Sequence[tuple[int, int]]:
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
|
# mappings from one category to another, each list contains
|
||||||
|
# ranges stored as (source, target, length), ordered by start and
|
||||||
|
# completed to have no "hole"
|
||||||
|
maps: dict[tuple[str, str], list[tuple[int, int, int]]] = {}
|
||||||
|
|
||||||
|
def find_location_ranges(
|
||||||
|
seeds: Sequence[tuple[int, int]],
|
||||||
|
) -> Sequence[tuple[int, int]]:
|
||||||
for map1, map2 in zip(MAP_ORDER[:-1], MAP_ORDER[1:]):
|
for map1, map2 in zip(MAP_ORDER[:-1], MAP_ORDER[1:]):
|
||||||
seeds = [s2 for s1 in seeds for s2 in find_range(s1, maps[map1, map2])]
|
seeds = [s2 for s1 in seeds for s2 in find_range(s1, maps[map1, map2])]
|
||||||
return seeds
|
return seeds
|
||||||
|
|
||||||
|
# parsing
|
||||||
|
index = 2
|
||||||
|
while index < len(lines):
|
||||||
|
p1, _, p2 = lines[index].split()[0].split("-")
|
||||||
|
|
||||||
|
# extract the existing ranges from the file - we store as (source, target, length)
|
||||||
|
# whereas the file is in order (target, source, length)
|
||||||
|
index += 1
|
||||||
|
values: list[tuple[int, int, int]] = []
|
||||||
|
while index < len(lines) and lines[index]:
|
||||||
|
n1, n2, n3 = lines[index].split()
|
||||||
|
values.append((int(n2), int(n1), int(n3)))
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
# sort by source value
|
||||||
|
values.sort()
|
||||||
|
|
||||||
|
# add a 'fake' interval starting at 0 if missing
|
||||||
|
if values[0][0] != 0:
|
||||||
|
values.insert(0, (0, 0, values[0][0]))
|
||||||
|
|
||||||
|
# fill gaps between intervals
|
||||||
|
for i in range(len(values) - 1):
|
||||||
|
next_start = values[i + 1][0]
|
||||||
|
end = values[i][0] + values[i][2]
|
||||||
|
if next_start != end:
|
||||||
|
values.insert(
|
||||||
|
i + 1,
|
||||||
|
(end, end, next_start - end),
|
||||||
|
)
|
||||||
|
|
||||||
|
# add an interval covering values up to at least 2**32 at the end
|
||||||
|
last_start, _, last_length = values[-1]
|
||||||
|
values.append((last_start + last_length, last_start + last_length, 2**32))
|
||||||
|
|
||||||
|
assert all(
|
||||||
|
v1[0] + v1[2] == v2[0] for v1, v2 in zip(values[:-1], values[1:])
|
||||||
|
)
|
||||||
|
assert values[0][0] == 0
|
||||||
|
assert values[-1][0] + values[-1][-1] >= 2**32
|
||||||
|
|
||||||
|
maps[p1, p2] = values
|
||||||
|
index += 1
|
||||||
|
|
||||||
# part 1 - use find_range() with range of length 1
|
# part 1 - use find_range() with range of length 1
|
||||||
seeds_p1 = [(int(s), 1) for s in lines[0].split(":")[1].strip().split()]
|
seeds_p1 = [(int(s), 1) for s in lines[0].split(":")[1].strip().split()]
|
||||||
answer_1 = min(start for start, _ in find_location_ranges(seeds_p1))
|
yield min(start for start, _ in find_location_ranges(seeds_p1))
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# # part 2
|
# # part 2
|
||||||
parts = lines[0].split(":")[1].strip().split()
|
parts = lines[0].split(":")[1].strip().split()
|
||||||
seeds_p2 = [(int(s), int(e)) for s, e in zip(parts[::2], parts[1::2])]
|
seeds_p2 = [(int(s), int(e)) for s, e in zip(parts[::2], parts[1::2])]
|
||||||
answer_2 = min(start for start, _ in find_location_ranges(seeds_p2))
|
yield min(start for start, _ in find_location_ranges(seeds_p2))
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import math
|
import math
|
||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def extreme_times_to_beat(time: int, distance: int) -> tuple[int, int]:
|
def extreme_times_to_beat(time: int, distance: int) -> tuple[int, int]:
|
||||||
@ -25,23 +27,23 @@ def extreme_times_to_beat(time: int, distance: int) -> tuple[int, int]:
|
|||||||
return t1, t2
|
return t1, t2
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
times = list(map(int, lines[0].split()[1:]))
|
times = list(map(int, lines[0].split()[1:]))
|
||||||
distances = list(map(int, lines[1].split()[1:]))
|
distances = list(map(int, lines[1].split()[1:]))
|
||||||
answer_1 = math.prod(
|
yield math.prod(
|
||||||
t2 - t1 + 1
|
t2 - t1 + 1
|
||||||
for t1, t2 in (
|
for t1, t2 in (
|
||||||
extreme_times_to_beat(time, distance)
|
extreme_times_to_beat(time, distance)
|
||||||
for time, distance in zip(times, distances)
|
for time, distance in zip(times, distances)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
time = int(lines[0].split(":")[1].strip().replace(" ", ""))
|
time = int(lines[0].split(":")[1].strip().replace(" ", ""))
|
||||||
distance = int(lines[1].split(":")[1].strip().replace(" ", ""))
|
distance = int(lines[1].split(":")[1].strip().replace(" ", ""))
|
||||||
t1, t2 = extreme_times_to_beat(time, distance)
|
t1, t2 = extreme_times_to_beat(time, distance)
|
||||||
answer_2 = t2 - t1 + 1
|
yield t2 - t1 + 1
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import sys
|
|
||||||
from collections import Counter, defaultdict
|
from collections import Counter, defaultdict
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
class HandTypes:
|
class HandTypes:
|
||||||
@ -32,18 +34,17 @@ def extract_key(hand: str, values: dict[str, int], joker: str = "0") -> tuple[in
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
cards = [(t[0], int(t[1])) for line in lines if (t := line.split())]
|
cards = [(t[0], int(t[1])) for line in lines if (t := line.split())]
|
||||||
|
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
values = {card: value for value, card in enumerate("23456789TJQKA")}
|
values = {card: value for value, card in enumerate("23456789TJQKA")}
|
||||||
cards.sort(key=lambda cv: extract_key(cv[0], values=values))
|
cards.sort(key=lambda cv: extract_key(cv[0], values=values))
|
||||||
answer_1 = sum(rank * value for rank, (_, value) in enumerate(cards, start=1))
|
yield sum(rank * value for rank, (_, value) in enumerate(cards, start=1))
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
values = {card: value for value, card in enumerate("J23456789TQKA")}
|
values = {card: value for value, card in enumerate("J23456789TQKA")}
|
||||||
cards.sort(key=lambda cv: extract_key(cv[0], values=values, joker="J"))
|
cards.sort(key=lambda cv: extract_key(cv[0], values=values, joker="J"))
|
||||||
answer_2 = sum(rank * value for rank, (_, value) in enumerate(cards, start=1))
|
yield sum(rank * value for rank, (_, value) in enumerate(cards, start=1))
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user