Remove mutable, simplify code add unit tests.
authorGreg Burri <greg.burri@gmail.com>
Sun, 3 Dec 2017 21:24:56 +0000 (22:24 +0100)
committerGreg Burri <greg.burri@gmail.com>
Sun, 3 Dec 2017 21:24:56 +0000 (22:24 +0100)
AdventOfCode2017.sln
AdventOfCode2017/AdventOfCode2017.fsproj
AdventOfCode2017/Day3.fs
AdventOfCode2017/packages.config
Tests/App.config [new file with mode: 0644]
Tests/AssemblyInfo.fs [new file with mode: 0644]
Tests/Day1 tests.fs [new file with mode: 0644]
Tests/Day2 tests.fs [new file with mode: 0644]
Tests/Day3 tests.fs [new file with mode: 0644]
Tests/Tests.fsproj [new file with mode: 0644]
Tests/packages.config [new file with mode: 0644]

index 78a0214..b07e76f 100644 (file)
@@ -11,6 +11,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
                LICENSE = LICENSE
        EndProjectSection
 EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tests", "Tests\Tests.fsproj", "{238BFFBE-E2D4-4DC4-804C-6E43205E4701}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
                {D3555943-8102-43D1-B3CB-570A4E4EC513}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {D3555943-8102-43D1-B3CB-570A4E4EC513}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {D3555943-8102-43D1-B3CB-570A4E4EC513}.Release|Any CPU.Build.0 = Release|Any CPU
+               {238BFFBE-E2D4-4DC4-804C-6E43205E4701}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {238BFFBE-E2D4-4DC4-804C-6E43205E4701}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {238BFFBE-E2D4-4DC4-804C-6E43205E4701}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {238BFFBE-E2D4-4DC4-804C-6E43205E4701}.Release|Any CPU.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
index 31f6027..01ee4ac 100644 (file)
     <Compile Include="Day3.fs" />
     <Compile Include="Program.fs" />
     <None Include="App.config" />
-    <Content Include="packages.config" />
     <None Include="Data\day1">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
     <None Include="Data\day2">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
+    <Content Include="packages.config" />
   </ItemGroup>
   <ItemGroup>
     <Reference Include="mscorlib" />
@@ -80,7 +80,8 @@
     <Reference Include="System.Core" />
     <Reference Include="System.Numerics" />
     <Reference Include="System.ValueTuple">
-      <HintPath>..\packages\System.ValueTuple.4.3.1\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
+      <HintPath>..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll</HintPath>
+      <Private>True</Private>
     </Reference>
   </ItemGroup>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
index a48ad7c..c34aebc 100644 (file)
@@ -1,43 +1,31 @@
 module AdventOfCode2017.Day3
 
-type Direction = Right | Up | Left | Down
-
-let nextDirection = function Right -> Up | Up -> Left | Left -> Down | Down -> Right
-
-let move (pos : int * int) (d : Direction) =
-    let x, y = pos
-    match d with Right -> x + 1, y | Up -> x, y + 1 | Left -> x - 1, y | Down -> x, y - 1
-
-let rec next (pos : int * int) (l : int) (d : Direction) : (int * int) seq =
-    let directions = Seq.unfold (fun d -> Some (d, nextDirection d)) d |> Seq.take 3 |> List.ofSeq
-
-    let mutable pos' = pos
-
-    seq {
-        for d in directions |> List.take 2 do
-            for _j = 1 to l do
-                pos' <- move pos' d
-                yield pos'
-        yield! next pos' (l + 1) (List.last directions)
-    }
+let directions = [| 1, 0; 0, 1; -1, 0; 0, -1 |]
+
+let spiral =
+    Seq.unfold (
+        fun (pos, dir, n, i) ->
+            let x, y = directions.[dir]
+            let pos_ =  fst pos + x, snd pos + y
+            let nMax = (i + 1) * 2 - 1
+            let i_, n_ = if n = nMax then i + 1, 0 else i, n + 1
+            let dir_ = if i <> i_ || n_ = nMax / 2 + 1 then (dir + 1) % 4 else dir
+            Some (pos, (pos_, dir_, n_, i_))
+    ) ((0, 0), 0, 0, 0)
 
 let spiralManhattanDistanceSum (n : int) =
