Skip to content

Instantly share code, notes, and snippets.

@james-d
Last active January 22, 2016 14:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save james-d/7a188f0ce19c5febf3a3 to your computer and use it in GitHub Desktop.
Save james-d/7a188f0ce19c5febf3a3 to your computer and use it in GitHub Desktop.
.word {
-fx-font-family: sans-serif ;
-fx-font-size: 32pt ;
-fx-padding: 12 ;
-fx-min-width: 300 ;
}
.controls {
-fx-alignment: center ;
-fx-padding: 12 ;
}
.buttons {
-fx-hgap: 2;
-fx-vgap: 2;
-fx-padding: 12 ;
}
.letter-button {
-fx-max-width: infinity ;
-fx-max-height: infinity ;
}
.life-lost {
-fx-background-color: -fx-color, black, -fx-color ;
-fx-background-insets: 0, 2, 4 ;
-fx-min-width: 36 ;
-fx-min-height: 36 ;
-fx-font-size: 24 ;
-fx-font-family: "comic sans ms" ;
}
.life-lost .text {
-fx-fill: red ;
}
package hangman;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
public class Hangman extends Application {
private StringBinding wordBinding;
private StringBinding statusBinding;
@Override
public void start(Stage primaryStage) {
Supplier<String> wordGenerator = new WordGenerator();
HangmanGameState game = new HangmanGameState(wordGenerator.get());
BorderPane root = new BorderPane();
Label word = new Label();
wordBinding = Bindings.createStringBinding(() -> {
if (game.isWordGuessed() || game.isLost()) {
return game.getWord();
}
return game.getWord()
.chars()
.map(c -> game.getGuesses().contains((char)c) ? (char)c : '?')
.mapToObj(c -> Character.toString((char)c))
.collect(Collectors.joining());
}, game.wordProperty(), game.getGuesses(),
game.wordGuessedProperty(), game.lostProperty());
word.textProperty().bind(wordBinding);
HBox livesRemaining = new HBox();
for (int i = 0 ; i < HangmanGameState.LIVES ; i++) {
Label lifeLost = new Label("X");
StackPane lifeLostHolder = new StackPane(lifeLost);
lifeLostHolder.getStyleClass().add("life-lost");
livesRemaining.getChildren().add(lifeLostHolder);
lifeLost.visibleProperty().bind(
game.wrongGuessesProperty().greaterThan(i));
}
Label statusLabel = new Label();
statusBinding = Bindings
.when(game.lostProperty())
.then("You lost!")
.otherwise("You won!");
statusLabel.textProperty().bind(statusBinding);
statusLabel.getStyleClass().add("status");
Button playAgain = new Button("Play again");
playAgain.setOnAction(e -> game.restartGame(wordGenerator.get()));
HBox controls = new HBox(5, statusLabel, playAgain);
controls.getStyleClass().add("controls");
controls.visibleProperty().bind(
game.wordGuessedProperty().or(game.lostProperty()));
TilePane buttons = new TilePane();
buttons.getStyleClass().add("buttons");
for (char c = 'A'; c <= 'Z'; c++) {
Character ch = new Character(c);
Button button = new Button(ch.toString());
button.disableProperty().bind(Bindings.createBooleanBinding(
() -> game.getGuesses().contains(ch),
game.getGuesses()));
button.visibleProperty().bind(button.disableProperty().not());
button.setOnAction(e -> game.getGuesses().add(ch));
button.getStyleClass().add("letter-button");
buttons.getChildren().add(button);
}
StackPane wordHolder = new StackPane(word);
wordHolder.getStyleClass().add("word");
root.setCenter(wordHolder);
root.setRight(buttons);
root.setTop(livesRemaining);
root.setBottom(controls);
Scene scene = new Scene(root);
scene.getStylesheets().add(
getClass().getResource("hangman.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
package hangman;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class HangmanGameState {
private final StringProperty word ;
public final static int LIVES = 6 ;
public final StringProperty wordProperty() {
return this.word;
}
public final String getWord() {
return this.wordProperty().get();
}
public final void setWord(final String word) {
this.wordProperty().set(word);
}
private final ObservableList<Character> guesses =
FXCollections.observableArrayList();
public ObservableList<Character> getGuesses() {
return guesses ;
}
private final ReadOnlyIntegerWrapper wrongGuesses =
new ReadOnlyIntegerWrapper();
public final ReadOnlyIntegerProperty wrongGuessesProperty() {
return this.wrongGuesses.getReadOnlyProperty();
}
public final int getWrongGuesses() {
return this.wrongGuessesProperty().get();
}
private final ReadOnlyBooleanWrapper wordGuessed =
new ReadOnlyBooleanWrapper() ;
public final ReadOnlyBooleanProperty wordGuessedProperty() {
return this.wordGuessed.getReadOnlyProperty();
}
public final boolean isWordGuessed() {
return this.wordGuessedProperty().get();
}
private final ReadOnlyBooleanWrapper lost = new ReadOnlyBooleanWrapper() ;
public ReadOnlyBooleanProperty lostProperty() {
return lost.getReadOnlyProperty() ;
}
public final boolean isLost() {
return lostProperty().get();
}
public HangmanGameState(String initialWord) {
word = new SimpleStringProperty(initialWord);
wrongGuesses.bind(Bindings.createIntegerBinding(() -> {
return (int) guesses.stream()
.map(c -> c.toString())
.filter(c -> ! word.get().contains(c))
.count();
}, word, guesses));
wordGuessed.bind(Bindings.createBooleanBinding(() -> {
return word.get()
.chars()
.mapToObj((int c) -> Character.valueOf((char)c))
.allMatch(guesses::contains);
}, word, guesses));
lost.bind(wrongGuesses.greaterThanOrEqualTo(LIVES));
}
public void restartGame(String newWord) {
guesses.clear();
word.set(newWord);
}
}
package hangman;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;
public class WordGenerator implements Supplier<String> {
private final String allWords = "Accident agree arrive astronomy "
+ "atlas attention award aware Balance banner bare base "
+ "beach besides blast board bounce brain branch brave "
+ "bright Cage calf calm career center cheer chew claw "
+ "clear cliff club collect connect core corner couple crowd "
+ "curious Damp dangerous dash dawn deep demolish design "
+ "discard dive dome doubt dozen Earth enemy evening exactly "
+ "excess Factory fair famous feast field finally flap float "
+ "flood fold fresh frighten fuel Gap gaze gift gravity greedy "
+ "Harm herd Idea insect instrument invent island Leader leap "
+ "lizard local lonely luxury March mention motor Nervous net "
+ "nibble notice Ocean Pack pale parade past peak planet "
+ "present proof Reflect rumor Safe scholar seal search settle "
+ "share shelter shiver shy skill slight smooth soil stack "
+ "steady strand stream support Team telescope tiny tower "
+ "travel tremble Universe Village Warn weak wealthy whisper "
+ "wise wonder worry Yard Zigzag" ;
private final List<String> words ;
private Iterator<String> iterator ;
public WordGenerator() {
words = Arrays.asList(allWords.split(" ")) ;
Collections.shuffle(words);
iterator = words.iterator() ;
}
public String get() {
if (! iterator.hasNext()) {
Collections.shuffle(words);
iterator = words.iterator() ;
}
return iterator.next().toUpperCase() ;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment