2
\$\begingroup\$

I wrote this Java game where the user memorizes a list, gets given letters and needs to find a word that contains those letters. The player has only a few seconds to enter the word.

Example:

Program outputs: AN

Player can input: ANTILIBERALISM, HUMANITARIANISM, NANOTECHNOLOGY if those are in the memorized list file and they haven't been entered yet.

Other possible inputs for the player:

  • pause: Pauses the game until the player types something.
  • skip: Skips the current letters.
  • stop: Stops the game, prints the scores.
  • new: Prints the new words that the player didn't memorize yet, those are at the end of the file.

Code:

import org.awaitility.Awaitility;
import org.awaitility.core.ConditionTimeoutException;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

class Game {
    private static final String F_NAME = "LIST.txt";
    private static final int TIME_PER_ANSWER = 10;
    private Map<String, List<String>> answersMap;
    private volatile String answer;
    private final Map<String, String> COMMANDS_OUTPUT_MAP = Collections.unmodifiableMap(Map.of(
        "STOP", "Game stopped manually.",
        "PAUSE", "Game paused.",
        "ANSWER_DOESNT_CONTAIN_SYLL", "Doesn't contain letters...",
        "WORDSLIST_DOESNT_CONTAIN_WORD", "The word is not in the list...",
        "ANSWER_ALREADY_TYPED", "Already put..."
    ));
    private final List<String> STOP_WAITING_FOR_ANSWER_COMMANDS = Collections.unmodifiableList(List.of(
            "STOP",
            "PAUSE",
            "SKIP"
    ));

    class ReaderThread implements Runnable {
        @Override
        public void run() {
            Scanner in = new Scanner(System.in);

            while (!Thread.interrupted()) {
                answer = in.nextLine().toUpperCase();
            }
        }
    }

    public static void main(String[] args) {
        new Game();
    }

    private Game() {
        System.out.println("New game. Input file: " + F_NAME + ". Time per answer: " + TIME_PER_ANSWER + " seconds.");
        System.out.println("Commands: " + STOP_WAITING_FOR_ANSWER_COMMANDS);

        this.answersMap = new TreeMap<>();
        Random rand = new Random();
        int wordCounter = 0, lifeCounter = 0;

        List<String> syllsList = Objects.requireNonNull(getSyllsList());
        List<String> wordsList = Objects.requireNonNull(getWordsList());
        Optional<List<String>> newWordsListOptional = getNewWordsListOptional(wordsList);
        newWordsListOptional.ifPresent((list) -> System.out.println("New words: " + list));

        Optional<List<String>> noSyllsMatchListOptional = getNoSyllsMatchListOptional(syllsList, wordsList);
        noSyllsMatchListOptional.ifPresent(list -> {
            System.out.println("WARNING: THESE LETTERS ARE IN NO WORDS: " + list);
            syllsList.removeAll(list);
        });
        Optional<List<String>> noWordsMatchListOptional = getNoWordsMatchListOptional(syllsList, wordsList);
        noWordsMatchListOptional.ifPresent(list -> {
            System.out.println("WARNING: THESE WORDS CONTAIN NO INPUT LETTERS: " + list);
            wordsList.removeAll(list);
        });

        Collections.shuffle(wordsList);
        int syllsListTotal = syllsList.size();
        String hint;
        new Thread(new ReaderThread()).start();

        while(syllsList.size() > 0) {
            resetAnswer();
            String syll = syllsList.get(rand.nextInt(syllsList.size()));
            if(findMatchingWord(syll, wordsList) == null) {
                syllsList.remove(syll);
                printSyllChangeMessage(syll, syllsListTotal, syllsList);
                continue;
            }
            System.out.println(syll);

            try {
                do {
                    if(answer.equals("PAUSE"))
                        Awaitility.await().forever().until(() -> !answer.equals("PAUSE"));
                    Awaitility.await().atMost(TIME_PER_ANSWER, TimeUnit.SECONDS).until(() -> checkAnswer(syll, wordsList, newWordsListOptional));
                } while(answer.equals("PAUSE"));
            } catch (ConditionTimeoutException ignore) { }

            if(answer.equals("STOP")) break;
            if (answer.equals("") || answer.equals("SKIP")) {
                hint = findMatchingWord(syll, wordsList);
                System.out.println("HINT: " + hint +".");
                lifeCounter++;
            } else {
                answersMap.computeIfAbsent(syll, liste -> new ArrayList<>()).add(answer);
                wordCounter++;
            }
        }

        for(String key : answersMap.keySet()) {
            System.out.println(key + " : " + answersMap.get(key));
        }
        System.out.println("Number of words : " + wordCounter + " | Number of lifes lost : " + lifeCounter);
        System.exit(0);
    }