-    let x, y = next (0, 0) 1 Right |> Seq.item (n - 2)
+    let x, y = spiral |> Seq.item (n - 1)
     abs x + abs y
 
 let spiralAdjacentSumBiggerThan (n : int) =
     let neighborsSum (dic : Map<int * int, int>) (pos : int * int) =
         let x, y = pos
         [ x + 1, y; x + 1, y + 1; x, y + 1; x - 1, y + 1; x - 1, y; x - 1, y - 1; x, y - 1; x + 1, y - 1]
-        |> List.map (
-            fun (x, y) ->
-                match dic |> Map.tryFind (x, y) with
-                | Some v -> v
-                | None -> 0
-        )
+        |> List.map (fun (x, y) -> match dic |> Map.tryFind (x, y) with  Some v -> v | None -> 0)
         |> List.sum
 
-    next (0, 0) 1 Right
+    spiral
+    |> Seq.skip 1
     |> Seq.scan (
         fun (_sum, dic) pos ->
             let sum = neighborsSum dic pos
index 222429d..a90f359 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="System.ValueTuple" version="4.3.1" targetFramework="net462" />
+  <package id="System.ValueTuple" version="4.4.0" targetFramework="net462" />
 </packages>
\ No newline at end of file
diff --git a/Tests/App.config b/Tests/App.config
new file mode 100644 (file)
index 0000000..7b47f78
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+  <appSettings>
+    <add key="xunit.methodDisplay" value="method"/>
+  </appSettings>
+</configuration>
\ No newline at end of file
diff --git a/Tests/AssemblyInfo.fs b/Tests/AssemblyInfo.fs
new file mode 100644 (file)
index 0000000..243ecf4
--- /dev/null
@@ -0,0 +1,41 @@
+namespace Tests.AssemblyInfo
+
+open System.Reflection
+open System.Runtime.CompilerServices
+open System.Runtime.InteropServices
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[<assembly: AssemblyTitle("Tests")>]
+[<assembly: AssemblyDescription("")>]
+[<assembly: AssemblyConfiguration("")>]
+[<assembly: AssemblyCompany("")>]
+[<assembly: AssemblyProduct("Tests")>]
+[<assembly: AssemblyCopyright("Copyright ©  2017")>]
+[<assembly: AssemblyTrademark("")>]
+[<assembly: AssemblyCulture("")>]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[<assembly: ComVisible(false)>]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[<assembly: Guid("238bffbe-e2d4-4dc4-804c-6e43205e4701")>]
+
+// Version information for an assembly consists of the following four values:
+// 
+//       Major Version
+//       Minor Version 
+//       Build Number
+//       Revision
+// 
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [<assembly: AssemblyVersion("1.0.*")>]
+[<assembly: AssemblyVersion("1.0.0.0")>]
+[<assembly: AssemblyFileVersion("1.0.0.0")>]
+
+do
+    ()
\ No newline at end of file
diff --git a/Tests/Day1 tests.fs b/Tests/Day1 tests.fs
new file mode 100644 (file)
index 0000000..f367c21
--- /dev/null
@@ -0,0 +1,24 @@
+namespace AdventOfCode2017.Tests
+
+open Xunit
+open Xunit.Abstractions
+open Swensen.Unquote
+
+open AdventOfCode2017
+
+type ``Day1 tests`` (output : ITestOutputHelper) =
+
+    [<Fact>]
+    let ``(Part1) From web page`` () =
+        Day1.solveCaptcha1 (Day1.parseInput "1122") =! 3
+        Day1.solveCaptcha1 (Day1.parseInput "1111") =! 4
+        Day1.solveCaptcha1 (Day1.parseInput "1234") =! 0
+        Day1.solveCaptcha1 (Day1.parseInput "91212129") =! 9
+
+    [<Fact>]
+    let ``(Part2) From web page`` () =
+        Day1.solveCaptcha2 (Day1.parseInput "1212") =! 6
+        Day1.solveCaptcha2 (Day1.parseInput "1221") =! 0
+        Day1.solveCaptcha2 (Day1.parseInput "123425") =! 4
+        Day1.solveCaptcha2 (Day1.parseInput "123123") =! 12
+        Day1.solveCaptcha2 (Day1.parseInput "12131415") =! 4
diff --git a/Tests/Day2 tests.fs b/Tests/Day2 tests.fs
new file mode 100644 (file)
index 0000000..18bedfc
--- /dev/null
@@ -0,0 +1,25 @@
+namespace AdventOfCode2017.Tests
+
+open Xunit
+open Xunit.Abstractions
+open Swensen.Unquote
+
+open AdventOfCode2017
+
+type ``Day2 tests`` (output : ITestOutputHelper) =
+
+    [<Fact>]
+    let ``(Part1) From web page`` () =
+        let input =
+            "5 1 9 5
+             7 5 3
+             2 4 6 8"
+        Day2.checksum1 (Day2.parseInput input) =! 18
+
+    [<Fact>]
+    let ``(Part2) From web page`` () =
+        let input =
+            "5 9 2 8
+             9 4 7 3
+             3 8 6 5"
+        Day2.checksum2 (Day2.parseInput input) =! 9
\ No newline at end of file
diff --git a/Tests/Day3 tests.fs b/Tests/Day3 tests.fs
new file mode 100644 (file)
index 0000000..c0a5d46
--- /dev/null
@@ -0,0 +1,26 @@
+namespace AdventOfCode2017.Tests
+
+open Xunit
+open Xunit.Abstractions
+open Swensen.Unquote
+
+open AdventOfCode2017
+
+type ``Day3 tests`` (output : ITestOutputHelper) =
+
+    [<Fact>]
+    let ``(Part1) From web page`` () =
+        Day3.spiralManhattanDistanceSum 12 =! 3
+        Day3.spiralManhattanDistanceSum 23 =! 2
+        Day3.spiralManhattanDistanceSum 1024 =! 31
+
+    [<Fact>]
+    let ``(Part2) From web page`` () =
+        Day3.spiralAdjacentSumBiggerThan 1 =! 2
+        Day3.spiralAdjacentSumBiggerThan 2 =! 4
+        Day3.spiralAdjacentSumBiggerThan 3 =! 4
+        Day3.spiralAdjacentSumBiggerThan 4 =! 5
+        Day3.spiralAdjacentSumBiggerThan 5 =! 10
+        Day3.spiralAdjacentSumBiggerThan 20 =! 23
+        Day3.spiralAdjacentSumBiggerThan 100 =! 122
+        Day3.spiralAdjacentSumBiggerThan 500 =! 747
\ No newline at end of file
diff --git a/Tests/Tests.fsproj b/Tests/Tests.fsproj
new file mode 100644 (file)
index 0000000..a0ec95c
--- /dev/null
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props" Condition="Exists('..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props')" />
+  <Import Project="..\packages\xunit.core.2.3.1\build\xunit.core.props" Condition="Exists('..\packages\xunit.core.2.3.1\build\xunit.core.props')" />
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>238bffbe-e2d4-4dc4-804c-6e43205e4701</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>Tests</RootNamespace>
+    <AssemblyName>Tests</AssemblyName>
+    <TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
+    <TargetFSharpCoreVersion>4.4.1.0</TargetFSharpCoreVersion>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Name>Tests</Name>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <Tailcalls>false</Tailcalls>
+    <OutputPath>bin\$(Configuration)\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <WarningLevel>3</WarningLevel>
+    <DocumentationFile>bin\$(Configuration)\$(AssemblyName).XML</DocumentationFile>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <Tailcalls>true</Tailcalls>
+    <OutputPath>bin\$(Configuration)\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <WarningLevel>3</WarningLevel>
+    <DocumentationFile>bin\$(Configuration)\$(AssemblyName).XML</DocumentationFile>
+  </PropertyGroup>
+  <PropertyGroup>
+    <MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
+  </PropertyGroup>
+  <Choose>
+    <When Condition="'$(VisualStudioVersion)' == '11.0'">
+      <PropertyGroup Condition=" '$(FSharpTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets') ">
+        <FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
+      </PropertyGroup>
+    </When>
+    <Otherwise>
+      <PropertyGroup Condition=" '$(FSharpTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets') ">
+        <FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
+      </PropertyGroup>
+    </Otherwise>
+  </Choose>
+  <Import Project="$(FSharpTargetsPath)" />
+  <ItemGroup>
+    <Compile Include="AssemblyInfo.fs" />
+    <Compile Include="Day1 tests.fs" />
+    <Compile Include="Day2 tests.fs" />
+    <Compile Include="Day3 tests.fs" />
+    <Content Include="packages.config" />
+    <Content Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="FSharp.Core">
+      <HintPath>..\packages\FSharp.Core.4.2.3\lib\net45\FSharp.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="mscorlib" />
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Numerics" />
+    <Reference Include="System.ValueTuple">
+      <HintPath>..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <ProjectReference Include="..\AdventOfCode2017\AdventOfCode2017.fsproj">
+      <Name>AdventOfCode2017</Name>
+      <Project>{d3555943-8102-43d1-b3cb-570a4e4ec513}</Project>
+      <Private>True</Private>
+    </ProjectReference>
+    <Reference Include="Unquote">
+      <HintPath>..\packages\Unquote.4.0.0\lib\net45\Unquote.dll</HintPath>
+    </Reference>
+    <Reference Include="xunit.abstractions">
+      <HintPath>..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
+    </Reference>
+    <Reference Include="xunit.assert">
+      <HintPath>..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll</HintPath>
+    </Reference>
+    <Reference Include="xunit.core">
+      <HintPath>..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll</HintPath>
+    </Reference>
+    <Reference Include="xunit.execution.desktop">
+      <HintPath>..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\xunit.core.2.3.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.3.1\build\xunit.core.props'))" />
+    <Error Condition="!Exists('..\packages\xunit.core.2.3.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.3.1\build\xunit.core.targets'))" />
+    <Error Condition="!Exists('..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props'))" />
+  </Target>
+  <Import Project="..\packages\xunit.core.2.3.1\build\xunit.core.targets" Condition="Exists('..\packages\xunit.core.2.3.1\build\xunit.core.targets')" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/Tests/packages.config b/Tests/packages.config
new file mode 100644 (file)
index 0000000..41b642a
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="FSharp.Core" version="4.2.3" targetFramework="net462" />
+  <package id="System.ValueTuple" version="4.4.0" targetFramework="net462" />
+  <package id="Unquote" version="4.0.0" targetFramework="net462" />
+  <package id="xunit" version="2.3.1" targetFramework="net462" />
+  <package id="xunit.abstractions" version="2.0.1" targetFramework="net462" />
+  <package id="xunit.analyzers" version="0.8.0" targetFramework="net462" />
+  <package id="xunit.assert" version="2.3.1" targetFramework="net462" />
+  <package id="xunit.core" version="2.3.1" targetFramework="net462" />
+  <package id="xunit.extensibility.core" version="2.3.1" targetFramework="net462" />
+  <package id="xunit.extensibility.execution" version="2.3.1" targetFramework="net462" />
+  <package id="xunit.runner.visualstudio" version="2.3.1" targetFramework="net462" developmentDependency="true" />
+</packages>
\ No newline at end of file