JavaRush /Java блог /Random UA /Гра на java для початківців
timurnav
21 рівень

Гра на java для початківців

Стаття з групи Random UA
Привіт, друзі та майбутні колеги! Гра на java для початківців - 1Нещодавно я проходив тестування для участі в реальному проекті, пройшов його, але так вже склалося, що я за особистими обставинами не зміг взяти участь у самому РП. Після таких цікавих завдань, як тест на РП, звичайні завдання курсу стали менш привабливим проведенням часу, тим більше, більшу частину я вже вирішив. Тому щоб талант не пропадав дарма продовжувати навчання, я вирішив створити розраховану на багато користувачів веб-гру. Посилання на інші ігри:
  1. продовження цієї статті
  2. 2048
Найпростішою грою мені здалися хрестики нулики, я вирішив розбити завдання на ряд підзавдань:
  1. Консольний додаток для відпрацювання ігрової логіки
  2. Мультиплеєр
  3. Прикручування бази даних гравців до консольної програми
  4. Створення дизайну фронтенду, написання шаблонів сторінок, ігрового інтерфейсу
  5. Складання "всього" воєдино
Є ймовірність, що мене посварять, за таку послідовність, і швидше за все серйозні проекти будуються в зовсім іншій послідовності, відповім відразу, напишіть про це пост "для початківців", щоб всі (і я в тому числі) навчабося цьому:) Щож. .приступимо до написання консольної програми! Я піду тими самими кроками, що й у великих завданнях 20-х рівнів. Що є у грі хрестики-нуліки?!
  1. поле
  2. два гравці, які ходять по черзі, один ставить хрестик, другий нолик. все просто.
