/ assign5 / HangmanCore.java
HangmanCore.java
  1  import java.lang.System.Logger;
  2  import java.lang.System.Logger.Level;
  3  import java.util.Scanner;
  4  
  5  /// Hangman game - core executable and embeddable class
  6  ///
  7  /// SPDX-License-Identifier: GPL-3.0-or-later
  8  /// SPDX-FileCopyrightText: 2024 Jonas Smedegaard <dr@jones.dk>
  9  ///
 10  /// Guess letters for a secret word
 11  ///
 12  /// Core class usable as self-contained executable via method main()
 13  /// and also embedded in a larger system by instantiating GridCore().
 14  ///
 15  /// * v0.0.3
 16  ///   * use System.Logger, and picocli for main executable
 17  /// * v0.0.2
 18  ///   * Java reimplementation from memory in 2024
 19  /// * v0.0.1
 20  ///   * C64 BASIC implementation in 1985
 21  ///     (lost on a tape somewhere)
 22  ///
 23  /// @version 0.0.3
 24  /// @see <https://app.radicle.xyz/nodes/ash.radicle.garden/rad:z3YzABgyz2D36LiKe3YcdJ6PcDCXM/tree/assign5/README.md>
 25  /// @see <https://moodle.ruc.dk/mod/assign/view.php?id=508316>
 26  public final class HangmanCore {
 27  
 28  	/// construct word object
 29  	///
 30  	/// @param logger   logger object
 31  	/// @param word     word to guess
 32  	/// @param missMax  maximum failed guesses
 33  	public HangmanCore(
 34  		final Logger logger,
 35  		final String word,
 36  		final int missMax
 37  	) {
 38  		this.logger = logger;
 39  		this.word = word;
 40  		this.missMax = missMax;
 41  	}
 42  
 43  	/// main method
 44  	///
 45  	/// @param args  (ignored)
 46  	public static void main(final String[] args) {
 47  
 48  		// simplify logging format
 49  		// @since JDK 7
 50  		// @see <https://stackoverflow.com/a/34229629/18619283>
 51  		System.setProperty("java.util.logging.SimpleFormatter.format",
 52  			"%5$s%6$s%n");
 53  
 54  		// Instantiation as dictated by assignment
 55  		HangmanCore core = new HangmanCore(
 56  			System.getLogger(""),
 57  			"banana",
 58  			8);
 59  
 60  		// minimal viable product
 61  		while (core.hasMaskedCharacters() && core.hasMoreAttempts()) {
 62  			core.logger.log(Level.INFO,
 63  				"[miss: {0}] Guess this word: {1}",
 64  				core.miss(),
 65  				core.mask());
 66  			core.ask("Guess a character, and hit ENTER: ");
 67  		}
 68  	}
 69  
 70  	/// logger object
 71  	// @see <https://renato.athaydes.com/posts/java-system-logging>
 72  	private final Logger logger;
 73  
 74  	/// maximum misses
 75  	private final Integer missMax;
 76  
 77  	/// the word to guess
 78  	private final String word;
 79  
 80  	/// characters guessed
 81  	private String chars = "";
 82  
 83  	/// amount of false guesses
 84  	private int miss;
 85  
 86  	/// is word still masked?
 87  	///
 88  	/// @return  boolean whether the word has been guessed
 89  	public boolean hasMaskedCharacters() {
 90  		if (!mask().contains(".")) {
 91  			logger.log(Level.INFO, "game over - word guessed");
 92  			return false;
 93  		}
 94  		return true;
 95  	}
 96  
 97  	/// are any guessing attempts left?
 98  	///
 99  	/// @return  boolean whether more gueeses remains
100  	public boolean hasMoreAttempts() {
101  		if (miss() >= missMax) {
102  			logger.log(Level.INFO, "game over - 8 wrong guesses");
103  			return false;
104  		}
105  		return true;
106  	}
107  
108  	/// getter for misses
109  	///
110  	/// @return  amount of misses
111  	public int miss() {
112  		return miss;
113  	}
114  
115  	/// query for and process a guess
116  	///
117  	/// @param prompt  string encouraging to type a character
118  	public void ask(final String prompt) {
119  		Scanner scanner = new Scanner(System.in);
120  		System.out.print(prompt);
121  		String input = scanner.next();
122  		while (input.isEmpty()) {
123  			input = scanner.next();
124  		}
125  
126  		String s = input.substring(0, 1);
127  		chars += s;
128  		if (!word.contains(s)) {
129  			logger.log(Level.INFO, "unused character {0}", s);
130  			miss++;
131  		}
132  	}
133  
134  	/// hide not yet guessed characters in word
135  	///
136  	/// @return  masked word
137  	public String mask() {
138  		String maskedWord = word;
139  
140  		// sloppily check potential duplicates repeatedly
141  		for (int i = 0; i < word.length(); i++) {
142  			String s = word.substring(i, i + 1);
143  			if (!chars.contains(s)) {
144  				maskedWord = maskedWord.replace(s, ".");
145  			}
146  		}
147  
148  		return maskedWord;
149  	}
150  }