diff --git a/config.json b/config.json
index d2a270f9e..dcffe3ed5 100644
--- a/config.json
+++ b/config.json
@@ -2218,6 +2218,21 @@
"recursion"
],
"difficulty": 5
+ },
+ {
+ "slug": "state-of-tic-tac-toe",
+ "name": "State of Tic-Tac-Toe",
+ "uuid": "4cb569ac-609b-49fd-a8cb-3da0be698c4c",
+ "practices": [
+ "multi-dimensional-arrays",
+ "result"
+ ],
+ "prerequisites": [
+ "multi-dimensional-arrays",
+ "chars",
+ "result"
+ ],
+ "difficulty": 5
}
],
"foregone": [
diff --git a/exercises/Exercises.sln b/exercises/Exercises.sln
index b676be98e..40399b4bf 100644
--- a/exercises/Exercises.sln
+++ b/exercises/Exercises.sln
@@ -307,6 +307,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ResistorColorTrio", "practi
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "KillerSudokuHelper", "practice\killer-sudoku-helper\KillerSudokuHelper.fsproj", "{FCE9E627-CFF9-4EF3-84BE-D42B354825AA}"
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "StateOfTicTacToe", "practice\state-of-tic-tac-toe\StateOfTicTacToe.fsproj", "{A12FEF19-5EE8-430E-BD66-2D93ADFC1944}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -904,6 +906,10 @@ Global
{FCE9E627-CFF9-4EF3-84BE-D42B354825AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCE9E627-CFF9-4EF3-84BE-D42B354825AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCE9E627-CFF9-4EF3-84BE-D42B354825AA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A12FEF19-5EE8-430E-BD66-2D93ADFC1944}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A12FEF19-5EE8-430E-BD66-2D93ADFC1944}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A12FEF19-5EE8-430E-BD66-2D93ADFC1944}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A12FEF19-5EE8-430E-BD66-2D93ADFC1944}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B404AA3C-A226-409A-A035-6C1DC66940DD} = {B7E719DB-FB8D-43B4-B529-55FCF6E3DC3F}
@@ -1056,5 +1062,6 @@ Global
{32F8738C-2782-4881-95C0-C621DC0D7ED9} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
{1850FAE9-5ACB-41D0-91BB-AD17A1021248} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
{FCE9E627-CFF9-4EF3-84BE-D42B354825AA} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
+ {A12FEF19-5EE8-430E-BD66-2D93ADFC1944} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
EndGlobalSection
EndGlobal
diff --git a/exercises/practice/state-of-tic-tac-toe/.config/dotnet-tools.json b/exercises/practice/state-of-tic-tac-toe/.config/dotnet-tools.json
new file mode 100644
index 000000000..0f7926bad
--- /dev/null
+++ b/exercises/practice/state-of-tic-tac-toe/.config/dotnet-tools.json
@@ -0,0 +1,12 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "fantomas-tool": {
+ "version": "4.7.9",
+ "commands": [
+ "fantomas"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/exercises/practice/state-of-tic-tac-toe/.docs/instructions.md b/exercises/practice/state-of-tic-tac-toe/.docs/instructions.md
new file mode 100644
index 000000000..1a03ebb6c
--- /dev/null
+++ b/exercises/practice/state-of-tic-tac-toe/.docs/instructions.md
@@ -0,0 +1,101 @@
+# Instructions
+
+In this exercise, you're going to implement a program that determines the state of a [tic-tac-toe][] game.
+(_You may also know the game as "noughts and crosses" or "Xs and Os"._)
+
+The game is played on a 3×3 grid.
+Players take turns to place `X`s and `O`s on the grid.
+The game ends when one player has won by placing three of marks in a row, column, or along a diagonal of the grid, or when the entire grid is filled up.
+
+In this exercise, we will assume that `X` starts.
+
+It's your job to determine which state a given game is in.
+
+There are 3 potential game states:
+
+- The game is **ongoing**.
+- The game ended in a **draw**.
+- The game ended in a **win**.
+
+If the given board is invalid, throw an appropriate error.
+
+If a board meets the following conditions, it is invalid:
+
+- The given board cannot be reached when turns are taken in the correct order (remember that `X` starts).
+- The game was played after it already ended.
+
+## Examples
+
+### Ongoing game
+
+```text
+ | |
+ X | |
+___|___|___
+ | |
+ | X | O
+___|___|___
+ | |
+ O | X |
+ | |
+```
+
+### Draw
+
+```text
+ | |
+ X | O | X
+___|___|___
+ | |
+ X | X | O
+___|___|___
+ | |
+ O | X | O
+ | |
+```
+
+### Win
+
+```text
+ | |
+ X | X | X
+___|___|___
+ | |
+ | O | O
+___|___|___
+ | |
+ | |
+ | |
+```
+
+### Invalid
+
+#### Wrong turn order
+
+```text
+ | |
+ O | O | X
+___|___|___
+ | |
+ | |
+___|___|___
+ | |
+ | |
+ | |
+```
+
+#### Continued playing after win
+
+```text
+ | |
+ X | X | X
+___|___|___
+ | |
+ O | O | O
+___|___|___
+ | |
+ | |
+ | |
+```
+
+[tic-tac-toe]: https://en.wikipedia.org/wiki/Tic-tac-toe
diff --git a/exercises/practice/state-of-tic-tac-toe/.meta/Example.fs b/exercises/practice/state-of-tic-tac-toe/.meta/Example.fs
new file mode 100644
index 000000000..34c9bc78f
--- /dev/null
+++ b/exercises/practice/state-of-tic-tac-toe/.meta/Example.fs
@@ -0,0 +1,45 @@
+module StateOfTicTacToe
+
+type EndGameState =
+ | Win
+ | Draw
+ | Ongoing
+
+type GameError =
+ | ConsecutiveMovesBySamePlayer
+ | WrongPlayerStarted
+ | MoveMadeAfterGameWasDone
+
+type Cell = char
+type Board = Cell [,]
+
+let won (player: Cell) (board: Board) =
+ let winning = [| player; player; player |]
+
+ Array.init 3 (fun i -> board[i, i]) = winning ||
+ Array.init 3 (fun i -> board[i, 2 - i]) = winning
+ || Array.init 3 (fun i -> board[i, *]) |> Array.contains winning
+ || Array.init 3 (fun i -> board[*, i]) |> Array.contains winning
+
+let gameState (board: Board) =
+ let numCells cell =
+ board
+ |> Seq.cast
+ |> Seq.filter ((=) cell)
+ |> Seq.length
+
+ let numNaughts = numCells 'O'
+ let numCrosses = numCells 'X'
+
+ if abs (numCrosses - numNaughts) > 1 then
+ Error ConsecutiveMovesBySamePlayer
+ elif numNaughts > numCrosses then
+ Error WrongPlayerStarted
+ elif won 'X' board && won 'O' board then
+ Error MoveMadeAfterGameWasDone
+ elif won 'X' board || won 'O' board then
+ Ok Win
+ elif numNaughts + numCrosses = 9 then
+ Ok Draw
+ else
+ Ok Ongoing
diff --git a/exercises/practice/state-of-tic-tac-toe/.meta/config.json b/exercises/practice/state-of-tic-tac-toe/.meta/config.json
new file mode 100644
index 000000000..f7c609687
--- /dev/null
+++ b/exercises/practice/state-of-tic-tac-toe/.meta/config.json
@@ -0,0 +1,19 @@
+{
+ "authors": [
+ "erikschierboom"
+ ],
+ "files": {
+ "solution": [
+ "StateOfTicTacToe.fs"
+ ],
+ "test": [
+ "StateOfTicTacToeTests.fs"
+ ],
+ "example": [
+ ".meta/Example.fs"
+ ]
+ },
+ "blurb": "Determine the game state of a match of Tic-Tac-Toe.",
+ "source": "Created by Sascha Mann for the Julia track of the Exercism Research Experiment.",
+ "source_url": "https://github.com/exercism/research_experiment_1/tree/julia-dev/exercises/julia-1-a"
+}
diff --git a/exercises/practice/state-of-tic-tac-toe/.meta/tests.toml b/exercises/practice/state-of-tic-tac-toe/.meta/tests.toml
new file mode 100644
index 000000000..8fc25e211
--- /dev/null
+++ b/exercises/practice/state-of-tic-tac-toe/.meta/tests.toml
@@ -0,0 +1,101 @@
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
+
+[fe8e9fa9-37af-4d7e-aa24-2f4b8517161a]
+description = "Won games -> Finished game where X won via left column victory"
+
+[96c30df5-ae23-4cf6-bf09-5ef056dddea1]
+description = "Won games -> Finished game where X won via middle column victory"
+
+[0d7a4b0a-2afd-4a75-8389-5fb88ab05eda]
+description = "Won games -> Finished game where X won via right column victory"
+
+[bd1007c0-ec5d-4c60-bb9f-1a4f22177d51]
+description = "Won games -> Finished game where O won via left column victory"
+
+[c032f800-5735-4354-b1b9-46f14d4ee955]
+description = "Won games -> Finished game where O won via middle column victory"
+
+[662c8902-c94a-4c4c-9d9c-e8ca513db2b4]
+description = "Won games -> Finished game where O won via right column victory"
+
+[2d62121f-7e3a-44a0-9032-0d73e3494941]
+description = "Won games -> Finished game where X won via top row victory"
+
+[108a5e82-cc61-409f-aece-d7a18c1beceb]
+description = "Won games -> Finished game where X won via middle row victory"
+include = false
+
+[346527db-4db9-4a96-b262-d7023dc022b0]
+description = "Won games -> Finished game where X won via middle row victory"
+reimplements = "108a5e82-cc61-409f-aece-d7a18c1beceb"
+
+[a013c583-75f8-4ab2-8d68-57688ff04574]
+description = "Won games -> Finished game where X won via bottom row victory"
+
+[2c08e7d7-7d00-487f-9442-e7398c8f1727]
+description = "Won games -> Finished game where O won via top row victory"
+
+[bb1d6c62-3e3f-4d1a-9766-f8803c8ed70f]
+description = "Won games -> Finished game where O won via middle row victory"
+
+[6ef641e9-12ec-44f5-a21c-660ea93907af]
+description = "Won games -> Finished game where O won via bottom row victory"
+
+[ab145b7b-26a7-426c-ab71-bf418cd07f81]
+description = "Won games -> Finished game where X won via falling diagonal victory"
+
+[7450caab-08f5-4f03-a74b-99b98c4b7a4b]
+description = "Won games -> Finished game where X won via rising diagonal victory"
+
+[c2a652ee-2f93-48aa-a710-a70cd2edce61]
+description = "Won games -> Finished game where O won via falling diagonal victory"
+
+[5b20ceea-494d-4f0c-a986-b99efc163bcf]
+description = "Won games -> Finished game where O won via rising diagonal victory"
+
+[035a49b9-dc35-47d3-9d7c-de197161b9d4]
+description = "Won games -> Finished game where X won via a row and a column victory"
+
+[e5dfdeb0-d2bf-4b5a-b307-e673f69d4a53]
+description = "Won games -> Finished game where X won via two diagonal victories"
+
+[b42ed767-194c-4364-b36e-efbfb3de8788]
+description = "Drawn games -> Draw"
+
+[227a76b2-0fef-4e16-a4bd-8f9d7e4c3b13]
+description = "Drawn games -> Another draw"
+
+[4d93f15c-0c40-43d6-b966-418b040012a9]
+description = "Ongoing games -> Ongoing game: one move in"
+
+[c407ae32-4c44-4989-b124-2890cf531f19]
+description = "Ongoing games -> Ongoing game: two moves in"
+
+[199b7a8d-e2b6-4526-a85e-78b416e7a8a9]
+description = "Ongoing games -> Ongoing game: five moves in"
+
+[1670145b-1e3d-4269-a7eb-53cd327b302e]
+description = "Invalid boards -> Invalid board: X went twice"
+
+[47c048e8-b404-4bcf-9e51-8acbb3253f3b]
+description = "Invalid boards -> Invalid board: O started"
+
+[b1dc8b13-46c4-47db-a96d-aa90eedc4e8d]
+description = "Invalid boards -> Invalid board"
+include = false
+
+[6c1920f2-ab5c-4648-a0c9-997414dda5eb]
+description = "Invalid boards -> Invalid board: X won and O kept playing"
+reimplements = "b1dc8b13-46c4-47db-a96d-aa90eedc4e8d"
+
+[4801cda2-f5b7-4c36-8317-3cdd167ac22c]
+description = "Invalid boards -> Invalid board: players kept playing after a win"
diff --git a/exercises/practice/state-of-tic-tac-toe/StateOfTicTacToe.fs b/exercises/practice/state-of-tic-tac-toe/StateOfTicTacToe.fs
new file mode 100644
index 000000000..da839d6dc
--- /dev/null
+++ b/exercises/practice/state-of-tic-tac-toe/StateOfTicTacToe.fs
@@ -0,0 +1,7 @@
+module StateOfTicTacToe
+
+// TODO: define the 'EndGameState' type
+// TODO: define the 'GameError' type
+
+let gameState board =
+ failwith "Please implement the 'gameState' function"
diff --git a/exercises/practice/state-of-tic-tac-toe/StateOfTicTacToe.fsproj b/exercises/practice/state-of-tic-tac-toe/StateOfTicTacToe.fsproj
new file mode 100644
index 000000000..a1b3fe460
--- /dev/null
+++ b/exercises/practice/state-of-tic-tac-toe/StateOfTicTacToe.fsproj
@@ -0,0 +1,22 @@
+
+
+ net8.0
+ false
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
\ No newline at end of file
diff --git a/exercises/practice/state-of-tic-tac-toe/StateOfTicTacToeTests.fs b/exercises/practice/state-of-tic-tac-toe/StateOfTicTacToeTests.fs
new file mode 100644
index 000000000..5e0e9a4dd
--- /dev/null
+++ b/exercises/practice/state-of-tic-tac-toe/StateOfTicTacToeTests.fs
@@ -0,0 +1,334 @@
+module StateOfTicTacToeTests
+
+open FsUnit.Xunit
+open Xunit
+
+open StateOfTicTacToe
+
+[]
+let ``Finished game where X won via left column victory`` () =
+ let board =
+ array2D [
+ [ 'X'; 'O'; 'O' ]
+ [ 'X'; ' '; ' ' ]
+ [ 'X'; ' '; ' ' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where X won via middle column victory`` () =
+ let board =
+ array2D [
+ [ 'O'; 'X'; 'O' ]
+ [ ' '; 'X'; ' ' ]
+ [ ' '; 'X'; ' ' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where X won via right column victory`` () =
+ let board =
+ array2D [
+ [ 'O'; 'O'; 'X' ]
+ [ ' '; ' '; 'X' ]
+ [ ' '; ' '; 'X' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where O won via left column victory`` () =
+ let board =
+ array2D [
+ [ 'O'; 'X'; 'X' ]
+ [ 'O'; 'X'; ' ' ]
+ [ 'O'; ' '; ' ' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where O won via middle column victory`` () =
+ let board =
+ array2D [
+ [ 'X'; 'O'; 'X' ]
+ [ ' '; 'O'; 'X' ]
+ [ ' '; 'O'; ' ' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where O won via right column victory`` () =
+ let board =
+ array2D [
+ [ 'X'; 'X'; 'O' ]
+ [ ' '; 'X'; 'O' ]
+ [ ' '; ' '; 'O' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where X won via top row victory`` () =
+ let board =
+ array2D [
+ [ 'X'; 'X'; 'X' ]
+ [ 'X'; 'O'; 'O' ]
+ [ 'O'; ' '; ' ' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where X won via middle row victory`` () =
+ let board =
+ array2D [
+ [ 'O'; ' '; ' ' ]
+ [ 'X'; 'X'; 'X' ]
+ [ ' '; 'O'; ' ' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where X won via bottom row victory`` () =
+ let board =
+ array2D [
+ [ ' '; 'O'; 'O' ]
+ [ 'O'; ' '; 'X' ]
+ [ 'X'; 'X'; 'X' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where O won via top row victory`` () =
+ let board =
+ array2D [
+ [ 'O'; 'O'; 'O' ]
+ [ 'X'; 'X'; 'O' ]
+ [ 'X'; 'X'; ' ' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where O won via middle row victory`` () =
+ let board =
+ array2D [
+ [ 'X'; 'X'; ' ' ]
+ [ 'O'; 'O'; 'O' ]
+ [ 'X'; ' '; ' ' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where O won via bottom row victory`` () =
+ let board =
+ array2D [
+ [ 'X'; 'O'; 'X' ]
+ [ ' '; 'X'; 'X' ]
+ [ 'O'; 'O'; 'O' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where X won via falling diagonal victory`` () =
+ let board =
+ array2D [
+ [ 'X'; 'O'; 'O' ]
+ [ ' '; 'X'; ' ' ]
+ [ ' '; ' '; 'X' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where X won via rising diagonal victory`` () =
+ let board =
+ array2D [
+ [ 'O'; ' '; 'X' ]
+ [ 'O'; 'X'; ' ' ]
+ [ 'X'; ' '; ' ' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where O won via falling diagonal victory`` () =
+ let board =
+ array2D [
+ [ 'O'; 'X'; 'X' ]
+ [ 'O'; 'O'; 'X' ]
+ [ 'X'; ' '; 'O' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where O won via rising diagonal victory`` () =
+ let board =
+ array2D [
+ [ ' '; ' '; 'O' ]
+ [ ' '; 'O'; 'X' ]
+ [ 'O'; 'X'; 'X' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where X won via a row and a column victory`` () =
+ let board =
+ array2D [
+ [ 'X'; 'X'; 'X' ]
+ [ 'X'; 'O'; 'O' ]
+ [ 'X'; 'O'; 'O' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``Finished game where X won via two diagonal victories`` () =
+ let board =
+ array2D [
+ [ 'X'; 'O'; 'X' ]
+ [ 'O'; 'X'; 'O' ]
+ [ 'X'; 'O'; 'X' ]
+ ]
+
+ let expected: Result = Ok Win
+ gameState board |> should equal expected
+
+[]
+let ``A draw`` () =
+ let board =
+ array2D [
+ [ 'X'; 'O'; 'X' ]
+ [ 'X'; 'X'; 'O' ]
+ [ 'O'; 'X'; 'O' ]
+ ]
+
+ let expected: Result = Ok Draw
+ gameState board |> should equal expected
+
+[]
+let ``Another draw`` () =
+ let board =
+ array2D [
+ [ 'X'; 'X'; 'O' ]
+ [ 'O'; 'X'; 'X' ]
+ [ 'X'; 'O'; 'O' ]
+ ]
+
+ let expected: Result = Ok Draw
+ gameState board |> should equal expected
+
+[]
+let ``Ongoing game: one move in`` () =
+ let board =
+ array2D [
+ [ ' '; ' '; ' ' ]
+ [ 'X'; ' '; ' ' ]
+ [ ' '; ' '; ' ' ]
+ ]
+
+ let expected: Result = Ok Ongoing
+ gameState board |> should equal expected
+
+[]
+let ``Ongoing game: two moves in`` () =
+ let board =
+ array2D [
+ [ 'O'; ' '; ' ' ]
+ [ ' '; 'X'; ' ' ]
+ [ ' '; ' '; ' ' ]
+ ]
+
+ let expected: Result = Ok Ongoing
+ gameState board |> should equal expected
+
+[]
+let ``Ongoing game: five moves in`` () =
+ let board =
+ array2D [
+ [ 'X'; ' '; ' ' ]
+ [ ' '; 'X'; 'O' ]
+ [ 'O'; 'X'; ' ' ]
+ ]
+
+ let expected: Result = Ok Ongoing
+ gameState board |> should equal expected
+
+[]
+let ``Invalid board: X went twice`` () =
+ let board =
+ array2D [
+ [ 'X'; 'X'; ' ' ]
+ [ ' '; ' '; ' ' ]
+ [ ' '; ' '; ' ' ]
+ ]
+
+ let expected: Result = Error ConsecutiveMovesBySamePlayer
+ gameState board
+ |> should equal expected
+
+[]
+let ``Invalid board: O started`` () =
+ let board =
+ array2D [
+ [ 'O'; 'O'; 'X' ]
+ [ ' '; ' '; ' ' ]
+ [ ' '; ' '; ' ' ]
+ ]
+
+ let expected: Result = Error WrongPlayerStarted
+ gameState board
+ |> should equal expected
+
+[]
+let ``Invalid board: X won and O kept playing`` () =
+ let board =
+ array2D [
+ [ 'X'; 'X'; 'X' ]
+ [ 'O'; 'O'; 'O' ]
+ [ ' '; ' '; ' ' ]
+ ]
+
+ let expected: Result = Error MoveMadeAfterGameWasDone
+ gameState board
+ |> should equal expected
+
+[]
+let ``Invalid board: players kept playing after a win`` () =
+ let board =
+ array2D [
+ [ 'X'; 'X'; 'X' ]
+ [ 'O'; 'O'; 'O' ]
+ [ 'X'; 'O'; 'X' ]
+ ]
+
+ let expected: Result = Error MoveMadeAfterGameWasDone
+ gameState board
+ |> should equal expected
diff --git a/generators/Generators.fs b/generators/Generators.fs
index 23fe473dd..8b010f89c 100644
--- a/generators/Generators.fs
+++ b/generators/Generators.fs
@@ -2053,3 +2053,6 @@ type ResistorColorTrio() =
type KillerSudokuHelper() =
inherit ExerciseGenerator()
+
+type StateOfTicTacToe() =
+ inherit ExerciseGenerator()