Мой цикл java do-while по какой-то причине работает только в половине случаев

Я ожидал, что цикл do-while будет заканчиваться каждый раз, когда я набираю «n», но это происходит только через раз. Никаких закономерностей в этом я не заметил. Я также пытаюсь закончить цикл, если количество точек равно 0 или меньше, но это тоже не работает.

Я пробовал это много раз, но не могу понять, где ошибка. Просто нужна помощь, пожалуйста, и спасибо.

Вот проблема:

Вот мой код:

package highlow;

import java.util.Scanner;

public class Highlow {

    public static void Rules() {
        System.out.println("RULES:");
        System.out.println("Numbers 1-6 are low ");
        System.out.println("Numbers 8-13");
        System.out.println("Number 7 is neither high or low\n\n");
    }

    public static void main(String[] args) {
        int number;
        int gambleAmount;
        int prediction;
        int correctRange;
        int winnings;
        int points;
        int numberOfGuesses = 0;
        String repeat;
        Scanner input = new Scanner(System.in);
        points = 1000;
        System.out.print("High Low Game\n\n");
        Rules();
        do {
            number = (int) (13 * Math.random() + 1);
            if (number > 7) {
                correctRange = 1;
            }
            else if (number < 7) {
                correctRange = 0;
            }
            else {
                correctRange = -1;
            }
            numberOfGuesses += 1;
            System.out.println("You have " + points + " points");
            do {
                System.out.println("Please insert a valid number of points");
                System.out.print("How many points would you like to gamble: ");
                gambleAmount = input.nextInt();
            } while (gambleAmount > points);
            System.out.print("Predict [1=high, 0=low]: ");
            prediction = input.nextInt();
            if (prediction == correctRange) {
                winnings = gambleAmount * 2;
                points = points + winnings;
                System.out.println("The number was " + number);
                System.out.print("You win!\n\n");
            }
            else {
                points = points - gambleAmount;
                System.out.println("The number was " + number);
                System.out.print("You lose\n\n");
            }
            System.out.print("Would you like to play again ('n' for no and 'y' for yes): ");
            repeat = input.next();
        } while (repeat.equals("n") || points != 0);
        System.out.print("You had " + numberOfGuesses + " guesses");
    }
}

Помните, что такие вещи, как nextInt и next, оставят в буфере висящий символ новой строки, а это означает, что в следующий раз, когда он попытается выполнить nextXxx, вместо этого вы получите этот символ. Используйте nextLine и вручную анализируйте результат или используйте его, чтобы очистить символ новой строки.

MadProgrammer 05.04.2023 02:08
'n' for norepeat.equals("NO") - Думаю, это вызовет проблемы.
Scary Wombat 05.04.2023 02:23

Итак, у вас есть логическая головоломка, которая в основном сводится к false || true (!repeat.equals("n") || points > 0), в данном случае она будет равняться true, так что ваш цикл do-while продолжится. Если вместо этого вы измените его на false && true, он будет равен false, и цикл завершится ... и да, моя голова все еще кружится, когда я пытаюсь это описать.

MadProgrammer 05.04.2023 02:23

@MadProgrammer Only nextLine имеет проблему с оборванными разделителями токенов. Любой другой метод пропускает разделители, пока не найдет допустимый токен.

Tom 05.04.2023 03:10

@Tom nextLine прочитает всю строку текста до токена новой строки, удалив ее в процессе - по моему опыту nextXxx, похоже, вызывает проблемы с выходом из этого и вызывает пропуск следующих next утверждений ... но я стараюсь избегать работать с Scanner таким образом и вместо этого склоняться к чтению строки из ввода, а затем использовать отдельный рабочий процесс синтаксического анализа, который просто игнорирует все эти связанные проблемы - но это я

MadProgrammer 05.04.2023 03:15

@MadProgrammer Ни «next», ни «nextInt», ни «nextFloat» (или любой другой метод, отличный от nextLine) не будут пропущены. Вы правы, что они не читают разделитель после токена, но им это и не нужно, так как они все равно пропускают такие разделители. Их поведение довольно стабильно и надежно. Проблема в nextLine. Он не следует обычному поведению класса Scanner, который пропускает разделители, пока не найдет допустимый токен. Он читается только до следующего разрыва строки, даже если прочитанное содержимое представляет собой просто пустую строку. Но да, читать построчно и конвертировать "вручную" тоже нормально.

Tom 05.04.2023 03:39

@Tom «Он читается только до следующего разрыва строки, даже если прочитанное содержимое представляет собой просто пустую строку» - конечно, но если вы сделаете два вызова nextLine, второй не будет пропущен, а мы получаем так много " почему мой следующий ввод пропускается" вопросы 🙄 - которые можно решить, просто поставив nextLine после next, чтобы удалить оборванный символ новой строки

MadProgrammer 05.04.2023 03:48

@MadProgrammer Да, это правильно. Я просто хотел указать, что ваш первый комментарий частично верен (проблемы есть не у каждого nextxxx, только у nextLine) и что «заставлять пропускать следующие next утверждения» также неверно. Некоторых эта проблема может сбить с толку, поэтому вводящая в заблуждение/неверная информация усугубит ситуацию.

Tom 05.04.2023 03:58

@Tom Да, по наблюдениям, nextInt, за которым следует nextLine, заставляет nextLine возвращать пустую String, что приводит к выводу, что nextInt оставляет оборванную новую строку, поэтому я всегда знаю, что просто очищаю буфер 😜. Я не думаю, что Scanner достаточно хорошо учат новых разработчиков (или таких старых, как я, которые выросли без него)

MadProgrammer 05.04.2023 04:07
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
9
83
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Цикл do-while повторяется, если утверждение while истинно.

