/ assign5 / Hangman.java
Hangman.java
  1  import java.io.IOException;
  2  import java.lang.System.Logger.Level;
  3  import picocli.CommandLine;
  4  import picocli.CommandLine.Command;
  5  import picocli.CommandLine.Option;
  6  
  7  /// Hangman game - picocli executable
  8  ///
  9  /// SPDX-License-Identifier: GPL-3.0-or-later
 10  /// SPDX-FileCopyrightText: 2024 Jonas Smedegaard <dr@jones.dk>
 11  ///
 12  /// Guess letters for a secret word
 13  ///
 14  /// * v0.0.3
 15  ///   * use System.Logger, and picocli for main executable
 16  /// * v0.0.2
 17  ///   * Java reimplementation from memory in 2024
 18  /// * v0.0.1
 19  ///   * C64 BASIC implementation in 1985
 20  ///     (lost on a tape somewhere)
 21  ///
 22  /// @version 0.0.3
 23  /// @since JDK 15
 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  @Command(name = "Hangman",
 27  	version = "Hangman 0.0.3",
 28  	mixinStandardHelpOptions = true,
 29  	// TODO: enable when picocli v4.7 is in Debian
 30  	// @see <https://bugs.debian.org/1086095>
 31  	//sortSynopsis = false,
 32  	description = "Guess letters in a secret word, limited to 8 wrong guesses.",
 33  	// use heredoc
 34  	// @since JDK 15
 35  	// @see <https://openjdk.org/jeps/378>
 36  	footer = """
 37  
 38  EXAMPLES:
 39    java Hangman
 40    java Hangman --word=juggernaut --clear
 41    export HANGMAN_WORD=juggernaut java Hangman --clear
 42  
 43  By default, a randomly selected word is used.
 44  to instead use a user-provided word,
 45  provide it either with command-line option --word
 46  or via environment variable HANGMAN_WORD.
 47  """)
 48  public final class Hangman implements Runnable {
 49  
 50  	/// Constructs a new instance of Hangman.
 51  	// (this is explicit only to silence javadoc)
 52  	public Hangman() {
 53  	}
 54  
 55  	/// main method for the CLI tool
 56  	///
 57  	/// @param args  command-line arguments
 58  	public static void main(final String[] args) {
 59  		int exitCode = new CommandLine(new Hangman()).execute(args);
 60  		System.exit(exitCode);
 61  	}
 62  
 63  	/// logger object
 64  	// @see <https://renato.athaydes.com/posts/java-system-logging>
 65  	private System.Logger logger;
 66  
 67  	/// word to guess
 68  	@Option(names = { "--word" }, paramLabel = "WORD",
 69  		description = "provide a word to guess")
 70  	private String word = "banana";
 71  
 72  	/// flag whether to clear screen initially
 73  	@Option(names = { "--clear" },
 74  		description = "try to initially clear the console")
 75  	private boolean clear;
 76  
 77  	public void run() {
 78  		if (clear) {
 79  			clearConsole();
 80  		}
 81  
 82  		// simplify logging format
 83  		// @since JDK 7
 84  		// @see <https://stackoverflow.com/a/34229629/18619283>
 85  		System.setProperty("java.util.logging.SimpleFormatter.format",
 86  			"%5$s%6$s%n");
 87  		logger = System.getLogger("");
 88  
 89  		// Instantiation as dictated by assignment
 90  		HangmanCore core = new HangmanCore(logger, word, 8);
 91  
 92  		// minimal viable product
 93  		while (core.hasMaskedCharacters() && core.hasMoreAttempts()) {
 94  			logger.log(Level.INFO,
 95  				"[miss: {0}] Guess this word: {1}",
 96  				core.miss(),
 97  				core.mask());
 98  			core.ask("Guess a character, and hit ENTER: ");
 99  		}
100  	}
101  
102  	/// Try to clear the console
103  	///
104  	/// @see <https://stackoverflow.com/a/64038023/18619283>
105  	public static void clearConsole() {
106  		try {
107  			if (System.getProperty("os.name").contains("Windows")) {
108  				new ProcessBuilder("cmd", "/c", "cls")
109  					.inheritIO().start().waitFor();
110  			} else {
111  				System.out.print("\033\143");
112  				System.out.flush();
113  			}
114  		} catch (IOException | InterruptedException ex) { }
115  	}
116  
117  	/// Environment variable lookup
118  	///
119  	/// @param variable  the environment variable to look up
120  	/// @param fallback  a fallback string if variable is undefined
121  	/// @return          contents of environment variable,
122  	///                  or fallback string
123  	public static String envString(
124  		final String variable,
125  		final String fallback
126  	) {
127  		String s = System.getenv(variable);
128  		if (s != null && s.length() > 0) {
129  			return s;
130  		}
131  		return fallback;
132  	}
133  }