├── .gitignore ├── .nuget ├── NuGet.Config ├── NuGet.exe └── NuGet.targets ├── .vscode ├── launch.json └── tasks.json ├── Chess.ConsoleUI ├── Chess.ConsoleUI.csproj └── Program.cs ├── Chess.Domain ├── Api.fs ├── Chess.Domain.fsproj ├── Chess.fs └── Validation.fs ├── Chess.FabUI.Android ├── Assets │ └── AboutAssets.txt ├── Chess.FabUI.Android.fsproj ├── MainActivity.fs ├── Properties │ ├── AndroidManifest.xml │ └── AssemblyInfo.fs ├── Resources │ ├── AboutResources.txt │ ├── Resource.designer.cs │ ├── layout │ │ ├── Tabbar.axml │ │ └── Toolbar.axml │ ├── mipmap-anydpi-v26 │ │ ├── icon.xml │ │ └── icon_round.xml │ ├── mipmap-hdpi │ │ ├── Icon.png │ │ └── launcher_foreground.png │ ├── mipmap-mdpi │ │ ├── icon.png │ │ └── launcher_foreground.png │ ├── mipmap-xhdpi │ │ ├── Icon.png │ │ └── launcher_foreground.png │ ├── mipmap-xxhdpi │ │ ├── Icon.png │ │ └── launcher_foreground.png │ ├── mipmap-xxxhdpi │ │ ├── Icon.png │ │ └── launcher_foreground.png │ └── values │ │ ├── colors.xml │ │ └── styles.xml └── packages.config ├── Chess.FabUI.WPF ├── App.fs ├── Chess.FabUI.WPF.fsproj ├── Images │ ├── pieces_black │ │ ├── bishop.png │ │ ├── king.png │ │ ├── knight.png │ │ ├── pawn.png │ │ ├── queen.png │ │ └── rook.png │ └── pieces_white │ │ ├── bishop.png │ │ ├── king.png │ │ ├── knight.png │ │ ├── pawn.png │ │ ├── queen.png │ │ └── rook.png ├── Properties │ └── AssemblyInfo.fs └── app.config ├── Chess.FabUI.iOS ├── AppDelegate.fs ├── Assets.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon1024.png │ │ ├── Icon120.png │ │ ├── Icon152.png │ │ ├── Icon167.png │ │ ├── Icon180.png │ │ ├── Icon20.png │ │ ├── Icon29.png │ │ ├── Icon40.png │ │ ├── Icon58.png │ │ ├── Icon60.png │ │ ├── Icon76.png │ │ ├── Icon80.png │ │ └── Icon87.png ├── Chess.FabUI.iOS.fsproj ├── Entitlements.plist ├── Info.plist ├── Properties │ └── AssemblyInfo.fs ├── Resources │ ├── Default-568h@2x.png │ ├── Default-Portrait.png │ ├── Default-Portrait@2x.png │ ├── Default.png │ ├── Default@2x.png │ └── LaunchScreen.storyboard └── packages.config ├── Chess.FabUI ├── Chess.FabUI.fs └── Chess.FabUI.fsproj ├── Chess.WebUI ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .paket │ ├── Paket.Restore.targets │ ├── paket.exe │ ├── paket.exe.config │ └── paket.targets ├── .vscode │ └── tasks.json ├── Chess.WebUI.sln ├── NuGet.Config ├── README.md ├── RELEASE_NOTES.md ├── package-lock.json ├── package.json ├── paket.dependencies ├── paket.lock ├── public │ ├── img │ │ ├── favicon-16x16.png │ │ └── favicon-32x32.png │ └── index.html ├── sass │ └── main.sass ├── src │ ├── App.fs │ ├── Chess.WebUI.fsproj │ ├── Counter │ │ ├── State.fs │ │ ├── Types.fs │ │ └── View.fs │ ├── Global.fs │ ├── Home │ │ ├── State.fs │ │ ├── Types.fs │ │ └── View.fs │ ├── Info │ │ └── View.fs │ ├── Navbar │ │ └── View.fs │ ├── State.fs │ ├── Types.fs │ └── paket.references └── webpack.config.js ├── Chess.WpfUI ├── App.config ├── App.xaml ├── App.xaml.cs ├── Chess.WpfUI.csproj ├── Images │ ├── pieces_black │ │ ├── bishop.png │ │ ├── king.png │ │ ├── knight.png │ │ ├── pawn.png │ │ ├── queen.png │ │ └── rook.png │ └── pieces_white │ │ ├── bishop.png │ │ ├── king.png │ │ ├── knight.png │ │ ├── pawn.png │ │ ├── queen.png │ │ └── rook.png ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings └── packages.config ├── Chess.sln └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/.nuget/NuGet.exe -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config")) 32 | 33 | 34 | 35 | 36 | $(SolutionDir).nuget 37 | packages.config 38 | 39 | 40 | 41 | 42 | $(NuGetToolsPath)\NuGet.exe 43 | @(PackageSource) 44 | 45 | "$(NuGetExePath)" 46 | mono --runtime=v4.0.30319 $(NuGetExePath) 47 | 48 | $(TargetDir.Trim('\\')) 49 | 50 | -RequireConsent 51 | -NonInteractive 52 | 53 | "$(SolutionDir.Trim('\\'))" 54 | "$(SolutionDir)" 55 | 56 | 57 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 58 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 59 | 60 | 61 | 62 | RestorePackages; 63 | $(BuildDependsOn); 64 | 65 | 66 | 67 | 68 | $(BuildDependsOn); 69 | BuildPackage; 70 | 71 | 72 | 73 | 74 | 75 | 76 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | 98 | 100 | 101 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/Chess.ConsoleUI/bin/Debug/netcoreapp2.0/Chess.ConsoleUI.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/Chess.ConsoleUI", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false, 19 | "internalConsoleOptions": "openOnSessionStart" 20 | }, 21 | { 22 | "name": ".NET Core Attach", 23 | "type": "coreclr", 24 | "request": "attach", 25 | "processId": "${command:pickProcess}" 26 | } 27 | ,] 28 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Chess.ConsoleUI/Chess.ConsoleUI.csproj" 11 | ], 12 | "problemMatcher": "$msCompile" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /Chess.ConsoleUI/Chess.ConsoleUI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Chess.ConsoleUI/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using static Chess.Domain.Api; 7 | 8 | namespace Chess.ConsoleUI 9 | { 10 | class Program 11 | { 12 | 13 | private static bool UseEmoji = false; 14 | static void Main(string[] args) 15 | { 16 | if (args.Length > 0 && args[0] == "-e") 17 | { 18 | UseEmoji = true; 19 | Console.OutputEncoding = Encoding.UTF8; 20 | } 21 | 22 | var api = new ChessApi(); 23 | RenderGame(api); 24 | } 25 | 26 | static void RenderGame(ChessApi api) 27 | { 28 | /* The board: 29 | 30 | A B C D E F G H 31 | ─ ─ ─ ─ ─ ─ ─ ─ 32 | 8 | r n b q k b n r 33 | 7 | p p p p p p p p 34 | 6 | . . . . . . . . 35 | 5 | . . . . . . . . 36 | 4 | . . . . . . . . 37 | 3 | . . . . . . . . 38 | 2 | P P P P P P P P 39 | 1 | R N B K Q B N R 40 | */ 41 | 42 | Console.WriteLine(); 43 | Console.WriteLine(" A B C D E F G H"); 44 | Console.WriteLine(" ─ ─ ─ ─ ─ ─ ─ ─"); 45 | 46 | var rows = new[] { "8", "7", "6", "5", "4", "3", "2", "1" }; 47 | var cols = new[] { "A", "B", "C", "D", "E", "F", "G", "H" }; 48 | var cells = api.Cells.ToDictionary(c => c.Coord); 49 | 50 | foreach (var row in rows) 51 | { 52 | Console.Write("{0} |", row); 53 | 54 | foreach (var col in cols) 55 | { 56 | var cell = cells[col + row]; 57 | Console.Write(" {0}", RenderCell(cell)); 58 | } 59 | 60 | Console.WriteLine(); 61 | } 62 | 63 | Console.WriteLine(); 64 | Console.WriteLine(api.Message); 65 | 66 | // TODO: Add game over state to the public facing api and handle here 67 | 68 | Console.WriteLine("Enter a move in the following format: A2 A3 (case insensitive)"); 69 | Console.Write("> "); 70 | 71 | var moveText = Console.ReadLine(); 72 | var move = new ParsedMove(moveText); 73 | if (move.IsValid) 74 | { 75 | try 76 | { 77 | api.Move(move.From, move.To); 78 | } 79 | catch (Exception) 80 | { 81 | } 82 | } 83 | 84 | Console.Clear(); 85 | RenderGame(api); 86 | } 87 | 88 | private static Dictionary rankChars = new Dictionary 89 | { 90 | ["pawn"] = "p", 91 | ["rook"] = "r", 92 | ["knight"] = "n", 93 | ["bishop"] = "b", 94 | ["queen"] = "q", 95 | ["king"] = "k" 96 | }; 97 | private static Dictionary rankEmojiBlack = new Dictionary 98 | { 99 | ["pawn"] = "♟", 100 | ["rook"] = "♜", 101 | ["knight"] = "♞", 102 | ["bishop"] = "♝", 103 | ["queen"] = "♛", 104 | ["king"] = "♚" 105 | }; 106 | private static Dictionary rankEmojiWhite = new Dictionary 107 | { 108 | ["pawn"] = "♙", 109 | ["rook"] = "♖", 110 | ["knight"] = "♘", 111 | ["bishop"] = "♗", 112 | ["queen"] = "♕", 113 | ["king"] = "♔" 114 | }; 115 | 116 | static string glyphForPiece(string rank, string color) 117 | { 118 | if (UseEmoji) 119 | { 120 | var glyphs = (color == "black") ? rankEmojiBlack : rankEmojiWhite; 121 | return glyphs[rank]; 122 | } 123 | else 124 | { 125 | var glyph = rankChars[rank]; 126 | return (color == "black") ? glyph.ToLower() : glyph.ToUpper(); 127 | } 128 | 129 | 130 | 131 | } 132 | 133 | static string RenderCell(CellDTO cell) 134 | { 135 | switch (cell) 136 | { 137 | case CellDTO c when !c.IsOccupied: 138 | return "."; 139 | 140 | case CellDTO c when c.Color == "black" && c.Rank == "pawn": 141 | return glyphForPiece(c.Rank, c.Color); 142 | case CellDTO c when c.Color == "black" && c.Rank == "rook": 143 | return glyphForPiece(c.Rank, c.Color); 144 | case CellDTO c when c.Color == "black" && c.Rank == "knight": 145 | return glyphForPiece(c.Rank, c.Color); 146 | case CellDTO c when c.Color == "black" && c.Rank == "bishop": 147 | return glyphForPiece(c.Rank, c.Color); 148 | case CellDTO c when c.Color == "black" && c.Rank == "queen": 149 | return glyphForPiece(c.Rank, c.Color); 150 | case CellDTO c when c.Color == "black" && c.Rank == "king": 151 | return glyphForPiece(c.Rank, c.Color); 152 | 153 | case CellDTO c when c.Color == "white" && c.Rank == "pawn": 154 | return glyphForPiece(c.Rank, c.Color); 155 | case CellDTO c when c.Color == "white" && c.Rank == "rook": 156 | return glyphForPiece(c.Rank, c.Color); 157 | case CellDTO c when c.Color == "white" && c.Rank == "knight": 158 | return glyphForPiece(c.Rank, c.Color); 159 | case CellDTO c when c.Color == "white" && c.Rank == "bishop": 160 | return glyphForPiece(c.Rank, c.Color); 161 | case CellDTO c when c.Color == "white" && c.Rank == "queen": 162 | return glyphForPiece(c.Rank, c.Color); 163 | case CellDTO c when c.Color == "white" && c.Rank == "king": 164 | return glyphForPiece(c.Rank, c.Color); 165 | 166 | default: 167 | throw new Exception("Invalid piece."); 168 | } 169 | } 170 | 171 | class ParsedMove 172 | { 173 | public ParsedMove(string move) 174 | { 175 | var parts = (move ?? string.Empty).ToUpper().Split(new char[] { ',', ' ', '-' }); 176 | if (parts.Length == 2) 177 | { 178 | IsValid = true; 179 | From = parts[0]; 180 | To = parts[1]; 181 | } 182 | else 183 | { 184 | IsValid = false; 185 | } 186 | } 187 | 188 | public string From { get; private set; } 189 | public string To { get; private set; } 190 | public bool IsValid { get; private set; } 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /Chess.Domain/Api.fs: -------------------------------------------------------------------------------- 1 | namespace Chess.Domain 2 | 3 | /// Facilitates interop when using the C# WPF UI. 4 | /// Not needed when using the Fabulous UI! 5 | module Api = 6 | open Chess.Domain.Entities 7 | 8 | type CellDTO = { Coord: string; IsOccupied: bool; Color: string; Rank: string } 9 | 10 | /// cellStr ex: "{ coord: "A1", piece { rank: "Bishop", color: "White" } 11 | let deserializeCoord (cellStr: string) : Cell = 12 | let colMap = List.zip ["A"; "B"; "C"; "D"; "E"; "F"; "G"; "H";] Column.List |> Map.ofList 13 | let rowMap = List.zip ["1"; "2"; "3"; "4"; "5"; "6"; "7"; "8";] Row.List |> Map.ofList 14 | match cellStr.ToCharArray() with 15 | | [| col; row |] -> { Col = colMap.[col.ToString()]; Row = rowMap.[row.ToString()] } 16 | | _ -> failwith "Invalid cell" 17 | 18 | let serializeCell cell = 19 | let colMap = List.zip Column.List ["A"; "B"; "C"; "D"; "E"; "F"; "G"; "H";] |> Map.ofList 20 | let rowMap = List.zip Row.List ["1"; "2"; "3"; "4"; "5"; "6"; "7"; "8";] |> Map.ofList 21 | colMap.[cell.Col] + rowMap.[cell.Row] 22 | 23 | let boardToCellsDto (board: Board) = 24 | let cell_piece_list = Map.toList board 25 | let cells = cell_piece_list |> List.map (fun (cell, piece) -> 26 | match piece with 27 | | Some (color,rank) -> 28 | { 29 | Coord = serializeCell cell 30 | IsOccupied = true 31 | Color = match color with | White -> "white" | Black -> "black" 32 | Rank = match rank with | Pawn _ -> "pawn" | Rook -> "rook" | Bishop -> "bishop" | King -> "king" | Queen -> "queen" | Knight -> "knight" 33 | } 34 | | None -> 35 | { 36 | Coord = serializeCell cell 37 | IsOccupied = false 38 | Color = null 39 | Rank = null 40 | }) 41 | cells 42 | 43 | type ChessApi() = 44 | 45 | let mutable gameState = Implementation.initGame() 46 | 47 | member this.InitGame() = gameState <- Implementation.initGame() 48 | 49 | member this.Cells with get() = boardToCellsDto gameState.Board |> List.toArray 50 | 51 | member this.Message with get() = gameState.Message 52 | 53 | member this.Move(fromCell: string, toCell: string) = 54 | let cell1 = deserializeCoord fromCell 55 | let cell2 = deserializeCoord toCell 56 | gameState <- Implementation.move gameState { FromCell = cell1; ToCell = cell2 } 57 | () -------------------------------------------------------------------------------- /Chess.Domain/Chess.Domain.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chess.Domain/Chess.fs: -------------------------------------------------------------------------------- 1 | namespace Chess.Domain 2 | 3 | module Entities = 4 | type Color = | White | Black 5 | type HasMoved = | NotMoved | Moved 6 | type Rank = | Pawn of HasMoved | Rook | Bishop | Knight | Queen | King 7 | type Piece = Color * Rank 8 | type Column = | A | B | C | D | E | F | G | H 9 | with static member List = [A;B;C;D;E;F;G;H] 10 | type Row = | One | Two | Three | Four | Five | Six | Seven | Eight 11 | with static member List = [One; Two; Three; Four; Five; Six; Seven; Eight] 12 | type Cell = { Col: Column; Row: Row } 13 | type Board = Map 14 | type GameProgress = | InProgress | WhiteWins | BlackWins 15 | type GameState = { Board: Board; NextMove: Color; Message: string } 16 | type AttemptedMove = { FromCell: Cell; ToCell: Cell } 17 | type Move = { FromPiece: Piece; FromCell: Cell; ToCell: Cell } 18 | 19 | module Implementation = 20 | open Entities 21 | 22 | let initGame () = 23 | let blackPawn = Some (Black, Pawn NotMoved) 24 | let whitePawn = Some (White, Pawn NotMoved) 25 | let white rank = Some (White, rank) 26 | let black rank = Some (Black, rank) 27 | 28 | let createRow row pieces = 29 | let cells = Column.List |> List.map (fun col -> { Col = col; Row = row }) 30 | List.zip cells pieces 31 | 32 | let board = 33 | Map ( (createRow Eight [black Rook; black Knight; black Bishop; black King; black Queen; black Bishop; black Knight; black Rook]) @ 34 | (createRow Seven [blackPawn; blackPawn; blackPawn; blackPawn; blackPawn; blackPawn; blackPawn; blackPawn]) @ 35 | (createRow Six [None; None; None; None; None; None; None; None]) @ 36 | (createRow Five [None; None; None; None; None; None; None; None]) @ 37 | (createRow Four [None; None; None; None; None; None; None; None]) @ 38 | (createRow Three [None; None; None; None; None; None; None; None]) @ 39 | (createRow Two [whitePawn; whitePawn; whitePawn; whitePawn; whitePawn; whitePawn; whitePawn; whitePawn]) @ 40 | (createRow One [white Rook; white Knight; white Bishop; white King; white Queen; white Bishop; white Knight; white Rook]) ) 41 | 42 | { Board = board; 43 | NextMove = White; 44 | Message = "Welcome to F# Chess!" } 45 | 46 | let validateFromPieceTurn gameState (attemptedMove: AttemptedMove) : Result = 47 | match gameState.Board.[attemptedMove.FromCell] with 48 | | Some (fromColor, fromRank) -> 49 | if fromColor = gameState.NextMove 50 | then Ok { FromPiece = (fromColor, fromRank); FromCell = attemptedMove.FromCell; ToCell = attemptedMove.ToCell } 51 | else Error "It's not your turn" 52 | | None -> 53 | Error "No piece was selected to move" 54 | 55 | let validateNotFriendlyTarget gameState move = 56 | match gameState.Board.[move.ToCell] with 57 | | Some (toColor, toRank) -> 58 | if gameState.NextMove = toColor 59 | then Error "Can not take a friendly piece" 60 | else Ok move 61 | | None -> Ok move 62 | 63 | let getHorizDist fromCell toCell = 64 | let toIdx = Column.List |> List.findIndex (fun c -> c = toCell.Col) 65 | let fromIdx = Column.List |> List.findIndex (fun c -> c = fromCell.Col) 66 | toIdx - fromIdx 67 | 68 | let getVertDist fromCell toCell = 69 | let toIdx = Row.List |> List.findIndex (fun r -> r = toCell.Row) 70 | let fromIdx = Row.List |> List.findIndex (fun r -> r = fromCell.Row) 71 | toIdx - fromIdx 72 | 73 | let tryGetCell (colIdx,rowIdx) = 74 | if colIdx < Column.List.Length && colIdx >= 0 && rowIdx < Column.List.Length && rowIdx >= 0 75 | then Some { Col = Column.List.[colIdx]; Row = Row.List.[rowIdx] } 76 | else None 77 | 78 | let getCoords cell = 79 | let colIdx = Column.List |> List.findIndex (fun c -> c = cell.Col) 80 | let rowIdx = Row.List |> List.findIndex (fun r -> r = cell.Row) 81 | (colIdx, rowIdx) 82 | 83 | let validateMoveShape gameState move : Result = 84 | let (fromPieceColor, fromPieceRank) = move.FromPiece 85 | let toPieceOpt = gameState.Board.Item move.ToCell 86 | 87 | let xDelta = getHorizDist move.FromCell move.ToCell 88 | let yDelta = getVertDist move.FromCell move.ToCell 89 | 90 | let validateKnight() = 91 | let isL = match (abs xDelta, abs yDelta) with | (1,2) -> true | (2,1) -> true | _ -> false 92 | if isL 93 | then Ok move 94 | else Error "Knight can only move in an L pattern" 95 | 96 | let validateRook() = 97 | let isUpDownLeftRight = (abs xDelta > 0 && yDelta = 0) || (xDelta = 0 && abs yDelta > 0) 98 | if isUpDownLeftRight 99 | then Ok move 100 | else Error "Rook can only move up, down, left or right" 101 | 102 | let validateBishop() = 103 | let isDiag = (abs xDelta = abs yDelta) 104 | if isDiag 105 | then Ok move 106 | else Error "Bishop can only move diagonally" 107 | 108 | let validateKing() = 109 | let isAnyDirectionOneSpace = (abs xDelta = 1 || xDelta = 0) && (abs yDelta = 1 || yDelta = 0) 110 | if isAnyDirectionOneSpace 111 | then Ok move 112 | else Error "King can only move one space in any direction" 113 | 114 | let validateQueen() = 115 | let isAnyDirection = (abs xDelta = abs yDelta) || (abs xDelta > 0 && yDelta = 0) || (xDelta = 0 && abs yDelta > 0) 116 | if isAnyDirection 117 | then Ok move 118 | else Error "Queen can only move diagonally, up, down, left or right" 119 | 120 | let validatePawn hasMoved = 121 | match toPieceOpt with 122 | | Some toPiece -> // Moving to an occupied cell 123 | // Check for diagonal captures 124 | match (fromPieceColor, abs xDelta, yDelta) with 125 | | (White, 1, 1) 126 | | (Black, 1, -1) 127 | -> Ok move 128 | | _ -> Error "Pawn can only capture moving one space diagonally" 129 | | None -> // Moving to an empty cell 130 | // Check for straight non-captures 131 | match (fromPieceColor, xDelta, yDelta, hasMoved) with 132 | | (White, 0, 1, _) -> Ok move // can always move forward one space to an empty cell 133 | | (White, 0, 2, NotMoved) -> Ok move // can move forward two spaces only if pawn has not yet moved 134 | | (Black, 0, -1, _) -> Ok move // can always move forward one space to an empty cell 135 | | (Black, 0, -2, NotMoved) -> Ok move // can move forward two spaces only if pawn has not yet moved 136 | | _ -> Error "Pawn can move forward one space (or two spaces on the first move)" 137 | 138 | match fromPieceRank with 139 | | Bishop -> validateBishop() 140 | | Rook -> validateRook() 141 | | King -> validateKing() 142 | | Queen -> validateQueen() 143 | | Knight -> validateKnight() 144 | | Pawn hasMoved -> validatePawn hasMoved 145 | 146 | let validateNoInterposition gameState move = 147 | match move.FromPiece with 148 | | color, Knight -> Ok move 149 | | _ -> 150 | let xDelta = getHorizDist move.FromCell move.ToCell 151 | let yDelta = getVertDist move.FromCell move.ToCell 152 | 153 | let normalize n = if n > 0 then 1 elif n < 0 then -1 else 0 154 | let addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2) 155 | let unitVector = (normalize xDelta, normalize yDelta) 156 | 157 | let rec moveSeq startCell vector = 158 | seq { 159 | let nextCell = 160 | startCell 161 | |> getCoords 162 | |> addVectors vector 163 | |> tryGetCell 164 | 165 | if nextCell.IsSome then 166 | yield nextCell.Value 167 | yield! moveSeq nextCell.Value vector 168 | } 169 | 170 | let valid = 171 | moveSeq move.FromCell unitVector 172 | |> Seq.takeWhile (fun nextCell -> nextCell <> move.ToCell) 173 | |> Seq.forall (fun nextCell -> gameState.Board.[nextCell].IsNone) 174 | 175 | if valid 176 | then Ok move 177 | else Error "Another piece is blocking this move" 178 | 179 | let updateBoard (board: Board) move = 180 | let fromPieceColor, fromPieceRank = move.FromPiece 181 | match fromPieceRank with 182 | | Pawn pi -> 183 | board.Add(move.FromCell, None).Add(move.ToCell, Some (fromPieceColor, Pawn Moved)) 184 | | _ -> 185 | board.Add(move.FromCell, None).Add(move.ToCell, Some (fromPieceColor, fromPieceRank)) 186 | 187 | let updateNextMoveColor color = 188 | match color with 189 | | Black -> White 190 | | White -> Black 191 | 192 | let validateMove (gameState: GameState) (attemptedMove: AttemptedMove) = 193 | attemptedMove 194 | |> validateFromPieceTurn gameState 195 | |> Result.bind (validateNotFriendlyTarget gameState) 196 | |> Result.bind (validateMoveShape gameState) 197 | |> Result.bind (validateNoInterposition gameState) 198 | 199 | let move (gameState: GameState) (attemptedMove: AttemptedMove) = 200 | let validatedMove = validateMove gameState attemptedMove 201 | match validatedMove with 202 | | Ok move -> 203 | { gameState with 204 | Board = updateBoard gameState.Board move 205 | NextMove = updateNextMoveColor(gameState.NextMove) 206 | Message = "" } 207 | | Error msg -> 208 | { gameState with Message = msg } 209 | -------------------------------------------------------------------------------- /Chess.Domain/Validation.fs: -------------------------------------------------------------------------------- 1 | namespace Chess.Domain 2 | 3 | module Validation = 4 | 5 | let (>>=) result f = 6 | match result with 7 | | Ok t -> f t 8 | | Error msg -> Error msg 9 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/Assets/AboutAssets.txt: -------------------------------------------------------------------------------- 1 | Any raw assets you want to be deployed with your application can be placed in 2 | this directory (and child directories) and given a Build Action of "AndroidAsset". 3 | 4 | These files will be deployed with you package and will be accessible using Android's 5 | AssetManager, like this: 6 | 7 | public class ReadAsset : Activity 8 | { 9 | protected override void OnCreate (Bundle bundle) 10 | { 11 | base.OnCreate (bundle); 12 | 13 | InputStream input = Assets.Open ("my_asset.txt"); 14 | } 15 | } 16 | 17 | Additionally, some Android functions will automatically load asset files: 18 | 19 | Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); 20 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/MainActivity.fs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Fabulous contributors. See LICENSE.md for license. 2 | namespace Chess.FabUI.Android 3 | 4 | open System 5 | 6 | open Android.App 7 | open Android.Content 8 | open Android.Content.PM 9 | open Android.Runtime 10 | open Android.Views 11 | open Android.Widget 12 | open Android.OS 13 | open Xamarin.Forms.Platform.Android 14 | 15 | [] 16 | type MainActivity() = 17 | inherit FormsAppCompatActivity() 18 | override this.OnCreate (bundle: Bundle) = 19 | FormsAppCompatActivity.TabLayoutResource <- Resources.Layout.Tabbar 20 | FormsAppCompatActivity.ToolbarResource <- Resources.Layout.Toolbar 21 | base.OnCreate (bundle) 22 | 23 | Xamarin.Essentials.Platform.Init(this, bundle) 24 | 25 | Xamarin.Forms.Forms.Init (this, bundle) 26 | 27 | let appcore = new Chess.FabUI.App() 28 | this.LoadApplication (appcore) 29 | 30 | override this.OnRequestPermissionsResult(requestCode: int, permissions: string[], [] grantResults: Android.Content.PM.Permission[]) = 31 | Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults) 32 | 33 | base.OnRequestPermissionsResult(requestCode, permissions, grantResults) 34 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/Properties/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/Properties/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Chess.FabUI.Android 2 | 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | open Android.App 7 | 8 | // the name of the type here needs to match the name inside the ResourceDesigner attribute 9 | type Resources = Chess.FabUI.Android.Resource 10 | [] 11 | 12 | // General Information about an assembly is controlled through the following 13 | // set of attributes. Change these attribute values to modify the information 14 | // associated with an assembly. 15 | [] 16 | [] 17 | [] 18 | [] 19 | [] 20 | [] 21 | [] 22 | [] 23 | [] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [] 35 | [] 36 | [] 37 | 38 | // Add some common permissions, these can be removed if not needed 39 | [] 40 | [] 41 | do() 42 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/AboutResources.txt: -------------------------------------------------------------------------------- 1 | Images, layout descriptions, binary blobs and string dictionaries can be included 2 | in your application as resource files. Various Android APIs are designed to 3 | operate on the resource IDs instead of dealing with images, strings or binary blobs 4 | directly. 5 | 6 | For example, a sample Android app that contains a user interface layout (main.xml), 7 | an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) 8 | would keep its resources in the "Resources" directory of the application: 9 | 10 | Resources/ 11 | drawable-hdpi/ 12 | icon.png 13 | 14 | drawable-ldpi/ 15 | icon.png 16 | 17 | drawable-mdpi/ 18 | icon.png 19 | 20 | layout/ 21 | main.xml 22 | 23 | values/ 24 | strings.xml 25 | 26 | In order to get the build system to recognize Android resources, set the build action to 27 | "AndroidResource". The native Android APIs do not operate directly with filenames, but 28 | instead operate on resource IDs. When you compile an Android application that uses resources, 29 | the build system will package the resources for distribution and generate a class called 30 | "Resource" that contains the tokens for each one of the resources included. For example, 31 | for the above Resources layout, this is what the Resource class would expose: 32 | 33 | public class Resource { 34 | public class drawable { 35 | public const int icon = 0x123; 36 | } 37 | 38 | public class layout { 39 | public const int main = 0x456; 40 | } 41 | 42 | public class strings { 43 | public const int first_string = 0xabc; 44 | public const int second_string = 0xbcd; 45 | } 46 | } 47 | 48 | You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main 49 | to reference the layout/main.xml file, or Resource.strings.first_string to reference the first 50 | string in the dictionary file values/strings.xml. 51 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/layout/Tabbar.axml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/layout/Toolbar.axml: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-anydpi-v26/icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-anydpi-v26/icon_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-hdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.Android/Resources/mipmap-hdpi/Icon.png -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-hdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.Android/Resources/mipmap-hdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.Android/Resources/mipmap-mdpi/icon.png -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-mdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.Android/Resources/mipmap-mdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-xhdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.Android/Resources/mipmap-xhdpi/Icon.png -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-xhdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.Android/Resources/mipmap-xhdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-xxhdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.Android/Resources/mipmap-xxhdpi/Icon.png -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-xxhdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.Android/Resources/mipmap-xxhdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-xxxhdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.Android/Resources/mipmap-xxxhdpi/Icon.png -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/mipmap-xxxhdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.Android/Resources/mipmap-xxxhdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | #3F51B5 5 | #303F9F 6 | #FF4081 7 | 8 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/Resources/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 26 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /Chess.FabUI.Android/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Chess.FabUI.WPF/App.fs: -------------------------------------------------------------------------------- 1 | namespace Chess.FabUI.WPF 2 | 3 | open System 4 | 5 | open Xamarin.Forms 6 | open Xamarin.Forms.Platform.WPF 7 | 8 | type MainWindow() = 9 | inherit FormsApplicationPage() 10 | 11 | module Main = 12 | [] 13 | [] 14 | let main(_args) = 15 | 16 | let app = new System.Windows.Application() 17 | Forms.Init() 18 | let window = MainWindow(Title = "F# Chess", Height=530., Width=402.) 19 | window.LoadApplication(new Chess.FabUI.App()) 20 | 21 | app.Run(window) 22 | -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Chess.FabUI.WPF.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | net472 5 | false 6 | 7 | 8 | 9 | PreserveNewest 10 | 11 | 12 | PreserveNewest 13 | 14 | 15 | PreserveNewest 16 | 17 | 18 | PreserveNewest 19 | 20 | 21 | PreserveNewest 22 | 23 | 24 | PreserveNewest 25 | 26 | 27 | PreserveNewest 28 | 29 | 30 | PreserveNewest 31 | 32 | 33 | PreserveNewest 34 | 35 | 36 | PreserveNewest 37 | 38 | 39 | PreserveNewest 40 | 41 | 42 | PreserveNewest 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 4.0 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_black/bishop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_black/bishop.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_black/king.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_black/king.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_black/knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_black/knight.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_black/pawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_black/pawn.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_black/queen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_black/queen.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_black/rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_black/rook.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_white/bishop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_white/bishop.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_white/king.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_white/king.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_white/knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_white/knight.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_white/pawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_white/pawn.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_white/queen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_white/queen.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Images/pieces_white/rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.WPF/Images/pieces_white/rook.png -------------------------------------------------------------------------------- /Chess.FabUI.WPF/Properties/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Chess.FabUI.WPF 2 | open System.Reflection 3 | open System.Runtime.CompilerServices 4 | 5 | // the name of the type here needs to match the name inside the ResourceDesigner attribute 6 | //type Resources = Chess.FabUI.Droid.Resource 7 | //[] 8 | 9 | [] 10 | [] 11 | [] 12 | [] 13 | [] 14 | [] 15 | [] 16 | 17 | // The assembly version has the format {Major}.{Minor}.{Build}.{Revision} 18 | 19 | [] 20 | 21 | //[] 22 | //[] 23 | 24 | () 25 | -------------------------------------------------------------------------------- /Chess.FabUI.WPF/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Chess.FabUI.iOS/AppDelegate.fs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Fabulous contributors. See LICENSE.md for license. 2 | namespace Chess.FabUI.iOS 3 | 4 | open System 5 | open UIKit 6 | open Foundation 7 | open Xamarin.Forms 8 | open Xamarin.Forms.Platform.iOS 9 | 10 | [] 11 | type AppDelegate () = 12 | inherit FormsApplicationDelegate () 13 | 14 | override this.FinishedLaunching (app, options) = 15 | Forms.Init() 16 | let appcore = new Chess.FabUI.App() 17 | this.LoadApplication (appcore) 18 | base.FinishedLaunching(app, options) 19 | 20 | module Main = 21 | [] 22 | let main args = 23 | UIApplication.Main(args, null, "AppDelegate") 24 | 0 25 | 26 | -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "scale": "2x", 5 | "size": "20x20", 6 | "idiom": "iphone", 7 | "filename": "Icon40.png" 8 | }, 9 | { 10 | "scale": "3x", 11 | "size": "20x20", 12 | "idiom": "iphone", 13 | "filename": "Icon60.png" 14 | }, 15 | { 16 | "scale": "2x", 17 | "size": "29x29", 18 | "idiom": "iphone", 19 | "filename": "Icon58.png" 20 | }, 21 | { 22 | "scale": "3x", 23 | "size": "29x29", 24 | "idiom": "iphone", 25 | "filename": "Icon87.png" 26 | }, 27 | { 28 | "scale": "2x", 29 | "size": "40x40", 30 | "idiom": "iphone", 31 | "filename": "Icon80.png" 32 | }, 33 | { 34 | "scale": "3x", 35 | "size": "40x40", 36 | "idiom": "iphone", 37 | "filename": "Icon120.png" 38 | }, 39 | { 40 | "scale": "2x", 41 | "size": "60x60", 42 | "idiom": "iphone", 43 | "filename": "Icon120.png" 44 | }, 45 | { 46 | "scale": "3x", 47 | "size": "60x60", 48 | "idiom": "iphone", 49 | "filename": "Icon180.png" 50 | }, 51 | { 52 | "scale": "1x", 53 | "size": "20x20", 54 | "idiom": "ipad", 55 | "filename": "Icon20.png" 56 | }, 57 | { 58 | "scale": "2x", 59 | "size": "20x20", 60 | "idiom": "ipad", 61 | "filename": "Icon40.png" 62 | }, 63 | { 64 | "scale": "1x", 65 | "size": "29x29", 66 | "idiom": "ipad", 67 | "filename": "Icon29.png" 68 | }, 69 | { 70 | "scale": "2x", 71 | "size": "29x29", 72 | "idiom": "ipad", 73 | "filename": "Icon58.png" 74 | }, 75 | { 76 | "scale": "1x", 77 | "size": "40x40", 78 | "idiom": "ipad", 79 | "filename": "Icon40.png" 80 | }, 81 | { 82 | "scale": "2x", 83 | "size": "40x40", 84 | "idiom": "ipad", 85 | "filename": "Icon80.png" 86 | }, 87 | { 88 | "scale": "1x", 89 | "size": "76x76", 90 | "idiom": "ipad", 91 | "filename": "Icon76.png" 92 | }, 93 | { 94 | "scale": "2x", 95 | "size": "76x76", 96 | "idiom": "ipad", 97 | "filename": "Icon152.png" 98 | }, 99 | { 100 | "scale": "2x", 101 | "size": "83.5x83.5", 102 | "idiom": "ipad", 103 | "filename": "Icon167.png" 104 | }, 105 | { 106 | "scale": "1x", 107 | "size": "1024x1024", 108 | "idiom": "ios-marketing", 109 | "filename": "Icon1024.png" 110 | } 111 | ], 112 | "properties": {}, 113 | "info": { 114 | "version": 1, 115 | "author": "xcode" 116 | } 117 | } -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon20.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon29.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon40.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon80.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Chess.FabUI.iOS.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | iPhoneSimulator 8 | 8.0.30703 9 | 2.0 10 | {18D06679-5E5E-484C-82F4-AFCA81594CC5} 11 | {FEACFBD2-3405-455C-9665-78FE426C6842};{F2A71F9B-5D33-465A-A702-920D77279786} 12 | {6143fdea-f3c2-4a09-aafa-6e230626515e} 13 | Exe 14 | Chess.FabUI.iOS 15 | Resources 16 | Chess.FabUI.iOS 17 | NSUrlSessionHandler 18 | 19 | 20 | true 21 | true 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\iPhoneSimulator\Debug 28 | DEBUG 29 | prompt 30 | 4 31 | false 32 | x86_64 33 | None 34 | true 35 | 36 | 37 | none 38 | true 39 | bin\iPhoneSimulator\Release 40 | prompt 41 | 4 42 | None 43 | x86_64 44 | false 45 | 46 | 47 | true 48 | full 49 | false 50 | bin\iPhone\Debug 51 | DEBUG 52 | prompt 53 | 4 54 | false 55 | ARM64 56 | iPhone Developer 57 | true 58 | Entitlements.plist 59 | 60 | 61 | none 62 | true 63 | bin\iPhone\Release 64 | prompt 65 | 4 66 | ARM64 67 | false 68 | iPhone Developer 69 | Entitlements.plist 70 | 71 | 72 | none 73 | True 74 | bin\iPhone\Ad-Hoc 75 | prompt 76 | 4 77 | False 78 | ARM64 79 | True 80 | Automatic:AdHoc 81 | iPhone Distribution 82 | Entitlements.plist 83 | 84 | 85 | none 86 | True 87 | bin\iPhone\AppStore 88 | prompt 89 | 4 90 | False 91 | ARM64 92 | Automatic:AppStore 93 | iPhone Distribution 94 | Entitlements.plist 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | false 108 | 109 | 110 | false 111 | 112 | 113 | false 114 | 115 | 116 | false 117 | 118 | 119 | false 120 | 121 | 122 | false 123 | 124 | 125 | false 126 | 127 | 128 | false 129 | 130 | 131 | false 132 | 133 | 134 | false 135 | 136 | 137 | false 138 | 139 | 140 | false 141 | 142 | 143 | false 144 | 145 | 146 | false 147 | 148 | 149 | 150 | 151 | True 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | ..\packages\Xamarin.Forms.3.4.0.1009999\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll 161 | 162 | 163 | ..\packages\Xamarin.Forms.3.4.0.1009999\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll 164 | 165 | 166 | ..\packages\Xamarin.Forms.3.4.0.1009999\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll 167 | 168 | 169 | ..\packages\Xamarin.Forms.3.4.0.1009999\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll 170 | 171 | 172 | ..\packages\Fabulous.Core.0.33.2\lib\netstandard2.0\Fabulous.Core.dll 173 | 174 | 175 | ..\packages\Fabulous.LiveUpdate.0.33.2\lib\netstandard2.0\Fabulous.LiveUpdate.dll 176 | 177 | 178 | ..\packages\Fabulous.CustomControls.0.33.2\lib\netstandard2.0\Fabulous.CustomControls.dll 179 | 180 | 181 | ..\packages\Newtonsoft.Json.11.0.2\lib\netstandard2.0\Newtonsoft.Json.dll 182 | 183 | 184 | ..\packages\Xamarin.Essentials.0.6.0-preview\lib\monoandroid71\Xamarin.Essentials.dll 185 | 186 | 187 | {2C44CD42-E4D1-48AE-805B-F30E85FB92D9} 188 | Chess.FabUI 189 | 190 | 191 | -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Entitlements.plist: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIDeviceFamily 6 | 7 | 1 8 | 2 9 | 10 | UISupportedInterfaceOrientations 11 | 12 | UIInterfaceOrientationPortrait 13 | UIInterfaceOrientationLandscapeLeft 14 | UIInterfaceOrientationLandscapeRight 15 | 16 | UISupportedInterfaceOrientations~ipad 17 | 18 | UIInterfaceOrientationPortrait 19 | UIInterfaceOrientationPortraitUpsideDown 20 | UIInterfaceOrientationLandscapeLeft 21 | UIInterfaceOrientationLandscapeRight 22 | 23 | MinimumOSVersion 24 | 8.0 25 | CFBundleDisplayName 26 | Chess.FabUI 27 | CFBundleIdentifier 28 | com.companyname 29 | CFBundleVersion 30 | 1.0 31 | UILaunchStoryboardName 32 | LaunchScreen 33 | CFBundleName 34 | Chess.FabUI 35 | XSAppIconAssets 36 | Assets.xcassets/AppIcon.appiconset 37 | 38 | 39 | -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Properties/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Chess.FabUI.iOS 2 | 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [] 11 | [] 12 | [] 13 | [] 14 | [] 15 | [] 16 | [] 17 | [] 18 | [] 19 | 20 | // Version information for an assembly consists of the following four values: 21 | // 22 | // Major Version 23 | // Minor Version 24 | // Build Number 25 | // Revision 26 | // 27 | // You can specify all the values or you can default the Build and Revision Numbers 28 | // by using the '*' as shown below: 29 | // [] 30 | [] 31 | [] 32 | do() 33 | 34 | -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Resources/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Resources/Default-568h@2x.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Resources/Default-Portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Resources/Default-Portrait.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Resources/Default-Portrait@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Resources/Default-Portrait@2x.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Resources/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Resources/Default.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Resources/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.FabUI.iOS/Resources/Default@2x.png -------------------------------------------------------------------------------- /Chess.FabUI.iOS/Resources/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Chess.FabUI.iOS/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Chess.FabUI/Chess.FabUI.fs: -------------------------------------------------------------------------------- 1 | namespace Chess.FabUI 2 | 3 | open System.Diagnostics 4 | open Fabulous.Core 5 | open Fabulous.DynamicViews 6 | open Xamarin.Forms 7 | open Chess.Domain 8 | open Chess.Domain.Entities 9 | 10 | module App = 11 | type Model = 12 | { GameState: Entities.GameState 13 | FromCell: Cell option } 14 | 15 | type Msg = 16 | /// User will pick a "from" cell, and then a "to" cell. 17 | | PickCell of Cell 18 | 19 | let init () = 20 | { GameState = Implementation.initGame(); FromCell = None }, Cmd.none 21 | 22 | let update msg model = 23 | match msg with 24 | | PickCell cell -> 25 | match model.FromCell with 26 | | None -> // FromCell was not previously selected 27 | match model.GameState.Board.[cell] with 28 | | Some piece -> { model with FromCell = Some cell }, Cmd.none // Selected a cell has a piece, so update FromCell 29 | | None -> model, Cmd.none // Selected cell has no piece, so ignore it 30 | 31 | | Some fromCell -> // FromCell was already selected 32 | let gameState = Implementation.move model.GameState { AttemptedMove.FromCell = fromCell; AttemptedMove.ToCell = cell } 33 | { model with GameState = gameState; FromCell = None }, Cmd.none 34 | 35 | let view (model: Model) dispatch = 36 | 37 | let getCellBgColor cell colIdx rowIdx = 38 | if Some cell = model.FromCell then Color.LightGreen 39 | elif (colIdx + rowIdx) % 2 = 0 then Xamarin.Forms.Color.White 40 | else Color.LightBlue 41 | 42 | let getCellBorderColor cell = 43 | if Some cell = model.FromCell then Color.Green 44 | else Color.Gray 45 | 46 | let indexedCells = 47 | let indexedCols = List.zip Entities.Column.List [0..7] 48 | let indexedRows = List.zip Entities.Row.List ([0..7] |> List.rev) 49 | 50 | [ for col, colIdx in indexedCols do 51 | for row, rowIdx in indexedRows do 52 | yield { Col = col; Row = row }, (colIdx, rowIdx) ] 53 | 54 | let imageForPiece pieceOpt = 55 | match pieceOpt with 56 | | Some (color, rank) -> 57 | let colorStr = match color with | White -> "white" | Black -> "black" 58 | let rankStr = match rank with | Pawn _ -> "pawn" | Rook -> "rook" | Bishop -> "bishop" | King -> "king" | Queen -> "queen" | Knight -> "knight" 59 | sprintf "Images/pieces_%s/%s.png" colorStr rankStr 60 | 61 | | None -> "" 62 | 63 | View.ContentPage( 64 | StackLayout.stackLayout [ // <-- Fabulous.SimpleElements nuget package 65 | StackLayout.Children [ 66 | Grid.grid [ 67 | Grid.Rows ([ for n in 1..8 -> 50. ] |> List.map GridLength) 68 | Grid.Columns ([ for n in 1..8 -> 50. ] |> List.map GridLength) 69 | Grid.ColumnSpacing 0. 70 | Grid.RowSpacing 0. 71 | Grid.Children [ 72 | for (cell, (colIdx, rowIdx)) in indexedCells do 73 | let bgColor = getCellBgColor cell colIdx rowIdx 74 | let borderColor = getCellBorderColor cell 75 | let imageSource = imageForPiece model.GameState.Board.[cell] 76 | let onTap = View.TapGestureRecognizer(command=(fun () -> dispatch (PickCell cell))) 77 | 78 | yield View.Frame( 79 | backgroundColor = bgColor, 80 | borderColor = borderColor, 81 | gestureRecognizers = [onTap] 82 | ).GridColumn(colIdx).GridRow(rowIdx) 83 | 84 | yield View.Image( 85 | source = imageSource, 86 | gestureRecognizers = [onTap] 87 | ).GridColumn(colIdx).GridRow(rowIdx) 88 | ] 89 | ] 90 | 91 | Label.label [ Label.Text model.GameState.Message ] 92 | ] 93 | ] 94 | ) 95 | 96 | // Note, this declaration is needed if you enable LiveUpdate 97 | let program = Program.mkProgram init update view 98 | 99 | 100 | 101 | 102 | type App () as app = 103 | inherit Application () 104 | 105 | let runner = 106 | App.program 107 | #if DEBUG 108 | |> Program.withConsoleTrace 109 | #endif 110 | |> Program.runWithDynamicView app 111 | 112 | #if DEBUG 113 | // Uncomment this line to enable live update in debug mode. 114 | // See https://fsprojects.github.io/Fabulous/tools.html for further instructions. 115 | // 116 | do runner.EnableLiveUpdate() 117 | #endif 118 | 119 | // Uncomment this code to save the application state to app.Properties using Newtonsoft.Json 120 | // See https://fsprojects.github.io/Fabulous/models.html for further instructions. 121 | #if APPSAVE 122 | let modelId = "model" 123 | override __.OnSleep() = 124 | 125 | let json = Newtonsoft.Json.JsonConvert.SerializeObject(runner.CurrentModel) 126 | Console.WriteLine("OnSleep: saving model into app.Properties, json = {0}", json) 127 | 128 | app.Properties.[modelId] <- json 129 | 130 | override __.OnResume() = 131 | Console.WriteLine "OnResume: checking for model in app.Properties" 132 | try 133 | match app.Properties.TryGetValue modelId with 134 | | true, (:? string as json) -> 135 | 136 | Console.WriteLine("OnResume: restoring model from app.Properties, json = {0}", json) 137 | let model = Newtonsoft.Json.JsonConvert.DeserializeObject(json) 138 | 139 | Console.WriteLine("OnResume: restoring model from app.Properties, model = {0}", (sprintf "%0A" model)) 140 | runner.SetCurrentModel (model, Cmd.none) 141 | 142 | | _ -> () 143 | with ex -> 144 | App.program.onError("Error while restoring model found in app.Properties", ex) 145 | 146 | override this.OnStart() = 147 | Console.WriteLine "OnStart: using same logic as OnResume()" 148 | this.OnResume() 149 | #endif 150 | 151 | 152 | -------------------------------------------------------------------------------- /Chess.FabUI/Chess.FabUI.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Chess.WebUI/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | insert_final_newline = true 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | indent_style = space 12 | indent_size = 4 13 | 14 | [*.styl] 15 | indent_size = 4 16 | -------------------------------------------------------------------------------- /Chess.WebUI/.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Chess.WebUI/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js* 2 | 3 | # Node 4 | node_modules/ 5 | 6 | ## Ignore Visual Studio temporary files, build results, and 7 | ## files generated by popular Visual Studio add-ons. 8 | 9 | # User-specific files 10 | *.suo 11 | *.user 12 | *.sln.docstates 13 | 14 | # Xamarin Studio / monodevelop user-specific 15 | *.userprefs 16 | *.dll.mdb 17 | *.exe.mdb 18 | 19 | # Build results 20 | 21 | [Dd]ebug/ 22 | [Rr]elease/ 23 | x64/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | 27 | # MSTest test Results 28 | [Tt]est[Rr]esult*/ 29 | [Bb]uild[Ll]og.* 30 | 31 | *_i.c 32 | *_p.c 33 | *.ilk 34 | *.meta 35 | *.obj 36 | *.pch 37 | *.pdb 38 | *.pgc 39 | *.pgd 40 | *.rsp 41 | *.sbr 42 | *.tlb 43 | *.tli 44 | *.tlh 45 | *.tmp 46 | *.tmp_proj 47 | *.log 48 | *.vspscc 49 | *.vssscc 50 | .builds 51 | *.pidb 52 | *.log 53 | *.scc 54 | 55 | # Visual C++ cache files 56 | ipch/ 57 | *.aps 58 | *.ncb 59 | *.opensdf 60 | *.sdf 61 | *.cachefile 62 | 63 | # Visual Studio profiler 64 | *.psess 65 | *.vsp 66 | *.vspx 67 | 68 | # Other Visual Studio data 69 | .vs/ 70 | 71 | # Guidance Automation Toolkit 72 | *.gpState 73 | 74 | # ReSharper is a .NET coding add-in 75 | _ReSharper*/ 76 | *.[Rr]e[Ss]harper 77 | 78 | # TeamCity is a build add-in 79 | _TeamCity* 80 | 81 | # DotCover is a Code Coverage Tool 82 | *.dotCover 83 | 84 | # NCrunch 85 | *.ncrunch* 86 | .*crunch*.local.xml 87 | 88 | # Installshield output folder 89 | #[Ee]xpress/ 90 | 91 | # DocProject is a documentation generator add-in 92 | DocProject/buildhelp/ 93 | DocProject/Help/*.HxT 94 | DocProject/Help/*.HxC 95 | DocProject/Help/*.hhc 96 | DocProject/Help/*.hhk 97 | DocProject/Help/*.hhp 98 | DocProject/Help/Html2 99 | DocProject/Help/html 100 | 101 | # Click-Once directory 102 | publish/ 103 | 104 | # Publish Web Output 105 | *.Publish.xml 106 | 107 | # Enable nuget.exe in the .nuget folder (though normally executables are not tracked) 108 | !.nuget/NuGet.exe 109 | 110 | # Windows Azure Build Output 111 | csx 112 | *.build.csdef 113 | 114 | # Windows Store app package directory 115 | AppPackages/ 116 | 117 | # Others 118 | sql/ 119 | *.Cache 120 | ClientBin/ 121 | [Ss]tyle[Cc]op.* 122 | ~$* 123 | *~ 124 | *.dbmdl 125 | *.[Pp]ublish.xml 126 | *.pfx 127 | *.publishsettings 128 | 129 | # RIA/Silverlight projects 130 | Generated_Code/ 131 | 132 | # Backup & report files from converting an old project file to a newer 133 | # Visual Studio version. Backup files are not needed, because we have git ;-) 134 | _UpgradeReport_Files/ 135 | Backup*/ 136 | UpgradeLog*.XML 137 | UpgradeLog*.htm 138 | 139 | # SQL Server files 140 | App_Data/*.mdf 141 | App_Data/*.ldf 142 | 143 | 144 | #LightSwitch generated files 145 | GeneratedArtifacts/ 146 | _Pvt_Extensions/ 147 | ModelManifest.xml 148 | 149 | # ========================= 150 | # Windows detritus 151 | # ========================= 152 | 153 | # Windows image file caches 154 | Thumbs.db 155 | ehthumbs.db 156 | 157 | # Folder config file 158 | Desktop.ini 159 | 160 | # Recycle Bin used on file shares 161 | $RECYCLE.BIN/ 162 | 163 | # Mac desktop service store files 164 | .DS_Store 165 | 166 | # =================================================== 167 | # Exclude F# project specific directories and files 168 | # =================================================== 169 | 170 | # NuGet Packages Directory 171 | packages/ 172 | 173 | # Generated documentation folder 174 | docs/output/ 175 | 176 | # Temp folder used for publishing docs 177 | temp*/ 178 | 179 | # Test results produced by build 180 | TestResults.xml 181 | TestResult.xml 182 | 183 | # Nuget outputs 184 | nuget/*.nupkg 185 | release.cmd 186 | release.sh 187 | localpackages/ 188 | paket-files 189 | *.orig 190 | docs/content/license.md 191 | docs/content/release-notes.md 192 | .fake 193 | docs/tools/FSharp.Formatting.svclog 194 | .ionide.debug 195 | *.bak 196 | project.lock.json 197 | -------------------------------------------------------------------------------- /Chess.WebUI/.paket/Paket.Restore.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 8 | 9 | true 10 | $(MSBuildThisFileDirectory) 11 | $(MSBuildThisFileDirectory)..\ 12 | $(PaketRootPath)paket-files\paket.restore.cached 13 | $(PaketRootPath)paket.lock 14 | /Library/Frameworks/Mono.framework/Commands/mono 15 | mono 16 | 17 | $(PaketRootPath)paket.exe 18 | $(PaketToolsPath)paket.exe 19 | "$(PaketExePath)" 20 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)" 21 | 22 | 23 | <_PaketExeExtension>$([System.IO.Path]::GetExtension("$(PaketExePath)")) 24 | dotnet "$(PaketExePath)" 25 | 26 | 27 | "$(PaketExePath)" 28 | 29 | $(PaketRootPath)paket.bootstrapper.exe 30 | $(PaketToolsPath)paket.bootstrapper.exe 31 | "$(PaketBootStrapperExePath)" 32 | $(MonoPath) --runtime=v4.0.30319 "$(PaketBootStrapperExePath)" 33 | 34 | 35 | 36 | 37 | true 38 | true 39 | 40 | 41 | 42 | 43 | 44 | 45 | true 46 | $(NoWarn);NU1603 47 | 48 | 49 | 50 | 51 | /usr/bin/shasum $(PaketRestoreCacheFile) | /usr/bin/awk '{ print $1 }' 52 | /usr/bin/shasum $(PaketLockFilePath) | /usr/bin/awk '{ print $1 }' 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | $([System.IO.File]::ReadAllText('$(PaketRestoreCacheFile)')) 66 | $([System.IO.File]::ReadAllText('$(PaketLockFilePath)')) 67 | true 68 | false 69 | true 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | $(MSBuildProjectDirectory)\obj\$(MSBuildProjectFile).paket.references.cached 79 | 80 | $(MSBuildProjectFullPath).paket.references 81 | 82 | $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references 83 | 84 | $(MSBuildProjectDirectory)\paket.references 85 | $(MSBuildProjectDirectory)\obj\$(MSBuildProjectFile).$(TargetFramework).paket.resolved 86 | true 87 | references-file-or-cache-not-found 88 | 89 | 90 | 91 | 92 | $([System.IO.File]::ReadAllText('$(PaketReferencesCachedFilePath)')) 93 | $([System.IO.File]::ReadAllText('$(PaketOriginalReferencesFilePath)')) 94 | references-file 95 | false 96 | 97 | 98 | 99 | 100 | false 101 | 102 | 103 | 104 | 105 | true 106 | target-framework '$(TargetFramework)' 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[0]) 124 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[1]) 125 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[4]) 126 | 127 | 128 | %(PaketReferencesFileLinesInfo.PackageVersion) 129 | All 130 | 131 | 132 | 133 | 134 | $(MSBuildProjectDirectory)/obj/$(MSBuildProjectFile).paket.clitools 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[0]) 144 | $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[1]) 145 | 146 | 147 | %(PaketCliToolFileLinesInfo.PackageVersion) 148 | 149 | 150 | 151 | 155 | 156 | 157 | 158 | 159 | 160 | false 161 | 162 | 163 | 164 | 165 | 166 | <_NuspecFilesNewLocation Include="$(BaseIntermediateOutputPath)$(Configuration)\*.nuspec"/> 167 | 168 | 169 | 170 | $(MSBuildProjectDirectory)/$(MSBuildProjectFile) 171 | true 172 | false 173 | true 174 | $(BaseIntermediateOutputPath)$(Configuration) 175 | $(BaseIntermediateOutputPath) 176 | 177 | 178 | 179 | <_NuspecFiles Include="$(AdjustedNuspecOutputPath)\*.nuspec"/> 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 232 | 233 | 274 | 275 | 276 | 277 | -------------------------------------------------------------------------------- /Chess.WebUI/.paket/paket.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WebUI/.paket/paket.exe -------------------------------------------------------------------------------- /Chess.WebUI/.paket/paket.exe.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | True 10 | 11 | 12 | 13 | 14 | 15 | 16 | True 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chess.WebUI/.paket/paket.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | true 7 | $(MSBuildThisFileDirectory) 8 | $(MSBuildThisFileDirectory)..\ 9 | $(PaketRootPath)paket.lock 10 | $(PaketRootPath)paket-files\paket.restore.cached 11 | /Library/Frameworks/Mono.framework/Commands/mono 12 | mono 13 | 14 | 15 | 16 | 17 | $(PaketRootPath)paket.exe 18 | $(PaketToolsPath)paket.exe 19 | "$(PaketExePath)" 20 | 21 | 22 | 23 | 24 | 25 | $(MSBuildProjectFullPath).paket.references 26 | 27 | 28 | 29 | 30 | $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references 31 | 32 | 33 | 34 | 35 | $(MSBuildProjectDirectory)\paket.references 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | $(PaketCommand) restore --references-file "$(PaketReferences)" 48 | 49 | RestorePackages; $(BuildDependsOn); 50 | 51 | 52 | 53 | true 54 | 55 | 56 | 57 | $([System.IO.File]::ReadAllText('$(PaketRestoreCacheFile)')) 58 | $([System.IO.File]::ReadAllText('$(PaketLockFilePath)')) 59 | true 60 | false 61 | true 62 | 63 | 64 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Chess.WebUI/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "command": "dotnet", 6 | "runner": "terminal", 7 | "args": [], 8 | "options": { 9 | "cwd": "${workspaceRoot}/src" 10 | }, 11 | "tasks": [ 12 | { 13 | "taskName": "Start", 14 | "args": ["fable", "yarn-run", "start" ], 15 | "isBuildCommand": true, 16 | "suppressTaskName": true, 17 | "isBackground": true, 18 | "problemMatcher": { 19 | "fileLocation": "absolute", 20 | "background": { 21 | "activeOnStart": true, 22 | "beginsPattern":{ 23 | "regexp": "webpack: Compiling" 24 | }, 25 | "endsPattern":{ 26 | "regexp": "webpack: (Compiled successfully|Failed to compile)" 27 | } 28 | }, 29 | "pattern": { 30 | "regexp": "^(.*)\\((\\d+),(\\d+)\\): \\((\\d+),(\\d+)\\) (warning|error) FABLE: (.*)$", 31 | "file": 1, 32 | "line": 2, 33 | "column": 3, 34 | "endLine": 4, 35 | "endColumn": 5, 36 | "severity": 6, 37 | "message": 7 38 | } 39 | } 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /Chess.WebUI/Chess.WebUI.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{f2a71f9b-5d33-465a-a702-920d77279786}") = "Chess.WebUI", "src/Chess.WebUI.fsproj", "{E5DEFC96-37CF-4844-9C9E-CFA0A9A8AED3}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {E5DEFC96-37CF-4844-9C9E-CFA0A9A8AED3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {E5DEFC96-37CF-4844-9C9E-CFA0A9A8AED3}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {E5DEFC96-37CF-4844-9C9E-CFA0A9A8AED3}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {E5DEFC96-37CF-4844-9C9E-CFA0A9A8AED3}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | EndGlobal 18 | -------------------------------------------------------------------------------- /Chess.WebUI/NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Chess.WebUI/README.md: -------------------------------------------------------------------------------- 1 | # Fable.Elmish.React Template 2 | 3 | This template can be used to generate a simple web app with [Fable](http://fable.io/) and [Elmish](https://fable-elmish.github.io/). 4 | You can find more templates by searching `Fable.Template` packages in [Nuget](https://www.nuget.org). 5 | 6 | ## Requirements 7 | 8 | * [dotnet SDK](https://www.microsoft.com/net/download/core) 2.0.0 or higher 9 | * [node.js](https://nodejs.org) 4.8.2 or higher 10 | * npm5: JS package manager 11 | 12 | Although is not a Fable requirement, on macOS and Linux you'll need [Mono](http://www.mono-project.com/) for other F# tooling like Paket or editor support. 13 | 14 | ## Editor 15 | 16 | The project can be used by editors compatible with the new .fsproj format, like VS Code + [Ionide](http://ionide.io/), Emacs with [fsharp-mode](https://github.com/fsharp/emacs-fsharp-mode) or [Rider](https://www.jetbrains.com/rider/). **Visual Studio for Mac** is also compatible but in the current version the package auto-restore function conflicts with Paket so you need to disable it: `Preferences > Nuget > General`. 17 | 18 | ## Installing the template 19 | 20 | In a terminal, run `dotnet new -i Fable.Template.Elmish.React::*` to install or update the template to latest version. 21 | 22 | > In some shells you many need quotations: `dotnet new -i "Fable.Template.Elmish.React::*"`. If you use dotnet SDK 2, you should only need to type `dotnet new -i Fable.Template.Elmish.React`. 23 | 24 | ## Creating a new project with the template 25 | 26 | In a terminal, run `dotnet fable-elmish-react` to create a project in the current directory. Type `dotnet new fable-elmish-react -n awesome` instead to create a subfolder named `awesome` and put the new project there. 27 | 28 | > The project will have the name of the directory. You may get some issues if the directory name contains some special characters like hyphens 29 | 30 | ## Building and running the app 31 | 32 | * Install JS dependencies: `npm install` 33 | * **Move to `src` folder**: `cd src` 34 | * Install F# dependencies: `dotnet restore` 35 | * Start Fable daemon and [Webpack](https://webpack.js.org/) dev server: `dotnet fable npm-start` 36 | * In your browser, open: http://localhost:8080/ 37 | 38 | > `dotnet fable yarn-start` (or `npm-start`) is used to start the Fable daemon and run a script in package.json concurrently. It's a shortcut of `yarn-run [SCRIP_NAME]`, e.g. `dotnet fable yarn-run start`. 39 | 40 | If you are using VS Code + [Ionide](http://ionide.io/), you can also use the key combination: Ctrl+Shift+B (Cmd+Shift+B on macOS) instead of typing the `dotnet fable yarn-start` command. This also has the advantage that Fable-specific errors will be highlighted in the editor along with other F# errors. 41 | 42 | Any modification you do to the F# code will be reflected in the web page after saving. When you want to output the JS code to disk, run `dotnet fable yarn-build` (or `npm-build`) and you'll get a minified JS bundle in the `public` folder. 43 | 44 | ## Project structure 45 | 46 | ### Paket 47 | 48 | [Paket](https://fsprojects.github.io/Paket/) is the package manager used for F# dependencies. It doesn't need a global installation, the binary is included in the `.paket` folder. Other Paket related files are: 49 | 50 | - **paket.dependencies**: contains all the dependencies in the repository. 51 | - **paket.references**: there should be one such a file next to each `.fsproj` file. 52 | - **paket.lock**: automatically generated, but should committed to source control, [see why](https://fsprojects.github.io/Paket/faq.html#Why-should-I-commit-the-lock-file). 53 | - **Nuget.Config**: prevents conflicts with Paket in machines with some Nuget configuration. 54 | 55 | > Paket dependencies will be installed in the `packages` directory. See [Paket website](https://fsprojects.github.io/Paket/) for more info. 56 | 57 | ### npm 58 | 59 | - **package.json**: contains the JS dependencies together with other info, like development scripts. 60 | - **package-lock.json**: is the lock file created by npm5. 61 | 62 | > JS dependencies will be installed in `node_modules`. See [npm](https://www.npmjs.com/) website for more info. 63 | 64 | ### Webpack 65 | 66 | [Webpack](https://webpack.js.org) is a bundler, which links different JS sources into a single file making deployment much easier. It also offers other features, like a static dev server that can automatically refresh the browser upon changes in your code or a minifier for production release. Fable interacts with Webpack through the `fable-loader`. 67 | 68 | - **webpack.config.js**: is the configuration file for Webpack. It allows you to set many things: like the path of the bundle, the port for the development server or [Babel](https://babeljs.io/) options. See [Webpack website](https://webpack.js.org) for more info. 69 | 70 | ### F# source files 71 | 72 | The template only contains two F# source files: the project (.fsproj) and a source file (.fs) in `src` folder. 73 | -------------------------------------------------------------------------------- /Chess.WebUI/RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | ### 0.3.5 2 | 3 | * Update paket.exe 4 | 5 | ### 0.3.4 6 | 7 | * Fix mistakes in the README 8 | * Add VSCode tasks 9 | * Fix IE11 support 10 | * Use source-map only during development 11 | * Update dependencies 12 | 13 | ### 0.3.3 14 | 15 | * Add Fable.Elmish.HMR 16 | 17 | ### 0.3.2 18 | 19 | * Fix #30: Upgrade dependencies, use Fable.React 1.2.0 20 | 21 | ### 0.3.1 22 | 23 | * Fix dotnet version in README 24 | 25 | ### 0.3.0 26 | 27 | * Go to netstandard2.0 28 | * Updates all deps (.Net & JS) 29 | 30 | ### 0.2.6 31 | 32 | * Include user choice between npm5 and yarn 33 | * Fix viewport (eg: for mobile) 34 | * Update dependencies to latest version 35 | * Better documention in the README.md 36 | * Webpack 3 37 | * Move project file into src 38 | 39 | ### 0.2.5 40 | 41 | * Use dotnet-fable as clitool 42 | * Fix Navbar display 43 | * Fix #15: Prevent template engine to process paket files 44 | * Do not emit: //+:cnd in App.fs 45 | 46 | ### 0.2.4 47 | 48 | * Update paket to latest version 49 | 50 | ### 0.2.3 51 | 52 | * Move RELEASE_NOTES into template directory 53 | * Fix #12: Could not download FSharp.Core 4.2 54 | 55 | ### 0.2.2 56 | 57 | * Update to Fable 1.0.8 58 | * Update npm packages to latest versions 59 | 60 | ### 0.2.0 61 | 62 | * Use Paket to resolve Fable deps 63 | * Change `GroupIdentity` to prevent conflict with *Fable simple* template 64 | 65 | ### 0.1.8 66 | 67 | * Simply `toHash` function. Remove `function` in favor of `match page with` 68 | * Remove `classList` helper. It's now present in `fable-react` 69 | * Set minimun version supported in package.json 70 | 71 | ### 0.1.7 72 | 73 | * Fix `fable-core` to use `next` version 74 | * Update react/*.proj file 75 | * Fix `DEBUG` conditional statement 76 | 77 | ### 0.1.6 78 | 79 | * Set all fable-dependencies to use `next` version 80 | * Fix nuget file. (There was node_modules inside of it.) 81 | * Fix void elements usage 82 | -------------------------------------------------------------------------------- /Chess.WebUI/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Chess.WebUI", 3 | "version": "1.0.0", 4 | "description": "Simple Fable App", 5 | "scripts": { 6 | "build": "webpack -p", 7 | "start": "webpack-dev-server", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "babel-runtime": "^6.26.0", 15 | "react": "^15.6.2", 16 | "react-dom": "^15.6.2", 17 | "remotedev": "^0.2.7" 18 | }, 19 | "devDependencies": { 20 | "babel-core": "^6.26.0", 21 | "babel-loader": "^7.1.2", 22 | "babel-plugin-transform-runtime": "^6.23.0", 23 | "babel-preset-es2015": "^6.24.1", 24 | "bulma": "^0.5.2", 25 | "css-loader": "^0.28.7", 26 | "fable-loader": "^1.1.2", 27 | "fable-utils": "^1.0.6", 28 | "loglevel": "^1.5.0", 29 | "node-sass": "^7.0.0", 30 | "sass-loader": "^6.0.6", 31 | "style-loader": "^0.18.2", 32 | "webpack": "^3.6.0", 33 | "webpack-dev-server": "^2.8.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Chess.WebUI/paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://nuget.org/api/v2 2 | 3 | storage:none 4 | 5 | nuget Fable.Elmish.HMR 6 | nuget FSharp.Core 7 | nuget Fable.Core 8 | nuget Fable.Elmish.React 9 | nuget Fable.Elmish.Browser 10 | nuget Fable.Elmish.Debugger 11 | 12 | clitool dotnet-fable -------------------------------------------------------------------------------- /Chess.WebUI/public/img/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WebUI/public/img/favicon-16x16.png -------------------------------------------------------------------------------- /Chess.WebUI/public/img/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WebUI/public/img/favicon-32x32.png -------------------------------------------------------------------------------- /Chess.WebUI/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Elmish Fable App 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Chess.WebUI/sass/main.sass: -------------------------------------------------------------------------------- 1 | // Colors 2 | $purple: #7a325d 3 | $primary: $purple 4 | $black: #202020 5 | 6 | @import '../node_modules/bulma/bulma' 7 | 8 | .navbar-bg 9 | background-color: $black 10 | .nav 11 | background-color: $black 12 | .title 13 | color: #686868 14 | font-weight: 600 15 | 16 | .twitter 17 | color: #55acee !important // Override bulma 18 | border-color: #55acee 19 | 20 | .github 21 | color: $black !important // Override bulma 22 | border-color: $black 23 | -------------------------------------------------------------------------------- /Chess.WebUI/src/App.fs: -------------------------------------------------------------------------------- 1 | module App.View 2 | 3 | open Elmish 4 | open Elmish.Browser.Navigation 5 | open Elmish.Browser.UrlParser 6 | open Fable.Core 7 | open Fable.Core.JsInterop 8 | open Fable.Import 9 | open Fable.Import.Browser 10 | open Types 11 | open App.State 12 | open Global 13 | 14 | importAll "../sass/main.sass" 15 | 16 | open Fable.Helpers.React 17 | open Fable.Helpers.React.Props 18 | 19 | let menuItem label page currentPage = 20 | li 21 | [ ] 22 | [ a 23 | [ classList [ "is-active", page = currentPage ] 24 | Href (toHash page) ] 25 | [ str label ] ] 26 | 27 | let menu currentPage = 28 | aside 29 | [ ClassName "menu" ] 30 | [ p 31 | [ ClassName "menu-label" ] 32 | [ str "General" ] 33 | ul 34 | [ ClassName "menu-list" ] 35 | [ menuItem "Home" Home currentPage 36 | menuItem "Counter sample" Counter currentPage 37 | menuItem "About" Page.About currentPage ] ] 38 | 39 | let root model dispatch = 40 | 41 | let pageHtml = 42 | function 43 | | Page.About -> Info.View.root 44 | | Counter -> Counter.View.root model.counter (CounterMsg >> dispatch) 45 | | Home -> Home.View.root model.home (HomeMsg >> dispatch) 46 | 47 | div 48 | [] 49 | [ div 50 | [ ClassName "navbar-bg" ] 51 | [ div 52 | [ ClassName "container" ] 53 | [ Navbar.View.root ] ] 54 | div 55 | [ ClassName "section" ] 56 | [ div 57 | [ ClassName "container" ] 58 | [ div 59 | [ ClassName "columns" ] 60 | [ div 61 | [ ClassName "column is-3" ] 62 | [ menu model.currentPage ] 63 | div 64 | [ ClassName "column" ] 65 | [ pageHtml model.currentPage ] ] ] ] ] 66 | 67 | open Elmish.React 68 | open Elmish.Debug 69 | open Elmish.HMR 70 | 71 | // App 72 | Program.mkProgram init update root 73 | |> Program.toNavigable (parseHash pageParser) urlUpdate 74 | #if DEBUG 75 | |> Program.withDebugger 76 | |> Program.withHMR 77 | #endif 78 | |> Program.withReact "elmish-app" 79 | |> Program.run 80 | -------------------------------------------------------------------------------- /Chess.WebUI/src/Chess.WebUI.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Chess.WebUI/src/Counter/State.fs: -------------------------------------------------------------------------------- 1 | module Counter.State 2 | 3 | open Elmish 4 | open Types 5 | 6 | let init () : Model * Cmd = 7 | 0, [] 8 | 9 | let update msg model = 10 | match msg with 11 | | Increment -> 12 | model + 1, [] 13 | | Decrement -> 14 | model - 1, [] 15 | | Reset -> 16 | 0, [] 17 | -------------------------------------------------------------------------------- /Chess.WebUI/src/Counter/Types.fs: -------------------------------------------------------------------------------- 1 | module Counter.Types 2 | 3 | type Model = int 4 | 5 | type Msg = 6 | | Increment 7 | | Decrement 8 | | Reset 9 | -------------------------------------------------------------------------------- /Chess.WebUI/src/Counter/View.fs: -------------------------------------------------------------------------------- 1 | module Counter.View 2 | 3 | open Fable.Core 4 | open Fable.Helpers.React 5 | open Fable.Helpers.React.Props 6 | open Types 7 | 8 | let simpleButton txt action dispatch = 9 | div 10 | [ ClassName "column is-narrow" ] 11 | [ a 12 | [ ClassName "button" 13 | OnClick (fun _ -> action |> dispatch) ] 14 | [ str txt ] ] 15 | 16 | let root model dispatch = 17 | div 18 | [ ClassName "columns is-vcentered" ] 19 | [ div [ ClassName "column" ] [ ] 20 | div 21 | [ ClassName "column is-narrow" 22 | Style 23 | [ CSSProp.Width "170px" ] ] 24 | [ str (sprintf "Counter value: %i" model) ] 25 | simpleButton "+1" Increment dispatch 26 | simpleButton "-1" Decrement dispatch 27 | simpleButton "Reset" Reset dispatch 28 | div [ ClassName "column" ] [ ] ] 29 | -------------------------------------------------------------------------------- /Chess.WebUI/src/Global.fs: -------------------------------------------------------------------------------- 1 | module Global 2 | 3 | type Page = 4 | | Home 5 | | Counter 6 | | About 7 | 8 | let toHash page = 9 | match page with 10 | | About -> "#about" 11 | | Counter -> "#counter" 12 | | Home -> "#home" 13 | -------------------------------------------------------------------------------- /Chess.WebUI/src/Home/State.fs: -------------------------------------------------------------------------------- 1 | module Home.State 2 | 3 | open Elmish 4 | open Types 5 | 6 | let init () : Model * Cmd = 7 | "", [] 8 | 9 | let update msg model : Model * Cmd = 10 | match msg with 11 | | ChangeStr str -> 12 | str, [] 13 | -------------------------------------------------------------------------------- /Chess.WebUI/src/Home/Types.fs: -------------------------------------------------------------------------------- 1 | module Home.Types 2 | 3 | type Model = string 4 | 5 | type Msg = 6 | | ChangeStr of string 7 | -------------------------------------------------------------------------------- /Chess.WebUI/src/Home/View.fs: -------------------------------------------------------------------------------- 1 | module Home.View 2 | 3 | open Fable.Core 4 | open Fable.Core.JsInterop 5 | open Fable.Helpers.React 6 | open Fable.Helpers.React.Props 7 | open Types 8 | 9 | let root model dispatch = 10 | div 11 | [ ] 12 | [ p 13 | [ ClassName "control" ] 14 | [ input 15 | [ ClassName "input" 16 | Type "text" 17 | Placeholder "Type your name" 18 | DefaultValue model 19 | AutoFocus true 20 | OnChange (fun ev -> !!ev.target?value |> ChangeStr |> dispatch ) ] ] 21 | br [ ] 22 | span 23 | [ ] 24 | [ str (sprintf "Hello %s" model) ] ] 25 | -------------------------------------------------------------------------------- /Chess.WebUI/src/Info/View.fs: -------------------------------------------------------------------------------- 1 | module Info.View 2 | 3 | open Fable.Helpers.React 4 | open Fable.Helpers.React.Props 5 | 6 | let root = 7 | div 8 | [ ClassName "content" ] 9 | [ h1 10 | [ ] 11 | [ str "About page" ] 12 | p 13 | [ ] 14 | [ str "This template is a simple application build with Fable + Elmish + React." ] ] 15 | -------------------------------------------------------------------------------- /Chess.WebUI/src/Navbar/View.fs: -------------------------------------------------------------------------------- 1 | module Navbar.View 2 | 3 | open Fable.Helpers.React 4 | open Fable.Helpers.React.Props 5 | 6 | let navButton classy href faClass txt = 7 | p 8 | [ ClassName "control" ] 9 | [ a 10 | [ ClassName (sprintf "button %s" classy) 11 | Href href ] 12 | [ span 13 | [ ClassName "icon" ] 14 | [ i 15 | [ ClassName (sprintf "fa %s" faClass) ] 16 | [ ] ] 17 | span 18 | [ ] 19 | [ str txt ] ] ] 20 | 21 | let navButtons = 22 | span 23 | [ ClassName "nav-item" ] 24 | [ div 25 | [ ClassName "field is-grouped" ] 26 | [ navButton "twitter" "https://twitter.com/FableCompiler" "fa-twitter" "Twitter" 27 | navButton "github" "https://github.com/fable-compiler/fable-elmish" "fa-github" "Fork me" 28 | navButton "github" "https://gitter.im/fable-compiler/Fable" "fa-comments" "Gitter" ] ] 29 | 30 | let root = 31 | nav 32 | [ ClassName "nav" ] 33 | [ div 34 | [ ClassName "nav-left" ] 35 | [ h1 36 | [ ClassName "nav-item is-brand title is-4" ] 37 | [ str "Elmish" ] ] 38 | div 39 | [ ClassName "nav-right" ] 40 | [ navButtons ] ] 41 | -------------------------------------------------------------------------------- /Chess.WebUI/src/State.fs: -------------------------------------------------------------------------------- 1 | module App.State 2 | 3 | open Elmish 4 | open Elmish.Browser.Navigation 5 | open Elmish.Browser.UrlParser 6 | open Fable.Import.Browser 7 | open Global 8 | open Types 9 | 10 | let pageParser: ParserPage,Page> = 11 | oneOf [ 12 | map About (s "about") 13 | map Counter (s "counter") 14 | map Home (s "home") 15 | ] 16 | 17 | let urlUpdate (result: Option) model = 18 | match result with 19 | | None -> 20 | console.error("Error parsing url") 21 | model,Navigation.modifyUrl (toHash model.currentPage) 22 | | Some page -> 23 | { model with currentPage = page }, [] 24 | 25 | let init result = 26 | let (counter, counterCmd) = Counter.State.init() 27 | let (home, homeCmd) = Home.State.init() 28 | let (model, cmd) = 29 | urlUpdate result 30 | { currentPage = Home 31 | counter = counter 32 | home = home } 33 | model, Cmd.batch [ cmd 34 | Cmd.map CounterMsg counterCmd 35 | Cmd.map HomeMsg homeCmd ] 36 | 37 | let update msg model = 38 | match msg with 39 | | CounterMsg msg -> 40 | let (counter, counterCmd) = Counter.State.update msg model.counter 41 | { model with counter = counter }, Cmd.map CounterMsg counterCmd 42 | | HomeMsg msg -> 43 | let (home, homeCmd) = Home.State.update msg model.home 44 | { model with home = home }, Cmd.map HomeMsg homeCmd 45 | -------------------------------------------------------------------------------- /Chess.WebUI/src/Types.fs: -------------------------------------------------------------------------------- 1 | module App.Types 2 | 3 | open Global 4 | 5 | type Msg = 6 | | CounterMsg of Counter.Types.Msg 7 | | HomeMsg of Home.Types.Msg 8 | 9 | type Model = { 10 | currentPage: Page 11 | counter: Counter.Types.Model 12 | home: Home.Types.Model 13 | } 14 | -------------------------------------------------------------------------------- /Chess.WebUI/src/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | Fable.Core 3 | Fable.Elmish.React 4 | Fable.Elmish.Browser 5 | Fable.Elmish.Debugger 6 | Fable.Elmish.HMR 7 | dotnet-fable 8 | -------------------------------------------------------------------------------- /Chess.WebUI/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var webpack = require("webpack"); 3 | var fableUtils = require("fable-utils"); 4 | 5 | function resolve(filePath) { 6 | return path.join(__dirname, filePath) 7 | } 8 | 9 | var babelOptions = fableUtils.resolveBabelOptions({ 10 | presets: [["es2015", { "modules": false }]], 11 | plugins: [["transform-runtime", { 12 | "helpers": true, 13 | // We don't need the polyfills as we're already calling 14 | // cdn.polyfill.io/v2/polyfill.js in index.html 15 | "polyfill": false, 16 | "regenerator": false 17 | }]] 18 | }); 19 | 20 | var isProduction = process.argv.indexOf("-p") >= 0; 21 | console.log("Bundling for " + (isProduction ? "production" : "development") + "..."); 22 | 23 | module.exports = { 24 | devtool: isProduction ? undefined : "source-map", 25 | entry: resolve('./src/Chess.WebUI.fsproj'), 26 | output: { 27 | filename: 'bundle.js', 28 | path: resolve('./public'), 29 | }, 30 | resolve: { 31 | modules: [ 32 | "node_modules", resolve("./node_modules/") 33 | ] 34 | }, 35 | devServer: { 36 | contentBase: resolve('./public'), 37 | port: 8080, 38 | hot: true, 39 | inline: true 40 | }, 41 | module: { 42 | rules: [ 43 | { 44 | test: /\.fs(x|proj)?$/, 45 | use: { 46 | loader: "fable-loader", 47 | options: { 48 | babel: babelOptions, 49 | define: isProduction ? [] : ["DEBUG"] 50 | } 51 | } 52 | }, 53 | { 54 | test: /\.js$/, 55 | exclude: /node_modules/, 56 | use: { 57 | loader: 'babel-loader', 58 | options: babelOptions 59 | }, 60 | }, 61 | { 62 | test: /\.sass$/, 63 | use: [ 64 | "style-loader", 65 | "css-loader", 66 | "sass-loader" 67 | ] 68 | } 69 | ] 70 | }, 71 | plugins: isProduction ? [] : [ 72 | new webpack.HotModuleReplacementPlugin(), 73 | new webpack.NamedModulesPlugin() 74 | ] 75 | }; 76 | -------------------------------------------------------------------------------- /Chess.WpfUI/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Chess.WpfUI/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Chess.WpfUI/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace Chess.WpfUI 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chess.WpfUI/Chess.WpfUI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7} 8 | WinExe 9 | Properties 10 | Chess.WpfUI 11 | Chess.WpfUI 12 | v4.7.1 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | true 17 | 18 | 19 | 20 | AnyCPU 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | AnyCPU 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | ..\packages\FSharp.Core.4.6.2\lib\net45\FSharp.Core.dll 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 4.0 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | MSBuild:Compile 60 | Designer 61 | 62 | 63 | MSBuild:Compile 64 | Designer 65 | 66 | 67 | App.xaml 68 | Code 69 | 70 | 71 | MainWindow.xaml 72 | Code 73 | 74 | 75 | 76 | 77 | Code 78 | 79 | 80 | True 81 | True 82 | Resources.resx 83 | 84 | 85 | True 86 | Settings.settings 87 | True 88 | 89 | 90 | ResXFileCodeGenerator 91 | Resources.Designer.cs 92 | 93 | 94 | 95 | SettingsSingleFileGenerator 96 | Settings.Designer.cs 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | {e2f18796-6da8-45e4-a1a3-0db4f80e2624} 106 | Chess.Domain 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 131 | -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_black/bishop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_black/bishop.png -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_black/king.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_black/king.png -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_black/knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_black/knight.png -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_black/pawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_black/pawn.png -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_black/queen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_black/queen.png -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_black/rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_black/rook.png -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_white/bishop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_white/bishop.png -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_white/king.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_white/king.png -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_white/knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_white/knight.png -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_white/pawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_white/pawn.png -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_white/queen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_white/queen.png -------------------------------------------------------------------------------- /Chess.WpfUI/Images/pieces_white/rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMarr/FsharpChess/d26c814cd810a3ae812c911981ee9bd46da7fe95/Chess.WpfUI/Images/pieces_white/rook.png -------------------------------------------------------------------------------- /Chess.WpfUI/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 42 | 43 | 44 | 47 | 48 | 49 | 52 | 53 | 54 | 57 | 58 | 59 | 62 | 63 | 64 | 67 | 68 | 69 | 72 | 73 | 74 | 75 | 78 | 79 | 80 | 83 | 84 | 85 | 88 | 89 | 90 | 93 | 94 | 95 | 98 | 99 | 100 | 103 | 104 | 105 | 108 | 109 | 110 | 113 | 114 | 115 | 116 | 119 | 120 | 121 | 124 | 125 | 126 | 129 | 130 | 131 | 134 | 135 | 136 | 139 | 140 | 141 | 144 | 145 | 146 | 149 | 150 | 151 | 154 | 155 | 156 | 157 | 160 | 161 | 162 | 165 | 166 | 167 | 170 | 171 | 172 | 175 | 176 | 177 | 180 | 181 | 182 | 185 | 186 | 187 | 190 | 191 | 192 | 195 | 196 | 197 | 198 | 201 | 202 | 203 | 206 | 207 | 208 | 211 | 212 | 213 | 216 | 217 | 218 | 221 | 222 | 223 | 226 | 227 | 228 | 231 | 232 | 233 | 236 | 237 | 238 | 239 | 242 | 243 | 244 | 247 | 248 | 249 | 252 | 253 | 254 | 257 | 258 | 259 | 262 | 263 | 264 | 267 | 268 | 269 | 272 | 273 | 274 | 277 | 278 | 279 | 280 | 283 | 284 | 285 | 288 | 289 | 290 | 293 | 294 | 295 | 298 | 299 | 300 | 303 | 304 | 305 | 308 | 309 | 310 | 313 | 314 | 315 | 318 | 319 | 320 | 321 | 324 | 325 | 326 | 329 | 330 | 331 | 334 | 335 | 336 | 339 | 340 | 341 | 344 | 345 | 346 | 349 | 350 | 351 | 354 | 355 | 356 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | -------------------------------------------------------------------------------- /Chess.WpfUI/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | using Chess.Domain; 16 | 17 | namespace Chess.WpfUI 18 | { 19 | /// 20 | /// Interaction logic for MainWindow.xaml 21 | /// 22 | public partial class MainWindow : Window 23 | { 24 | Api.ChessApi _api; 25 | List _moveQueue; 26 | 27 | 28 | public MainWindow() 29 | { 30 | InitializeComponent(); 31 | 32 | _api = new Api.ChessApi(); 33 | _moveQueue = new List(); 34 | } 35 | 36 | private void Window_Loaded(object sender, RoutedEventArgs e) 37 | { 38 | RefreshBoard(); 39 | } 40 | 41 | private void RefreshBoard() 42 | { 43 | foreach (var cell in _api.Cells) 44 | { 45 | var border = (Border)FindName(cell.Coord); 46 | var button = border.Child as Button; 47 | var img = button.Content as Image; 48 | if (cell.IsOccupied) 49 | { 50 | var path = $"/Images/pieces_{cell.Color}/{cell.Rank}.png"; 51 | var bm = new BitmapImage(new Uri(path, UriKind.Relative)); 52 | img.Source = bm; 53 | } 54 | else 55 | { 56 | img.Source = null; 57 | } 58 | } 59 | 60 | StatusMessage.Text = _api.Message; 61 | } 62 | 63 | private void Cell_Click(object sender, RoutedEventArgs e) 64 | { 65 | var btn = sender as Button; 66 | var border = btn.Parent as Border; 67 | 68 | if (_moveQueue.FirstOrDefault()?.Coord == border.Name) 69 | { 70 | _moveQueue.ForEach(q => q.RevertColor()); 71 | _moveQueue.Clear(); 72 | return; // Can't click a cell twice 73 | } 74 | 75 | _moveQueue.Add(new QueuedCell(border)); 76 | 77 | if (_moveQueue.Count > 1) 78 | { 79 | _api.Move(_moveQueue[0].Coord, _moveQueue[1].Coord); 80 | _moveQueue.ForEach(q => q.RevertColor()); 81 | _moveQueue.Clear(); 82 | RefreshBoard(); 83 | } 84 | } 85 | 86 | 87 | private class QueuedCell 88 | { 89 | public QueuedCell(Border border) 90 | { 91 | Border = border; 92 | Coord = border.Name; 93 | OrigColor = (border.Background as SolidColorBrush).Color; 94 | 95 | // Highlight cell 96 | border.Background = new SolidColorBrush(Colors.LightPink); 97 | } 98 | 99 | public Border Border { get; private set; } 100 | public string Coord { get; private set; } 101 | public Color OrigColor { get; private set; } 102 | 103 | public void RevertColor() 104 | { 105 | Border.Background = new SolidColorBrush(OrigColor); 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Chess.WpfUI/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("Chess.WpfUI")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("Chess.WpfUI")] 15 | [assembly: AssemblyCopyright("Copyright © 2017")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /Chess.WpfUI/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Chess.WpfUI.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Chess.WpfUI.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Chess.WpfUI/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /Chess.WpfUI/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Chess.WpfUI.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chess.WpfUI/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Chess.WpfUI/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Chess.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28729.10 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Chess.Domain", "Chess.Domain\Chess.Domain.fsproj", "{E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chess.WpfUI", "Chess.WpfUI\Chess.WpfUI.csproj", "{039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UI", "UI", "{C352872B-5D72-4081-A29A-A8C86F5AB683}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chess.ConsoleUI", "Chess.ConsoleUI\Chess.ConsoleUI.csproj", "{227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Fabulous UI", "Fabulous UI", "{4DA72EBA-3BF8-416E-AA80-B2FF49619085}" 15 | EndProject 16 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Chess.FabUI", "Chess.FabUI\Chess.FabUI.fsproj", "{28527D6A-4CB7-4AE7-9E98-561833460E6B}" 17 | EndProject 18 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Chess.FabUI.WPF", "Chess.FabUI.WPF\Chess.FabUI.WPF.fsproj", "{3A91CBBE-E5B4-43F8-9D1D-8E6147675246}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Ad-Hoc|Any CPU = Ad-Hoc|Any CPU 23 | Ad-Hoc|iPhone = Ad-Hoc|iPhone 24 | Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator 25 | AppStore|Any CPU = AppStore|Any CPU 26 | AppStore|iPhone = AppStore|iPhone 27 | AppStore|iPhoneSimulator = AppStore|iPhoneSimulator 28 | Debug|Any CPU = Debug|Any CPU 29 | Debug|iPhone = Debug|iPhone 30 | Debug|iPhoneSimulator = Debug|iPhoneSimulator 31 | Release|Any CPU = Release|Any CPU 32 | Release|iPhone = Release|iPhone 33 | Release|iPhoneSimulator = Release|iPhoneSimulator 34 | EndGlobalSection 35 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 36 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU 37 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU 38 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU 39 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU 40 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU 41 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU 42 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.AppStore|Any CPU.ActiveCfg = Release|Any CPU 43 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.AppStore|Any CPU.Build.0 = Release|Any CPU 44 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.AppStore|iPhone.ActiveCfg = Release|Any CPU 45 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.AppStore|iPhone.Build.0 = Release|Any CPU 46 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU 47 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU 48 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Debug|iPhone.ActiveCfg = Debug|Any CPU 51 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Debug|iPhone.Build.0 = Debug|Any CPU 52 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 53 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 54 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Release|iPhone.ActiveCfg = Release|Any CPU 57 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Release|iPhone.Build.0 = Release|Any CPU 58 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 59 | {E2F18796-6DA8-45E4-A1A3-0DB4F80E2624}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 60 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU 61 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU 62 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU 63 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU 64 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU 65 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU 66 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.AppStore|Any CPU.ActiveCfg = Release|Any CPU 67 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.AppStore|Any CPU.Build.0 = Release|Any CPU 68 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.AppStore|iPhone.ActiveCfg = Release|Any CPU 69 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.AppStore|iPhone.Build.0 = Release|Any CPU 70 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU 71 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU 72 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 73 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Debug|Any CPU.Build.0 = Debug|Any CPU 74 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Debug|iPhone.ActiveCfg = Debug|Any CPU 75 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Debug|iPhone.Build.0 = Debug|Any CPU 76 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 77 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 78 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Release|Any CPU.ActiveCfg = Release|Any CPU 79 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Release|Any CPU.Build.0 = Release|Any CPU 80 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Release|iPhone.ActiveCfg = Release|Any CPU 81 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Release|iPhone.Build.0 = Release|Any CPU 82 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 83 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 84 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU 85 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU 86 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU 87 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU 88 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU 89 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU 90 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.AppStore|Any CPU.ActiveCfg = Release|Any CPU 91 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.AppStore|Any CPU.Build.0 = Release|Any CPU 92 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.AppStore|iPhone.ActiveCfg = Release|Any CPU 93 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.AppStore|iPhone.Build.0 = Release|Any CPU 94 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU 95 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU 96 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 97 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Debug|Any CPU.Build.0 = Debug|Any CPU 98 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Debug|iPhone.ActiveCfg = Debug|Any CPU 99 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Debug|iPhone.Build.0 = Debug|Any CPU 100 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 101 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 102 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Release|Any CPU.ActiveCfg = Release|Any CPU 103 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Release|Any CPU.Build.0 = Release|Any CPU 104 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Release|iPhone.ActiveCfg = Release|Any CPU 105 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Release|iPhone.Build.0 = Release|Any CPU 106 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 107 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 108 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU 109 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU 110 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU 111 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU 112 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU 113 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU 114 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU 115 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.AppStore|Any CPU.Build.0 = Debug|Any CPU 116 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU 117 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.AppStore|iPhone.Build.0 = Debug|Any CPU 118 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU 119 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU 120 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 121 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Debug|Any CPU.Build.0 = Debug|Any CPU 122 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Debug|iPhone.ActiveCfg = Debug|Any CPU 123 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Debug|iPhone.Build.0 = Debug|Any CPU 124 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 125 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 126 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Release|Any CPU.ActiveCfg = Release|Any CPU 127 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Release|Any CPU.Build.0 = Release|Any CPU 128 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Release|iPhone.ActiveCfg = Release|Any CPU 129 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Release|iPhone.Build.0 = Release|Any CPU 130 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 131 | {28527D6A-4CB7-4AE7-9E98-561833460E6B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 132 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU 133 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU 134 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU 135 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU 136 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU 137 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU 138 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU 139 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.AppStore|Any CPU.Build.0 = Debug|Any CPU 140 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.AppStore|iPhone.ActiveCfg = Debug|Any CPU 141 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.AppStore|iPhone.Build.0 = Debug|Any CPU 142 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU 143 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU 144 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 145 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Debug|Any CPU.Build.0 = Debug|Any CPU 146 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Debug|iPhone.ActiveCfg = Debug|Any CPU 147 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Debug|iPhone.Build.0 = Debug|Any CPU 148 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 149 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 150 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Release|Any CPU.ActiveCfg = Release|Any CPU 151 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Release|Any CPU.Build.0 = Release|Any CPU 152 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Release|iPhone.ActiveCfg = Release|Any CPU 153 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Release|iPhone.Build.0 = Release|Any CPU 154 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 155 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 156 | EndGlobalSection 157 | GlobalSection(SolutionProperties) = preSolution 158 | HideSolutionNode = FALSE 159 | EndGlobalSection 160 | GlobalSection(NestedProjects) = preSolution 161 | {039D0DC3-6D17-4409-8FC8-0BB427AD7DB7} = {C352872B-5D72-4081-A29A-A8C86F5AB683} 162 | {227BA2EA-261C-4EAC-B9FE-F6E16A1AD5E1} = {C352872B-5D72-4081-A29A-A8C86F5AB683} 163 | {28527D6A-4CB7-4AE7-9E98-561833460E6B} = {4DA72EBA-3BF8-416E-AA80-B2FF49619085} 164 | {3A91CBBE-E5B4-43F8-9D1D-8E6147675246} = {4DA72EBA-3BF8-416E-AA80-B2FF49619085} 165 | EndGlobalSection 166 | GlobalSection(ExtensibilityGlobals) = postSolution 167 | SolutionGuid = {69C12E65-33D6-402A-90D6-D37AB418B7C0} 168 | EndGlobalSection 169 | EndGlobal 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FsharpChess 2 | Chess with an F# domain engine and a simple WPF UI. 3 | 4 | I created this project as part of my ongoing effort to understand F#. I am a full time C# developer, so I am currently trying to rethink my imperative approach to designing software of doing everything with mutable state. 5 | 6 | I would really love it if anyone has any ideas on how to make this better, more "functional", or anything, really. 7 | 8 | # F# Domain 9 | 10 | ## Chess.fs 11 | This contains the domain entities (nouns), use cases (actions or methods), and the implementation methods (moving, validation, etc). 12 | 13 | ## Api.fs 14 | This provides a "ChessApi" class that allows the C# code to interface with the F# backend. The ChessApi class contains a mutable copy of the game state (the board, which is just a dictionary/map of cells and optional pieces, whose turn it is, etc). This is currently the only mutable state in the F# library. The idea is that I didn't want to make the C# calling code responsible for receiving the state and passing in back in for each move, as that seems like a very functional way of doing things. Instead, I wanted to "encapsulate" mutable state management within the F# ChessApi class. 15 | 16 | # C# WPF UI 17 | The WPF UI is a very simple code-behind design (no MVVM here), because my purpose was to focus on the F# bits. 18 | It creates an instance of the F# ChessApi class and it refreshes the UI after each move based on the newly generated game state. 19 | 20 | # TODO 21 | - [x] Implement "interposition" (or "blocking") - all pieces that capture linearly (all except the knight) should not be able to skip past enemy pieces. They should only be allowed the capture the first piece in their movement path. 22 | - [ ] Implement GameProgress to handle "check" and "checkmate" (game over + victor) and "stalement" status 23 | - [ ] Create checkmate detection - engine needs to be able to detect when a move results in the other color's king having no valid escape moves 24 | - [ ] Implement "legal" move detection - a player cannot move their own king into danger 25 | - [ ] Implement stalemate detection - when a player is not in check, but has no legal moves 26 | - [ ] If a player's king is in check, their next move MUST remove them from check state 27 | --------------------------------------------------------------------------------