Рассмотрим ваш цикл:

do {
   ...
} while(repeat.equals("n") || points!=0)

Вы действительно хотите продолжать играть, когда repeat равно n или points не равно нулю?

Как указано в комментариях (и другом ответе), ваша проблема заключается в условии [внешнего] цикла do-while в методе main класса Highlow. Это условие нужно изменить. Во-первых, вы должны проверить, больше ли баллов больше нуля, поскольку пользователь не может рисковать баллами, если у него их нет. Если у пользователя есть баллы, то проверьте, является ли ответ «y», т.е. пользователь хочет играть снова. Желательно также игнорировать регистр введенного ответа, т.е. играть снова, если пользователь ввел «Y» или «y». Другими словами, вы повторяете цикл, если у пользователя больше нуля баллов и он ответил «y». Если у пользователя ноль баллов или если он не ответил «y», выйти из цикла. Обратите внимание, что это означает, что вы должны инициализировать repeat значением «y».

Приведенный ниже код представляет собой код из вашего вопроса с исправлениями, описанными выше, а также другими изменениями, которые подробно описаны после кода.

package highlow;

import java.util.InputMismatchException;
import java.util.Random;
import java.util.Scanner;

public class Highlow {
    private static final int FACTOR = 2;
    private static final int HI = 1;
    private static final int INIT = 2;
    private static final int LO = 0;
    private static final int LOSER = 7;
    private static final int LOWER = 1;
    private static final int UPPER = 13;

    public static void rules() {
        System.out.println("RULES:");
        System.out.println("Numbers 1-6 are low ");
        System.out.println("Numbers 8-13");
        System.out.println("Number 7 is neither high or low\n\n");
    }

    public static void main(String[] args) {
        int number;
        int gambleAmount;
        int prediction = INIT;
        int correctRange;
        int winnings;
        int points = 1000;;
        int numberOfGuesses = 0;
        String repeat = "y";
        Scanner input = new Scanner(System.in);
        System.out.print("High Low Game\n\n");
        rules();
        Random rand = new Random();
        do {
            number = rand.nextInt(LOWER, UPPER + 1); // (int) (13 * Math.random() + 1);
            if (number > LOSER) {
                correctRange = 1;
            }
            else if (number < LOSER) {
                correctRange = 0;
            }
            else {
                correctRange = -1;
            }
            System.out.println("You have " + points + " points");
            System.out.printf("Enter points to risk [1 - %d]: ", points);
            try {
                gambleAmount = input.nextInt();
                if (gambleAmount <= 0  ||  gambleAmount > points) {
                    System.out.println("Invalid value. Please re-enter.");
                    continue;
                }
            }
            catch (InputMismatchException x) {
                System.out.println("You did not enter a number. Please re-enter.");
                input.nextLine();
                continue;
            }
            do {
                System.out.print("Predict [1=high, 0=low]: ");
                try {
                    prediction = input.nextInt();
                    if (prediction < LO  ||  prediction > HI) {
                        System.out.println("Invalid prediction. Please re-enter.");
                        continue;
                    }
                }
                catch (InputMismatchException x) {
                    System.out.println("You did not enter a number. Please re-enter.");
                    input.nextLine();
                    continue;
                }
            } while (prediction < LO  ||  prediction > HI);
            numberOfGuesses += 1;
            if (prediction == correctRange) {
                winnings = gambleAmount * FACTOR;
                points += winnings;
                System.out.println("The number was " + number);
                System.out.print("You win!\n\n");
            }
            else {
                points -= gambleAmount;
                System.out.println("The number was " + number);
                System.out.print("You lose.\n\n");
            }
            if (points > 0) {
                System.out.print("Would you like to play again ('n' for no and 'y' for yes): ");
                repeat = input.next();
            }
        } while (points > 0 && "y".equalsIgnoreCase(repeat));
        String plural = numberOfGuesses == 1 ? "" : "es";
        System.out.printf("You had %d guess%s.%n", numberOfGuesses, plural);
    }
}

Хотя это и не указано в задаче, приведенный выше код проверяет правильность значений, введенных пользователем. Вызов метода nextInt выдаст InputMismatchException, если он прочитает значение, которое не является допустимым целым числом. Обратите внимание, что блок catch также вызывает nextLine. Причина указана в комментариях к вопросу. Также см. Сканер пропускает nextLine() после использования next() или nextFoo()?

Нет необходимости во внутреннем цикле do-while, чтобы просить пользователя ввести допустимое количество баллов для риска. Просто повторите внешний цикл, поскольку это напоминает пользователю, сколькими очками он может рискнуть.

Однако нет необходимости повторять весь цикл, если пользователь вводит неверный «прогноз». Отсюда внутренний цикл do-while для получения прогноза пользователя.

Обратите внимание, что именно так, как я считаю, должна быть логика программы. Так поступать не правильнее и не менее правильно.

numberOfGuesses следует увеличивать после того, как пользователь введет действительное количество баллов риска и правильный прогноз. Его не следует увеличивать сразу после входа во [внешний] цикл do-while.

Если пользователь только что потерял все свои очки, то вы должны выйти из [внешнего] do-while цикла, т.е. нет смысла спрашивать пользователя, хочет ли он играть снова, когда у него нет очков, которым можно было бы рисковать.

Хотя эта строка кода не вызывает проблем:

repeat = input.next();

рассмотрите вместо этого вызов метода nextLine.
Посмотрите, что происходит, когда пользователь просто нажимает ENTER, то есть не вводит значение. (Кстати, то же самое относится и к методу nextInt.)

Согласно соглашению об именах Java, имена методов должны начинаться со строчной буквы, поэтому я изменил метод Rules на метод rules.

Другие вопросы по теме