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