В прошлый раз мы с вами установили IDE Eclipse и создали в ней свой первый проект. Но пришла пора заняться чем-то посерьезней! Поскольку обучение легче всего происходит в игровой форме, предлагаю вашему внимаю небольшую консольную игру. Крестики-нолики, так же известную как tic-tac-toe :-).
Теперь нам необходимо определиться с тем, как мы собственно будем писать нашу игру? Прежде всего нам нужно игровое поле. Пусть это будет двумерный массив символов размером 3×3 (почитать про разные типы данных в Java можно тут). Если в какой-то клетке игрового поля у нас ничего нет, то ставим туда например плюсик, если в ячейке нолик пусть это будет буква «О» если крестик то буква «Х». Начнем с задания самого поля:
// Игровое поле public static char[][] field = { { '+', '+', '+' }, { '+', '+', '+' }, { '+', '+', '+' } };
Наше игоровое поле представляет собой двумерный массив символов Field, все значения в котором задаются при его создании. О том, что этот массив двумерный говорят две квадратные скобочки после описания типа char. При этом сначала у нас везде стоят плюсики — т.е. все поле свободно. Следующее, что мы сделаем — это организуем ход пользователя:
// Ход человека public static void HumanMove() throws IOException { int x , y; System.out.println("Enter y (1..3):"); BufferedReader br = new BufferedReader(new InputStreamReader( System.in)); x = Integer.parseInt(br.readLine())-1; System.out.println("Enter x (1..3):"); y = Integer.parseInt(br.readLine())-1; while (field[x][y] == '0' || field[x][y] == 'X' || x < 0 || x > 2 || y < 0 || y > 2) { System.out.println("Enter x:"); x = Integer.parseInt(br.readLine())-1; System.out.println("Enter y:"); y = Integer.parseInt(br.readLine())-1; } field[x][y] = 'X'; }
Итак, что же здесь происходит? Первая строка — это описание матода, который реализует ход человека. О методах и их описании этом мы поговорим попозже. Во второй строке объявляются две переменные x и y, это будут координаты, куда поставит крестик игрок. Затем, с помощью конструкции System.out.println() мы выводим на экран приглашение ввести координату по оси Y. В следующей строке создается буфферизированный поток br с помошью которого можно считывать из консоли вводимые пользователем строки.
В строке 7 конструкция х=Integer.parseInt(String p) преобразует строковый параметр p в целочисленное значение и это значение мы записываем в переменную х.
Думаю две следующие строки, думаю, понятны по аналогии 🙂 А вот дальше мы проверяем в цикле с предусловием во-первых, не попал ли ход игрока на уже занятое поле, во-вторых, не выходят ли введенные пользователем цифры за границы нашего игрового поля. Если хоть одно из этих условий не выполняется, тогда мы заново просим ввести позицию Y и X. Ну а если все нормально, то цикл не выполнится и мы запишем на игровое поле ход игрока.
Теперь думаем что делать со вторым игроком. Поскольку заморачиваться на стратегии игры мне совсем не хотелось то бот ходит просто случайно. Алгоритм очень прост и код сильно напоминает предидущий кусок:
// Ход компьютера (random-бот) public static void CompMove() { int x = (int) (Math.random() * 3), y = (int) (Math.random() * 3); while (field[x][y] == '0' || field[x][y] == 'X') { x = (int) (Math.random() * 3); y = (int) (Math.random() * 3); } field[x][y] = '0'; }
Здесь Math.random() генерирует случайное число в интервале от 0 до 1, затем полученное число мы умножаем на три и округляем до целого. В итоге получаем числа X и Y в итервале от нуля до двух включительно. Проверяем, может ли бот сделать ход на полученные координаты, и если не может, то точно так же генерируем два новых числа и проверяем их. Алгоритм прост и реализация, как мне кажется не должна вызывать вопросов.
Следующий метод определяет остались ли еще на игровом поле свободные клетки.
public static boolean CanMove() { boolean p = false; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (field[i][j] == '+') { p = true; break; } } if (p = true) { break; } } return p; }
В переменной p изначально хранится значение false (ложь). То есть сначала считаем, что свободных клеток не осталось. А потом начинаем проверять, есть ли они на самом деле или их нет. Мы проходим по каждой клетке игрового поля (два вложенных друг в друга цикла for) и если (почитать про условия в Java) где-то встречаем хоть одну свободную клетку, то записываем в p значение true (истина) и прерываем циклы. Затем при любом раскладе мы возващаем значение p. Получается что если мы встретили хоть одно пустое поле, то мы вернем истину, а если свободных клеточек больше не осталось, то вернем ложь.
Следующий метод просто выводит на экран содержимое игрового поля:
// Вывод игрового поля на экран public static void PrintField() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { System.out.print(field[i][j]); } System.out.println(); } }
Комментировать думаю излишне?
На этом на сегодня думаю пора прекращать 🙂 Если есть какие-то вопросы, то не стетсняйтесь, постараюсь ответить. Если Я где-то что-то упустил, пишите! Закончим в следующий раз!
Приветствую.
Хорошая статья! Хотелось бы добавить:
— Не хватает итогового листинга программы.
— Хоть и код больше образовательный и я лично не написал ещё ни одной программы на Java, но для чистоты кода думаю заменить циклы на do-while с пост условием.
— CanMove можно использовать метку для прерывания цикла, дабы избежать двойного прерывания цикла.
Спасибо!
Вот в этой статье есть готовый проект там весь код полностью.
По поводу цикла согласен, смотреться будет значительно лучше 🙂
На то Вам и код в руки! 🙂
«генерирует случайное число в интервале от 0 до 1, затем полученное число мы умножаем на три и округляем до целого»
на самом деле не «округляет», а отбрасывает целую часть
Да в строчках где вводятся x и y запрашивается y, а вводится x и наоборот.
Спасибо!
Счас проверим 🙂 И поправим 🙂
Поправил 🙂
А почему для получения данных от человека использовался именно буферизированный поток?
Ведь можно и так сделать:
Scanner reader = new Scanner(System.in);
System.out.println(«Enter y (1..3):»);
x=reader.nextInt()-1;
System.out.println(«Enter x (1..3):»);
y=reader.nextInt()-1;
И при этом меньше импорта получается…
Я спрашиваю, потому что эти крестики-нолики — моя вторая программа после «Hello World» на яве 🙂
🙂 Тут объяснение крайне просто 🙂 Я на момент написания не знал о такой конструкции 🙂
Комп ходит абы как. Неосмысленно. Как обезьяна.
А что если поле будет 5х5 ? Он вообще никогда не выиграет.
Ну он же в случайном порядке ходит 🙂 это естественно 🙂
Статья написана хорошо, но, имхо, все слишком закручено. То же самое, а может и лучше можно сделать существенно упростив код.
Подскажите, как добавить проверку — ввелось ли число и в диапозоне от 1 до 3? если ввелась буква или чего другое, то выдавалась бы ошибка и цикл повторялся.
А то сейчас при вводе вместо 1 например «а» программа рушиться.
А также всеже было бы здорово написать стратегию для компьютера, если кто смог бы поделитья, буду очень благодарен!
Считывайте строку, переводите ее в число, используя try catch и проверяйте полученное число.
скриншота нехватает
Дык всеж в консоли 🙂 Какие уж тут скриншоты…
В методе, определяющем остались ли свободные клетки во внешнем цикле в условии if, вероятно, должен быть двойной знак равенства. Иначе, условие «(p = true)» всегда будет верным, так как выполняется присваивание.
да-да-да!!! Потратил на это минут 30, пока понял в чем баг… ((((
должно быть просто if (p) , ну или if(p==true)
Пожалуйста извините, счас так мало времени на сайт, что просто не успеваю даже на комментарии отвечать 🙁
Не понимаю откуда берутся тегу public что за Анг слова не понятно