From 8f5882d4d94ad80c51e60269d0b0b3c53b41af05 Mon Sep 17 00:00:00 2001 From: SowinskiBraeden Date: Mon, 10 Nov 2025 12:42:23 -0800 Subject: [PATCH] complete lab09 --- .gitignore | 1 + data/countries.txt | 194 ++++++++++++++++++ data/highscore.txt | 1 + .../bcit/comp2522/lab09/HighScoreService.java | 107 ++++++++++ .../ca/bcit/comp2522/lab09/LoggerService.java | 101 +++++++++ .../ca/bcit/comp2522/lab09/LuckyVault.java | 131 ++++++++++++ src/code/ca/bcit/comp2522/lab09/Main.java | 36 ++++ src/code/ca/bcit/comp2522/lab09/WordList.java | 57 +++++ 8 files changed, 628 insertions(+) create mode 100644 data/countries.txt create mode 100644 data/highscore.txt create mode 100644 src/code/ca/bcit/comp2522/lab09/HighScoreService.java create mode 100644 src/code/ca/bcit/comp2522/lab09/LoggerService.java create mode 100644 src/code/ca/bcit/comp2522/lab09/LuckyVault.java create mode 100644 src/code/ca/bcit/comp2522/lab09/Main.java create mode 100644 src/code/ca/bcit/comp2522/lab09/WordList.java diff --git a/.gitignore b/.gitignore index 41a25f1..f9dc3c2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /.idea/* /src/code/ca/bcit/comp2522/testing/* +/data/logs/* \ No newline at end of file diff --git a/data/countries.txt b/data/countries.txt new file mode 100644 index 0000000..9227254 --- /dev/null +++ b/data/countries.txt @@ -0,0 +1,194 @@ +Afghanistan +Albania +Algeria +Andorra +Angola +Antigua and Barbuda +Argentina +Armenia +Australia +Austria +Azerbaijan +Bahamas +Bahrain +Bangladesh +Barbados +Belarus +Belgium +Belize +Benin +Bhutan +Bolivia +Bosnia and Herzegovina +Botswana +Brazil +Brunei +Bulgaria +Burkina Faso +Burundi +Cabo Verde +Cambodia +Cameroon +Canada +Central African Republic +Chad +Chile +China +Colombia +Comoros +Congo (Congo-Brazzaville) +Costa Rica +Croatia +Cuba +Cyprus +Czechia +Democratic Republic of the Congo +Denmark +Djibouti +Dominica +Dominican Republic +Ecuador +Egypt +El Salvador +Equatorial Guinea +Eritrea +Estonia +Eswatini +Ethiopia +Fiji +Finland +France +Gabon +Gambia +Georgia +Germany +Ghana +Greece +Grenada +Guatemala +Guinea +Guinea-Bissau +Guyana +Haiti +Honduras +Hungary +Iceland +India +Indonesia +Iran +Iraq +Ireland +Israel +Italy +Jamaica +Japan +Jordan +Kazakhstan +Kenya +Kiribati +Kuwait +Kyrgyzstan +Laos +Latvia +Lebanon +Lesotho +Liberia +Libya +Liechtenstein +Lithuania +Luxembourg +Madagascar +Malawi +Malaysia +Maldives +Mali +Malta +Marshall Islands +Mauritania +Mauritius +Mexico +Micronesia +Moldova +Monaco +Mongolia +Montenegro +Morocco +Mozambique +Myanmar +Namibia +Nauru +Nepal +Netherlands +New Zealand +Nicaragua +Niger +Nigeria +North Korea +North Macedonia +Norway +Oman +Pakistan +Palau +Palestine +Panama +Papua New Guinea +Paraguay +Peru +Philippines +Poland +Portugal +Qatar +Romania +Russia +Rwanda +Saint Kitts and Nevis +Saint Lucia +Saint Vincent and the Grenadines +Samoa +San Marino +Sao Tome and Principe +Saudi Arabia +Senegal +Serbia +Seychelles +Sierra Leone +Singapore +Slovakia +Slovenia +Solomon Islands +Somalia +South Africa +South Korea +South Sudan +Spain +Sri Lanka +Sudan +Suriname +Sweden +Switzerland +Syria +Tajikistan +Tanzania +Thailand +Timor-Leste +Togo +Tonga +Trinidad and Tobago +Tunisia +Turkey +Turkmenistan +Tuvalu +Uganda +Ukraine +United Arab Emirates +United Kingdom +United States +Uruguay +Uzbekistan +Vanuatu +Vatican City +Venezuela +Vietnam +Yemen +Zambia +Zimbabwe diff --git a/data/highscore.txt b/data/highscore.txt new file mode 100644 index 0000000..dc460a7 --- /dev/null +++ b/data/highscore.txt @@ -0,0 +1 @@ +COUNTRY=14 \ No newline at end of file diff --git a/src/code/ca/bcit/comp2522/lab09/HighScoreService.java b/src/code/ca/bcit/comp2522/lab09/HighScoreService.java new file mode 100644 index 0000000..1b1b48e --- /dev/null +++ b/src/code/ca/bcit/comp2522/lab09/HighScoreService.java @@ -0,0 +1,107 @@ +package ca.bcit.comp2522.lab09; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +/** + * Handles loading, tracking, and saving the player's best score. + * + * @author Nico Agostini + * @author Braeden Sowinski + * @author Trishaan Shetty + * @author Calvin Arifianto + * @version 1.0.0 + */ +public class HighScoreService +{ + private static final String FILE_PATH = "./data/highscore.txt"; + private static final String PREFIX = "COUNTRY="; + + private final BufferedWriter writer; + private final int highScore; + + /** + * Creates a HighScoreService and loads the stored high score. + * + * @throws IOException if the high score file cannot be read or created. + */ + public HighScoreService() + throws IOException + { + final File scoreFile; + final BufferedReader reader; + final String line; + + scoreFile = new File(FILE_PATH); + scoreFile.createNewFile(); + + reader = new BufferedReader(new FileReader(FILE_PATH)); + line = reader.readLine(); + + if (line == null) + { + this.highScore = Integer.MAX_VALUE; + } + else + { + this.highScore = Integer.parseInt(line.replaceAll(PREFIX, "")); + } + + reader.close(); + + this.writer = new BufferedWriter(new FileWriter(FILE_PATH)); + } + + /** + * Returns the current high score. + * + * @return the saved high score. + */ + public int getHighScore() + { + return this.highScore; + } + + /** + * Updates the high score if the new score is better. + * + * @param newScore the new score to compare. + */ + public void updateCurrentScore(final int newScore) + { + try + { + if (newScore < this.highScore) + { + this.writer.write(PREFIX + newScore); + } + else + { + this.writer.write(PREFIX + this.highScore); + } + } + catch (IOException e) + { + System.err.printf("Failed to update high score: %s\n", e.getMessage()); + } + } + + /** + * Saves the high score file. + */ + public void saveHighScore() + { + try + { + writer.close(); + } + catch(IOException e) + { + System.err.println("Error closing the writer: " + e.getMessage()); + } + } +} diff --git a/src/code/ca/bcit/comp2522/lab09/LoggerService.java b/src/code/ca/bcit/comp2522/lab09/LoggerService.java new file mode 100644 index 0000000..49b953e --- /dev/null +++ b/src/code/ca/bcit/comp2522/lab09/LoggerService.java @@ -0,0 +1,101 @@ +package ca.bcit.comp2522.lab09; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * Creates and writes game logs for each play session. + * Each guess and outcome is recorded with a timestamp. + * + * @author Nico Agostini + * @author Braeden Sowinski + * @author Trishaan Shetty + * @author Calvin Arifianto + * @version 1.0.0 + */ +public class LoggerService +{ + private static final Path BASE_PATH = Paths.get("data", "logs"); + private static final String DATE_PATTERN = "YYYY-MM-dd_HH-mm-ss"; + + private final BufferedWriter writer; + + /** + * getDateTime returns a string of the + * date and time for the logs + * @return date time string formatted as the pattern + */ + private static String getDateTime() + { + final LocalDateTime now; + final DateTimeFormatter formatter; + + now = LocalDateTime.now(); + formatter = DateTimeFormatter.ofPattern(DATE_PATTERN); + + return now.format(formatter); + } + + /** + * Creates a new log file for a game session. + * + * @param country the correct word used in the round. + * @throws IOException if the log directory or file cannot be created. + */ + public LoggerService(final String country) + throws IOException + { + Files.createDirectories(BASE_PATH); + + final String logFilename = BASE_PATH + "/" + getDateTime() + "_" + country + ".txt"; + + this.writer = new BufferedWriter(new FileWriter(logFilename)); + } + + /** + * Writes a log entry. + * + * @param guess the player's guess. + * @param outcome the result of the guess. + */ + public void log(final String guess, final String outcome) + { + final StringBuilder log = new StringBuilder(); + + log.append(getDateTime()); + log.append(" Guess: "); + log.append(guess); + log.append(" Outcome: "); + log.append(outcome); + + try { + this.writer.write(log.toString()); + this.writer.newLine(); + } + catch (final IOException e) + { + System.err.printf("Failed to write to log: %s\n", e.getMessage()); + } + } + + /** + * Saves and closes the log file. + */ + public void saveLog() + { + try + { + writer.close(); + } + catch(IOException e) + { + System.err.println("Error closing the writer: " + e.getMessage()); + } + } +} diff --git a/src/code/ca/bcit/comp2522/lab09/LuckyVault.java b/src/code/ca/bcit/comp2522/lab09/LuckyVault.java new file mode 100644 index 0000000..6089a4b --- /dev/null +++ b/src/code/ca/bcit/comp2522/lab09/LuckyVault.java @@ -0,0 +1,131 @@ +package ca.bcit.comp2522.lab09; + +import java.io.IOException; +import java.util.Scanner; +import java.io.FileNotFoundException; + +/** + * Game controller for Lucky Vault. Generates a random word and manages gameplay. + * Tracks user guesses, logging, and high score updates. + * + * @author Nico Agostini + * @author Braeden Sowinski + * @author Trishaan Shetty + * @author Calvin Arifianto + * @version 1.0.0 + */ +public class LuckyVault +{ + private static final int STARTING_VALUE = 0; + private static final int INVALID_MATCH = -1; + + private final String country; + private final LoggerService logger; + private final HighScoreService highScore; + + /* + * matchingChars checks how many characters match two + * given strings + * @param s1 string one to match + * @param s2 string two to match + * @return number of characters matching (same position) + */ + private static int matchingChars( + final String s1, + final String s2 + ) { + if (s1.length() != s2.length()) + { + return INVALID_MATCH; + } + + int matched = STARTING_VALUE; + + for (int i = 0; i < s1.length(); i++) + { + if (s1.charAt(i) == s2.charAt(i)) + { + matched++; + } + } + + return matched; + } + + /** + * Constructs a LuckyVault game with a random word. + * + * @throws IOException if files are missing or unreadable. + */ + public LuckyVault() + throws IOException, FileNotFoundException + { + final WordList countries = new WordList("./data/countries.txt"); + + this.country = countries.getRandomWord(); + this.logger = new LoggerService(this.country); + this.highScore = new HighScoreService(); + } + + /** + * Runs the game session. + */ + public void run() + { + final Scanner inputScanner = new Scanner(System.in); + String input; + int attempts = STARTING_VALUE; + + System.out.println("Lucky Vault — COUNTRY MODE. Type QUIT to exit."); + System.out.printf("Secret Word Length: %d\n", country.length()); + System.out.println("Current Best: " + (this.highScore.getHighScore() == Integer.MAX_VALUE ? + "-" : this.highScore.getHighScore())); + + while(true) + { + System.out.print("Your guess: "); + input = inputScanner.nextLine(); + + if (input.isEmpty()) + { + System.out.println("Empty guess. Try again."); + continue; + } + + if(input.equalsIgnoreCase("QUIT")) + { + System.out.println("Bye!"); + break; + } + + attempts++; + + if (input.length() != this.country.length()) + { + System.out.printf("Wrong length (%d). Need %d\n", input.length(), this.country.length()); + this.logger.log(input, "wrong_length"); + continue; + } + + if (!input.equalsIgnoreCase(this.country)) + { + final int matching = matchingChars(input, this.country); + + System.out.printf("Not it. %d letter(s) correct (correct position).\n", matching); + this.logger.log(input, "matches=" + matching); + continue; + } + + System.out.printf("Correct in %d attempts! Word was: %s\n", attempts, this.country); + + this.logger.log(input, "CORRECT_in=" + attempts); + this.highScore.updateCurrentScore(attempts); + + break; + } + + inputScanner.close(); + this.logger.saveLog(); + this.highScore.saveHighScore(); + } +} diff --git a/src/code/ca/bcit/comp2522/lab09/Main.java b/src/code/ca/bcit/comp2522/lab09/Main.java new file mode 100644 index 0000000..b9d1602 --- /dev/null +++ b/src/code/ca/bcit/comp2522/lab09/Main.java @@ -0,0 +1,36 @@ +package ca.bcit.comp2522.lab09; + +import java.io.IOException; + +/** + * Entry point for the Lucky Vault game. + * + * @author Nico Agostini + * @author Braeden Sowinski + * @author Trishaan Shetty + * @author Calvin Arifianto + * @version 1.0.0 + */ +public class Main +{ + /** + * Starts the Lucky Vault application. + * + * @param args command line arguments (unused). + */ + public static void main(final String[] args) + { + final LuckyVault vault; + + try { + vault = new LuckyVault(); + } + catch (final IOException e) + { + System.err.printf("An error occurred during an IO operation: %s\n", e.getMessage()); + return; + } + + vault.run(); + } +} diff --git a/src/code/ca/bcit/comp2522/lab09/WordList.java b/src/code/ca/bcit/comp2522/lab09/WordList.java new file mode 100644 index 0000000..0455171 --- /dev/null +++ b/src/code/ca/bcit/comp2522/lab09/WordList.java @@ -0,0 +1,57 @@ +package ca.bcit.comp2522.lab09; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.Scanner; + +/** + * Loads a list of words from a text file and provides random selection. + * @author Nico Agostini + * @author Braeden Sowinski + * @author Trishaan Shetty + * @author Calvin Arifianto + * @version 1.0.0 + */ +public class WordList +{ + private final List wordList; + + /** + * Creates a word list by reading each line of the given file. + * + * @param filePath the file containing words. + * @throws FileNotFoundException if the file cannot be opened. + */ + public WordList(final String filePath) + throws FileNotFoundException + { + this.wordList = new ArrayList<>(); + + final Scanner fileScanner; + final File file = new File(filePath); + + fileScanner = new Scanner(file); + + while (fileScanner.hasNextLine()) + { + this.wordList.add(fileScanner.nextLine()); + } + + fileScanner.close(); + } + + /** + * Returns a random word from the list. + * + * @return a randomly selected word. + */ + public String getRandomWord() + { + final Random rand = new Random(); + + return this.wordList.get(rand.nextInt(this.wordList.size())); + } +}