diff options
Diffstat (limited to 'example')
-rw-r--r-- | example/src/main/java/zajc/akiwrapper/AkinatorExample.java | 307 |
1 files changed, 141 insertions, 166 deletions
diff --git a/example/src/main/java/zajc/akiwrapper/AkinatorExample.java b/example/src/main/java/zajc/akiwrapper/AkinatorExample.java index 148394f..0a903e6 100644 --- a/example/src/main/java/zajc/akiwrapper/AkinatorExample.java +++ b/example/src/main/java/zajc/akiwrapper/AkinatorExample.java | |||
@@ -3,7 +3,7 @@ package zajc.akiwrapper; | |||
3 | import static com.github.markozajc.akiwrapper.Akiwrapper.Answer.*; | 3 | import static com.github.markozajc.akiwrapper.Akiwrapper.Answer.*; |
4 | import static com.github.markozajc.akiwrapper.core.entities.Server.GuessType.CHARACTER; | 4 | import static com.github.markozajc.akiwrapper.core.entities.Server.GuessType.CHARACTER; |
5 | import static com.github.markozajc.akiwrapper.core.entities.Server.Language.ENGLISH; | 5 | import static com.github.markozajc.akiwrapper.core.entities.Server.Language.ENGLISH; |
6 | import static java.lang.Integer.parseInt; | 6 | import static java.lang.Character.toLowerCase; |
7 | import static java.lang.System.*; | 7 | import static java.lang.System.*; |
8 | import static java.util.stream.Collectors.joining; | 8 | import static java.util.stream.Collectors.joining; |
9 | 9 | ||
@@ -19,121 +19,82 @@ import com.github.markozajc.akiwrapper.core.exceptions.ServerNotFoundException; | |||
19 | @SuppressWarnings("javadoc") | 19 | @SuppressWarnings("javadoc") |
20 | public class AkinatorExample { | 20 | public class AkinatorExample { |
21 | 21 | ||
22 | public static final double PROBABILITY_THRESHOLD = 0.85; | 22 | private static final String ANSWER_TIP = |
23 | // This is used to determine which guesses are probable (probability is determined by | 23 | "Y or yes, N or no, DK or don't know, P or probably, PN or probably not, or go back one step with B or back."; |
24 | // Akinator) enough to propose to the player. | 24 | private static final Scanner IN = new Scanner(System.in).useDelimiter("\n"); |
25 | 25 | ||
26 | @SuppressWarnings("null") | 26 | public static void main(String[] args) { |
27 | public static void main(String[] args) throws Exception { | 27 | boolean filterProfanity = getProfanityFilter(); |
28 | try (var in = new Scanner(System.in)) { | 28 | // Gets player's age. Like the Akinator's website, this will turn on the profanity |
29 | boolean filterProfanity = getProfanityFilter(in); | 29 | // filter if the age entered is below 16. |
30 | // Gets player's age. Like the Akinator's website, this will turn on the profanity | 30 | |
31 | // filter if the age entered is below 16. | 31 | var language = getLanguage(); |
32 | 32 | // Gets player's language. Akinator will give the user localized questions and | |
33 | var language = getLanguage(in); | 33 | // guesses depending on user's language. |
34 | // Gets player's language. Akinator will give the user localized questions and | 34 | |
35 | // guesses depending on user's language. | 35 | var guessType = getGuessType(); |
36 | 36 | // Gets the guess type. | |
37 | var guessType = getGuessType(in); | 37 | |
38 | // Gets the guess type. | 38 | Akiwrapper aw; |
39 | 39 | try { | |
40 | Akiwrapper aw; | 40 | aw = new AkiwrapperBuilder().setFilterProfanity(filterProfanity) |
41 | try { | 41 | .setLanguage(language) |
42 | aw = new AkiwrapperBuilder().setFilterProfanity(filterProfanity) | 42 | .setGuessType(guessType) |
43 | .setLanguage(language) | 43 | .build(); |
44 | .setGuessType(guessType) | 44 | } catch (ServerNotFoundException e) { |
45 | .build(); | 45 | err.println("Unsupported combination of language and guess type"); |
46 | } catch (ServerNotFoundException e) { | 46 | return; |
47 | err.println("Unsupported combination of language and guess type"); | ||
48 | return; | ||
49 | } | ||
50 | // Builds the Akiwrapper instance, this is what we'll be using to perform | ||
51 | // operations such as answering questions, fetching guesses, etc | ||
52 | |||
53 | var rejected = new ArrayList<Long>(); | ||
54 | // A list of rejected guesses, used to prevent them from repeating | ||
55 | |||
56 | while (aw.getQuestion() != null) { | ||
57 | // Runs while there are still questions left | ||
58 | |||
59 | Question question = aw.getQuestion(); | ||
60 | if (question == null) | ||
61 | break; | ||
62 | // Breaks the loop if question is null; /should/ not occur, but safety is still | ||
63 | // first | ||
64 | |||
65 | out.printf("Question #%d%n", question.getStep() + 1); | ||
66 | out.printf("\t%s%n", question.getQuestion()); | ||
67 | // Displays the question. | ||
68 | |||
69 | if (question.getStep() == 0) | ||
70 | out.printf("%nAnswer with " + | ||
71 | "Y (yes), N (no), DK (don't know), P (probably) or PN (probably not) " + | ||
72 | "or go back in time with B (back).%n"); | ||
73 | // Displays the tip (only for the first time) | ||
74 | |||
75 | answerQuestion(in, aw); | ||
76 | |||
77 | reviewGuesses(in, aw, rejected); | ||
78 | // Iterates over any available guesses. | ||
79 | } | ||
80 | |||
81 | for (Guess guess : aw.getGuesses()) { | ||
82 | if (reviewGuess(guess, in)) { | ||
83 | // Reviews all final guesses. | ||
84 | finish(true); | ||
85 | exit(0); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | finish(false); | ||
90 | // Loses if all guesses are rejected. | ||
91 | } | 47 | } |
92 | } | 48 | // Builds the Akiwrapper instance, this is what we'll be using to perform |
49 | // operations such as answering questions, fetching guesses, etc | ||
93 | 50 | ||
94 | private static void answerQuestion(@Nonnull Scanner sc, @Nonnull Akiwrapper aw) { | 51 | while (aw.getQuestion() != null) { |
95 | boolean answered = false; | 52 | // Runs while there are still questions left |
96 | while (!answered) { | ||
97 | // Iterates while the questions remains unanswered. | ||
98 | 53 | ||
99 | var answer = sc.nextLine().toLowerCase(); | 54 | Question question = aw.getQuestion(); |
55 | if (question == null) | ||
56 | break; | ||
57 | // Breaks the loop if question is null; /should/ not occur, but safety is still | ||
58 | // first | ||
100 | 59 | ||
101 | if (answer.equals("y")) { | 60 | out.printf("Question #%d%n\t%s%n", question.getStep() + 1, question.getQuestion()); |
102 | aw.answer(YES); | 61 | // Displays the question. |
103 | 62 | ||
104 | } else if (answer.equals("n")) { | 63 | if (question.getStep() == 0) |
105 | aw.answer(NO); | 64 | out.printf("%nAnswer with %s%n", ANSWER_TIP); |
65 | // Displays the tip (only for the first time) | ||
106 | 66 | ||
107 | } else if (answer.equals("dk")) { | 67 | out.print("> "); |
108 | aw.answer(DONT_KNOW); | ||
109 | 68 | ||
110 | } else if (answer.equals("p")) { | 69 | answerQuestion(aw); |
111 | aw.answer(PROBABLY); | 70 | // Displays the question and prompts the player for an answer |
112 | 71 | ||
113 | } else if (answer.equals("pn")) { | 72 | reviewSuggestedGuess(aw); |
114 | aw.answer(PROBABLY_NOT); | 73 | // Checks if any guess is available and prompts the player |
74 | } | ||
115 | 75 | ||
116 | } else if (answer.equals("b")) { | 76 | reviewSuggestedGuess(aw); |
117 | aw.undoAnswer(); | 77 | // Reviews the final guess |
118 | 78 | ||
119 | } else if (answer.equals("debug")) { | 79 | finish(false); |
120 | out.printf("Debug information:%n\tCurrent API server: %s%n\tCurrent guess count: %d%n", | 80 | // Loses if all guesses are rejected. |
121 | aw.getServer().getUrl(), aw.getGuesses().size()); | 81 | } |
122 | continue; | ||
123 | // Displays some debug information. | ||
124 | 82 | ||
83 | private static Guess reviewSuggestedGuess(@Nonnull Akiwrapper aw) { | ||
84 | var guess = aw.suggestGuess(); | ||
85 | if (guess != null) { | ||
86 | if (reviewGuess(guess)) { | ||
87 | aw.confirmGuess(guess); | ||
88 | finish(true); | ||
89 | exit(0); | ||
125 | } else { | 90 | } else { |
126 | out.println("Please answer with either " + | 91 | aw.rejectLastGuess(); |
127 | "[Y]ES, [N]O, [D|ONT |K]NOW, [P]ROBABLY or [P|ROBABLY |N]OT or go back one step with [B]ACK."); | ||
128 | continue; | ||
129 | } | 92 | } |
130 | |||
131 | answered = true; | ||
132 | // Answers the question. | ||
133 | } | 93 | } |
94 | return guess; | ||
134 | } | 95 | } |
135 | 96 | ||
136 | private static boolean reviewGuess(@Nonnull Guess guess, @Nonnull Scanner sc) { | 97 | private static boolean reviewGuess(@Nonnull Guess guess) { |
137 | out.println(guess.getName()); | 98 | out.println(guess.getName()); |
138 | out.print("\t"); | 99 | out.print("\t"); |
139 | if (guess.getDescription() == null) | 100 | if (guess.getDescription() == null) |
@@ -143,49 +104,77 @@ public class AkinatorExample { | |||
143 | out.println(); | 104 | out.println(); |
144 | // Displays the guess' information | 105 | // Displays the guess' information |
145 | 106 | ||
146 | boolean answered = false; | 107 | out.print("Is this what you're thinking of? [Y/n] "); |
147 | boolean isCharacter = false; | 108 | var input = IN.next().trim(); |
148 | while (!answered) { | 109 | // Asks the player if the guess is correct |
149 | // Asks the player if the guess is correct | 110 | |
111 | return input.isEmpty() || toLowerCase(input.charAt(0)) == 'y'; | ||
112 | } | ||
113 | |||
114 | private static void answerQuestion(@Nonnull Akiwrapper aw) { | ||
115 | main: while (true) { | ||
116 | // Prompts the player for an answer | ||
117 | |||
118 | var input = IN.next().toLowerCase(); | ||
150 | 119 | ||
151 | out.println("Is this your character? (y/n)"); | 120 | switch (input) { |
152 | String line = sc.nextLine(); | ||
153 | switch (line) { | ||
154 | case "y": | 121 | case "y": |
155 | // If the player has responded positively. | 122 | case "yes": |
156 | answered = true; | 123 | aw.answer(YES); |
157 | isCharacter = true; | 124 | break main; |
158 | break; | ||
159 | 125 | ||
160 | case "n": | 126 | case "n": |
161 | // If the player has responded negatively. | 127 | case "no": |
162 | answered = true; | 128 | aw.answer(NO); |
163 | isCharacter = false; | 129 | break main; |
130 | |||
131 | case "dk": | ||
132 | case "don'tknow": | ||
133 | case "dontknow": | ||
134 | case "dont know": | ||
135 | case "don't know": | ||
136 | aw.answer(DONT_KNOW); | ||
137 | break main; | ||
138 | |||
139 | case "p": | ||
140 | case "probably": | ||
141 | aw.answer(PROBABLY); | ||
142 | break main; | ||
143 | |||
144 | case "pn": | ||
145 | case "probablynot": | ||
146 | case "probably not": | ||
147 | aw.answer(PROBABLY_NOT); | ||
148 | break main; | ||
149 | |||
150 | case "b": | ||
151 | case "back": | ||
152 | if (aw.getStep() == 0) | ||
153 | out.println("Can't undo on the first question."); | ||
154 | else | ||
155 | aw.undoAnswer(); | ||
156 | break main; | ||
157 | |||
158 | case "debug": | ||
159 | displayDebug(aw); | ||
164 | break; | 160 | break; |
161 | // Displays some debug information. | ||
165 | 162 | ||
166 | default: | 163 | default: |
164 | out.println("Please answer with either " + ANSWER_TIP); | ||
167 | break; | 165 | break; |
168 | } | 166 | } |
169 | } | 167 | } |
170 | |||
171 | return isCharacter; | ||
172 | } | 168 | } |
173 | 169 | ||
174 | private static void reviewGuesses(@Nonnull Scanner sc, @Nonnull Akiwrapper aw, @Nonnull List<Long> declined) { | 170 | private static void displayDebug(Akiwrapper aw) { |
175 | for (var guess : aw.getGuessesAboveProbability(PROBABILITY_THRESHOLD)) { | 171 | var question = aw.getQuestion(); |
176 | if (!declined.contains(Long.valueOf(guess.getIdLong()))) { | 172 | out.println("Debug information:"); |
177 | // Checks if this guess complies with the conditions. | 173 | out.printf("\tCurrent API server: %s%n", aw.getServer().getUrl()); |
178 | 174 | out.printf("\tProgression: %f%%%n", question == null ? -1 : question.getProgression()); | |
179 | if (reviewGuess(guess, sc)) { | 175 | out.println("\tGuesses:"); |
180 | // If the player accepts this guess | 176 | aw.getGuesses().stream().forEach(g -> out.printf("\t\t%f - %s%n", g.getProbability(), g.getName())); |
181 | finish(true); | 177 | out.print("> "); |
182 | exit(0); | ||
183 | } | ||
184 | |||
185 | declined.add(guess.getIdLong()); | ||
186 | // Registers this guess as rejected. | ||
187 | } | ||
188 | } | ||
189 | } | 178 | } |
190 | 179 | ||
191 | private static void finish(boolean win) { | 180 | private static void finish(boolean win) { |
@@ -200,67 +189,53 @@ public class AkinatorExample { | |||
200 | } | 189 | } |
201 | } | 190 | } |
202 | 191 | ||
203 | private static boolean getProfanityFilter(@Nonnull Scanner sc) { | 192 | private static boolean getProfanityFilter() { |
204 | out.println("What's your age? (default: 18)"); | 193 | out.print("Enable profanity filtering? [y/N] "); |
205 | while (true) { | 194 | var input = IN.next().trim(); |
206 | var age = sc.nextLine(); | 195 | return !input.isEmpty() && toLowerCase(input.charAt(0)) == 'y'; |
207 | |||
208 | if (age.equals("")) | ||
209 | return false; | ||
210 | |||
211 | try { | ||
212 | return parseInt(age) < 16; | ||
213 | } catch (NumberFormatException e) { | ||
214 | out.println("That's not a number"); | ||
215 | } | ||
216 | } | ||
217 | } | 196 | } |
218 | 197 | ||
219 | @Nonnull | 198 | @Nonnull |
220 | private static Language getLanguage(@Nonnull Scanner sc) { | 199 | @SuppressWarnings("null") |
200 | private static Language getLanguage() { | ||
221 | var languages = EnumSet.allOf(Language.class); | 201 | var languages = EnumSet.allOf(Language.class); |
222 | 202 | ||
223 | out.println("What's your language? (default: English)"); | 203 | out.print("What's your language? [English] "); |
224 | while (true) { | 204 | while (true) { |
225 | String selectedLanguage = sc.nextLine().toLowerCase().trim(); | 205 | var input = IN.next().trim().toUpperCase(); |
226 | 206 | ||
227 | if (selectedLanguage.equals("")) | 207 | if (input.isEmpty()) |
228 | return ENGLISH; | 208 | return ENGLISH; |
229 | 209 | ||
230 | var language = languages.stream() | 210 | var language = languages.stream().filter(l -> l.toString().equals(input)).findAny(); |
231 | .filter(l -> l.toString().toLowerCase().equals(selectedLanguage)) | ||
232 | .findAny() | ||
233 | .orElse(null); | ||
234 | 211 | ||
235 | if (language != null) { | 212 | if (language.isPresent()) { |
236 | return language; | 213 | return language.orElseThrow(); |
237 | 214 | ||
238 | } else { | 215 | } else { |
239 | out.println(languages.stream() | 216 | out.println(languages.stream() |
240 | .map(Enum::toString) | 217 | .map(Enum::toString) |
241 | .collect(joining("\n-", "Sorry, that language isn't supported. Choose between\n-", ""))); | 218 | .collect(joining("\n-", "Sorry, that language isn't supported. Available options:\n-", ""))); |
242 | } | 219 | } |
243 | } | 220 | } |
244 | } | 221 | } |
245 | 222 | ||
246 | @Nonnull | 223 | @Nonnull |
247 | private static GuessType getGuessType(@Nonnull Scanner sc) { | 224 | @SuppressWarnings("null") |
225 | private static GuessType getGuessType() { | ||
248 | var guessTypes = EnumSet.allOf(GuessType.class); | 226 | var guessTypes = EnumSet.allOf(GuessType.class); |
249 | 227 | ||
250 | out.println("What will you be guessing? (default: character)"); | 228 | out.print("What will you be guessing? [character] "); |
251 | while (true) { | 229 | while (true) { |
252 | String selectedGuessType = sc.nextLine().toLowerCase().trim(); | 230 | var input = IN.next().trim().toUpperCase(); |
253 | 231 | ||
254 | if (selectedGuessType.equals("")) | 232 | if (input.isEmpty()) |
255 | return CHARACTER; | 233 | return CHARACTER; |
256 | 234 | ||
257 | var guessType = guessTypes.stream() | 235 | var guessType = guessTypes.stream().filter(l -> l.toString().equals(input)).findAny(); |
258 | .filter(l -> l.toString().toLowerCase().equals(selectedGuessType)) | ||
259 | .findAny() | ||
260 | .orElse(null); | ||
261 | 236 | ||
262 | if (guessType != null) { | 237 | if (guessType.isPresent()) { |
263 | return guessType; | 238 | return guessType.orElseThrow(); |
264 | 239 | ||
265 | } else { | 240 | } else { |
266 | out.println("" + guessTypes.stream() | 241 | out.println("" + guessTypes.stream() |