This commit is contained in:
orion kindel
2025-12-05 12:34:12 -06:00
parent 2ede2f5095
commit 29c5861b11
5 changed files with 1363 additions and 14 deletions

View File

@@ -19,6 +19,7 @@ import Data.String as String
import Data.Tuple.Nested (type (/\), (/\))
import Parsing (ParseError(..), fail, region, runParser) as Parse
import Parsing (Parser)
import Parsing.BigInt as Parse
import Parsing.Combinators (sepBy) as Parse
import Parsing.String (regex, string) as Parse
import Partial.Unsafe (unsafeCrashWith, unsafePartial)
@@ -40,7 +41,7 @@ parser =
ctx :: forall a. String -> Parser String a -> Parser String a
ctx c = Parse.region \(Parse.ParseError m pos) -> Parse.ParseError (c <> " > " <> m) pos
idRange = (/\) <$> (ctx "first id" $ bigint <* Parse.string "-") <*> (ctx "second id" bigint)
idRange = (/\) <$> (ctx "first id" $ Parse.bigint <* Parse.string "-") <*> (ctx "second id" Parse.bigint)
in
List.toUnfoldable <$> Parse.sepBy (ctx "id range" idRange) (Parse.string ",")
@@ -71,19 +72,6 @@ isValid id =
in
len <= 1 || valid unit
bigint :: Parser String BigInt
bigint = do
section <- bigintRegex <|> Parse.fail "Expected Int"
case BigInt.fromString section of
Nothing -> Parse.fail "Expected Int"
Just x -> pure x
bigintRegex :: Parser String String
bigintRegex =
either unsafeCrashWith identity $ Parse.regex pattern mempty
where
pattern = "[+-]?[0-9]+"
-- might use a small amount of memory lol
bigintRange :: BigInt -> BigInt -> Array BigInt
bigintRange a b = ST.run do

1227
src/Day5.purs Normal file

File diff suppressed because it is too large Load Diff

106
src/Day5/Ingredient.purs Normal file
View File

@@ -0,0 +1,106 @@
module Day5.Ingredient where
import Prelude
import Control.Monad.Rec.Class (Step(..), tailRec)
import Data.Array as Array
import Data.BigInt (BigInt)
import Data.BigInt as BigInt
import Data.Either (Either)
import Data.Filterable (filter)
import Data.Foldable (any, sum)
import Data.FoldableWithIndex (foldlWithIndex)
import Data.Generic.Rep (class Generic)
import Data.List as List
import Data.Maybe (Maybe(..))
import Data.Show.Generic (genericShow)
import Data.Tuple.Nested (type (/\), (/\))
import Effect.Console as Console
import Effect.Unsafe (unsafePerformEffect)
import Parsing (ParseError, runParser) as Parse
import Parsing (Parser)
import Parsing.BigInt (bigint) as Parse
import Parsing.Combinators (manyTill, sepBy) as Parse
import Parsing.String (string) as Parse
type Id = BigInt
type IdRange = BigInt /\ BigInt
data Ingredients = Ingredients (Array IdRange) (Array Id)
derive instance Generic Ingredients _
instance Show Ingredients where show = genericShow
inSpec :: Ingredients -> Array Id
inSpec (Ingredients spec ids) =
let
within id (from /\ to) = id >= from && id <= to
inSpec' id = any (within id) spec
in
filter inSpec' ids
legalCount :: Ingredients -> BigInt
legalCount (Ingredients spec _) =
let
specNonOverlap =
let
hasOverlap spec' = flip any spec' <<< idRangesOverlap
withoutOverlap spec' =
let
f x spec'' _ =
case join $ Array.index spec'' x of
Just a ->
flip map spec'' \b ->
b >>= idRangesNonOverlapping a
Nothing ->
spec''
in
Array.nub $ Array.catMaybes $ foldlWithIndex f (Just <$> spec') spec'
go spec' =
if any (hasOverlap spec') spec' then
Loop $ withoutOverlap spec'
else
Done spec'
in
tailRec go spec
in
sum (idRangeSize <$> specNonOverlap)
parseIngredients :: String -> Either Parse.ParseError Ingredients
parseIngredients = flip Parse.runParser parser
parser :: Parser String Ingredients
parser =
let
id = Parse.bigint
idRange = (/\) <$> (id <* Parse.string "-") <*> id
ltoa = List.toUnfoldable
in
Ingredients
<$> (ltoa <$> Parse.manyTill (idRange <* Parse.string "\n") (Parse.string "\n"))
<*> (ltoa <$> Parse.sepBy id (Parse.string "\n"))
idRangeSize :: IdRange -> BigInt
idRangeSize (from /\ to) = one + to - from
idRangesNonOverlapping :: IdRange -> IdRange -> Maybe IdRange
idRangesNonOverlapping a@(aa /\ ab) b@(ba /\ bb)
| aa == ba && ab == bb = Just a
-- `a` fully contains `b`
| ba >= aa && bb <= ab = Nothing
-- `a` contains `b`s first endpoint
| ba >= aa && ba <= ab = Just ((ab + one) /\ bb)
-- `a` contains `b`s last endpoint
| bb >= ab && bb <= ab = Just (ba /\ (aa - one))
| otherwise = Just b
idRangesOverlap :: IdRange -> IdRange -> Boolean
idRangesOverlap (aa /\ ab) (ba /\ bb) = (aa /= ba || ab /= bb) && ((ba >= aa && ba <= ab) || (bb >= aa && bb <= ab))
tap :: forall a. String -> a -> a
tap m a = unsafePerformEffect do
Console.log m
pure a
idRangeString :: IdRange -> String
idRangeString (a /\ b) = "[" <> BigInt.toString a <> ", " <> BigInt.toString b <> "]"

View File

@@ -6,6 +6,7 @@ import Day1 as Day1
import Day2 as Day2
import Day3 as Day3
import Day4 as Day4
import Day5 as Day5
import Effect (Effect)
main :: Effect Unit
@@ -14,3 +15,4 @@ main = do
when false $ Day2.main
when false $ Day3.main
when false $ Day4.main
when true $ Day5.main

26
src/Parsing.BigInt.purs Normal file
View File

@@ -0,0 +1,26 @@
module Parsing.BigInt where
import Prelude
import Control.Alt ((<|>))
import Data.BigInt (BigInt)
import Data.BigInt as BigInt
import Data.Either (either)
import Data.Maybe (Maybe(..))
import Parsing (fail) as Parse
import Parsing (Parser)
import Parsing.String (regex) as Parse
import Partial.Unsafe (unsafeCrashWith)
bigintRegex :: Parser String String
bigintRegex =
either unsafeCrashWith identity $ Parse.regex pattern mempty
where
pattern = "[+-]?[0-9]+"
bigint :: Parser String BigInt
bigint = do
section <- bigintRegex <|> Parse.fail "Expected Int"
case BigInt.fromString section of
Nothing -> Parse.fail "Expected Int"
Just x -> pure x