Skip to content

Commit bea1013

Browse files
authored
Adding state of tic tac toe practice exercise (exercism#2647)
* Adding state of tic tac toe structure and tests, missing reference resolutions * Using latests gradle build, updating tests and adding reference implementations * Adding prerequisites * fix after running configlet format update * Renaming enum to GameState, and applying suggestion for the basic message of the sample given to students * Renaming GameState enum * Renaming GameState file in config.json
1 parent e61b86b commit bea1013

11 files changed

Lines changed: 648 additions & 0 deletions

File tree

config.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,19 @@
10491049
],
10501050
"difficulty": 5
10511051
},
1052+
{
1053+
"slug": "state-of-tic-tac-toe",
1054+
"name": "State Of Tic Tac Toe",
1055+
"uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
1056+
"practices": [],
1057+
"prerequisites": [
1058+
"arrays",
1059+
"for-loops",
1060+
"if-else-statements",
1061+
"strings"
1062+
],
1063+
"difficulty": 5
1064+
},
10521065
{
10531066
"slug": "affine-cipher",
10541067
"name": "Affine Cipher",
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 games 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: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"authors": [
3+
"manumafe98"
4+
],
5+
"files": {
6+
"solution": [
7+
"src/main/java/StateOfTicTacToe.java"
8+
],
9+
"test": [
10+
"src/test/java/StateOfTicTacToeTest.java"
11+
],
12+
"example": [
13+
".meta/src/reference/java/StateOfTicTacToe.java"
14+
],
15+
"editor": [
16+
"src/main/java/GameState.java"
17+
],
18+
"invalidator": [
19+
"build.gradle"
20+
]
21+
},
22+
"blurb": "Determine the game state of a match of Tic-Tac-Toe.",
23+
"source": "Created by Sascha Mann for the Julia track of the Exercism Research Experiment.",
24+
"source_url": "https://github.com/exercism/research_experiment_1/tree/julia-dev/exercises/julia-1-a"
25+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
enum GameState {
2+
WIN,
3+
DRAW,
4+
ONGOING
5+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import java.util.List;
2+
import java.util.ArrayList;
3+
import java.util.Arrays;
4+
5+
class StateOfTicTacToe {
6+
public GameState determineState(String[] board) {
7+
int xWin = 0;
8+
int oWin = 0;
9+
10+
List<String> columns = getColumns(board);
11+
List<String> diagonals = getDiagonals(board);
12+
13+
int xCount = count('X', board);
14+
int oCount = count('O', board);
15+
16+
if (oCount > xCount) {
17+
throw new IllegalArgumentException("Wrong turn order: O started");
18+
}
19+
20+
if (xCount > oCount + 1) {
21+
throw new IllegalArgumentException("Wrong turn order: X went twice");
22+
}
23+
24+
List<String> allCombos = new ArrayList<>();
25+
26+
allCombos.addAll(Arrays.asList(board));
27+
allCombos.addAll(columns);
28+
allCombos.addAll(diagonals);
29+
30+
for (String row : allCombos) {
31+
row = row.trim();
32+
33+
if (row.equals("XXX")) {
34+
if (xWin > 1 || oWin > 0) {
35+
throw new IllegalArgumentException(
36+
"Impossible board: game should have ended after the game was won"
37+
);
38+
} else {
39+
xWin++;
40+
}
41+
}
42+
43+
if (row.equals("OOO")) {
44+
if (xWin > 0 || oWin > 0) {
45+
throw new IllegalArgumentException(
46+
"Impossible board: game should have ended after the game was won"
47+
);
48+
} else {
49+
oWin++;
50+
}
51+
}
52+
}
53+
54+
if (xWin > 0 || oWin > 0) {
55+
return GameState.WIN;
56+
}
57+
58+
if (xCount + oCount < 9) {
59+
return GameState.ONGOING;
60+
}
61+
62+
return GameState.DRAW;
63+
}
64+
65+
private List<String> getColumns(String[] board) {
66+
String[] cols = new String[3];
67+
68+
for (String row : board) {
69+
for (int j = 0; j < 3; j++) {
70+
if (cols[j] == null) {
71+
cols[j] = String.valueOf(row.charAt(j));
72+
} else {
73+
cols[j] += String.valueOf(row.charAt(j));
74+
}
75+
}
76+
}
77+
78+
return Arrays.asList(cols);
79+
}
80+
81+
private List<String> getDiagonals(String[] board) {
82+
String[] diags = new String[2];
83+
84+
for (int i = 0; i < 3; i++) {
85+
86+
if (diags[0] == null) {
87+
diags[0] = String.valueOf(board[i].charAt(i));
88+
} else {
89+
diags[0] += String.valueOf(board[i].charAt(i));
90+
}
91+
92+
if (diags[1] == null) {
93+
diags[1] = String.valueOf(board[i].charAt(2 - i));
94+
} else {
95+
diags[1] += String.valueOf(board[i].charAt(2 - i));
96+
}
97+
}
98+
99+
return Arrays.asList(diags);
100+
}
101+
102+
private int count(char mark, String[] board) {
103+
int result = 0;
104+
105+
for (String str : board) {
106+
result += str.chars().filter(ch -> ch == mark).count();
107+
}
108+
109+
return result;
110+
}
111+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[fe8e9fa9-37af-4d7e-aa24-2f4b8517161a]
13+
description = "Won games -> Finished game where X won via left column victory"
14+
15+
[96c30df5-ae23-4cf6-bf09-5ef056dddea1]
16+
description = "Won games -> Finished game where X won via middle column victory"
17+
18+
[0d7a4b0a-2afd-4a75-8389-5fb88ab05eda]
19+
description = "Won games -> Finished game where X won via right column victory"
20+
21+
[bd1007c0-ec5d-4c60-bb9f-1a4f22177d51]
22+
description = "Won games -> Finished game where O won via left column victory"
23+
24+
[c032f800-5735-4354-b1b9-46f14d4ee955]
25+
description = "Won games -> Finished game where O won via middle column victory"
26+
27+
[662c8902-c94a-4c4c-9d9c-e8ca513db2b4]
28+
description = "Won games -> Finished game where O won via right column victory"
29+
30+
[2d62121f-7e3a-44a0-9032-0d73e3494941]
31+
description = "Won games -> Finished game where X won via top row victory"
32+
33+
[108a5e82-cc61-409f-aece-d7a18c1beceb]
34+
description = "Won games -> Finished game where X won via middle row victory"
35+
include = false
36+
37+
[346527db-4db9-4a96-b262-d7023dc022b0]
38+
description = "Won games -> Finished game where X won via middle row victory"
39+
reimplements = "108a5e82-cc61-409f-aece-d7a18c1beceb"
40+
41+
[a013c583-75f8-4ab2-8d68-57688ff04574]
42+
description = "Won games -> Finished game where X won via bottom row victory"
43+
44+
[2c08e7d7-7d00-487f-9442-e7398c8f1727]
45+
description = "Won games -> Finished game where O won via top row victory"
46+
47+
[bb1d6c62-3e3f-4d1a-9766-f8803c8ed70f]
48+
description = "Won games -> Finished game where O won via middle row victory"
49+
50+
[6ef641e9-12ec-44f5-a21c-660ea93907af]
51+
description = "Won games -> Finished game where O won via bottom row victory"
52+
53+
[ab145b7b-26a7-426c-ab71-bf418cd07f81]
54+
description = "Won games -> Finished game where X won via falling diagonal victory"
55+
56+
[7450caab-08f5-4f03-a74b-99b98c4b7a4b]
57+
description = "Won games -> Finished game where X won via rising diagonal victory"
58+
59+
[c2a652ee-2f93-48aa-a710-a70cd2edce61]
60+
description = "Won games -> Finished game where O won via falling diagonal victory"
61+
62+
[5b20ceea-494d-4f0c-a986-b99efc163bcf]
63+
description = "Won games -> Finished game where O won via rising diagonal victory"
64+
65+
[035a49b9-dc35-47d3-9d7c-de197161b9d4]
66+
description = "Won games -> Finished game where X won via a row and a column victory"
67+
68+
[e5dfdeb0-d2bf-4b5a-b307-e673f69d4a53]
69+
description = "Won games -> Finished game where X won via two diagonal victories"
70+
71+
[b42ed767-194c-4364-b36e-efbfb3de8788]
72+
description = "Drawn games -> Draw"
73+
74+
[227a76b2-0fef-4e16-a4bd-8f9d7e4c3b13]
75+
description = "Drawn games -> Another draw"
76+
77+
[4d93f15c-0c40-43d6-b966-418b040012a9]
78+
description = "Ongoing games -> Ongoing game: one move in"
79+
80+
[c407ae32-4c44-4989-b124-2890cf531f19]
81+
description = "Ongoing games -> Ongoing game: two moves in"
82+
83+
[199b7a8d-e2b6-4526-a85e-78b416e7a8a9]
84+
description = "Ongoing games -> Ongoing game: five moves in"
85+
86+
[1670145b-1e3d-4269-a7eb-53cd327b302e]
87+
description = "Invalid boards -> Invalid board: X went twice"
88+
89+
[47c048e8-b404-4bcf-9e51-8acbb3253f3b]
90+
description = "Invalid boards -> Invalid board: O started"
91+
92+
[b1dc8b13-46c4-47db-a96d-aa90eedc4e8d]
93+
description = "Invalid boards -> Invalid board"
94+
include = false
95+
96+
[6c1920f2-ab5c-4648-a0c9-997414dda5eb]
97+
description = "Invalid boards -> Invalid board: X won and O kept playing"
98+
reimplements = "b1dc8b13-46c4-47db-a96d-aa90eedc4e8d"
99+
100+
[4801cda2-f5b7-4c36-8317-3cdd167ac22c]
101+
description = "Invalid boards -> Invalid board: players kept playing after a win"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apply plugin: "java"
2+
apply plugin: "eclipse"
3+
apply plugin: "idea"
4+
5+
// set default encoding to UTF-8
6+
compileJava.options.encoding = "UTF-8"
7+
compileTestJava.options.encoding = "UTF-8"
8+
9+
repositories {
10+
mavenCentral()
11+
}
12+
13+
dependencies {
14+
testImplementation "junit:junit:4.13.1"
15+
testImplementation "org.assertj:assertj-core:3.25.1"
16+
}
17+
18+
test {
19+
testLogging {
20+
exceptionFormat = 'full'
21+
showStandardStreams = true
22+
events = ["passed", "failed", "skipped"]
23+
}
24+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
enum GameState {
2+
WIN,
3+
DRAW,
4+
ONGOING
5+
}

0 commit comments

Comments
 (0)