Skip to content

Commit 50dd9bd

Browse files
Add state-of-tic-tac-toe exercise (#2385)
1 parent 7c31666 commit 50dd9bd

File tree

11 files changed

+827
-0
lines changed

11 files changed

+827
-0
lines changed

config.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2275,6 +2275,21 @@
22752275
],
22762276
"difficulty": 5
22772277
},
2278+
{
2279+
"slug": "state-of-tic-tac-toe",
2280+
"name": "State of Tic-Tac-Toe",
2281+
"uuid": "afabe6ff-ba63-42d5-9d74-3b6b27b0675a",
2282+
"practices": [
2283+
"arrays"
2284+
],
2285+
"prerequisites": [
2286+
"arrays",
2287+
"enums",
2288+
"strings",
2289+
"for-loops"
2290+
],
2291+
"difficulty": 5
2292+
},
22782293
{
22792294
"slug": "word-search",
22802295
"name": "Word Search",

exercises/Exercises.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SquareRoot", "practice\squa
353353
EndProject
354354
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Satellite", "practice\satellite\Satellite.csproj", "{8E276065-1371-4CFA-BA20-95225EC6AEBC}"
355355
EndProject
356+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StateOfTicTacToe", "practice\state-of-tic-tac-toe\StateOfTicTacToe.csproj", "{67E9BAB3-9805-42F1-9298-E9BBB795140E}"
357+
EndProject
356358
Global
357359
GlobalSection(SolutionConfigurationPlatforms) = preSolution
358360
Debug|Any CPU = Debug|Any CPU
@@ -1051,6 +1053,10 @@ Global
10511053
{8E276065-1371-4CFA-BA20-95225EC6AEBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
10521054
{8E276065-1371-4CFA-BA20-95225EC6AEBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
10531055
{8E276065-1371-4CFA-BA20-95225EC6AEBC}.Release|Any CPU.Build.0 = Release|Any CPU
1056+
{67E9BAB3-9805-42F1-9298-E9BBB795140E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1057+
{67E9BAB3-9805-42F1-9298-E9BBB795140E}.Debug|Any CPU.Build.0 = Debug|Any CPU
1058+
{67E9BAB3-9805-42F1-9298-E9BBB795140E}.Release|Any CPU.ActiveCfg = Release|Any CPU
1059+
{67E9BAB3-9805-42F1-9298-E9BBB795140E}.Release|Any CPU.Build.0 = Release|Any CPU
10541060
EndGlobalSection
10551061
GlobalSection(SolutionProperties) = preSolution
10561062
HideSolutionNode = FALSE
@@ -1229,6 +1235,7 @@ Global
12291235
{5C05051E-D46C-4544-9CF8-F2A748F63172} = {E276EF69-669A-43E0-88AC-8ABB17A9C026}
12301236
{BEBBD420-075D-46F1-AE51-CC9A05FECE4A} = {E276EF69-669A-43E0-88AC-8ABB17A9C026}
12311237
{8E276065-1371-4CFA-BA20-95225EC6AEBC} = {E276EF69-669A-43E0-88AC-8ABB17A9C026}
1238+
{67E9BAB3-9805-42F1-9298-E9BBB795140E} = {E276EF69-669A-43E0-88AC-8ABB17A9C026}
12321239
EndGlobalSection
12331240
GlobalSection(ExtensibilityGlobals) = postSolution
12341241
SolutionGuid = {AB4EA6C9-5461-4024-BDC7-2AE0C3A85CD1}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Instructions
2+
3+
In this exercise, you're going to implement a program that determines the state of a [tic-tac-toe][] game.
4+
(_You may also know the game as "noughts and crosses" or "Xs and Os"._)
5+
6+
The game is played on a 3×3 grid.
7+
Players take turns to place `X`s and `O`s on the grid.
8+
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.
9+
10+
In this exercise, we will assume that `X` starts.
11+
12+
It's your job to determine which state a given game is in.
13+
14+
There are 3 potential game states:
15+
16+
- The game is **ongoing**.
17+
- The game ended in a **draw**.
18+
- The game ended in a **win**.
19+
20+
If the given board is invalid, throw an appropriate error.
21+
22+
If a board meets the following conditions, it is invalid:
23+
24+
- The given board cannot be reached when turns are taken in the correct order (remember that `X` starts).
25+
- The game was played after it already ended.
26+
27+
## Examples
28+
29+
### Ongoing game
30+
31+
```text
32+
| |
33+
X | |
34+
___|___|___
35+
| |
36+
| X | O
37+
___|___|___
38+
| |
39+
O | X |
40+
| |
41+
```
42+
43+
### Draw
44+
45+
```text
46+
| |
47+
X | O | X
48+
___|___|___
49+
| |
50+
X | X | O
51+
___|___|___
52+
| |
53+
O | X | O
54+
| |
55+
```
56+
57+
### Win
58+
59+
```text
60+
| |
61+
X | X | X
62+
___|___|___
63+
| |
64+
| O | O
65+
___|___|___
66+
| |
67+
| |
68+
| |
69+
```
70+
71+
### Invalid
72+
73+
#### Wrong turn order
74+
75+
```text
76+
| |
77+
O | O | X
78+
___|___|___
79+
| |
80+
| |
81+
___|___|___
82+
| |
83+
| |
84+
| |
85+
```
86+
87+
#### Continued playing after win
88+
89+
```text
90+
| |
91+
X | X | X
92+
___|___|___
93+
| |
94+
O | O | O
95+
___|___|___
96+
| |
97+
| |
98+
| |
99+
```
100+
101+
[tic-tac-toe]: https://en.wikipedia.org/wiki/Tic-tac-toe
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
###############################
2+
# Core EditorConfig Options #
3+
###############################
4+
5+
; This file is for unifying the coding style for different editors and IDEs.
6+
; More information at:
7+
; https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2017
8+
; https://docs.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options?view=vs-2017
9+
10+
root = true
11+
12+
[*]
13+
indent_style = space
14+
15+
[StateOfTicTacToe.cs]
16+
indent_size = 4
17+
18+
###############################
19+
# .NET Coding Conventions #
20+
###############################
21+
22+
# Organize usings
23+
dotnet_sort_system_directives_first = true
24+
dotnet_separate_import_directive_groups = true
25+
26+
# this. preferences
27+
dotnet_style_qualification_for_field = false:suggestion
28+
dotnet_style_qualification_for_property = false:suggestion
29+
dotnet_style_qualification_for_method = false:suggestion
30+
dotnet_style_qualification_for_event = false:suggestion
31+
32+
# Language keywords vs BCL types preferences
33+
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
34+
dotnet_style_predefined_type_for_member_access = true:suggestion
35+
36+
# Parentheses preferences
37+
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
38+
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
39+
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
40+
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion
41+
42+
# Modifier preferences
43+
dotnet_style_require_accessibility_modifiers = always:suggestion
44+
dotnet_style_readonly_field = true:suggestion
45+
46+
# Expression-level preferences
47+
dotnet_style_object_initializer = true:suggestion
48+
dotnet_style_collection_initializer = true:suggestion
49+
dotnet_style_explicit_tuple_names = true:suggestion
50+
dotnet_style_prefer_inferred_tuple_names = true:suggestion
51+
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
52+
dotnet_style_prefer_auto_properties = true:suggestion
53+
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
54+
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
55+
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
56+
dotnet_style_coalesce_expression = true:suggestion
57+
dotnet_style_null_propagation = true:suggestion
58+
59+
###############################
60+
# Naming Conventions #
61+
###############################
62+
63+
# Style Definitions
64+
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
65+
66+
# Use PascalCase for constant fields
67+
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
68+
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
69+
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
70+
dotnet_naming_symbols.constant_fields.applicable_kinds = field
71+
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
72+
dotnet_naming_symbols.constant_fields.required_modifiers = const
73+
74+
###############################
75+
# C# Code Style Rules #
76+
###############################
77+
78+
# var preferences
79+
csharp_style_var_for_built_in_types = true:none
80+
csharp_style_var_when_type_is_apparent = true:none
81+
csharp_style_var_elsewhere = true:none
82+
83+
# Expression-bodied members
84+
csharp_style_expression_bodied_methods = true:suggestion
85+
csharp_style_expression_bodied_constructors = true:suggestion
86+
csharp_style_expression_bodied_operators = true:suggestion
87+
csharp_style_expression_bodied_properties = true:suggestion
88+
csharp_style_expression_bodied_indexers = true:suggestion
89+
csharp_style_expression_bodied_accessors = true:suggestion
90+
91+
# Pattern-matching preferences
92+
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
93+
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
94+
95+
# Null-checking preferences
96+
csharp_style_throw_expression = true:suggestion
97+
csharp_style_conditional_delegate_call = true:suggestion
98+
99+
# Modifier preferences
100+
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
101+
102+
# Expression-level preferences
103+
csharp_prefer_braces = true:none
104+
csharp_prefer_simple_default_expression = true:suggestion
105+
csharp_style_deconstructed_variable_declaration = true:suggestion
106+
csharp_style_pattern_local_over_anonymous_function = true:suggestion
107+
csharp_style_inlined_variable_declaration = true:suggestion
108+
109+
###############################
110+
# C# Formatting Rules #
111+
###############################
112+
113+
# New line preferences
114+
csharp_new_line_before_open_brace = all
115+
csharp_new_line_before_else = true
116+
csharp_new_line_before_catch = true
117+
csharp_new_line_before_finally = true
118+
csharp_new_line_before_members_in_object_initializers = false
119+
csharp_new_line_before_members_in_anonymous_types = false
120+
csharp_new_line_between_query_expression_clauses = true
121+
122+
# Indentation preferences
123+
csharp_indent_case_contents = true
124+
csharp_indent_switch_labels = true
125+
csharp_indent_labels = flush_left
126+
127+
# Space preferences
128+
csharp_space_after_cast = false
129+
csharp_space_after_keywords_in_control_flow_statements = true
130+
csharp_space_between_method_declaration_parameter_list_parentheses = false
131+
csharp_space_between_method_call_parameter_list_parentheses = false
132+
csharp_space_before_colon_in_inheritance_clause = true
133+
csharp_space_after_colon_in_inheritance_clause = true
134+
csharp_space_around_binary_operators = before_and_after
135+
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
136+
csharp_space_between_method_call_name_and_opening_parenthesis = false
137+
csharp_space_between_method_call_empty_parameter_list_parentheses = false
138+
139+
# Wrapping preferences
140+
csharp_preserve_single_line_blocks = true
141+
csharp_preserve_single_line_statements = true
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
public enum State
5+
{
6+
Win,
7+
Draw,
8+
Ongoing,
9+
Invalid
10+
}
11+
12+
public class TicTacToe(string[] rows)
13+
{
14+
public State State
15+
{
16+
get
17+
{
18+
if (Moves.GetValueOrDefault('X', 0) - Moves.GetValueOrDefault('O', 0) > 1 ||
19+
Moves.GetValueOrDefault('O', 0) > Moves.GetValueOrDefault('X', 0) ||
20+
Winners.Distinct().Count() > 1)
21+
return State.Invalid;
22+
23+
if (Winners.Length != 0)
24+
return State.Win;
25+
26+
if (Cells.Contains(' '))
27+
return State.Ongoing;
28+
29+
return State.Draw;
30+
}
31+
}
32+
33+
private char[] Winners =>
34+
rows.Concat(Diagonals).Concat(Columns)
35+
.Where(cells => cells is "XXX" or "OOO")
36+
.Select(cells => cells[0])
37+
.ToArray();
38+
39+
private string[] Diagonals =>
40+
[
41+
$"{rows[0][0]}{rows[1][1]}{rows[2][2]}",
42+
$"{rows[0][2]}{rows[1][1]}{rows[2][0]}"
43+
];
44+
45+
private string[] Columns =>
46+
[
47+
$"{rows[0][0]}{rows[1][0]}{rows[2][0]}",
48+
$"{rows[0][1]}{rows[1][1]}{rows[2][1]}",
49+
$"{rows[0][2]}{rows[1][2]}{rows[2][2]}",
50+
];
51+
52+
private char[] Cells => rows.SelectMany(row => row).ToArray();
53+
54+
private Dictionary<char, int> Moves =>
55+
Cells.Where(char.IsLetter)
56+
.CountBy(cell => cell)
57+
.ToDictionary();
58+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Xunit;
2+
3+
public class {{ testClass }}
4+
{
5+
{{- for test in tests }}
6+
[Fact{{ if !for.first }}(Skip = "Remove this Skip property to run this test"){{ end }}]
7+
public void {{ test.testMethod }}()
8+
{
9+
string[] board = [
10+
{{- for row in test.input.board }}
11+
{{ row | string.literal }}{{- if !for.last }},{{ end -}}
12+
{{ end }}
13+
];
14+
var game = new TicTacToe(board);
15+
{{- if test.expected.error }}
16+
Assert.Equal({{ "Invalid" | enum "State" }}, game.State);
17+
{{ else }}
18+
Assert.Equal({{ test.expected | enum "State" }}, game.State);
19+
{{ end -}}
20+
}
21+
{{ end -}}
22+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"erikschierboom"
4+
],
5+
"files": {
6+
"solution": [
7+
"StateOfTicTacToe.cs"
8+
],
9+
"test": [
10+
"StateOfTicTacToeTests.cs"
11+
],
12+
"example": [
13+
".meta/Example.cs"
14+
]
15+
},
16+
"blurb": "Determine the game state of a match of Tic-Tac-Toe.",
17+
"source": "Created by Sascha Mann for the Julia track of the Exercism Research Experiment.",
18+
"source_url": "https://github.com/exercism/research_experiment_1/tree/julia-dev/exercises/julia-1-a"
19+
}

0 commit comments

Comments
 (0)