Поле робимо стандартне поле 3х3. У чому можна зберігати таке поле? перший варіант - двовимірний масив. Які елементи повинні бути у цьому масиві? відповідь - треба подумати, що ми робитимемо з цими елементами, це висновок на екран і порівняння для пошуку переможця. Якби ми їх тільки виводабо на екран, то логічно було б тримати їх у вигляді рядка, тоді сам масив та виведення на екран у циклі виглядали б якось так:
String[][] strings = {{"O", "O", "_"},
                    {"_", "X", "O"},
                    {"X", "X", "X"},
for (String [] ss : strings){
    for (String s : ss) System.out.print(s + " ");
    System.out.println(); //для перевода строки
}
на екрані б відобразилося:
O O _
_ X O
X X X
Але крім відображення, ми маємо ще порівняння значень, а тут уже можливі варіанти. Можна порівнювати рядки, можна створити спеціальний клас-перерахування ( enum), але я хотів би порівнювати числа, а на "Х" і "О" замінювати їх тільки при виведенні на екран. Нехай буде, наприклад, 1 – “Х”, 2 – “О”, 0 – “_”. Отже, як же перевіряти поле на потрійний збіг Х чи О?
Найперший алгоритм - це перевірка всього поля
int[][] canvas = {{00, 01, 02},
                 {10, 11, 12},
                 {20, 21, 22}}
Комбінації для виграшу:
00-01-02, 10-11-12, 20-21-22, 00-10-20, 01-11-21, 02-12-22, 00-11-22, 20-11-02 — всего 8.
Перевірка порівнянням цифр, але це виходить потрібно щоразу перевіряти ВСЕ поле, всі 8 комбінацій. Звичайно ж, це не багато, це не пошук чисел Армстронга в інтервалі від 0 до 1 млрд, тут обчислень трохи більше, ніж немає зовсім, але все одно хочеться щось більш оптимальне, ніж перевірка всього поля. Друга ідея, яка мене відвідала, це перевіряти лише осередок, який відзначабо на попередньому ході, так ще можна визначити переможця, адже ми знатимемо хто зробив цей хід. Таким чином, замість всіх 8 комбінацій ми отримуємо всього 2, 3 або 4 комбінації, залежно від комірки, див. Гра на java для початківців - 2тепер потрібно вигадати, як визначити яку комбінацію потрібно запустити? Ось тут я зрозумів, що використовувати двомірний масив не дуже зручно. Я вирішив розглянути варіанти. Спочатку я вигадав, що поле можна тримати в дев'ятизначній цифрі, наприклад, те саме поле, яке ми вивели на екран можна записати так 220012111, поясню на пальцях що це ... Шифр ​​колишній, 1 - "Х", 2 - "О" , 0 – " ", означає 220012111 = "OO__XOXXX", або якщо після кожної третьої цифри вставити перенесення рядка і додати пробіли для наочності:
О О _
_ Х О
Х Х Х
ось знову, зручно для зберігання, пристосу для відображення вигадали, але незручно для порівняння! Рішення знайшлося коли я пронумерував осередки 1-9, потім подумав, адже у програмуванні відлік починається з 0 і пронумерував як на картинці Гра на java для початківців - 3Чи не помітабо жодних особливостей? якщо подивитися на картинку вище, то стане ясно, що рішення, що мають 2 комбінації, мають непарний порядковий номер, 4 комбінації - це порядковий номер 4, 3 комбінації - інші. так я і прийшов до того, що потрібно тримати ігрове поле в звичайному масиві чисел: проста ітерація між числами, можливість порівняння за вибраним алгоритмом, простий виведення на екран. Щодо самого алгоритму порівняння. йдемо по порядку: у всіх випадках є перевірка рядка та стовпця, перевіряємо лише їх. якщо пошук не справ результатів, перевіряємо номер осередку на чет/чет, якщо непарна то повертаємося до гри, немає сенсу перевіряти далі, якщо парна, перевіряємо чи лежить на лівій діагоналі цей осередок, номери цієї діагоналі при розподілі на 4 в залишку мають 0. Якщо вона лежить перевіряємо на збіги, якщо збігів не знайдено, то перевіряємо на цифру 4, якщо ні – повернення в гру, якщо так йдемо далі за кодом та повертаємо результат перевірки останньої діагоналі. Ймовірно, для непідготовленої людини, це складно зрозуміти, прочитавши набір літер вище, а хтось може сказати, що багато літер і в самому коді, що можна простіше, буду радий обговорити це. З полем розібралися, тепер потрібно розібратися з двома користувачами, які ходять по черзі і у кожного з них свій знак, Х або О. На першому етапі у нас немає жодної розрахованої на багато користувачів, значить найпростіше буде використовувати значки по черзі. Перший хід робить завжди Х, другий завжди О, потім знову Х і таке інше. Напрошується поставити прапорець ( це складно зрозуміти, прочитавши набір літер вище, а хтось може сказати, що багато літер і в самому коді, що можна простіше, буду радий обговорити це. З полем розібралися, тепер потрібно розібратися з двома користувачами, які ходять по черзі і у кожного з них свій знак, Х або О. На першому етапі у нас немає жодної розрахованої на багато користувачів, значить найпростіше буде використовувати значки по черзі. Перший хід робить завжди Х, другий завжди О, потім знову Х і таке інше. Напрошується поставити прапорець ( це складно зрозуміти, прочитавши набір літер вище, а хтось може сказати, що багато літер і в самому коді, що можна простіше, буду радий обговорити це. З полем розібралися, тепер потрібно розібратися з двома користувачами, які ходять по черзі і у кожного з них свій знак, Х або О. На першому етапі у нас немає жодної розрахованої на багато користувачів, значить найпростіше буде використовувати значки по черзі. Перший хід робить завжди Х, другий завжди О, потім знову Х і таке інше. Напрошується поставити прапорець ( значить найпростіше використовуватиме значки по черзі. Перший хід робить завжди Х, другий завжди О, потім знову Х і таке інше. Напрошується поставити прапорець ( значить найпростіше використовуватиме значки по черзі. Перший хід робить завжди Х, другий завжди О, потім знову Х і таке інше. Напрошується поставити прапорець (true/false ), і якщо true – то поточний гравець X, якщо false – то О і спочатку кожного ходу прапорець =! Тут нам знадобиться наш незабутній BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); Гравці будуть вводити номери комірок, в консоль, і за натисканням Enter буде проводитися хід. Осередок, який відповідає введеному номеру, змінюватиме значення з 0 на 1 або 2, залежно від поточного стану прапорця, який обговорювався абзацом вище. Ось тут важливо зробити валідацію введення, щоб ніхто не зміг поміняти Х на О, коли осередок вже заповнений :) Що може ввести в консоль гравець?
  1. порожня стрічка
  2. букви, розділові знаки, дужки.. одним словом неЦифри
  3. некоректні цифри - негативні або за межами розмірів масиву, зайняті осередки.
Стандартний метод отримання цифри з рядка це статичний метод parseInt класу Integer Integer.parseInt("2");Він кидає винятокNumberFormatExceptionякщо не може отримати цифру із заданого рядка, захист від перших двох пунктів ми зможемо забезпечити перехопленням цього виключення. Для третього пункту я б створив ще один метод, який перевіряє введене значення, але найправильніше буде винести запит рядка в окремий метод, в якому проводитиметься валідація, а повертатиме він лише число. Резюмуємо, ми створабо поле, зробабо метод, який його відображає, зробабо метод, який проводить перевірку "а чи не переміг цей гравець годиною?", зробабо валідацію чисел, що вводяться. Залишилося зовсім небагато, зробити перевірку на нічию - окремий метод, який пробігає масивом і шукає 0, і відображення результатів гри. На цьому все, код готовий, гра вийшла невеликою, всього один клас, тому жорсткі копіпастери можуть не розбираючись, просто все скопіювати в свій проект і запустити його в себе, я і сам був, але зараз намагаюся вже так не робити і нікому не раджу :) Всім удачі у вивченні JAVA! ps решта пунктів - мультиплеєр і БД будуть пізніше, я вже почав вивчення матеріалу :)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class GameField {

    static int [] canvas = {0,0,0,
                            0,0,0,
                            0,0,0};

    //012, 345, 678, 036, 147, 258, 048, 246
    public static void main(String[] args){

        boolean b;
        boolean isCurrentX = false;
        do {
            isCurrentX = !isCurrentX;
            drawCanvas();
            System.out.println("mark " + (isCurrentX ? "X" : "O"));
            int n = getNumber();
            canvas[n] = isCurrentX ? 1 : 2;
            b = !isGameOver(n);
            if (isDraw()){
                System.out.println("Draw");
                return;
            }
        } while (b);
        drawCanvas();
        System.out.println();

        System.out.println("The winner is " + (isCurrentX ? "X" : "O") + "!");
    }

    static int getNumber(){
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true){
            try {
                int n = Integer.parseInt(reader.readLine());
                if (n >= 0 && n < canvas.length && canvas[n]==0){
                    return n;
                }
                System.out.println("Choose free cell and enter its number");
            } catch (NumberFormatException e) {
                System.out.println("Please enter the number");
            } catch (IOException e) {
            }
        }
    }

    static boolean isGameOver(int n){
        // 0 1 2
        // 3 4 5
        // 6 7 8
        //поиск совпадений по горизонтали
        int row = n-n%3; //номер строки - проверяем только её
        if (canvas[row]==canvas[row+1] &&
                canvas[row]==canvas[row+2]) return true;
        //поиск совпадений по вертикали
        int column = n%3; //номер столбца - проверяем только его
        if (canvas[column]==canvas[column+3])
            if (canvas[column]==canvas[column+6]) return true;
        //мы здесь, значит, первый поиск не положительного результата
        //если значення n находится на одной из граней - возвращаем false
        if (n%2!=0) return false;
        //проверяем принадлежит ли к левой диагонали значення
        if (n%4==0){
            //проверяем есть ли совпадения на левой диагонали
            if (canvas[0] == canvas[4] &&
                    canvas[0] == canvas[8]) return true;
            if (n!=4) return false;
        }
        return canvas[2] == canvas[4] &&
                canvas[2] == canvas[6];
    }

    static void drawCanvas(){
        System.out.println("     |     |     ");
        for (int i = 0; i < canvas.length; i++) {
            if (i!=0){
                if (i%3==0) {
                    System.out.println();
                    System.out.println("_____|_____|_____");
                    System.out.println("     |     |     ");
                }
                else
                    System.out.print("|");
            }

            if (canvas[i]==0) System.out.print("  " + i + "  ");
            if (canvas[i]==1) System.out.print("  X  ");
            if (canvas[i]==2) System.out.print("  O  ");
        }
        System.out.println();
        System.out.println("     |     |     ");
    }

    public static boolean isDraw() {
        for (int n : canvas) if (n==0) return false;
        return true;
    }
}
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