    private LinkedList<String> getSyllsList() {
        try {
            return new LinkedList<>(Arrays.asList(Files.lines(Paths.get(F_NAME))
                    .findFirst()
                    .orElse("")
                    .split("\\|")));
        } catch(IOException io) {
            io.printStackTrace();
            return null;
        }
    }

    private List<String> getWordsList() {
        try {
            return Files.lines(Paths.get(F_NAME))
                    .skip(1)
                    .collect(Collectors.toList());
        } catch(IOException io) {
            io.printStackTrace();
            return null;
        }
    }

    private Optional<List<String>> getNoSyllsMatchListOptional(List<String> syllsList, List<String> wordsList) {
        final String wordsListString = wordsList.toString();
        List<String> noSyllsMatchList = syllsList.stream()
                .filter(s -> !wordsListString.contains(s))
                .collect(Collectors.toList());
        return Optional.ofNullable(noSyllsMatchList.isEmpty() ? null : noSyllsMatchList);
    }

    private Optional<List<String>> getNoWordsMatchListOptional(List<String> syllsList, List<String> wordsList) {
        List<String> noWordsMatchList = wordsList.stream()
                .filter(word -> {
                    for(String syll : syllsList)
                        if(word.contains(syll)) return false;
                    return true;
                })
                .collect(Collectors.toList());
        return Optional.ofNullable(noWordsMatchList.isEmpty() ? null : noWordsMatchList);
    }

    private boolean checkAnswer(String syll, List<String> wordsList, Optional<List<String>> newWordsListOptional) {
        if(answer.equals("")) return false;
        if(answer.equals("NEW")) {
            if(newWordsListOptional.isPresent())
                System.out.println("New words: " + newWordsListOptional.get());
            else
                System.out.println("No new words.");
            resetAnswer();
            return false;
        }

        String command = answer;
        if(!STOP_WAITING_FOR_ANSWER_COMMANDS.contains(command)) {
            if (!answer.contains(syll)) command = "ANSWER_DOESNT_CONTAIN_SYLL";
            else if (answerAlreadyTyped(answer)) command = "ANSWER_ALREADY_TYPED";
            else if (!wordsList.contains(answer)) command = "WORDSLIST_DOESNT_CONTAIN_WORD";
        }

        final Optional<String> OUTPUT_MESSAGE = Optional.ofNullable(COMMANDS_OUTPUT_MAP.get(command));
        if(OUTPUT_MESSAGE.isPresent()) {
            System.out.println(OUTPUT_MESSAGE.get());

            if (!STOP_WAITING_FOR_ANSWER_COMMANDS.contains(command)) {
                resetAnswer();
                return false;
            }
        }
        return true;
    }

    private void resetAnswer() {
        answer = "";
    }

    private boolean answerAlreadyTyped(String answer) {
        return answersMap.values()
            .stream()
            .flatMap(List::stream)
            .collect(Collectors.toList())
            .contains(answer);
    }

    private String findMatchingWord(String syll, List<String> wordsList) {
        return wordsList.stream()
            .filter(word -> word.contains(syll))
            .filter(word -> !answerAlreadyTyped(word))
            .findAny()
            .orElse(null);
    }

    private Optional<List<String>> getNewWordsListOptional (List<String> wordsList) {
        Optional<String> newWordsSeparator = wordsList.stream()
            .filter(word -> word.startsWith("-"))
            .findFirst();

        if(newWordsSeparator.isPresent()) {
            int index = wordsList.indexOf(newWordsSeparator.get());
            wordsList.remove(index);
            return Optional.of(List.copyOf(wordsList.subList(index, wordsList.size())));
        }
        return Optional.empty();
    }

    private void printSyllChangeMessage(String syll, int syllsListTotal, List<String> syllsList) {
        System.out.println("ALL WORDS HAVE BEEN PUT FOR LETTERS: " + syll + ". LETTERS REMOVED. " +
                "ALL LETTERS PUT FOR: " + (syllsListTotal - syllsList.size()) + " / " + (syllsListTotal));
    }
}

Test with file LIST.txt:

BE|AN|IS|XY
BENEDICTION
BEAUTIFULLNESS
ANTILIBERALISM
RADIOLABELLING
IMPRESSIONISTIC
UNDISTINGUISHED
HUMANITARIANISM
AUTOTRANSFORMER
INTELLIGENT
-------------------------- MY NEW WORDS TO MEMORIZE -------------------------
FEATHERBEDDING
BIOGEOCHEMISTRY
CIRCUMSTANTIAL
NANOTECHNOLOGY
ANTIAPHRODISIAC
ESSENTIALISM

A few questions:

  • I use Awaibility (there was no tag for it...) to wait for the user input until his words is correct or time is running out, won't work without it. If you want to test the code, download awaibility and awaibility-dependancies from there. Is there an easy alternative to it?
  • I don't think the code is optimized. How could I optimize it?
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.