diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 9bba3d9..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: test - -on: - push: - branches: - - master - - main - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: erlef/setup-beam@v1 - with: - otp-version: "27.0.1" - gleam-version: "1.6.3" - rebar3-version: "3" - # elixir-version: "1.15.4" - - run: gleam deps download - - run: gleam test - - run: gleam format --check src test diff --git a/.gitignore b/.gitignore index a263f17..0fa5c73 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ -*.beam -*.ez -/build -erl_crash.dump -data/*.txt +**/target/** +**/deps/** +**/_build/** +**/.cookie +**/.envrc +**/benchmarks/** +**/.clj-kondo/** +**/.lsp/** diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 6133265..0000000 --- a/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - advent_of_code - Copyright (C) 2024 sreedevk - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - advent_of_code Copyright (C) 2024 sreedevk - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/README.md b/README.md deleted file mode 100644 index 1836783..0000000 --- a/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Advent Of Code 2024 - -```sh -# Run Tests Against all solutions (example inputs used) -gleam test - -# Run the solution against actual input for a particular day -gleam run -``` - -## Solution Status - -1. [x] [Day 01 Historian Hysteria](https://github.com/sreedevk/advent-of-code/blob/main/src/historian_hysteria.gleam) -2. [x] [Day 02 Red Nosed Reports](https://github.com/sreedevk/advent-of-code/blob/main/src/red_nosed_reports.gleam) -3. [x] [Day 03 Mull It Over](https://github.com/sreedevk/advent-of-code/blob/main/src/mull_it_over.gleam) -4. [x] [Day 04 Ceres Search](https://github.com/sreedevk/advent-of-code/blob/main/src/ceres_search.gleam) -5. [x] [Day 05 Print Queue](https://github.com/sreedevk/advent-of-code/blob/main/src/print_queue.gleam) -6. [x] [Day 06 Guard Gallivant](https://github.com/sreedevk/advent-of-code/blob/main/src/guard_gallivant.gleam) -7. [x] [Day 07 Bridge Repair](https://github.com/sreedevk/advent-of-code/blob/main/src/bridge_repair.gleam) -8. [x] [Day 08 Resonant Collinearity](https://github.com/sreedevk/advent-of-code/blob/main/src/resonant_collinearity.gleam) -9. [ ] [Day 09 Disk Fragmenter](https://github.com/sreedevk/advent-of-code/blob/main/src/disk_fragmenter.gleam) -10. [ ] [Day 10 Hoof It](https://github.com/sreedevk/advent-of-code/blob/main/src/hoof_it.gleam) diff --git a/data/.keep b/data/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/data/day01.txt b/data/day01.txt new file mode 100644 index 0000000..2094f91 --- /dev/null +++ b/data/day01.txt @@ -0,0 +1,14 @@ +1000 +2000 +3000 + +4000 + +5000 +6000 + +7000 +8000 +9000 + +10000 diff --git a/data/day02.txt b/data/day02.txt new file mode 100644 index 0000000..db60e36 --- /dev/null +++ b/data/day02.txt @@ -0,0 +1,3 @@ +A Y +B X +C Z diff --git a/data/day03.txt b/data/day03.txt new file mode 100644 index 0000000..f17e726 --- /dev/null +++ b/data/day03.txt @@ -0,0 +1,6 @@ +vJrwpWtwJgWrhcsFMMfFFhFp +jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL +PmmdzqPrVvPwwTWBwg +wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn +ttgJtRGJQctTZtZT +CrZsJsPPZsGzwwsLwLmpwMDw diff --git a/data/day04.txt b/data/day04.txt new file mode 100644 index 0000000..9f9e9cf --- /dev/null +++ b/data/day04.txt @@ -0,0 +1,6 @@ +2-4,6-8 +2-3,4-5 +5-7,7-9 +2-8,3-7 +6-6,4-6 +2-6,4-8 diff --git a/data/day05.txt b/data/day05.txt new file mode 100644 index 0000000..84933bb --- /dev/null +++ b/data/day05.txt @@ -0,0 +1,9 @@ + [D] +[N] [C] +[Z] [M] [P] + 1 2 3 + +move 1 from 2 to 1 +move 3 from 1 to 3 +move 2 from 2 to 1 +move 1 from 1 to 2 diff --git a/data/day06.txt b/data/day06.txt new file mode 100644 index 0000000..e1d0a43 --- /dev/null +++ b/data/day06.txt @@ -0,0 +1 @@ +zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw diff --git a/data/day07.txt b/data/day07.txt new file mode 100644 index 0000000..09a921e --- /dev/null +++ b/data/day07.txt @@ -0,0 +1,23 @@ +$ cd / +$ ls +dir a +14848514 b.txt +8504156 c.dat +dir d +$ cd a +$ ls +dir e +29116 f +2557 g +62596 h.lst +$ cd e +$ ls +584 i +$ cd .. +$ cd .. +$ cd d +$ ls +4060174 j +8033020 d.log +5626152 d.ext +7214296 k diff --git a/data/day08.txt b/data/day08.txt new file mode 100644 index 0000000..16d6fbd --- /dev/null +++ b/data/day08.txt @@ -0,0 +1,5 @@ +30373 +25512 +65332 +33549 +35390 diff --git a/gleam.toml b/gleam.toml deleted file mode 100644 index 79d3ab7..0000000 --- a/gleam.toml +++ /dev/null @@ -1,24 +0,0 @@ -name = "advent_of_code" -version = "1.0.0" - -# Fill out these fields if you intend to generate HTML documentation or publish -# your project to the Hex package manager. -# -# description = "" -# licences = ["Apache-2.0"] -# repository = { type = "github", user = "", repo = "" } -# links = [{ title = "Website", href = "" }] -# -# For a full reference of all the available options, you can have a look at -# https://gleam.run/writing-gleam/gleam-toml/. - -[dependencies] -gleam_stdlib = ">= 0.34.0 and < 2.0.0" -simplifile = ">= 2.2.0 and < 3.0.0" -gleam_regexp = ">= 1.0.0 and < 2.0.0" -gleam_yielder = ">= 1.1.0 and < 2.0.0" -gleam_otp = ">= 0.14.1 and < 1.0.0" -argv = ">= 1.0.2 and < 2.0.0" - -[dev-dependencies] -gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/main.rb b/main.rb new file mode 100644 index 0000000..284659f --- /dev/null +++ b/main.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require_relative './src/calorie_counting' +require_relative './src/rock_paper_scissors' +require_relative './src/rucksack_reorganization' +require_relative './src/camp_cleanup' +require_relative './src/supply_stacks' +require_relative './src/tuning_trouble' +require_relative './src/no_space_left_on_device' +require_relative './src/treetop_tree_house' + +def main(day, input) + case day + when '1' + puts 'Advent of Code Day 01' + puts "Part A: #{CalorieCounting.new(input).solve1}" + puts "Part B: #{CalorieCounting.new(input).solve2}" + when '2' + puts 'Advent of Code Day 02' + puts "Part A: #{RockPaperScissors.new(input).solve1}" + puts "Part B: #{RockPaperScissors.new(input).solve2}" + when '3' + puts 'Advent of Code Day 03' + puts "Part A: #{RucksackReorganization.new(input).solve1}" + puts "Part B: #{RucksackReorganization.new(input).solve2}" + when '4' + puts 'Advent of Code Day 04' + puts "Part A: #{CampCleanup.new(input).solve1}" + puts "Part B: #{CampCleanup.new(input).solve2}" + when '5' + puts 'Advent of Code Day 05' + puts "Part A: #{SupplyStacks.new(input).solve1}" + puts "Part B: #{SupplyStacks.new(input).solve2}" + when '6' + puts 'Advent of Code Day 06' + puts "Part A: #{TuningTrouble.new(input).solve1}" + puts "Part B: #{TuningTrouble.new(input).solve2}" + when '7' + puts 'Advent of Code Day 07' + solver = NoSpaceLeftOnDevice.new(input) + puts "Part A: #{solver.solve1}" + puts "Part B: #{solver.solve2}" + when '8' + puts 'Advent of Code Day 08' + solver = TreetopTreeHouse.new(input) + puts "Part A: #{solver.solve1}" + puts "Part B: #{solver.solve2}" + end +end + +main ARGV[0], File.read(ARGV[1]) diff --git a/manifest.toml b/manifest.toml deleted file mode 100644 index 3e9655e..0000000 --- a/manifest.toml +++ /dev/null @@ -1,23 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, - { name = "filepath", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "67A6D15FB39EEB69DD31F8C145BB5A421790581BD6AA14B33D64D5A55DBD6587" }, - { name = "gleam_erlang", version = "0.33.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "A1D26B80F01901B59AABEE3475DD4C18D27D58FA5C897D922FCB9B099749C064" }, - { name = "gleam_otp", version = "0.14.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5A8CE8DBD01C29403390A7BD5C0A63D26F865C83173CF9708E6E827E53159C65" }, - { name = "gleam_regexp", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "A3655FDD288571E90EE9C4009B719FEF59FA16AFCDF3952A76A125AF23CF1592" }, - { name = "gleam_stdlib", version = "0.45.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "206FCE1A76974AECFC55AEBCD0217D59EDE4E408C016E2CFCCC8FF51278F186E" }, - { name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" }, - { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, - { name = "simplifile", version = "2.2.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0DFABEF7DC7A9E2FF4BB27B108034E60C81BEBFCB7AB816B9E7E18ED4503ACD8" }, -] - -[requirements] -argv = { version = ">= 1.0.2 and < 2.0.0" } -gleam_otp = { version = ">= 0.14.1 and < 1.0.0" } -gleam_regexp = { version = ">= 1.0.0 and < 2.0.0" } -gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } -gleam_yielder = { version = ">= 1.1.0 and < 2.0.0" } -gleeunit = { version = ">= 1.0.0 and < 2.0.0" } -simplifile = { version = ">= 2.2.0 and < 3.0.0" } diff --git a/src/advent_of_code.gleam b/src/advent_of_code.gleam deleted file mode 100644 index 6e07c20..0000000 --- a/src/advent_of_code.gleam +++ /dev/null @@ -1,162 +0,0 @@ -import argv -import bridge_repair as day07 -import ceres_search as day04 -import disk_fragmenter as day09 -import gleam/int -import gleam/io -import gleam/result -import guard_gallivant as day06 -import historian_hysteria as day01 -import hoof_it as day10 -import mull_it_over as day03 -import print_queue as day05 -import red_nosed_reports as day02 -import resonant_collinearity as day08 -import simplifile.{read} - -pub fn main() { - case argv.load().arguments { - ["1"] -> { - result.unwrap( - result.map(read("data/day1.txt"), fn(data) { - io.println( - "[1] Historian Hysteria (Part 1): " - <> int.to_string(day01.solve_a(data)), - ) - io.println( - "[1] Historian Hysteria (Part 2): " - <> int.to_string(day01.solve_b(data)), - ) - }), - Nil, - ) - } - ["2"] -> { - result.unwrap( - result.map(read("data/day2.txt"), fn(data) { - io.println( - "[2] Red Nosed Reports (Part 1): " - <> int.to_string(day02.solve_a(data)), - ) - io.println( - "[2] Red Nosed Reports (Part 2): " - <> int.to_string(day02.solve_b(data)), - ) - }), - Nil, - ) - } - ["3"] -> { - result.unwrap( - result.map(read("data/day3.txt"), fn(data) { - io.println( - "[3] Mull It Over (Part 1): " <> int.to_string(day03.solve_a(data)), - ) - io.println( - "[3] Mull It Over (Part 2): " <> int.to_string(day03.solve_b(data)), - ) - }), - Nil, - ) - } - ["4"] -> { - result.unwrap( - result.map(read("data/day4.txt"), fn(data) { - io.println( - "[4] Ceres Search (Part 1): " <> int.to_string(day04.solve_a(data)), - ) - io.println( - "[4] Ceres Search (Part 2): " <> int.to_string(day04.solve_b(data)), - ) - }), - Nil, - ) - } - ["5"] -> { - result.unwrap( - result.map(read("data/day5.txt"), fn(data) { - io.println( - "[5] Print Queue (Part 1): " <> int.to_string(day05.solve_a(data)), - ) - io.println( - "[5] Print Queue (Part 2): " <> int.to_string(day05.solve_b(data)), - ) - }), - Nil, - ) - } - ["6"] -> { - result.unwrap( - result.map(read("data/day6.txt"), fn(data) { - io.println( - "[6] Guard Gallivant (Part 1): " - <> int.to_string(day06.solve_a(data)), - ) - io.println( - "[6] Guard Gallivant (Part 2): " - <> int.to_string(day06.solve_b(data)), - ) - }), - Nil, - ) - } - ["7"] -> { - result.unwrap( - result.map(read("data/day7.txt"), fn(data) { - io.println( - "[7] Bridge Repair (Part 1): " <> int.to_string(day07.solve_a(data)), - ) - io.println( - "[7] Bridge Repair (Part 2): " <> int.to_string(day07.solve_b(data)), - ) - }), - Nil, - ) - } - - ["8"] -> { - result.unwrap( - result.map(read("data/day8.txt"), fn(data) { - io.println( - "[8] Resonant Collinearity (Part 1): " - <> int.to_string(day08.solve_a(data)), - ) - io.println( - "[8] Resonant Collinearity (Part 2): " - <> int.to_string(day08.solve_b(data)), - ) - }), - Nil, - ) - } - ["9"] -> { - result.unwrap( - result.map(read("data/day9.txt"), fn(data) { - io.println( - "[9] Disk Fragmenter (Part 1): " - <> int.to_string(day09.solve_a(data)), - ) - io.println( - "[9] Disk Fragmenter (Part 2): " - <> int.to_string(day09.solve_b(data)), - ) - }), - Nil, - ) - } - ["10"] -> { - result.unwrap( - result.map(read("data/day10.txt"), fn(data) { - io.println( - "[10] Hoof It (Part 1): " <> int.to_string(day10.solve_a(data)), - ) - io.println( - "[10] Hoof It (Part 2): " <> int.to_string(day10.solve_b(data)), - ) - }), - Nil, - ) - } - _ -> io.println_error("invalid arguments!") - } -} diff --git a/src/bridge_repair.gleam b/src/bridge_repair.gleam deleted file mode 100644 index 2a2ff44..0000000 --- a/src/bridge_repair.gleam +++ /dev/null @@ -1,95 +0,0 @@ -import gleam/int -import gleam/list -import gleam/otp/task -import gleam/result -import gleam/string -import utils/list as li - -type Equation { - Equation(target: Int, values: List(String)) -} - -fn parse_eq(line: String) -> Equation { - let assert Ok(#(tar_str, vals_str)) = string.split_once(line, ": ") - let assert Ok(tar_int) = int.parse(tar_str) - let values = string.split(vals_str, " ") - - Equation(target: tar_int, values: values) -} - -fn parse_file(input: String) -> List(Equation) { - input - |> string.trim - |> string.split("\n") - |> list.map(parse_eq) -} - -fn parse_num(x: String) -> Int { - result.unwrap(int.parse(x), 0) -} - -fn mult(lhs: String, rhs: String) -> String { - int.to_string(int.multiply(parse_num(lhs), parse_num(rhs))) -} - -fn add(lhs: String, rhs: String) -> String { - int.to_string(int.add(parse_num(lhs), parse_num(rhs))) -} - -fn solve_part(eq: List(String)) -> List(String) { - case eq { - [lhs, opr, rhs] -> - case opr { - "*" -> list.wrap(mult(lhs, rhs)) - "+" -> list.wrap(add(lhs, rhs)) - "|" -> list.wrap(lhs <> rhs) - _ -> [] - } - _ -> [] - } -} - -fn solve_eq_lr(eq: List(String)) -> List(String) { - case list.length(eq) { - 1 | 0 -> eq - _ -> { - case list.split(eq, 3) { - #(head, tail) -> solve_eq_lr(list.append(solve_part(head), tail)) - } - } - } -} - -fn is_satisfiable(eq: Equation, operators: List(String)) -> Bool { - li.repeated_permutation(operators, list.length(eq.values) - 1) - |> list.map(fn(combo) { list.interleave([eq.values, combo]) }) - |> list.flat_map(solve_eq_lr) - |> list.any(fn(soln) { eq.target == parse_num(soln) }) -} - -fn extract(x: #(Equation, Bool)) { - case x { - #(eq, True) -> Ok(eq.target) - _ -> Error(Nil) - } -} - -fn is_satisfiable_async(eq: Equation, operators: List(String)) { - task.async(fn() { #(eq, is_satisfiable(eq, operators)) }) -} - -pub fn solve_a(input: String) -> Int { - parse_file(input) - |> list.map(is_satisfiable_async(_, ["*", "+"])) - |> list.map(task.await_forever) - |> list.filter_map(extract) - |> list.fold(0, int.add) -} - -pub fn solve_b(input: String) -> Int { - parse_file(input) - |> list.map(is_satisfiable_async(_, ["*", "+", "|"])) - |> list.map(task.await_forever) - |> list.filter_map(extract) - |> list.fold(0, int.add) -} diff --git a/src/calorie_counting.rb b/src/calorie_counting.rb new file mode 100644 index 0000000..33ff8b7 --- /dev/null +++ b/src/calorie_counting.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class CalorieCounting + def initialize(data) + @data = data + end + + def solve1 + @data + .strip + .split("\n\n") + .map { |elf| elf.split("\n").map(&:strip).map(&:to_i).sum } + .max + end + + def solve2 + @data + .strip + .split("\n\n") + .map { |elf| elf.split("\n").map(&:strip).map(&:to_i).sum } + .sort + .reverse + .take(3) + .sum + end +end diff --git a/src/camp_cleanup.rb b/src/camp_cleanup.rb new file mode 100644 index 0000000..9c31a69 --- /dev/null +++ b/src/camp_cleanup.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# Advent of Code Day4 +class CampCleanup + def initialize(data) + @data = parse(data) + end + + def parse(input) + input.lines.map do |line| + line.split(',').map do |elf| + elf + .split('-') + .map(&:to_i) + .reduce(&Range.method(:new)) + end + end + end + + def solve1 + @data.count { |(elf1, elf2)| elf1.cover?(elf2) || elf2.cover?(elf1) } + end + + def solve2 + @data.count { |(elf1, elf2)| elf1.cover?(elf2.first) || elf2.cover?(elf1.first) } + end +end diff --git a/src/ceres_search.gleam b/src/ceres_search.gleam deleted file mode 100644 index 7891bdf..0000000 --- a/src/ceres_search.gleam +++ /dev/null @@ -1,83 +0,0 @@ -import gleam/dict.{type Dict} -import gleam/int -import gleam/list -import gleam/pair -import gleam/result -import gleam/string - -type Point = - #(Int, Int) - -type Grid = - Dict(Point, String) - -fn process_grid(input: String, f: fn(Grid) -> Int) -> Int { - input - |> string.trim() - |> string.split("\n") - |> list.map(string.to_graphemes) - |> list.index_map(fn(line, y) { - use grid, char, x <- list.index_fold(line, dict.new()) - dict.insert(grid, #(x, y), char) - }) - |> list.reduce(dict.merge) - |> result.unwrap(dict.new()) - |> f -} - -fn word(points: List(Point), grid: Grid) -> String { - use word, point <- list.fold(points, "") - string.append(word, result.unwrap(dict.get(grid, point), "")) -} - -fn all_neighbors(point: Point, word: String) -> List(List(Point)) { - let id = fn(x, _d) { x } - use f <- list.map([ - #(int.add, id), - #(int.subtract, id), - #(id, int.subtract), - #(id, int.add), - #(int.subtract, int.subtract), - #(int.add, int.subtract), - #(int.subtract, int.add), - #(int.add, int.add), - ]) - use d <- list.map(list.range(0, string.length(word) - 1)) - let #(x, y) = point - #(pair.first(f)(x, d), pair.second(f)(y, d)) -} - -fn x_neighbors(point: #(Int, Int)) { - let #(x, y) = point - [ - [#(x - 1, y - 1), point, #(x + 1, y + 1)], - [#(x + 1, y - 1), point, #(x - 1, y + 1)], - ] -} - -fn xmas_count_at_point(grid: Grid, point: Point) -> Int { - all_neighbors(point, "XMAS") - |> list.map(word(_, grid)) - |> list.count(fn(w) { w == "XMAS" }) -} - -fn point_has_x_mas(grid: Grid, point: Point) -> Bool { - x_neighbors(point) - |> list.map(word(_, grid)) - |> list.all(fn(w) { w == "MAS" || w == "SAM" }) -} - -pub fn solve_a(input: String) -> Int { - use grid <- process_grid(input) - - dict.keys(grid) - |> list.map(xmas_count_at_point(grid, _)) - |> list.fold(0, int.add) -} - -pub fn solve_b(input: String) -> Int { - use grid <- process_grid(input) - - dict.keys(grid) - |> list.count(point_has_x_mas(grid, _)) -} diff --git a/src/disk_fragmenter.gleam b/src/disk_fragmenter.gleam deleted file mode 100644 index 4134d12..0000000 --- a/src/disk_fragmenter.gleam +++ /dev/null @@ -1,105 +0,0 @@ -import gleam/int -import gleam/list -import gleam/result -import gleam/string -import utils/list as li - -type Block { - File(Int) - None -} - -type Disk = - List(Block) - -fn generate_file_block(block_count, file_index) -> Disk { - case block_count { - "0" -> [] - _ -> - list.range(1, result.unwrap(int.parse(block_count), 0)) - |> list.map(fn(_) { File(file_index / 2) }) - } -} - -fn generate_space_block(block_count) -> Disk { - case block_count { - "0" -> [] - _ -> - list.range(1, result.unwrap(int.parse(block_count), 0)) - |> list.map(fn(_) { None }) - } -} - -fn generate_block(block_count, file_index) -> Disk { - case int.is_even(file_index) { - True -> generate_file_block(block_count, file_index) - False -> generate_space_block(block_count) - } -} - -fn disk_map(input: String) -> Disk { - string.to_graphemes(input) - |> list.index_map(generate_block) - |> list.flatten -} - -fn defrag_complete(disk: List(Block), current_index: Int) -> Bool { - let #(_, after) = list.split(disk, current_index + 1) - - result.is_error(list.find(after, fn(x) { x != None })) -} - -fn rotate_block(disk: Disk, state: Disk) -> Disk { - case li.pop(disk) { - #(Ok(None), rest) -> rotate_block(rest, list.append(state, [None])) - #(Ok(anyval), rest) -> list.append([anyval], list.append(rest, state)) - #(Error(_), _) -> disk - } -} - -fn disk_defrag(disk: Disk, current_index: Int) -> Disk { - case defrag_complete(disk, current_index) { - True -> disk - False -> - case li.at(disk, current_index) { - Ok(None) -> { - let #(defragged, undefragged) = list.split(disk, current_index) - let assert #([None], remaining_undefragged) = - list.split(undefragged, 1) - let new_current_disk_map = - list.append( - defragged, - list.append(rotate_block(remaining_undefragged, []), [None]), - ) - - disk_defrag(new_current_disk_map, current_index + 1) - } - Ok(_) -> disk_defrag(disk, current_index + 1) - Error(_) -> disk - } - } -} - -fn get_blkid(blk: Block) -> Result(Int, Nil) { - case blk { - File(id) -> Ok(id) - None -> Error(Nil) - } -} - -fn checksum(input: Disk) -> Int { - input - |> list.filter_map(fn(x) { get_blkid(x) }) - |> list.index_map(int.multiply) - |> list.fold(0, int.add) -} - -pub fn solve_a(input: String) -> Int { - disk_map(input) - |> disk_defrag(0) - |> checksum -} - -pub fn solve_b(_input: String) -> Int { - 0 -} diff --git a/src/guard_gallivant.gleam b/src/guard_gallivant.gleam deleted file mode 100644 index 4f9bbab..0000000 --- a/src/guard_gallivant.gleam +++ /dev/null @@ -1,217 +0,0 @@ -import gleam/dict.{type Dict} -import gleam/list -import gleam/option.{type Option, Some} -import gleam/otp/task -import gleam/pair.{first as pf, second as ps} -import gleam/result -import gleam/string -import utils/list as li - -type Entity { - Guard - Obstacle - Path - Unknown(String) -} - -type Point = - #(Int, Int) - -type Grid = - Dict(Point, Entity) - -type Direction { - Up - Down - Left - Right -} - -type History = - #(Point, Direction) - -type IterState { - IterState( - point: Point, - direction: Direction, - grid: Grid, - histories: List(History), - max_bound: Int, - looping: Bool, - ) -} - -fn parse_column(line, y) { - use grid, char, x <- list.index_fold(line, dict.new()) - dict.insert(grid, #(x, y), case char { - "." -> Path - "#" -> Obstacle - "^" -> Guard - uchr -> Unknown(uchr) - }) -} - -fn parse_grid(input: String) -> Grid { - input - |> string.trim() - |> string.split("\n") - |> list.map(string.to_graphemes) - |> list.index_map(parse_column) - |> list.reduce(dict.merge) - |> result.unwrap(dict.new()) -} - -fn is_guard_position(pos: Point, g: Grid) -> Bool { - case dict.get(g, pos) { - Ok(Guard) -> True - _ -> False - } -} - -fn init_state(g: Grid) -> IterState { - let assert Ok(gp) = list.find(dict.keys(g), is_guard_position(_, g)) - IterState( - point: gp, - direction: Up, - grid: g, - histories: [#(gp, Up)], - max_bound: li.max(list.map(dict.keys(g), pf)), - looping: False, - ) -} - -fn incr_pos(pos: Point, dir: Direction) -> Point { - case dir { - Up -> #(pf(pos), ps(pos) - 1) - Down -> #(pf(pos), ps(pos) + 1) - Left -> #(pf(pos) - 1, ps(pos)) - Right -> #(pf(pos) + 1, ps(pos)) - } -} - -fn is_obstacle(position: Point, grid: Grid) -> Bool { - case dict.get(grid, position) { - Ok(Obstacle) -> True - _ -> False - } -} - -fn rotate_cw(d: Direction) -> Direction { - case d { - Up -> Right - Down -> Left - Right -> Down - Left -> Up - } -} - -fn next_coordinates(pos: Point, dir: Direction, g: Grid) -> #(Point, Direction) { - let np = incr_pos(pos, dir) - case is_obstacle(np, g) { - True -> next_coordinates(pos, rotate_cw(dir), g) - False -> #(np, dir) - } -} - -fn guard_exited(pos: Point, b: Int) -> Bool { - let #(x, y) = pos - x > b || x < 0 || y < 0 || y > b -} - -fn emulate(is: IterState) -> IterState { - let #(ngp, ngd) = next_coordinates(is.point, is.direction, is.grid) - case guard_exited(ngp, is.max_bound) { - True -> is - False -> - emulate(IterState( - point: ngp, - direction: ngd, - grid: is.grid, - histories: list.append(is.histories, [#(ngp, ngd)]), - max_bound: is.max_bound, - looping: False, - )) - } -} - -fn visited_count(is: IterState) -> Int { - is.histories - |> list.map(pf) - |> list.unique - |> list.length -} - -fn visited_paths(is: IterState) -> #(Grid, List(Point)) { - #(is.grid, list.unique(list.map(is.histories, pf))) -} - -fn iterate(is: IterState) -> IterState { - let #(ngp, ngd) = next_coordinates(is.point, is.direction, is.grid) - let ngh = list.append(is.histories, [#(ngp, ngd)]) - - case guard_exited(ngp, is.max_bound) { - True -> is - False -> - case li.contains(is.histories, #(ngp, ngd)) { - True -> - IterState( - point: ngp, - direction: ngd, - grid: is.grid, - histories: ngh, - max_bound: is.max_bound, - looping: True, - ) - False -> - iterate(IterState( - point: ngp, - direction: ngd, - grid: is.grid, - histories: ngh, - max_bound: is.max_bound, - looping: False, - )) - } - } -} - -fn add_obst(g: Grid, pt: Point) -> Grid { - dict.upsert(g, pt, fn(ent: Option(Entity)) { - case ent { - Some(Guard) -> Guard - _ -> Obstacle - } - }) -} - -fn is_infinite_loop_obstacle(g: Grid, point: Point) { - task.async(fn() { - add_obst(g, point) - |> init_state - |> iterate - |> fn(x) { x.looping } - }) -} - -fn count_infinite_loop_obstacles(info) -> Int { - let #(g, original_path) = info - - original_path - |> list.map(is_infinite_loop_obstacle(g, _)) - |> list.count(task.await_forever) -} - -pub fn solve_a(input: String) -> Int { - parse_grid(input) - |> init_state - |> emulate - |> visited_count -} - -pub fn solve_b(input: String) -> Int { - parse_grid(input) - |> init_state - |> emulate - |> visited_paths - |> count_infinite_loop_obstacles -} diff --git a/src/historian_hysteria.gleam b/src/historian_hysteria.gleam deleted file mode 100644 index c2ae741..0000000 --- a/src/historian_hysteria.gleam +++ /dev/null @@ -1,43 +0,0 @@ -import gleam/int -import gleam/list -import gleam/pair -import gleam/result -import gleam/string - -fn parse_line(line: String) -> List(Int) { - use a <- list.map(string.split(line, " ")) - result.unwrap(int.parse(a), 0) -} - -fn dist(scans: #(Int, Int)) { - int.absolute_value(pair.second(scans) - pair.first(scans)) -} - -fn freq_score_gen(ys: List(Int)) { - fn(x) { x * list.count(ys, fn(y) { y == x }) } -} - -pub fn solve_a(input) -> Int { - input - |> string.trim() - |> string.split("\n") - |> list.map(parse_line) - |> list.transpose() - |> list.map(list.sort(_, int.compare)) - |> list.reduce(fn(x, y) { list.map(list.zip(x, y), dist) }) - |> result.map(list.reduce(_, int.add)) - |> result.flatten() - |> result.unwrap(0) -} - -pub fn solve_b(input) -> Int { - input - |> string.trim() - |> string.split("\n") - |> list.map(parse_line) - |> list.transpose() - |> list.reduce(fn(x, y) { list.map(x, freq_score_gen(y)) }) - |> result.map(list.reduce(_, int.add)) - |> result.flatten() - |> result.unwrap(0) -} diff --git a/src/hoof_it.gleam b/src/hoof_it.gleam deleted file mode 100644 index e566585..0000000 --- a/src/hoof_it.gleam +++ /dev/null @@ -1,7 +0,0 @@ -pub fn solve_a(_: String) -> Int { - 0 -} - -pub fn solve_b(_: String) -> Int { - 0 -} diff --git a/src/mull_it_over.gleam b/src/mull_it_over.gleam deleted file mode 100644 index b0986db..0000000 --- a/src/mull_it_over.gleam +++ /dev/null @@ -1,45 +0,0 @@ -import gleam/int -import gleam/list -import gleam/pair -import gleam/regexp -import gleam/result - -pub fn process_isr(instr: String) -> Int { - let assert Ok(args_rex) = regexp.from_string("\\d{1,3}") - regexp.scan(args_rex, instr) - |> list.map(fn(x) { x.content }) - |> list.map(fn(x) { result.unwrap(int.parse(x), -1) }) - |> list.fold_right(1, int.multiply) -} - -pub fn process_cond_isr(state: #(Bool, Int), instr: String) -> #(Bool, Int) { - case instr { - "do()" -> #(True, pair.second(state)) - "don't()" -> #(False, pair.second(state)) - _ -> - case pair.first(state) { - True -> #(True, int.add(process_isr(instr), pair.second(state))) - False -> state - } - } -} - -pub fn solve_a(input: String) -> Int { - let assert Ok(rex) = regexp.from_string("(mul\\([0-9]{1,3},[0-9]{1,3}\\))") - - regexp.scan(rex, input) - |> list.map(fn(match) { match.content }) - |> list.map(process_isr) - |> list.reduce(int.add) - |> result.unwrap(0) -} - -pub fn solve_b(input: String) -> Int { - let assert Ok(rex) = - regexp.from_string("(mul\\([0-9]{1,3},[0-9]{1,3}\\)|do\\(\\))|don't\\(\\)") - - regexp.scan(rex, input) - |> list.map(fn(match) { match.content }) - |> list.fold(#(True, 0), process_cond_isr) - |> pair.second -} diff --git a/src/no_space_left_on_device.rb b/src/no_space_left_on_device.rb new file mode 100644 index 0000000..3f5a72f --- /dev/null +++ b/src/no_space_left_on_device.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +# Solution +class NoSpaceLeftOnDevice + attr_accessor :data, :cpath, :fs + + def initialize(input) + @data = input + .lines + .map(&:strip) + .map { |command| command.split(' ') } + + @cpath = '' + @fs = Hash.new(0) + end + + def process_ls(_line, line_index) + sum = data[(line_index + 1)..] + .take_while { |x| x[0] != '$' } + .filter { |l| l[0] != 'dir' } + .map { |l| l[0].to_i } + .sum + + assign_cp_size(sum) + end + + def assign_cp_size(size) + @cpath + .split('/') + .reject(&:empty?) + .inject(['/home']) { |mpath, cdir| mpath << "#{mpath[-1]}/#{cdir}" } + .map { |dir| @fs[dir] += size } + end + + def process_cd(line, _line_index) + case line [2] + when '/' + @cpath = '' + when '..' + @cpath = cpath[...cpath.rindex('/')] + else + @cpath += "/#{line[2]}" + end + end + + def process_line(line, line_index) + return unless line[0] == '$' + + case line [1] + when 'ls' + process_ls(line, line_index) + when 'cd' + process_cd(line, line_index) + end + end + + def solve1 + data + .map.with_index + .filter { |line, _| line[0] == '$' } + .each { |line, index| process_line(line, index) } + + fs + .filter { |_k, v| v <= 100_000 } + .values + .sum + end + + def solve2 + available_space = 70_000_000 - fs['/home'] + required_space = 30_000_000 + space_to_clear = required_space - available_space + + fs + .filter { |_, size| size >= space_to_clear } + .values + .min + end +end diff --git a/src/print_queue.gleam b/src/print_queue.gleam deleted file mode 100644 index 4f4b908..0000000 --- a/src/print_queue.gleam +++ /dev/null @@ -1,98 +0,0 @@ -import gleam/bool -import gleam/int -import gleam/list -import gleam/pair.{first as pf, second as ps} -import gleam/result -import gleam/string -import gleam/yielder -import utils/list as li - -type Rule = - #(Int, Int) - -type Order = - List(Int) - -fn parse_rule(input: String) -> Rule { - let assert Ok(#(first, second)) = string.split_once(input, "|") - let assert Ok(fnum) = int.parse(first) - let assert Ok(snum) = int.parse(second) - - #(fnum, snum) -} - -fn parse_order(input: String) -> List(Int) { - use numres <- list.map(string.split(input, ",")) - - result.unwrap(int.parse(numres), 0) -} - -fn parse_file(input: String) -> #(List(Rule), List(Order)) { - let assert Ok(#(rules, orders)) = - string.split_once(string.trim(input), "\n\n") - - let parsed_rules = list.map(string.split(rules, "\n"), parse_rule) - let parsed_orders = list.map(string.split(orders, "\n"), parse_order) - #(parsed_rules, parsed_orders) -} - -fn rule_applicable(order: Order, rule: Rule) -> Bool { - list.all(li.from_pair(rule), li.contains(order, _)) -} - -fn rule_satisfied(order: Order, rule: Rule) -> Bool { - let #(rf, rs) = rule - let assert [x, y] = list.filter(order, fn(x) { x == rf || x == rs }) - - x == rf && y == rs -} - -fn order_follows_rules(order: Order, rules: List(Rule)) -> Bool { - rules - |> list.filter(rule_applicable(order, _)) - |> list.all(rule_satisfied(order, _)) -} - -fn rule_satisfied_or_swap(order: Order, rule: Rule) -> Order { - case rule_satisfied(order, rule) { - True -> order - False -> li.swap(order, pf(rule), ps(rule)) - } -} - -fn make_order_follow_rules(order: Order, rules: List(Rule)) -> Order { - case order_follows_rules(order, rules) { - True -> order - False -> { - list.filter(rules, rule_applicable(order, _)) - |> list.fold(order, rule_satisfied_or_swap) - |> make_order_follow_rules(rules) - } - } -} - -fn middle(order: Order) -> Int { - order - |> yielder.from_list - |> yielder.at(list.length(order) / 2) - |> result.unwrap(0) -} - -pub fn solve_a(input: String) -> Int { - let #(rules, orders) = parse_file(input) - - orders - |> list.filter(order_follows_rules(_, rules)) - |> list.map(middle) - |> list.fold(0, int.add) -} - -pub fn solve_b(input: String) -> Int { - let #(rules, orders) = parse_file(input) - - orders - |> list.filter(fn(order) { bool.negate(order_follows_rules(order, rules)) }) - |> list.map(make_order_follow_rules(_, rules)) - |> list.map(middle) - |> list.fold(0, int.add) -} diff --git a/src/red_nosed_reports.gleam b/src/red_nosed_reports.gleam deleted file mode 100644 index 781f599..0000000 --- a/src/red_nosed_reports.gleam +++ /dev/null @@ -1,56 +0,0 @@ -import gleam/int -import gleam/list -import gleam/pair -import gleam/result -import gleam/string - -fn parse_line(line: String) -> List(Int) { - use a <- list.map(string.split(line, " ")) - result.unwrap(int.parse(a), 0) -} - -fn all_incr(report: List(Int)) -> Bool { - use nums <- list.all(list.window_by_2(report)) - case pair.second(nums) - pair.first(nums) { - x if x > 0 && x < 4 -> True - _ -> False - } -} - -fn all_decr(report: List(Int)) -> Bool { - use nums <- list.all(list.window_by_2(report)) - case pair.first(nums) - pair.second(nums) { - x if x > 0 && x < 4 -> True - _ -> False - } -} - -fn valid(report: List(Int), d: Bool) -> Bool { - case d { - False -> all_incr(report) || all_decr(report) - True -> - valid(report, False) - || { - report - |> list.combinations(list.length(report) - 1) - |> list.any(valid(_, False)) - } - } -} - -pub fn solve_a(input: String) -> Int { - input - |> string.trim() - |> string.split("\n") - |> list.map(parse_line) - |> list.count(valid(_, False)) -} - -pub fn solve_b(input: String) -> Int { - input - |> string.trim() - |> string.split("\n") - |> list.map(parse_line) - |> list.filter(valid(_, True)) - |> list.length() -} diff --git a/src/resonant_collinearity.gleam b/src/resonant_collinearity.gleam deleted file mode 100644 index 425394d..0000000 --- a/src/resonant_collinearity.gleam +++ /dev/null @@ -1,160 +0,0 @@ -import gleam/dict.{type Dict} -import gleam/float -import gleam/int -import gleam/list -import gleam/result -import gleam/string - -type Point = - #(Float, Float) - -type Grid = - Dict(Point, String) - -type Direction { - Forward - Reverse -} - -fn parse_column(line, y) { - use grid, char, x <- list.index_fold(line, dict.new()) - dict.insert(grid, #(int.to_float(x), int.to_float(y)), char) -} - -fn square(input: Float) -> Float { - case float.power(input, 2.0) { - Ok(val) -> val - _ -> 0.0 - } -} - -fn find_collinear_vector( - point_a: Point, - point_b: Point, -) -> #(Float, #(Float, Float)) { - let #(x1, y1) = point_a - let #(x2, y2) = point_b - - let assert Ok(distance) = - float.add(square(float.subtract(x2, x1)), square(float.subtract(y2, y1))) - |> float.square_root - - let assert Ok(dx) = float.divide(float.subtract(x2, x1), distance) - let assert Ok(dy) = float.divide(float.subtract(y2, y1), distance) - - #(distance, #(dx, dy)) -} - -fn find_collinear_in_direction( - points: List(Point), - direction: Direction, -) -> Point { - let assert [point_a, point_b] = points - let #(distance, #(dx, dy)) = find_collinear_vector(point_a, point_b) - let #(x1, y1) = point_a - let #(x2, y2) = point_b - - case direction { - Forward -> #( - float.ceiling(float.add(x2, float.multiply(dx, distance))), - float.ceiling(float.add(y2, float.multiply(dy, distance))), - ) - Reverse -> #( - float.ceiling(float.subtract(x1, float.multiply(dx, distance))), - float.ceiling(float.subtract(y1, float.multiply(dy, distance))), - ) - } -} - -fn find_harmonic_collinear_in_direction( - found: List(Point), - grid: Grid, - direction: Direction, -) -> List(Point) { - let #(head, rest) = list.split(found, 2) - let assert [p1, p2] = head - let collinear_in_direction = find_collinear_in_direction(head, direction) - - case point_within_map(collinear_in_direction, grid) { - True -> - case direction { - Forward -> - find_harmonic_collinear_in_direction( - list.append([p2, collinear_in_direction], list.append(rest, [p1])), - grid, - direction, - ) - Reverse -> - find_harmonic_collinear_in_direction( - list.append([collinear_in_direction, p1], list.append(rest, [p2])), - grid, - direction, - ) - } - False -> found - } -} - -fn find_all_harmonic_collinears(points: List(Point), grid: Grid) -> List(Point) { - list.append( - find_harmonic_collinear_in_direction(points, grid, Forward), - find_harmonic_collinear_in_direction(points, grid, Reverse), - ) -} - -fn find_all_collinears(points: List(Point)) -> List(Point) { - list.wrap(find_collinear_in_direction(points, Forward)) - |> list.append(list.wrap(find_collinear_in_direction(points, Reverse))) -} - -fn find_antenna_locations(antenna: String, grid: Grid) -> List(Point) { - dict.keys(dict.filter(grid, fn(_, ant) { ant == antenna })) -} - -fn point_within_map(point: Point, g: Grid) -> Bool { - result.is_ok(list.find(dict.keys(g), fn(x) { x == point })) -} - -fn collinears_for_antenna(antenna: String, g: Grid) -> List(Point) { - find_antenna_locations(antenna, g) - |> list.combinations(2) - |> list.flat_map(find_all_collinears) - |> list.filter(point_within_map(_, g)) - |> list.unique -} - -fn harmonic_collinears_for_antenna(antenna: String, g: Grid) -> List(Point) { - find_antenna_locations(antenna, g) - |> list.combinations(2) - |> list.flat_map(find_all_harmonic_collinears(_, g)) - |> list.filter(point_within_map(_, g)) -} - -fn parse_input(input: String) -> Grid { - input - |> string.trim() - |> string.split("\n") - |> list.map(string.to_graphemes) - |> list.index_map(parse_column) - |> list.fold(dict.new(), dict.merge) -} - -pub fn solve_a(input: String) -> Int { - let grid = parse_input(input) - - list.filter(list.unique(dict.values(grid)), fn(x) { x != "." }) - |> list.map(collinears_for_antenna(_, grid)) - |> list.flatten - |> list.unique - |> list.length -} - -pub fn solve_b(input: String) -> Int { - let grid = parse_input(input) - - list.filter(list.unique(dict.values(grid)), fn(x) { x != "." }) - |> list.map(harmonic_collinears_for_antenna(_, grid)) - |> list.flatten - |> list.unique - |> list.length -} diff --git a/src/rock_paper_scissors.rb b/src/rock_paper_scissors.rb new file mode 100644 index 0000000..d9a0f47 --- /dev/null +++ b/src/rock_paper_scissors.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +# AoC 2022 Day 2 +class RockPaperScissors + attr_accessor :data + + SIGN_SCORES = %i[rock paper scissors].freeze + STRATEGIES = { + rock: :scissors, + scissors: :paper, + paper: :rock + }.freeze + + def initialize(data) + @data = data.lines.map(&:strip) + end + + def solve1 + data + .lazy + .map { |input| input.split(' ').map(&method(:to_sym)) } + .map { |input| score(*input) } + .sum + end + + def solve2 + data + .lazy + .map { |input| input.split(' ') } + .map { |input| syms(*input) } + .map { |input| score(*input) } + .sum + end + + private + + def syms(player0, outcome) + player0_sym = to_sym(player0) + case outcome + when 'X' + [player0_sym, STRATEGIES[player0_sym]] + when 'Y' + [player0_sym, player0_sym] + when 'Z' + [player0_sym, STRATEGIES.key(player0_sym)] + end + end + + def score(player0, player1) + return SIGN_SCORES.index(player1) + 1 if STRATEGIES[player0] == player1 + return SIGN_SCORES.index(player1) + 4 if player0 == player1 + + SIGN_SCORES.index(player1) + 7 if STRATEGIES.key(player0) == player1 + end + + def to_sym(raw) + case raw + when 'A', 'X' + :rock + when 'B', 'Y' + :paper + when 'C', 'Z' + :scissors + end + end +end diff --git a/src/rucksack_reorganization.rb b/src/rucksack_reorganization.rb new file mode 100644 index 0000000..7c9d137 --- /dev/null +++ b/src/rucksack_reorganization.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Day 3 2022 +class RucksackReorganization + UPPER = Set.new('A'..'Z') + + def initialize(data) + @data = data + .lines + .lazy + .map(&:strip) + .map(&:chars) + end + + def solve1 + @data + .map { _1.each_slice(_1.size / 2) } + .map { _1.reduce(&:&).first } + .map { _1.ord - (UPPER.member?(_1) ? 38 : 96) } + .sum + end + + def solve2 + @data + .each_slice(3) + .map { _1.reduce(&:&).first } + .map { _1.ord - (UPPER.member?(_1) ? 38 : 96) } + .sum + end +end diff --git a/src/supply_stacks.rb b/src/supply_stacks.rb new file mode 100644 index 0000000..1ffd42e --- /dev/null +++ b/src/supply_stacks.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +# Day 5 2022 +class SupplyStacks + def initialize(data) + @data = data.split("\n\n") + end + + def solve1 + parse_instructions(@data[1]) + .reduce(parse_stacks(@data[0]), &method(:process_instruction_9000)) + .map(&:last) + .join + end + + def solve2 + parse_instructions(@data[1]) + .reduce(parse_stacks(@data[0]), &method(:process_instruction_9001)) + .map(&:last) + .join + end + + private + + def process_instruction_9000(stack, instruction) + stack.tap do |stk| + stk[instruction[:to]] + .push(*stk[instruction[:from]].pop(instruction[:count]).reverse) + end + end + + def process_instruction_9001(stack, instruction) + stack.tap do |stk| + stk[instruction[:to]] + .push(*stk[instruction[:from]].pop(instruction[:count])) + end + end + + def parse_instructions(instructions) + instructions + .lines + .map { |line| line.match(/move\s(?\d+)\sfrom\s(?\d+)\sto\s(?\d+)/) } + .map { |line| { count: line[:count].to_i, from: line[:from].to_i.pred, to: line[:to].to_i.pred } } + end + + def parse_stacks(stacks) + stacks + .lines + .map { |line| line.chars.each_slice(4).map(&:join).map(&:strip) } + .slice(..-2) + .transpose + .map(&:reverse) + .map { |stack| stack.reject(&:empty?).map { _1.tr('[]', '') } } + end +end diff --git a/src/treetop_tree_house.rb b/src/treetop_tree_house.rb new file mode 100644 index 0000000..da399c2 --- /dev/null +++ b/src/treetop_tree_house.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +# Solution +class TreetopTreeHouse + attr_accessor :grid + + def initialize(input) + @grid = input.lines + .map(&:strip) + .map(&:chars) + end + + def visible?(coordx, coordy) + visible_from_right?(coordx, coordy) || + visible_from_left?(coordx, coordy) || + visible_from_top?(coordx, coordy) || + visible_from_bottom?(coordx, coordy) + end + + def visible_from_right?(coordx, coordy) + @grid[coordy][(coordx + 1)..] + .compact + .count { |ot| ot >= @grid[coordy][coordx] } + .then { |ct| ct < 1 } + end + + def visible_from_left?(coordx, coordy) + @grid[coordy][0...coordx] + .compact + .count { |ot| ot >= @grid[coordy][coordx] } + .then { |ct| ct < 1 } + end + + def visible_from_top?(coordx, coordy) + @grid[0...coordy] + .map { |grow| grow[coordx] } + .compact + .count { |ot| ot >= @grid[coordy][coordx] } + .then { |ct| ct < 1 } + end + + def visible_from_bottom?(coordx, coordy) + @grid[(coordy + 1)..] + .map { |grow| grow[coordx] } + .compact + .count { |ot| ot >= @grid[coordy][coordx] } + .then { |ct| ct < 1 } + end + + def scenic_score(coordx, coordy) + %i[top_scenic_score bottom_scenic_score left_scenic_score right_scenic_score] + .map { |mname| method(mname).call(coordx, coordy) } + .inject(&:*) + end + + def right_scenic_score(coordx, coordy) + trees_to_the_right = grid[coordy][(coordx + 1)..] + visible_trees = trees_to_the_right.take_while { |ctree| ctree < grid[coordy][coordx] } + trees_to_the_right.size.eql?(visible_trees.size) ? visible_trees.size : visible_trees.size + 1 + end + + def left_scenic_score(coordx, coordy) + trees_to_the_left = grid[coordy][...coordx].reverse + visible_trees = trees_to_the_left.take_while { |ctree| ctree < grid[coordy][coordx] } + trees_to_the_left.size.eql?(visible_trees.size) ? visible_trees.size : visible_trees.size + 1 + end + + def top_scenic_score(coordx, coordy) + trees_above = grid[...coordy].map { |cgrid| cgrid[coordx] }.reverse + visible_trees = trees_above.take_while { |ctree| ctree < grid[coordy][coordx] } + trees_above.size.eql?(visible_trees.size) ? visible_trees.size : visible_trees.size + 1 + end + + def bottom_scenic_score(coordx, coordy) + trees_below = grid[(coordy + 1)..].map { |cgrid| cgrid[coordx] } + visible_trees = trees_below.take_while { |ctree| ctree < grid[coordy][coordx] } + trees_below.size.eql?(visible_trees.size) ? visible_trees.size : visible_trees.size + 1 + end + + def solve2 + (0..grid.size.pred) + .map { |y| (0..grid.first.size.pred).map { |x| scenic_score(x, y) } } + .flatten + .max + end + + def solve1 + (0..grid.size.pred).sum do |y| + (0..grid.first.size.pred).count { |x| visible?(x, y) } + end + end +end diff --git a/src/tuning_trouble.rb b/src/tuning_trouble.rb new file mode 100644 index 0000000..039cc30 --- /dev/null +++ b/src/tuning_trouble.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Day 6 2022 +class TuningTrouble + def initialize(data) + @data = data + end + + def solve1 + @data + .chars + .each_cons(4) + .with_index + .take_while { _1 != _1.uniq } + .last[-1] + 5 + end + + def solve2 + @data + .chars + .each_cons(14) + .with_index + .take_while { _1 != _1.uniq } + .last[-1] + 15 + end +end diff --git a/src/utils/list.gleam b/src/utils/list.gleam deleted file mode 100644 index f755c3b..0000000 --- a/src/utils/list.gleam +++ /dev/null @@ -1,64 +0,0 @@ -import gleam/list as li -import gleam/pair.{first as pf, second as ps} -import gleam/result as res - -pub fn index(ls: List(a), item: a) -> Result(Int, Nil) { - li.index_map(ls, fn(x, index) { #(index, x) }) - |> li.find(fn(x) { ps(x) == item }) - |> res.map(pf) -} - -pub fn at(ls: List(a), i: Int) -> Result(a, Nil) { - li.index_map(ls, fn(x, ci) { #(x, ci) }) - |> li.find(fn(x) { ps(x) == i }) - |> res.map(pf) -} - -pub fn from_pair(p: #(a, a)) -> List(a) { - [pf(p), ps(p)] -} - -pub fn pop(ls: List(a)) -> #(Result(a, Nil), List(a)) { - case at(ls, li.length(ls) - 1) { - Ok(someval) -> { - #(Ok(someval), li.take(ls, li.length(ls) - 1)) - } - Error(_) -> #(Error(Nil), ls) - } -} - -pub fn index_filter(ls: List(a), f: fn(Int, a) -> Bool) -> List(a) { - li.index_map(ls, fn(item, index) { #(index, item) }) - |> li.filter(fn(x) { f(pf(x), ps(x)) }) - |> li.map(ps) -} - -pub fn contains(ls: List(a), item: a) -> Bool { - res.is_ok(li.find(ls, fn(x) { x == item })) -} - -pub fn swap(ls: List(a), ea: a, eb: a) -> List(a) { - use x <- li.map(ls) - case x { - val if val == ea -> eb - val if val == eb -> ea - val -> val - } -} - -pub fn max(ls: List(Int)) -> Int { - li.fold(ls, 0, fn(cmx, cx) { - case cx { - cxx if cxx > cmx -> cxx - _ -> cmx - } - }) -} - -pub fn repeated_permutation(ls: List(a), n: Int) { - use acc, _ <- li.fold(li.range(1, n), [[]]) - use y <- li.flat_map(acc) - use z <- li.map(ls) - - li.append(y, li.wrap(z)) -} diff --git a/test/advent_of_code_test.gleam b/test/advent_of_code_test.gleam deleted file mode 100644 index ecd12ad..0000000 --- a/test/advent_of_code_test.gleam +++ /dev/null @@ -1,5 +0,0 @@ -import gleeunit - -pub fn main() { - gleeunit.main() -} diff --git a/test/bridge_repair_test.gleam b/test/bridge_repair_test.gleam deleted file mode 100644 index 647462e..0000000 --- a/test/bridge_repair_test.gleam +++ /dev/null @@ -1,21 +0,0 @@ -import bridge_repair as day07 -import gleam/string -import gleeunit/should - -fn test_data() -> String { - string.join( - [ - "190: 10 19", "3267: 81 40 27", "83: 17 5", "156: 15 6", "7290: 6 8 6 15", - "161011: 16 10 13", "192: 17 8 14", "21037: 9 7 18 13", "292: 11 6 16 20", - ], - "\n", - ) -} - -pub fn solve_a_test() { - should.equal(day07.solve_a(test_data()), 3749) -} - -pub fn solve_b_test() { - should.equal(day07.solve_b(test_data()), 11_387) -} diff --git a/test/calorie_counting_test.rb b/test/calorie_counting_test.rb new file mode 100644 index 0000000..b73077d --- /dev/null +++ b/test/calorie_counting_test.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative '../src/calorie_counting' + +class TestCalorieCounting < Minitest::Test + def data + "1000\n2000\n3000\n\n4000\n\n5000\n6000\n\n7000\n8000\n9000\n\n10000\n" + end + + def test_solve1 + assert_equal 24_000, CalorieCounting.new(data).solve1 + end + + def test_solve2 + assert_equal 45_000, CalorieCounting.new(data).solve2 + end +end diff --git a/test/camp_cleanup_test.rb b/test/camp_cleanup_test.rb new file mode 100644 index 0000000..775a5ff --- /dev/null +++ b/test/camp_cleanup_test.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative '../src/camp_cleanup' + +class TestCampCleanup < Minitest::Test + def data + ['2-4,6-8', '2-3,4-5', '5-7,7-9', '2-8,3-7', '6-6,4-6', '2-6,4-8'].join("\n") + end + + def test_solve1 + assert_equal 2, CampCleanup.new(data).solve1 + end + + def test_solve2 + assert_equal 4, CampCleanup.new(data).solve2 + end +end diff --git a/test/ceres_search_test.gleam b/test/ceres_search_test.gleam deleted file mode 100644 index 79a3b42..0000000 --- a/test/ceres_search_test.gleam +++ /dev/null @@ -1,21 +0,0 @@ -import ceres_search as day04 -import gleam/string -import gleeunit/should - -fn test_data() -> String { - string.join( - [ - "MMMSXXMASM", "MSAMXMSMSA", "AMXSXMAAMM", "MSAMASMSMX", "XMASAMXAMM", - "XXAMMXXAMA", "SMSMSASXSS", "SAXAMASAAA", "MAMMMXMMMM", "MXMXAXMASX", - ], - "\n", - ) -} - -pub fn solve_a_test() { - should.equal(day04.solve_a(test_data()), 18) -} - -pub fn solve_b_test() { - should.equal(day04.solve_b(test_data()), 9) -} diff --git a/test/disk_fragmenter_test.gleam b/test/disk_fragmenter_test.gleam deleted file mode 100644 index 6f7e85b..0000000 --- a/test/disk_fragmenter_test.gleam +++ /dev/null @@ -1,10 +0,0 @@ -import disk_fragmenter as day09 -import gleeunit/should - -pub fn solve_a_test() { - should.equal(day09.solve_a("2333133121414131402"), 1928) -} - -pub fn solve_b_test() { - should.equal(day09.solve_b("2333133121414131402"), 0) -} diff --git a/test/guard_gallivant_test.gleam b/test/guard_gallivant_test.gleam deleted file mode 100644 index 930ae9b..0000000 --- a/test/guard_gallivant_test.gleam +++ /dev/null @@ -1,21 +0,0 @@ -import gleam/string -import gleeunit/should -import guard_gallivant as day05 - -fn test_data() { - string.join( - [ - "....#.....", ".........#", "..........", "..#.......", ".......#..", - "..........", ".#..^.....", "........#.", "#.........", "......#...", - ], - "\n", - ) -} - -pub fn solve_a_test() { - should.equal(day05.solve_a(test_data()), 41) -} - -pub fn solve_b_test() { - should.equal(day05.solve_b(test_data()), 6) -} diff --git a/test/historian_hysteria_test.gleam b/test/historian_hysteria_test.gleam deleted file mode 100644 index ce24da7..0000000 --- a/test/historian_hysteria_test.gleam +++ /dev/null @@ -1,14 +0,0 @@ -import gleeunit/should -import historian_hysteria as day01 - -fn test_data() -> String { - "3 4\n4 3\n2 5\n1 3\n3 9\n3 3" -} - -pub fn solve_a_test() { - should.equal(day01.solve_a(test_data()), 11) -} - -pub fn solve_b_test() { - should.equal(day01.solve_b(test_data()), 31) -} diff --git a/test/hoof_it_test.gleam b/test/hoof_it_test.gleam deleted file mode 100644 index af1258f..0000000 --- a/test/hoof_it_test.gleam +++ /dev/null @@ -1,21 +0,0 @@ -import gleam/string -import gleeunit/should -import hoof_it as day10 - -fn test_data() -> String { - string.join( - [ - "89010123", "78121874", "87430965", "96549874", "45678903", "32019012", - "01329801", "10456732", - ], - "\n", - ) -} - -pub fn solve_a_test() { - should.equal(day10.solve_a(test_data()), 36) -} - -pub fn solve_b_test() { - should.equal(day10.solve_b(test_data()), 0) -} diff --git a/test/mull_it_over_test.gleam b/test/mull_it_over_test.gleam deleted file mode 100644 index 1b61b91..0000000 --- a/test/mull_it_over_test.gleam +++ /dev/null @@ -1,14 +0,0 @@ -import gleeunit/should -import mull_it_over as day03 - -pub fn solve_a_test() { - "xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))" - |> day03.solve_a() - |> should.equal(161) -} - -pub fn solve_b_test() { - "xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))" - |> day03.solve_b() - |> should.equal(48) -} diff --git a/test/no_space_left_on_device_test.rb b/test/no_space_left_on_device_test.rb new file mode 100644 index 0000000..d6cab8d --- /dev/null +++ b/test/no_space_left_on_device_test.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative '../src/no_space_left_on_device' + +class TestCampCleanup < Minitest::Test + def data + [ + '$ cd /', + '$ ls', + 'dir a', + '14848514 b.txt', + '8504156 c.dat', + 'dir d', + '$ cd a', + '$ ls', + 'dir e', + '29116 f', + '2557 g', + '62596 h.lst', + '$ cd e', + '$ ls', + '584 i', + '$ cd ..', + '$ cd ..', + '$ cd d', + '$ ls', + '4060174 j', + '8033020 d.log', + '5626152 d.ext', + '7214296 k' + ].join("\n") + end + + def setup + @solver = NoSpaceLeftOnDevice.new(data) + @solution1 = @solver.solve1 + @solution2 = @solver.solve2 + end + + def test_solve1 + assert_equal 95_437, @solution1 + end + + def test_solve2 + assert_equal 24_933_642, @solution2 + end +end diff --git a/test/print_queue_test.gleam b/test/print_queue_test.gleam deleted file mode 100644 index c34ac33..0000000 --- a/test/print_queue_test.gleam +++ /dev/null @@ -1,38 +0,0 @@ -import gleam/string -import gleeunit/should -import print_queue as day05 - -fn test_data() -> String { - string.join( - [ - string.join( - [ - "47|53", "97|13", "97|61", "97|47", "75|29", "61|13", "75|53", "29|13", - "97|29", "53|29", "61|53", "97|53", "61|29", "47|13", "75|47", "97|75", - "47|61", "75|61", "47|29", "75|13", "53|13", - ], - "\n", - ), - string.join( - [ - "75,47,61,53,29", "97,61,53,29,13", "75,29,13", "75,97,47,61,53", - "61,13,29", "97,13,75,29,47", - ], - "\n", - ), - ], - "\n\n", - ) -} - -pub fn solve_a_test() { - test_data() - |> day05.solve_a() - |> should.equal(143) -} - -pub fn solve_b_test() { - test_data() - |> day05.solve_b() - |> should.equal(123) -} diff --git a/test/red_nosed_reports_test.gleam b/test/red_nosed_reports_test.gleam deleted file mode 100644 index 5bc090e..0000000 --- a/test/red_nosed_reports_test.gleam +++ /dev/null @@ -1,21 +0,0 @@ -import gleam/string -import gleeunit/should -import red_nosed_reports as day01 - -fn test_data() -> String { - string.join( - [ - "7 6 4 2 1", "1 2 7 8 9", "9 7 6 2 1", "1 3 2 4 5", "8 6 4 4 1", - "1 3 6 7 9", - ], - "\n", - ) -} - -pub fn solve_a_test() { - should.equal(day01.solve_a(test_data()), 2) -} - -pub fn solve_b_test() { - should.equal(day01.solve_b(test_data()), 4) -} diff --git a/test/resonant_collinearity_test.gleam b/test/resonant_collinearity_test.gleam deleted file mode 100644 index 57f2d9d..0000000 --- a/test/resonant_collinearity_test.gleam +++ /dev/null @@ -1,22 +0,0 @@ -import gleam/string -import gleeunit/should -import resonant_collinearity as day08 - -fn test_data() -> String { - string.join( - [ - "............", "........0...", ".....0......", ".......0....", - "....0.......", "......A.....", "............", "............", - "........A...", ".........A..", "............", "............", - ], - "\n", - ) -} - -pub fn solve_a_test() { - should.equal(day08.solve_a(test_data()), 14) -} - -pub fn solve_b_test() { - should.equal(day08.solve_b(test_data()), 34) -} diff --git a/test/rock_paper_scissors_test.rb b/test/rock_paper_scissors_test.rb new file mode 100644 index 0000000..ab5cd63 --- /dev/null +++ b/test/rock_paper_scissors_test.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative '../src/rock_paper_scissors' + +class TestRockPaperScissors < Minitest::Test + def data + "A Y\nB X\nC Z\n" + end + + def test_solve1 + assert_equal 15, RockPaperScissors.new(data).solve1 + end + + def test_solve2 + assert_equal 12, RockPaperScissors.new(data).solve2 + end +end diff --git a/test/rucksack_reorganization_test.rb b/test/rucksack_reorganization_test.rb new file mode 100644 index 0000000..2150052 --- /dev/null +++ b/test/rucksack_reorganization_test.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative '../src/rucksack_reorganization' + +# Day 3 2022 +class TestRucksackReorganization < Minitest::Test + def data + %w[vJrwpWtwJgWrhcsFMMfFFhFp jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL PmmdzqPrVvPwwTWBwg + wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn ttgJtRGJQctTZtZT CrZsJsPPZsGzwwsLwLmpwMDw].join("\n") + end + + def test_solve1 + assert_equal 157, RucksackReorganization.new(data).solve1 + end + + def test_solve2 + assert_equal 70, RucksackReorganization.new(data).solve2 + end +end diff --git a/test/supply_stacks_test.rb b/test/supply_stacks_test.rb new file mode 100644 index 0000000..719af26 --- /dev/null +++ b/test/supply_stacks_test.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative '../src/supply_stacks' + +# Day 3 2022 +class TestSupplyStacks < Minitest::Test + def data + [' [D] ', '[N] [C] ', '[Z] [M] [P]', ' 1 2 3 ', '', 'move 1 from 2 to 1', 'move 3 from 1 to 3', + 'move 2 from 2 to 1', 'move 1 from 1 to 2'].join("\n") + end + + def test_solve1 + assert_equal 'CMZ', SupplyStacks.new(data).solve1 + end + + def test_solve2 + assert_equal 'MCD', SupplyStacks.new(data).solve2 + end +end diff --git a/test/treetop_tree_house_test.rb b/test/treetop_tree_house_test.rb new file mode 100644 index 0000000..5dc5446 --- /dev/null +++ b/test/treetop_tree_house_test.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative '../src/treetop_tree_house' + +# Day 3 2022 +class TestSupplyStacks < Minitest::Test + def data + %w[30373 25512 65332 33549 35390].join("\n") + end + + def test_solve1 + assert_equal 21, TreetopTreeHouse.new(data).solve1 + end + + def test_solve2 + assert_equal 8, TreetopTreeHouse.new(data).solve2 + end +end diff --git a/test/tuning_trouble_test.rb b/test/tuning_trouble_test.rb new file mode 100644 index 0000000..5adc441 --- /dev/null +++ b/test/tuning_trouble_test.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative '../src/tuning_trouble' + +# Day 3 2022 +class TestTuningTrouble < Minitest::Test + def data + 'zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw' + end + + def test_solve1 + assert_equal 11, TuningTrouble.new(data).solve1 + end + + def test_solve2 + assert_equal 26, TuningTrouble.new(data).solve2 + end +end