Skip to content

Commit 1ae7137

Browse files
committed
fixed anagram puzzle not saving in the game
1 parent 2907f66 commit 1ae7137

File tree

1 file changed

+136
-50
lines changed

1 file changed

+136
-50
lines changed

src/main/java/com/escapegame/AnagramPuzzleController.java

Lines changed: 136 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@
66
import java.io.IOException;
77
import java.io.OutputStream;
88
import java.net.URL;
9+
import java.text.Normalizer;
10+
import java.util.Locale;
911
import java.util.Properties;
1012
import java.util.ResourceBundle;
1113

14+
import com.model.User;
15+
1216
import javafx.fxml.FXML;
1317
import javafx.fxml.Initializable;
1418
import javafx.scene.control.Alert;
@@ -22,9 +26,12 @@
2226

2327
/**
2428
* Anagram puzzle controller — scrambled LPAEP -> APPLE (category Fruit)
25-
* Manages the anagram puzzle UI: background loading, attempts/hints state,
26-
* simple persistence to a properties file, and navigation on solve/quit.
27-
* UI references are checked for null to be defensive when used from FXML.
29+
* Improvements:
30+
* - per-user save file (.escapegame_anagram_<userid>.properties)
31+
* - debug prints showing the save path and loaded state
32+
* - safe parsing of integer properties
33+
* - normalization of user input for robust matching
34+
* - clearSaveForCurrentUser() helper for testing
2835
*/
2936
public class AnagramPuzzleController implements Initializable {
3037

@@ -54,16 +61,9 @@ public class AnagramPuzzleController implements Initializable {
5461
"apple fruit"
5562
};
5663

57-
private final File SAVE_FILE = new File(System.getProperty("user.home"), ".escapegame_anagram.properties");
58-
5964
private static final String RESOURCE_PATH = "/images/background.png";
6065
private static final String DEV_FALLBACK = "file:/mnt/data/Screenshot 2025-11-22 202825.png";
61-
/**
62-
* Initialize the puzzle screen: load background image (resource then fallback),
63-
* set initial UI labels, draw hearts and restore saved progress.
64-
* @param location not used
65-
* @param resources not used
66-
*/
66+
6767
@Override
6868
public void initialize(URL location, ResourceBundle resources) {
6969
System.out.println("AnagramPuzzleController.initialize() start");
@@ -73,7 +73,7 @@ public void initialize(URL location, ResourceBundle resources) {
7373
URL res = getClass().getResource(RESOURCE_PATH);
7474
if (res != null) {
7575
Image img = new Image(res.toExternalForm());
76-
backgroundImage.setImage(img);
76+
if (backgroundImage != null) backgroundImage.setImage(img);
7777
loaded = true;
7878
}
7979
} catch (Exception ex) {
@@ -83,7 +83,7 @@ public void initialize(URL location, ResourceBundle resources) {
8383
if (!loaded) {
8484
try {
8585
Image img = new Image(DEV_FALLBACK);
86-
backgroundImage.setImage(img);
86+
if (backgroundImage != null) backgroundImage.setImage(img);
8787
loaded = true;
8888
} catch (Exception ex) {
8989
System.err.println("Error loading dev fallback image: " + ex.getMessage());
@@ -104,19 +104,73 @@ public void initialize(URL location, ResourceBundle resources) {
104104
if (hintsLabel != null) hintsLabel.setText(hintsLeft + " hint(s) available");
105105
if (scrambledLabel != null) scrambledLabel.setText("Scrambled: LPAEP");
106106
if (categoryLabel != null) categoryLabel.setText("Category: Fruit");
107-
if (promptLabel != null) promptLabel.setText("Prompt: Unscramble the letters to find the word");
107+
if (promptLabel != null) promptLabel.setText("Prompt: Unscramble the letters to find the word: LPAEP");
108108
refreshHearts();
109+
110+
// debug: show exactly which save file will be used
111+
System.out.println("DEBUG: Anagram save path -> " + getSaveFileForCurrentUser().getAbsolutePath());
112+
109113
loadSave();
110114

115+
// If save marked solved=true, automatically clear it (one-time reset) and re-enable UI
116+
if (solved) {
117+
System.out.println("DEBUG: Saved state indicates solved=true — clearing save to reset puzzle.");
118+
clearSaveForCurrentUser();
119+
solved = false;
120+
attemptsLeft = 3;
121+
hintsLeft = 3;
122+
nextHintIndex = 0;
123+
if (hintsLabel != null) hintsLabel.setText(hintsLeft + " hint(s) available");
124+
if (statusLabel != null) statusLabel.setText("");
125+
if (btnSubmit != null) btnSubmit.setDisable(false);
126+
if (btnHint != null) btnHint.setDisable(false);
127+
if (answerField != null) answerField.setDisable(false);
128+
refreshHearts();
129+
}
130+
111131
System.out.println("AnagramPuzzleController.initialize() done");
112132
}
113-
/**
114-
* Draw hearts representing remaining attempts and disable inputs when none remain.
115-
*/
133+
134+
private File getSaveFileForCurrentUser() {
135+
String userId = "guest";
136+
try {
137+
User u = null;
138+
try {
139+
u = App.getCurrentUser();
140+
} catch (Throwable t) {
141+
// fallback to reflection if needed
142+
try {
143+
java.lang.reflect.Method gu = App.class.getMethod("getCurrentUser");
144+
Object userObj = gu.invoke(null);
145+
if (userObj instanceof User) {
146+
u = (User) userObj;
147+
} else if (userObj != null) {
148+
try {
149+
java.lang.reflect.Method getId = userObj.getClass().getMethod("getUserId");
150+
Object idObj = getId.invoke(userObj);
151+
if (idObj != null) {
152+
final String idStr = idObj.toString();
153+
u = new User() { @Override public String getUserId() { return idStr; } };
154+
}
155+
} catch (Throwable ignored) { }
156+
}
157+
} catch (Throwable ignored) { }
158+
}
159+
if (u != null && u.getUserId() != null && !u.getUserId().trim().isEmpty()) {
160+
userId = u.getUserId().trim();
161+
}
162+
} catch (Throwable t) {
163+
// ignore, use guest
164+
}
165+
String clean = userId.replaceAll("\\s+", "_").toLowerCase(Locale.ROOT);
166+
String filename = ".escapegame_anagram_" + clean + ".properties";
167+
return new File(System.getProperty("user.home"), filename);
168+
}
169+
116170
private void refreshHearts() {
117171
if (heartsBox == null) return;
118172
heartsBox.getChildren().clear();
119-
for (int i = 0; i < attemptsLeft; i++) {
173+
for (int i = 0; i < Math.max(0, attemptsLeft); i++) {
120174
Label heart = new Label("\u2764");
121175
heart.setStyle("-fx-text-fill: #ff4d6d; -fx-font-size: 20px;");
122176
heartsBox.getChildren().add(heart);
@@ -126,26 +180,24 @@ private void refreshHearts() {
126180
if (answerField != null) answerField.setDisable(true);
127181
}
128182
}
129-
/**
130-
* Called when the user submits an answer. Validates input, checks accepted
131-
* answers, updates state, shows alerts, saves progress and navigates on success.
132-
*/
183+
133184
@FXML
134185
private void onSubmit() {
135186
if (solved) {
136187
if (statusLabel != null) statusLabel.setText("You already solved the puzzle.");
137188
return;
138189
}
139190

140-
String answer = (answerField == null || answerField.getText() == null) ? "" : answerField.getText().trim().toLowerCase();
191+
String raw = (answerField == null || answerField.getText() == null) ? "" : answerField.getText();
192+
String answer = normalize(raw);
141193
if (answer.isEmpty()) {
142194
if (statusLabel != null) statusLabel.setText("Please enter an answer.");
143195
return;
144196
}
145197

146198
boolean ok = false;
147199
for (String a : ACCEPTED_ANSWERS) {
148-
if (a.equals(answer)) { ok = true; break; }
200+
if (normalize(a).equals(answer)) { ok = true; break; }
149201
}
150202

151203
if (ok) {
@@ -158,7 +210,7 @@ private void onSubmit() {
158210
saveProgress();
159211
try { App.setRoot("opened5"); } catch (IOException e) { e.printStackTrace(); }
160212
} else {
161-
attemptsLeft--;
213+
attemptsLeft = Math.max(0, attemptsLeft - 1);
162214
refreshHearts();
163215
if (attemptsLeft <= 0) {
164216
if (statusLabel != null) statusLabel.setText("No attempts left. The correct answer was: \"apple\".");
@@ -171,9 +223,7 @@ private void onSubmit() {
171223
}
172224
}
173225
}
174-
/**
175-
* Shows the next hint (if available), updates hint counters and saves progress.
176-
*/
226+
177227
@FXML
178228
private void onHint() {
179229
if (solved) {
@@ -186,62 +236,63 @@ private void onHint() {
186236
}
187237

188238
String hint = HINTS[Math.min(nextHintIndex, HINTS.length - 1)];
189-
nextHintIndex++;
190-
hintsLeft--;
239+
nextHintIndex = Math.min(nextHintIndex + 1, HINTS.length);
240+
hintsLeft = Math.max(0, hintsLeft - 1);
191241
if (hintsLabel != null) hintsLabel.setText(hintsLeft + " hint(s) available");
192242
new Alert(Alert.AlertType.INFORMATION, hint).showAndWait();
193243
saveProgress();
194244
}
195-
/** Saves progress to the user's properties file and updates the status label. */
245+
196246
@FXML
197247
private void onSave() {
198248
if (statusLabel != null) statusLabel.setText(saveProgress() ? "Progress saved." : "Save failed.");
199249
}
200-
/** Quit action: navigate back to the previous/opened4 screen. */
250+
201251
@FXML
202252
private void onQuit() {
203-
try { App.setRoot("opened4");
204-
} catch (IOException e) {
205-
e.printStackTrace();
206-
}
253+
try { App.setRoot("opened4"); } catch (IOException e) { e.printStackTrace(); }
207254
}
208-
/**
209-
* Persist puzzle state to a properties file under the user's home directory.
210-
* @return true if save succeeded, false otherwise
211-
*/
255+
212256
private boolean saveProgress() {
213257
try {
214258
Properties p = new Properties();
215259
p.setProperty("attemptsLeft", String.valueOf(attemptsLeft));
216260
p.setProperty("hintsLeft", String.valueOf(hintsLeft));
217261
p.setProperty("solved", String.valueOf(solved));
218262
p.setProperty("nextHintIndex", String.valueOf(nextHintIndex));
219-
try (OutputStream os = new FileOutputStream(SAVE_FILE)) {
263+
File out = getSaveFileForCurrentUser();
264+
try (OutputStream os = new FileOutputStream(out)) {
220265
p.store(os, "Anagram puzzle save");
221266
}
267+
System.out.println("DEBUG saved -> " + out.getAbsolutePath());
222268
return true;
223269
} catch (Exception e) {
224270
System.err.println("Save error: " + e.getMessage());
225271
return false;
226272
}
227273
}
228-
/**
229-
* Load saved puzzle state from properties file if present and update UI.
230-
*/
274+
231275
private void loadSave() {
232276
try {
233-
if (!SAVE_FILE.exists()) return;
277+
File f = getSaveFileForCurrentUser();
278+
if (!f.exists()) {
279+
System.out.println("DEBUG: save file not found -> " + f.getAbsolutePath());
280+
return;
281+
}
234282
Properties p = new Properties();
235-
try (FileInputStream fis = new FileInputStream(SAVE_FILE)) {
283+
try (FileInputStream fis = new FileInputStream(f)) {
236284
p.load(fis);
237285
}
238-
attemptsLeft = Integer.parseInt(p.getProperty("attemptsLeft", "3"));
239-
hintsLeft = Integer.parseInt(p.getProperty("hintsLeft", "3"));
240-
solved = Boolean.parseBoolean(p.getProperty("solved", "false"));
241-
nextHintIndex = Integer.parseInt(p.getProperty("nextHintIndex", "0"));
286+
attemptsLeft = getSafeInt(p.getProperty("attemptsLeft"), attemptsLeft);
287+
hintsLeft = getSafeInt(p.getProperty("hintsLeft"), hintsLeft);
288+
solved = Boolean.parseBoolean(p.getProperty("solved", String.valueOf(solved)));
289+
nextHintIndex = getSafeInt(p.getProperty("nextHintIndex"), nextHintIndex);
242290
if (hintsLabel != null) hintsLabel.setText(hintsLeft + " hint(s) available");
243291
refreshHearts();
244292

293+
System.out.println("DEBUG loaded - attemptsLeft=" + attemptsLeft
294+
+ " hintsLeft=" + hintsLeft + " nextHintIndex=" + nextHintIndex + " solved=" + solved
295+
+ " (from " + f.getAbsolutePath() + ")");
245296
if (solved) {
246297
if (btnSubmit != null) btnSubmit.setDisable(true);
247298
if (answerField != null) answerField.setDisable(true);
@@ -252,4 +303,39 @@ private void loadSave() {
252303
System.err.println("Load save failed: " + e.getMessage());
253304
}
254305
}
306+
307+
private int getSafeInt(String s, int fallback) {
308+
if (s == null) return fallback;
309+
try {
310+
return Integer.parseInt(s.trim());
311+
} catch (NumberFormatException ex) {
312+
System.err.println("Invalid integer in save file: \"" + s + "\"; using fallback " + fallback);
313+
return fallback;
314+
}
315+
}
316+
317+
private static String normalize(String raw) {
318+
if (raw == null) return "";
319+
String n = Normalizer.normalize(raw, Normalizer.Form.NFKC)
320+
.toLowerCase(Locale.ROOT)
321+
.replaceAll("[^\\p{L}\\p{N}\\s]", " ")
322+
.trim()
323+
.replaceAll("\\s+", " ");
324+
if (n.startsWith("a ")) n = n.substring(2).trim();
325+
else if (n.startsWith("an ")) n = n.substring(3).trim();
326+
else if (n.startsWith("the ")) n = n.substring(4).trim();
327+
return n;
328+
}
329+
330+
private boolean clearSaveForCurrentUser() {
331+
File f = getSaveFileForCurrentUser();
332+
if (f.exists()) {
333+
boolean deleted = f.delete();
334+
System.out.println("DEBUG: clearSaveForCurrentUser() deleted=" + deleted + " -> " + f.getAbsolutePath());
335+
return deleted;
336+
} else {
337+
System.out.println("DEBUG: clearSaveForCurrentUser() not found -> " + f.getAbsolutePath());
338+
return true;
339+
}
340+
}
255341
}

0 commit comments

Comments
 (0)