diff --git a/.github/workflows/dotnet.yaml b/.github/workflows/dotnet.yaml index 9f02513..e735ffd 100644 --- a/.github/workflows/dotnet.yaml +++ b/.github/workflows/dotnet.yaml @@ -27,6 +27,8 @@ jobs: run: dotnet build --no-restore - name: Test run: dotnet test --no-build --verbosity normal + - name: Run app + run: dotnet run --project AdventOfCode2022.App/AdventOfCode2022.App.fsproj --no-build check-format: runs-on: ubuntu-latest diff --git a/AdventOfCode2022.App/AdventOfCode2022.App.fsproj b/AdventOfCode2022.App/AdventOfCode2022.App.fsproj new file mode 100644 index 0000000..7c30c85 --- /dev/null +++ b/AdventOfCode2022.App/AdventOfCode2022.App.fsproj @@ -0,0 +1,22 @@ + + + + Exe + net7.0 + + + + + + + + + + + + + + + + + diff --git a/AdventOfCode2022.App/Program.fs b/AdventOfCode2022.App/Program.fs new file mode 100644 index 0000000..40c3c94 --- /dev/null +++ b/AdventOfCode2022.App/Program.fs @@ -0,0 +1,63 @@ +namespace AdventOfCode2022.App + +open System.Diagnostics +open System.IO +open System.Reflection +open AdventOfCode2022 + +module Program = + type Dummy = + class + end + + let readResource (name : string) : string = + let asm = Assembly.GetAssembly typeof + + use stream = asm.GetManifestResourceStream (sprintf "AdventOfCode2022.App.%s" name) + + use reader = new StreamReader (stream) + reader.ReadToEnd () + + [] + let main _ = + let days = Array.init 6 (fun day -> readResource $"Day%i{day + 1}.txt") + + let inline day (i : int) = days.[i - 1] + + let time = Stopwatch.StartNew () + time.Restart () + + do + let lines = StringSplitEnumerator.make '\n' (day 1) + printfn "%i" (Day1.part1 lines) + printfn "%i" (Day1.part2 lines) + + do + let lines = StringSplitEnumerator.make '\n' (day 2) + printfn "%i" (Day2.part1 lines) + printfn "%i" (Day2.part2 lines) + + do + let day3 = day 3 + let lines = StringSplitEnumerator.make '\n' day3 + printfn "%i" (Day3Efficient.part1 lines) + printfn "%i" (Day3Efficient.part2 (day3.Split '\n')) + + do + let lines = StringSplitEnumerator.make '\n' (day 4) + printfn "%i" (Day4.part1 lines) + printfn "%i" (Day4.part2 lines) + + do + let lines = StringSplitEnumerator.make '\n' (day 5) + printfn "%s" (Day5.part1 lines) + printfn "%s" (Day5.part2 lines) + + do + let day6 = day 6 + printfn "%i" (Day6.part1 day6) + printfn "%i" (Day6.part2 day6) + + time.Stop () + printfn $"Took %i{time.ElapsedMilliseconds}ms" + 0 diff --git a/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj b/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj index 846c41e..30de338 100644 --- a/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj +++ b/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj @@ -13,11 +13,13 @@ + + diff --git a/AdventOfCode2022.Test/Day1.fs b/AdventOfCode2022.Test/Day1.fs index a26af65..ab961e1 100644 --- a/AdventOfCode2022.Test/Day1.fs +++ b/AdventOfCode2022.Test/Day1.fs @@ -1,6 +1,5 @@ namespace AdventOfCode2022.Test -open System open AdventOfCode2022 open NUnit.Framework open FsUnitTyped @@ -28,18 +27,18 @@ module TestDay1 = [] let ``Part 1`` () = let input = Assembly.readResource "Day1.txt" - input.Split '\n' |> Day1.part1 |> shouldEqual 66306 + Day1.part1 (StringSplitEnumerator.make '\n' input) |> shouldEqual 66306 [] let ``Part 2`` () = let input = Assembly.readResource "Day1.txt" - input.Split '\n' |> Day1.part2 |> shouldEqual 195292 + Day1.part2 (StringSplitEnumerator.make '\n' input) |> shouldEqual 195292 [] let ``Part 1, given example`` () = - testInput.Split Environment.NewLine |> Day1.part1 |> shouldEqual 24000 + Day1.part1 (StringSplitEnumerator.make '\n' testInput) |> shouldEqual 24000 [] let ``Part 2, given example`` () = - testInput.Split Environment.NewLine |> Day1.part2 |> shouldEqual 45000 + Day1.part2 (StringSplitEnumerator.make '\n' testInput) |> shouldEqual 45000 diff --git a/AdventOfCode2022.Test/Day2.fs b/AdventOfCode2022.Test/Day2.fs index 5ceb019..c4b76f8 100644 --- a/AdventOfCode2022.Test/Day2.fs +++ b/AdventOfCode2022.Test/Day2.fs @@ -15,19 +15,19 @@ C Z""" [] let ``Part 1, given`` () = - testInput.Split Environment.NewLine |> Day2.part1 |> shouldEqual 15 + Day2.part1 (StringSplitEnumerator.make '\n' testInput) |> shouldEqual 15 [] let ``Part 1`` () = let input = Assembly.readResource "Day2.txt" - input.Split '\n' |> Day2.part1 |> shouldEqual 9651 + Day2.part1 (StringSplitEnumerator.make '\n' input) |> shouldEqual 9651 [] let ``Part 2, given`` () = - testInput.Split Environment.NewLine |> Day2.part2 |> shouldEqual 12 + Day2.part2 (StringSplitEnumerator.make '\n' testInput) |> shouldEqual 12 [] let ``Part 2`` () = let input = Assembly.readResource "Day2.txt" - input.Split '\n' |> Day2.part2 |> shouldEqual 10560 + Day2.part2 (StringSplitEnumerator.make '\n' input) |> shouldEqual 10560 diff --git a/AdventOfCode2022.Test/Day3.fs b/AdventOfCode2022.Test/Day3.fs index ec9fee8..46bb333 100644 --- a/AdventOfCode2022.Test/Day3.fs +++ b/AdventOfCode2022.Test/Day3.fs @@ -9,8 +9,11 @@ open AdventOfCode2022 [] type TestDay3 (efficient : bool) = - let part1 (s : string seq) = - if efficient then Day3Efficient.part1 s else Day3.part1 s + let part1 (s : string) = + if efficient then + Day3Efficient.part1 (StringSplitEnumerator.make '\n' s) + else + s.Split '\n' |> Seq.filter (not << String.IsNullOrEmpty) |> Day3.part1 let part2 (s : string seq) = if efficient then Day3Efficient.part2 s else Day3.part2 s @@ -23,7 +26,6 @@ PmmdzqPrVvPwwTWBwg wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn ttgJtRGJQctTZtZT CrZsJsPPZsGzwwsLwLmpwMDw""" - |> fun s -> s.Split System.Environment.NewLine |> part1 |> shouldEqual 157 @@ -31,10 +33,7 @@ CrZsJsPPZsGzwwsLwLmpwMDw""" member _.``Part 1`` () = let input = Assembly.readResource "Day3.txt" - input.Split '\n' - |> Seq.filter (not << String.IsNullOrWhiteSpace) - |> part1 - |> shouldEqual 8018 + input |> part1 |> shouldEqual 8018 [] diff --git a/AdventOfCode2022.Test/Day4.fs b/AdventOfCode2022.Test/Day4.fs index 7cb1ee3..890e64b 100644 --- a/AdventOfCode2022.Test/Day4.fs +++ b/AdventOfCode2022.Test/Day4.fs @@ -1,6 +1,5 @@ namespace AdventOfCode2022.Test -open System open NUnit.Framework open FsUnitTyped open AdventOfCode2022 @@ -18,32 +17,20 @@ module TestDay4 = [] let ``Part 1, given`` () = - testInput - |> fun s -> s.Split Environment.NewLine - |> Day4.part1 - |> shouldEqual 2 + Day4.part1 (StringSplitEnumerator.make '\n' testInput) |> shouldEqual 2 [] let ``Part 1`` () = let input = Assembly.readResource "Day4.txt" - input.Split '\n' - |> Seq.filter (not << String.IsNullOrWhiteSpace) - |> Day4.part1 - |> shouldEqual 433 + Day4.part1 (StringSplitEnumerator.make '\n' input) |> shouldEqual 433 [] let ``Part 2, given`` () = - testInput - |> fun s -> s.Split Environment.NewLine - |> Day4.part2 - |> shouldEqual 4 + Day4.part2 (StringSplitEnumerator.make '\n' testInput) |> shouldEqual 4 [] let ``Part 2`` () = let input = Assembly.readResource "Day4.txt" - input.Split '\n' - |> Seq.filter (not << String.IsNullOrWhiteSpace) - |> Day4.part2 - |> shouldEqual 852 + Day4.part2 (StringSplitEnumerator.make '\n' input) |> shouldEqual 852 diff --git a/AdventOfCode2022.Test/Day6.fs b/AdventOfCode2022.Test/Day6.fs new file mode 100644 index 0000000..cbc3166 --- /dev/null +++ b/AdventOfCode2022.Test/Day6.fs @@ -0,0 +1,45 @@ +namespace AdventOfCode2022.Test + +open System +open NUnit.Framework +open FsUnitTyped +open AdventOfCode2022 + +[] +module TestDay6 = + + let testInput1 = + [ + "mjqjpqmgbljsphdztnvjfqwrcgsmlb", 7 + "bvwbjplbgvbhsrlpgdmjqwftvncz", 5 + "nppdvjthqldpwncqszvftbrmjlhg", 6 + "nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg", 10 + "zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw", 11 + ] + |> List.map TestCaseData + + [] + let ``Part 1, given`` (input : string, output : int) = Day6.part1 input |> shouldEqual output + + [] + let ``Part 1`` () = + let input = Assembly.readResource "Day6.txt" + + Day6.part1 (input.TrimEnd ()) |> shouldEqual 1544 + + let part2Data = + [ + "bvwbjplbgvbhsrlpgdmjqwftvncz", 23 + "nppdvjthqldpwncqszvftbrmjlhg", 23 + "nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg", 29 + "zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw", 26 + ] + |> List.map TestCaseData + + [] + let ``Part 2, given`` (input : string, output : int) = Day6.part2 input |> shouldEqual output + + [] + let ``Part 2`` () = + let input = Assembly.readResource "Day6.txt" + Day6.part2 input |> shouldEqual 2145 diff --git a/AdventOfCode2022.Test/Inputs/Day6.txt b/AdventOfCode2022.Test/Inputs/Day6.txt new file mode 100644 index 0000000..38d36ba --- /dev/null +++ b/AdventOfCode2022.Test/Inputs/Day6.txt @@ -0,0 +1 @@ +mgwwjddqzqdqsstctjjsdjsdsrsfsmfsfwwltwlwhwnhhlffzddgffwlffbsfshfshhgvvdrrltlzlnzznrrnrsnnhgnnfjnnvpnnbjjnwwrcwrrhlhvlhhmzmqzqrqtqmqpmpwwmssgsrgrgtgmtgmtgtdtvdvmvsvsbvsbvbtthmmftmmdnmddcrcvcrrfjfhhfjhffjllcpllmcctjtrttwmtwmwffrlrqlqzzpddsqdqqgjqgjgngwnncjnnvsnswwbzbtzzflzzqsqbsbvbmbnnjpnpnnpfpmpmnpmmjljtltssqnsqslstswtwswwjddvmmzlzqlzqzqjjlttmtrtbtmtgmtmsttrctrrsqrqvvrzrcrhhlnhllbfbtthrhdhllmwlmlgglgsgmgsmszzprpwpfprfftffpssjzjgzjzddqfqmmwqwvwlvlqqtbtwwrwttmsmppbmmpcmctcnnhssnjncnlcnctcjjrzrwrfwfcwffczztrtsrtstlsssljssmvssjzssrqqrcqqwlqwlwffsflssrrzhzzhrzzdgdppspwplpqptttvddggzszccrrnzzwwdwjddrvvwggpvgpvvhdhqddffrnngcncjcjlcchrrftrrjccrcrqqgcglcgcscmmlzmmtcmcffwfcfrcrggdmggdvvnrvnnphnngzzpdpgpspqqgrrnffmfpmffmgfmmjmzztlljlggljjcnnrqnqpnqndnffnwwbpwpjwjjlslmsmtmtjttsvsggrmmdpmmcjjswsqqwfwwrwffczfzggqvggdlldhllsdsfdsdhhmmzmjjmpjpddsccqrrjhjlhjjcnnpwnnffjwwcsszrrnmnsmnnjbnndwnnnhnwwjtwtlwtwqqbnqnbnqqfjfdjdbbwbqwqpqggbcbhhtrtqrrddpdwdlwdddzvzwvvdfdpdcdvdtdpttwwdzdzmdmqmzmnzmnmhmwmjwwshhcqcpcvvzgzdggnjnnhwnhhswwvccqrqlqggcngnmggmffblbglltlstshhrjjlvlppsqslljtjtgglvltvlvmllhrhdrrmqrmqmjjdcjjppqwwllvsvsrszslsvvghvvhmmfbfvfpfmmvdvppwggtrrjvvsbbzffbmffpqqqhnhncclzczwcwpwssrprfrsrbsbnbvnnwzwqqpsqsspmssztssstrtcczsznzvvpvttnssdjdhjddngdgvvmsszbzsbsmbbgsbgsgmmhwhghrhjhphshchgglmlvlhlbhlldwdggdsscvcbcssfbbvggvwwtstltrrwttjdtdvttlsttfhfmhhcbclbcbffqqslshlldhdqhhjwwlffrbrdbrrgcrrfmffbhhlslrslrslrlsrsnsnvvqfqnnfdfmfmttmcmcrrcmmmjttjvtvvjbjqjnnbtbnblbtlblplgltlqltlztzvvtdvvtpvvwdwfflbflfrrhbrbbmjmcjmccztzwwjzwwzwdzdnnwcclbllqgghjhlhthwrdglrmcpbmtrnrdtvjrpmzqmljzzrtpzsrhnjrsdmpnsgdhvqchcfqjqdncjqfnscwjqvszpzzfhpjljmvsqnjzmrsgsbzlvrddtdmwbwwgprlvdfflrpztdzrhtmlzrrtdmpmcprqzzwlnmfjvsrltfjgcnnfllnzmbjcbthvbffczsspmczrpgpdjmvrvfmprfmnqdcnfwwvgdrwvrbtlqmhrrjvtrmmgrlprtnzdlszgbtbwztdrmpmlfblshzcnsczlblgwzrpnlccwhmcqhssmpznbdnnqgzzmjprjttdjhmjbmgqvzblsjwmplzsthrswhsdbvtqgrfzmbpqtpqgqdqcvzlgjrtvrhvzgmcmrwdmfpdvjddsmmsnvrdgnsbsdzcbprbqchqcgnwmfsrmqtrcdhdtzztbvmpblftwqlmlmmjcjhhjlgnnhljnncvbnjhgbjrltlwscswgvqmcnssbcdrtbgnhgmpmvjwtrbrbrdbdqfrncvhdstwztwcpbjrjwzmdlwvlvmsrhghjwjnjstbcqjqtjrgcvhzjdhdgbgdlhvjmztwvhgzzggwwhhhzvtrldchztmwfjvnqnvhnwpfvzzvnlvsccmvsngzgtnttssmdmhwzlhtpnfhczsdfnrstbwvwpqmslcvpvhfzttzhsgzpbhqdtswshljpncznjhzmgvvbcllmzprhrvwljwcjpcdqmwbzvsdcgtmwnrhswsgqhwpwhbjpnhnpjvgsqcjltzrqvqfflcdcvpwnznvtqbfbtlpmtdgbbwdwncqsqnbtgfdzzqzzvjnwmzdmlgstmnjwznjqghglvmwjzlqrnddcqhgndlhlbmqdhrqgrjqztnhpzssnwmrqclmwpgbvfrvgqqvtthznsqwgndjrprbgrhcvhpzbfhdmgnhsrqjvjstbtmnltsbjfzczvjqnhtldqclsflbhvvlzjwrqqgbgpwqwpfjctqpzdqwcfstmwbzgrgrtzngljjnvtggrqcbgjwtqsdgwmfjqppnzgfsfdmlctztbhnntnntdlvrsdvnllvmpggjzspqfhzwrttwzpqrnqjhmpjnmrzrpnqzshcqgctbtflqflcrzpmnphgbbghhwzplljwngbtffwmrwggdztvtfgwldlswqvjptvbfvnbpglhgrdgcfmvrslqldmwjqvjpvwgpjddvglllvpqwvbchqsmjrncgvgmqbsbcwfbsbpqcqzjfpcdzszgmvqgqjlflpfzbsrhsrzrdbpssrjbcfhvztftlzqpsglpwhbscgwdlbgghzsbwznnbgnnsgjghmmpmmrmqmdhnflgvgprqfcbpzbcpjscvnpfrmtvzsbflmffvcfsvdsggzdqtppcjzphcqwrqtrczqmwcdmdqndzmhdpnfqsbndnvjlzrsjzmpcrfgjwccsdtzvslccwhlvzjwjgvwpsnsggmqgsjfbwmjstsgnqmtjhljvfnflnngdrqvscwlqqdsglhghczhjdvgrjcqblmncdbjvsbwgptgpvvzhcjgjnvttrgzrjnqlvfbrmpzdcbbnnrqptpzpssznbsrstdphbgdrsnrhcjwwgsncdzvqfnmnvqcmcgdgjdbqjzdrvvbvhjdfcqndmqwscmsvppclzrhgbldqtwctbdhpbbwfvwpcpsvddmrhqbhlrrmrblnmqqqbwvcwwbwprlmhtdncmjhmjgphmrrhcdrqgmcrzwsznqzpngbtsvjgglrddhjflbrhvqwmmhmqzhphwnvqwzczdvqjsnlhfqbcgddtwgnlcgbfqmzfpqmnbpvfhdhjlnwtrlmggtbfnfvmqrzjvjjvffctsrwgfcpghhnzqmwtlsfhjrvqpwqhngrhpswslsvtgnbvbmwsfwmpntfsfpshrjzvghhpvnlbmnrhltfpmqdwzfhztvhlmbnmhnbvdzbbtczvwbvwtvjghhjjrtgbrqrhmbgvssstdwztdmdsqtctghjhsnpslqttdlvndmjfnmdzwrblfjqcwptfttvlcgsvwcbmfzbdlmrtchgqlfspwznbzfjthjtfwshqgfsfdsmzsmpptzschlzjshvfwtmpszvrvlggbrgpcnqwndhjjprztdfddblhfljbvttfvhchhdfsftrhccrbncmhwpcpwfqthngcqptmvsmpcswdrdlcbqvvhwmcqqwbzlblrgfcrrndwdvlvnpjvwchzjzmgrqhzzmgqqdsdflpclpdtlhvhcthzjfbvjvzsnbvwfsnglvbnwnbgrqwpbgclhjhztttbjwvmlmmgmzncbwswncqhmcfjfnwnpbrmchhpgwngrfwgdfdqmblwlghdjvdhjftdblrtcvvgbvpmbjhfwgpmghqbqrcpgfvhtvqtlbjdblggcpjzlrhpbsqwntfhbhwwszpdlsgbpfqhvrjrhsldcgvqhqmwdfcrcmhrvvwvbrfsrrcvwzhqqvgltlnhwhdrhrdqsvmdzjwgmqdsccwhcgwltfhdfqpsltjccwsttmrc diff --git a/AdventOfCode2022.sln b/AdventOfCode2022.sln index 5723522..0439762 100644 --- a/AdventOfCode2022.sln +++ b/AdventOfCode2022.sln @@ -4,6 +4,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "AdventOfCode2022", "AdventO EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "AdventOfCode2022.Test", "AdventOfCode2022.Test\AdventOfCode2022.Test.fsproj", "{F22BC060-E7D0-44EE-95F1-0CEFFD8D46E1}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "AdventOfCode2022.App", "AdventOfCode2022.App\AdventOfCode2022.App.fsproj", "{D6E55366-EEEC-4016-9E0B-62549FE04ADC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +20,9 @@ Global {F22BC060-E7D0-44EE-95F1-0CEFFD8D46E1}.Debug|Any CPU.Build.0 = Debug|Any CPU {F22BC060-E7D0-44EE-95F1-0CEFFD8D46E1}.Release|Any CPU.ActiveCfg = Release|Any CPU {F22BC060-E7D0-44EE-95F1-0CEFFD8D46E1}.Release|Any CPU.Build.0 = Release|Any CPU + {D6E55366-EEEC-4016-9E0B-62549FE04ADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6E55366-EEEC-4016-9E0B-62549FE04ADC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6E55366-EEEC-4016-9E0B-62549FE04ADC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6E55366-EEEC-4016-9E0B-62549FE04ADC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/AdventOfCode2022/AdventOfCode2022.fsproj b/AdventOfCode2022/AdventOfCode2022.fsproj index 4626d63..49ff19c 100644 --- a/AdventOfCode2022/AdventOfCode2022.fsproj +++ b/AdventOfCode2022/AdventOfCode2022.fsproj @@ -14,6 +14,7 @@ + diff --git a/AdventOfCode2022/Day1.fs b/AdventOfCode2022/Day1.fs index b860d93..2036470 100644 --- a/AdventOfCode2022/Day1.fs +++ b/AdventOfCode2022/Day1.fs @@ -1,34 +1,44 @@ namespace AdventOfCode2022 open System +open System.Collections.Generic [] module Day1 = - let counts (lines : string seq) : int list = - ((0, []), lines) - ||> Seq.fold (fun (acc, counts) line -> - if String.IsNullOrWhiteSpace line then - 0, (acc :: counts) + let counts (lines : byref) : int IReadOnlyList = + let counts = ResizeArray () + let mutable acc = 0 + + for line in lines do + if line.IsWhiteSpace () then + counts.Add acc + acc <- 0 else - acc + int line, counts - ) - |> snd + acc <- acc + Int32.Parse line + + if acc <> 0 then + counts.Add acc + + counts /// Expects a trailing newline (as is present in the given input data). - let part1 (lines : string seq) : int = lines |> counts |> List.max + let part1 (lines : StringSplitEnumerator) : int = + let mutable lines = lines + counts &lines |> Seq.max /// I wanted to save cloning the entire seq, so here is some bonus efficiency. - let maxThree (inputs : int list) : struct (int * int * int) = + let maxThree (inputs : int IReadOnlyList) : struct (int * int * int) = ((struct (Int32.MinValue, Int32.MinValue, Int32.MinValue)), inputs) - ||> List.fold (fun (struct (max1, max2, max3) as maxes) input -> + ||> Seq.fold (fun (struct (max1, max2, max3) as maxes) input -> if input <= max3 then maxes elif input <= max2 then struct (max1, max2, input) elif input <= max1 then struct (max1, input, max2) else struct (input, max1, max2) ) - let part2 (lines : string seq) : int = - let struct (a, b, c) = lines |> counts |> maxThree + let part2 (lines : StringSplitEnumerator) : int = + let mutable lines = lines + let struct (a, b, c) = counts &lines |> maxThree a + b + c diff --git a/AdventOfCode2022/Day2.fs b/AdventOfCode2022/Day2.fs index 3bdcda0..6efe790 100644 --- a/AdventOfCode2022/Day2.fs +++ b/AdventOfCode2022/Day2.fs @@ -43,46 +43,54 @@ module RockPaperScissors = [] module Day2 = - let part1 (lines : string seq) : int = - lines - |> Seq.filter (not << String.IsNullOrWhiteSpace) - |> Seq.map (fun s -> - let s = s.Trim () + let part1 (lines : StringSplitEnumerator) : int = + let mutable sum = 0 - if s.Length <> 3 || s.[1] <> ' ' then - failwithf $"Bad format of string: %s{s}" + for line in lines do + if not (line.IsWhiteSpace ()) then + let line = line.Trim () - RockPaperScissors.parse s.[0], RockPaperScissors.parse s.[2] - ) - |> Seq.map (fun (opponent, self) -> RockPaperScissors.outcome self opponent + RockPaperScissors.shapeScore self) - |> Seq.sum + if line.Length <> 3 || line.[1] <> ' ' then + failwithf $"Bad format of string: %s{line.ToString ()}" + let opponent = RockPaperScissors.parse line.[0] + let self = RockPaperScissors.parse line.[2] - let part2 (lines : string seq) : int = - lines - |> Seq.filter (not << String.IsNullOrWhiteSpace) - |> Seq.map (fun s -> - let s = s.Trim () + sum <- + sum + + RockPaperScissors.outcome self opponent + + RockPaperScissors.shapeScore self - if s.Length <> 3 || s.[1] <> ' ' then - failwithf $"Bad format of string: %s{s}" + sum - let move = RockPaperScissors.parse s.[0] + let part2 (lines : StringSplitEnumerator) : int = + let mutable sum = 0 - let myMove = - match Char.ToLower s.[2] with - | 'x' -> - // Need to lose - RockPaperScissors.wouldBeBeaten move - | 'y' -> - // Need to draw - move - | 'z' -> - // Need to win - RockPaperScissors.wouldBeat move - | c -> failwithf $"Unexpected strategy: %c{c}" + for line in lines do + if not (line.IsWhiteSpace ()) then + let line = line.Trim () - move, myMove - ) - |> Seq.map (fun (opponent, self) -> RockPaperScissors.outcome self opponent + RockPaperScissors.shapeScore self) - |> Seq.sum + if line.Length <> 3 || line.[1] <> ' ' then + failwithf $"Bad format of string: %s{line.ToString ()}" + + let opponent = RockPaperScissors.parse line.[0] + + let self = + match Char.ToLower line.[2] with + | 'x' -> + // Need to lose + RockPaperScissors.wouldBeBeaten opponent + | 'y' -> + // Need to draw + opponent + | 'z' -> + // Need to win + RockPaperScissors.wouldBeat opponent + | c -> failwithf $"Unexpected strategy: %c{c}" + + sum <- + sum + + RockPaperScissors.outcome self opponent + + RockPaperScissors.shapeScore self + + sum diff --git a/AdventOfCode2022/Day3.fs b/AdventOfCode2022/Day3.fs index 1b08f16..ae062c5 100644 --- a/AdventOfCode2022/Day3.fs +++ b/AdventOfCode2022/Day3.fs @@ -1,5 +1,7 @@ namespace AdventOfCode2022 +open System + [] type Priority diff --git a/AdventOfCode2022/Day3Efficient.fs b/AdventOfCode2022/Day3Efficient.fs index 8d127e9..13a9db8 100644 --- a/AdventOfCode2022/Day3Efficient.fs +++ b/AdventOfCode2022/Day3Efficient.fs @@ -68,18 +68,20 @@ module Day3Efficient = else go set s (i + 1) - let part1 (lines : string seq) : int = - lines - |> Seq.map (fun s -> - let s = s.AsSpan().Trim () + let part1 (lines : StringSplitEnumerator) : int = + let mutable sum = 0 + + for s in lines do + let s = s.Trim () let set = CharSet.ofSpan (s.Slice (0, s.Length / 2)) - go set (s.Slice (s.Length / 2)) 0 - ) - |> Seq.sum + sum <- sum + go set (s.Slice (s.Length / 2)) 0 + + sum let part2 (lines : string seq) : int = lines + |> Seq.filter (not << String.IsNullOrEmpty) |> Seq.chunkBySize 3 |> Seq.map (fun strArr -> strArr diff --git a/AdventOfCode2022/Day4.fs b/AdventOfCode2022/Day4.fs index 501921b..03991ef 100644 --- a/AdventOfCode2022/Day4.fs +++ b/AdventOfCode2022/Day4.fs @@ -22,15 +22,24 @@ module Day4 = let secondElf2 = secondElf.Slice (secondDashIndex + 1) (Int32.Parse firstElf1, Int32.Parse firstElf2), (Int32.Parse secondElf1, Int32.Parse secondElf2) - let part1 (lines : string seq) : int = - lines - |> Seq.map (fun s -> parse (s.AsSpan ())) - |> Seq.filter (fun (firstElf, secondElf) -> fullyContains firstElf secondElf || fullyContains secondElf firstElf - ) - |> Seq.length + let part1 (lines : StringSplitEnumerator) : int = + let mutable count = 0 - let part2 (lines : string seq) : int = - lines - |> Seq.map (fun s -> parse (s.AsSpan ())) - |> Seq.filter (fun (firstElf, secondElf) -> overlaps firstElf secondElf) - |> Seq.length + for line in lines do + let firstElf, secondElf = parse line + + if fullyContains firstElf secondElf || fullyContains secondElf firstElf then + count <- count + 1 + + count + + let part2 (lines : StringSplitEnumerator) : int = + let mutable count = 0 + + for line in lines do + let firstElf, secondElf = parse line + + if overlaps firstElf secondElf then + count <- count + 1 + + count diff --git a/AdventOfCode2022/Day6.fs b/AdventOfCode2022/Day6.fs new file mode 100644 index 0000000..6168753 --- /dev/null +++ b/AdventOfCode2022/Day6.fs @@ -0,0 +1,31 @@ +namespace AdventOfCode2022 + +open System + +[] +module Day6 = + + let findDuplicateSorted (a : 'a array) : int ValueOption = + let mutable i = 0 + let mutable result = ValueNone + + while result.IsNone && i + 1 < a.Length do + if a.[i] = a.[i + 1] then + result <- ValueSome i + + i <- i + 1 + + result + + let rec private go (count : int) (s : ReadOnlySpan) (i : int) = + let fourChars = s.Slice (i - count + 1, count) + let arr = fourChars.ToArray () + Array.sortInPlace arr + + match findDuplicateSorted arr with + | ValueSome _ -> go count s (i + 1) + | ValueNone -> i + 1 + + let part1 (line : string) : int = go 4 (line.AsSpan ()) 3 + + let part2 (line : string) : int = go 14 (line.AsSpan ()) 13