Skip to content

Commit 1e6e148

Browse files
committed
Synchronize tests & Update solution to match tests.toml
1 parent d96363a commit 1e6e148

11 files changed

Lines changed: 323 additions & 286 deletions

File tree

exercises/practice/hangman/.meta/config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"contributors": [
66
"mirkoperillo",
77
"msomji",
8-
"muzimuzhi"
8+
"muzimuzhi",
9+
"thibault2705"
910
],
1011
"files": {
1112
"solution": [
Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,90 @@
11
import io.reactivex.Observable;
22

3-
import java.util.*;
3+
import java.util.ArrayList;
4+
import java.util.LinkedHashSet;
5+
import java.util.List;
6+
import java.util.Set;
47

58
class Hangman {
69

7-
Observable<Output> play(
8-
Observable<String> words,
10+
Observable<Output> guess(
11+
String word,
912
Observable<String> letters) {
10-
return Observable
11-
.combineLatest(
12-
words,
13-
letters.startWith(""),
14-
(word, letter) -> new AbstractMap.SimpleEntry<>(word, letter))
15-
.scan(
16-
Output.empty(),
17-
(state, entry) -> {
18-
System.out.println(state + " -> " + entry);
19-
if (state == null || state.status != Status.PLAYING) {
20-
return createNewGame(entry.getKey());
21-
} else {
22-
return processNewLetter(state, entry.getValue());
13+
return letters
14+
.startWith("")
15+
.scan(Output.empty(), (state, letter) -> {
16+
if (state == null || state.state == null) {
17+
return createNewGame(word);
2318
}
19+
20+
if (letter.isEmpty()) {
21+
return state;
22+
}
23+
24+
if (state.state == Status.WIN) {
25+
throw new IllegalStateException("cannot guess after the game is won");
26+
}
27+
28+
if (state.state == Status.LOSE) {
29+
throw new IllegalStateException("cannot guess after the game is lost");
30+
}
31+
32+
return processNewLetter(state, letter);
2433
})
25-
.skip(1); // Skip the initial state
34+
.skip(1);
2635
}
2736

2837
private static Output createNewGame(String word) {
2938
return Output.initialState(word);
3039
}
3140

32-
private static Output processNewLetter(
33-
Output state,
34-
String letter) {
41+
private static Output processNewLetter(Output state, String letter) {
3542
if (state.isLetterAlreadyPlayed(letter)) {
36-
throw new IllegalArgumentException("Letter " + letter + " was already played");
43+
return processIncorrectGuess(state, letter);
3744
}
45+
3846
if (state.isLetterInSecret(letter)) {
3947
return processCorrectGuess(state, letter);
40-
} else {
41-
return processIncorrectGuess(state, letter);
4248
}
49+
50+
return processIncorrectGuess(state, letter);
4351
}
4452

4553
private static Output processCorrectGuess(Output state, String letter) {
46-
Set<String> newGuess = new HashSet<>(state.guess);
47-
newGuess.add(letter);
48-
String discovered = Output.getGuessedWord(state.secret, newGuess);
49-
Status newStatus = Output.isWin(state.secret, newGuess) ? Status.WIN : Status.PLAYING;
54+
Set<String> newGuesses = new LinkedHashSet<>(state.guesses);
55+
newGuesses.add(letter);
56+
57+
String discovered = Output.getGuessedWord(state.word, newGuesses);
58+
Status newStatus = Output.isWin(state.word, newGuesses) ? Status.WIN : Status.ON_GOING;
59+
5060
return new Output(
51-
state.secret,
52-
discovered,
53-
newGuess,
54-
state.misses,
55-
state.parts,
56-
newStatus);
61+
state.word,
62+
discovered,
63+
newGuesses,
64+
state.misses,
65+
state.parts,
66+
newStatus);
5767
}
5868

5969
private static Output processIncorrectGuess(Output state, String letter) {
60-
Set<String> newMisses = new HashSet<>(state.misses);
70+
Set<String> newMisses = new LinkedHashSet<>(state.misses);
6171
newMisses.add(letter);
72+
6273
List<Part> newParts = new ArrayList<>(state.parts);
63-
newParts.add(order[newParts.size()]);
64-
Status newStatus = Output.isLoss(newMisses) ? Status.LOSS : Status.PLAYING;
74+
if (newParts.size() < order.length) {
75+
newParts.add(order[newParts.size()]);
76+
}
77+
78+
Status newStatus = newParts.size() >= order.length ? Status.LOSE : Status.ON_GOING;
79+
6580
return new Output(
66-
state.secret,
67-
state.discovered,
68-
state.guess,
81+
state.word,
82+
state.maskedWord,
83+
state.guesses,
6984
newMisses,
7085
newParts,
7186
newStatus);
7287
}
7388

7489
static Part[] order = Part.values();
75-
7690
}
Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,94 @@
1-
import static java.util.stream.Collectors.joining;
2-
31
import java.util.ArrayList;
42
import java.util.Collections;
5-
import java.util.HashSet;
3+
import java.util.LinkedHashSet;
64
import java.util.List;
75
import java.util.Set;
86

7+
import static java.util.stream.Collectors.joining;
8+
99
class Output {
1010

11-
public final String secret;
12-
public final String discovered;
13-
public final Set<String> guess;
11+
static final int MAX_FAILURES = Part.values().length;
12+
13+
public final String word;
14+
public final String maskedWord;
15+
public final Set<String> guesses;
1416
public final Set<String> misses;
1517
public final List<Part> parts;
16-
public final Status status;
18+
public final Status state;
19+
public final int remainingFailures;
1720

1821
Output(
19-
final String secret,
20-
final String discovered,
21-
final Set<String> guess,
22-
final Set<String> misses,
23-
final List<Part> parts,
24-
final Status status) {
25-
this.secret = secret;
26-
this.discovered = discovered;
27-
this.guess = Set.copyOf(guess);
22+
final String word,
23+
final String maskedWord,
24+
final Set<String> guesses,
25+
final Set<String> misses,
26+
final List<Part> parts,
27+
final Status state) {
28+
this.word = word;
29+
this.maskedWord = maskedWord;
30+
this.guesses = Set.copyOf(guesses);
2831
this.misses = Set.copyOf(misses);
2932
this.parts = List.copyOf(parts);
30-
this.status = status;
33+
this.state = state;
34+
this.remainingFailures = Math.max(0, MAX_FAILURES - 1 - parts.size());
3135
}
3236

3337
static Output empty() {
3438
return new Output(
35-
null,
36-
null,
37-
Collections.emptySet(),
38-
Collections.emptySet(),
39-
Collections.emptyList(),
40-
null);
39+
null,
40+
null,
41+
Collections.emptySet(),
42+
Collections.emptySet(),
43+
Collections.emptyList(),
44+
null);
4145
}
4246

4347
static Output initialState(final String secret) {
4448
return new Output(
45-
secret,
46-
getGuessedWord(secret, Collections.emptySet()),
47-
new HashSet<>(),
48-
new HashSet<>(),
49-
new ArrayList<>(),
50-
Status.PLAYING);
49+
secret,
50+
getGuessedWord(secret, Collections.emptySet()),
51+
new LinkedHashSet<>(),
52+
new LinkedHashSet<>(),
53+
new ArrayList<>(),
54+
Status.ON_GOING);
5155
}
5256

5357
boolean isLetterAlreadyPlayed(final String letter) {
54-
return guess.contains(letter) || misses.contains(letter);
58+
return guesses.contains(letter) || misses.contains(letter);
5559
}
5660

5761
boolean isLetterInSecret(final String letter) {
58-
return secret.contains(letter);
62+
return word.contains(letter);
5963
}
6064

6165
static String getGuessedWord(String secret, Set<String> letters) {
6266
return secret.chars()
63-
.mapToObj(i -> String.valueOf((char) i))
64-
.map(c -> letters.contains(c) ? c : "_")
65-
.collect(joining());
67+
.mapToObj(i -> String.valueOf((char) i))
68+
.map(c -> letters.contains(c) ? c : "_")
69+
.collect(joining());
6670
}
6771

6872
static boolean isWin(String secret, Set<String> guessedLetters) {
6973
return secret.chars()
70-
.mapToObj(i -> String.valueOf((char) i))
71-
.allMatch(guessedLetters::contains);
74+
.mapToObj(i -> String.valueOf((char) i))
75+
.allMatch(guessedLetters::contains);
7276
}
7377

74-
static boolean isLoss(Set<String> missedLetters) {
75-
return missedLetters.size() >= Part.values().length;
78+
static boolean isLoss(List<Part> parts) {
79+
return parts.size() >= MAX_FAILURES;
7680
}
7781

7882
@Override
7983
public String toString() {
8084
return "Output{" +
81-
"secret='" + secret + '\'' +
82-
", discovered='" + discovered + '\'' +
83-
", guess=" + guess +
84-
", misses=" + misses +
85-
", parts=" + parts +
86-
", status=" + status +
87-
'}';
85+
"secret='" + word + '\'' +
86+
", discovered='" + maskedWord + '\'' +
87+
", guess=" + guesses +
88+
", misses=" + misses +
89+
", parts=" + parts +
90+
", status=" + state +
91+
", remainingFailures=" + remainingFailures +
92+
'}';
8893
}
8994
}

exercises/practice/hangman/.meta/src/reference/java/Part.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,9 @@ enum Part {
44
LEFT_ARM,
55
RIGHT_ARM,
66
LEFT_LEG,
7-
RIGHT_LEG
7+
RIGHT_LEG,
8+
LEFT_EYE,
9+
RIGHT_EYE,
10+
NOSE,
11+
MOUTH
812
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
enum Status {
2-
PLAYING,
2+
ON_GOING,
33
WIN,
4-
LOSS
4+
LOSE
55
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
[2419ffe6-16d8-4059-856a-9a101998a418]
13+
description = "Initially 9 failures are allowed and no letters are guessed"
14+
15+
[17c4296d-daab-44dc-8155-37c77caa52c1]
16+
description = "After 10 failures the game is over"
17+
18+
[77c9ae1f-bbc4-4ed4-b67e-08110cbcfc17]
19+
description = "Losing with several correct guesses"
20+
21+
[25101d8d-9874-405b-9825-193a14b69753]
22+
description = "Feeding a correct letter removes underscores"
23+
24+
[8e6bd521-bc9b-458f-9cce-f57f4140c173]
25+
description = "Feeding a correct letter twice counts as a failure"
26+
27+
[5e6971b7-5e5f-49c2-b85d-1dd6aeb53bd5]
28+
description = "Guessing a repeated letter reveals all instances"
29+
30+
[a6c69d92-01ef-4b81-b9d9-801131e79bbb]
31+
description = "Getting all the letters right makes for a win"
32+
33+
[2dc47994-b434-4a26-b70c-1eebeff77fe4]
34+
description = "Winning on the last guess is still a win"
35+
36+
[52801d56-6963-494b-a901-5736e46ddc12]
37+
description = "Guessing after a lose is error"
38+
39+
[29a874f3-a413-4e1b-9a60-6be018e70b60]
40+
description = "Guessing after a win is error"

exercises/practice/hangman/src/main/java/Hangman.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
class Hangman {
44

5-
Observable<Output> play(Observable<String> words, Observable<String> letters) {
5+
Observable<Output> guess(String word, Observable<String> letters) {
66
throw new UnsupportedOperationException("Delete this statement and write your own implementation.");
77
}
88

0 commit comments

Comments
 (0)