Skip to content

Commit

Permalink
Add state-of-tic-tac-toe exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikSchierboom committed Oct 21, 2024
1 parent dea489d commit 85b2e8c
Show file tree
Hide file tree
Showing 11 changed files with 666 additions and 1 deletion.
16 changes: 15 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2215,7 +2215,21 @@
],
"prerequisites": [
"lists",
"recursion"
"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
}
Expand Down
7 changes: 7 additions & 0 deletions exercises/Exercises.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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
12 changes: 12 additions & 0 deletions exercises/practice/state-of-tic-tac-toe/.config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"fantomas-tool": {
"version": "4.7.9",
"commands": [
"fantomas"
]
}
}
}
101 changes: 101 additions & 0 deletions exercises/practice/state-of-tic-tac-toe/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -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
45 changes: 45 additions & 0 deletions exercises/practice/state-of-tic-tac-toe/.meta/Example.fs
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions exercises/practice/state-of-tic-tac-toe/.meta/config.json
Original file line number Diff line number Diff line change
@@ -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"
}
101 changes: 101 additions & 0 deletions exercises/practice/state-of-tic-tac-toe/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -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"
7 changes: 7 additions & 0 deletions exercises/practice/state-of-tic-tac-toe/StateOfTicTacToe.fs
Original file line number Diff line number Diff line change
@@ -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"
22 changes: 22 additions & 0 deletions exercises/practice/state-of-tic-tac-toe/StateOfTicTacToe.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<GenerateProgramFile>false</GenerateProgramFile>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<Compile Include="StateOfTicTacToe.fs" />
<Compile Include="StateOfTicTacToeTests.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Exercism.Tests" Version="0.1.0-beta1" />
<PackageReference Include="FsUnit.xUnit" Version="4.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
Loading

0 comments on commit 85b2e8c

Please sign in to comment.