Hangman.Отлов повторно введенной буквы

Пишу программу "виселица". Уже почти все пункты реализованы. Остался один - отлавливать повторно введенную одну и ту же, отсутствующую букву в слове, по условиям, она не должна дублироваться в массиве, и тратить attempts. Я немножко завяз на данном этапе. Можно ли это попробовать сделать через два цикла?

for (int i = 0; i < guessedLetters.length; i++) {
    for (int j = i + 1; j < guessedLetters.length; j++) {
        if (guessedLetters[i] == guessedLetters[j]) {
            break;
        }
    }
} 

Основная логика

String[] figuresWord  = {"one", "two", "three"};
 
String secretWord = figuresWord[(int) (Math.random() * figuresWord.length)].toLowerCase();
char[] guessedLetters = new char[secretWord.length()];
int attempts = 5;
 
for (int i = 0; i < guessedLetters.length; i++) {
    guessedLetters[i] = '_';
}
while (attempts > 0) {
    System.out.println("Текущее слово: " + String.valueOf(guessedLetters));
 
    System.out.print("Угадай букву: ");
    char guess = scan.next().toLowerCase().charAt(0);
 
    if (!isLetter(guess)) {
        System.out.println("Invalid input. Please enter a letter.");
        continue;
    }
 
    boolean found = false;
    for (int i = 0; i < secretWord.length(); i++) {
        if (secretWord.charAt(i) == guess) {
            guessedLetters[i] = guess;
            found = true;
        }
    }
 
    if (found && attempts != 5) {
        attempts++;
        System.out.println("Угадали букву, попыток " + attempts);
    } else if(!found) {
        attempts--;
        System.out.println("Не угадали букву, попыток " + attempts);
    }
    switch (attempts) {
        case 0: System.out.println(" |------| ");
        case 1: System.out.println(" O");
        case 2: System.out.println("/ \\ ");
        case 3: System.out.println(" | ");
        case 4: System.out.println("/ \\");
    }
    if (String.valueOf(guessedLetters).equals(secretWord)) {
        System.out.println("Вы угадали слово: " + secretWord);
        break;
    }
}
if (attempts == 0) {
    System.out.println("Попытки закончились");
}
private static boolean isLetter(char c) {
    return (c >= 'a' && c <= 'z');
}

Ответы (2 шт):

Автор решения: Эникейщик

Можно все введенные буквы собирать в строку и каждую новую проверять на наличие в этой строке:

if (guessedLetters.indexOf(guess) < 0) { // буквы нет
    // do your stuff
}
→ Ссылка
Автор решения: Nowhere Man

Двойной цикл не нужен совершенно.

Для того, чтобы отслеживать ввод букв, достаточно завести массив булевых флажков Boolean[] usedLetters = new Boolean[26], в котором после инициализации будут записаны null значения. Также можно было бы запоминать введённые буквы в сете символов Set<Character>, но скорей всего этот материал ещё не учили.

Перед проверкой введённой буквы в слове следует проверить, содержит ли массив null-значение для данной буквы. Если нет, значит данную букву уже вводили, иначе после поиска введённой буквы в загаданном слове следует установить соответствующий флажок.

// ...
Boolean[] usedLetters = new Boolean[26];
while (attempts > 0) {
    System.out.println("Текущее слово: " + String.valueOf(guessedLetters));
 
    System.out.print("Угадай букву: ");
    char guess = Character.toLowerCase(scan.next().charAt(0));
 
    if (!isLetter(guess)) {
        System.out.println("Invalid input. Please enter a letter.");
        continue;
    }
    if (null != usedLetters[guess - 'a']) {
        System.out.println("You have already tried letter '" + guess + "'");
        continue;
    }
 
    boolean found = false;
    for (int i = 0; i < secretWord.length(); i++) {
        if (secretWord.charAt(i) == guess) {
            guessedLetters[i] = guess;
            found = true;
        }
    }
    usedLetters[guess - 'a'] = found;
// ...
}

Варианты с Set:

  • С использованием метода contains и HashSet в качестве реализации
// ...
Set<Character> usedLetters = new HashSet<>();
while (attempts > 0) {
    System.out.println("Текущее слово: " + String.valueOf(guessedLetters));
 
    System.out.print("Угадай букву: ");
    char guess = Character.toLowerCase(scan.next().charAt(0));
 
    if (!isLetter(guess)) {
        System.out.println("Invalid input. Please enter a letter.");
        continue;
    }
    if (usedLetters.contains(guess)) {
        System.out.println("You have already tried letter '" + guess + "'");
        continue;
    }
 
    boolean found = false;
    for (int i = 0; i < secretWord.length(); i++) {
        if (secretWord.charAt(i) == guess) {
            guessedLetters[i] = guess;
            found = true;
        }
    }
    usedLetters.add(guess);
// ...
}
  • Более короткий вариант, с использованием только метода add, который будет возвращать false при попытке добавить существующее значение (т.е., сет не будет изменяться).
    Также используется реализация LinkedHashSet, которая сохранит порядок вставки использованных букв.
// ...
Set<Character> usedLetters = new LinkedHashSet<>();
while (attempts > 0) {
    System.out.println("Текущее слово: " + String.valueOf(guessedLetters));
 
    System.out.print("Угадай букву: ");
    char guess = Character.toLowerCase(scan.next().charAt(0));
 
    if (!isLetter(guess)) {
        System.out.println("Invalid input. Please enter a letter.");
        continue;
    }
    if (!usedLetters.add(guess)) {  // всегда пытаемся добавить букву в сет
        System.out.println("You have already tried letter '" + guess + "'");
        continue;
    }
 
    boolean found = false;
    for (int i = 0; i < secretWord.length(); i++) {
        if (secretWord.charAt(i) == guess) {
            guessedLetters[i] = guess;
            found = true;
        }
    }
// ...
}

Если нужно выводить особое сообщение, была ли введённая буква обнаружена в слове или нет, можно предложить ещё один вариант с использованием мапы Map<Character, Boolean> по аналогии с массивом Boolean, упомянутым в начале ответа.

При проверке наличия введённой буквы в мапе следует использовать метод Map::containsKey, и затем для добавления элемента в мапу -- обычный Map::put:

// ...
Map<Character, Boolean> usedLetters = new LinkedHashMap<>();
while (attempts > 0) {
    System.out.println("Текущее слово: " + String.valueOf(guessedLetters));
 
    System.out.print("Угадай букву: ");
    char guess = Character.toLowerCase(scan.next().charAt(0));
 
    if (!isLetter(guess)) {
        System.out.println("Invalid input. Please enter a letter.");
        continue;
    }
    if (usedLetters.containsKey(guess)) {
        System.out.println("You have already tried letter '" + guess + "'");
        if (usedLetters.get(guess)) {
            System.out.println("The secret word contains this letter");
        } else {
            System.out.println("The secret word does not contain this letter");
        }
        continue;
    }
 
    boolean found = false;
    for (int i = 0; i < secretWord.length(); i++) {
        if (secretWord.charAt(i) == guess) {
            guessedLetters[i] = guess;
            found = true;
        }
    }
    usedLetters.put(guess, found);
// ...
}
→ Ссылка