JavaRush /Blog Java /Random-PL /Gra Java dla początkujących
timurnav
Poziom 21

Gra Java dla początkujących

Opublikowano w grupie Random-PL
Witajcie przyjaciele i przyszli koledzy! Gra Java dla początkujących - 1Całkiem niedawno zdawałem egzamin na udział w prawdziwym projekcie, zdałem go, ale tak się złożyło, że z powodów osobistych nie mogłem wziąć udziału w samym RP. Po tak ciekawych zadaniach jak test RP, zwykłe zadania z kursu stały się mniej atrakcyjną rozrywką, zwłaszcza że większość z nich już rozwiązałem. Dlatego, aby mój talent nie zmarnował się na dalszą naukę, zdecydowałem się stworzyć grę internetową dla wielu graczy. Linki do innych gier:
  1. kontynuacja tego artykułu
  2. 2048
Kółko i krzyżyk wydawało mi się najprostszą grą, więc zdecydowałem się podzielić zadanie na kilka podzadań:
  1. Aplikacja konsolowa do testowania logiki gry
  2. Tryb wieloosobowy
  3. Dołączanie bazy danych graczy do aplikacji konsolowej
  4. Tworzenie projektu front-endu, pisanie szablonów stron, interfejsu gry
  5. Kładąc wszystko razem
Istnieje możliwość, że zostanę zbesztany za taką sekwencję, a najprawdopodobniej wszystkie poważne projekty są budowane w zupełnie innej kolejności, odpowiem od razu, napiszę o tym post „dla początkujących”, aby wszyscy (łącznie ze mną) można się tego nauczyć :) No cóż, zacznijmy pisać aplikację konsolową! Wykonam te same kroki, co w przypadku dużych wyzwań poziomu 20. O co chodzi w grze w kółko i krzyżyk?!
  1. pole
  2. dwóch graczy na zmianę, jeden stawia krzyżyk, drugi zero. To proste.
Robimy pole standardowym polem 3x3. Jak można zapisać takie pole? pierwszą opcją jest tablica dwuwymiarowa. Jakie elementy powinna zawierać ta tablica? Odpowiedź jest taka, że ​​musimy pomyśleć o tym, co zrobimy z tymi elementami, czyli o wyświetlaniu i porównywaniu, aby znaleźć zwycięzcę. Gdybyśmy wyświetlali je tylko na ekranie, to logiczne byłoby zachowanie ich w postaci ciągu znaków, wówczas sama tablica i wyświetlenie jej na ekranie w pętli wyglądałoby mniej więcej tak:
String[][] strings = {{"O", "O", "_"},
                    {"_", "X", "O"},
                    {"X", "X", "X"},
for (String [] ss : strings){
    for (String s : ss) System.out.print(s + " ");
    System.out.println(); //для перевода строки
}
na ekranie wyświetli się:
O O _
_ X O
X X X
Ale oprócz wyświetlania mamy także porównanie wartości i tutaj opcje są już możliwe. Można porównywać ciągi znaków, można utworzyć specjalną klasę wyliczeniową ( enum), ale ja wolę porównywać liczby i zamieniać je na „X” i „O” tylko wtedy, gdy są wyświetlane na ekranie. Niech to będzie na przykład 1 - „X”, 2 - „O”, 0 - „_”. Jak więc sprawdzić, czy w polu występuje potrójne dopasowanie X lub O?
Pierwszy algorytm sprawdza całe pole
int[][] canvas = {{00, 01, 02},
                 {10, 11, 12},
                 {20, 21, 22}}
Zwycięskie kombinacje:
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.
Sprawdzam przez porównanie liczb, ale okazuje się, że za każdym razem trzeba sprawdzić CAŁE pole, wszystkie 8 kombinacji. Oczywiście to niewiele, nie jest to szukanie liczb Armstronga z zakresu od 0 do 1 miliarda, obliczeń jest tu niewiele więcej niż żadnych, ale i tak chcesz czegoś bardziej optymalnego niż sprawdzanie całego pola. Drugi pomysł, który mi przyszedł do głowy, to sprawdzić tylko komórkę, która została zaznaczona w poprzednim ruchu, dzięki czemu nadal możemy wyłonić zwycięzcę, ponieważ będziemy wiedzieć, kto wykonał ten ruch. Zatem zamiast wszystkich 8 kombinacji otrzymujemy tylko 2, 3 lub 4 kombinacje, w zależności od komórki, patrz obrazek: Gra Java dla początkujących - 2Teraz musimy dowiedzieć się, jak określić, która kombinacja ma zostać uruchomiona? W tym miejscu zdałem sobie sprawę, że używanie tablicy dwuwymiarowej nie jest zbyt wygodne. Postanowiłem rozważyć inne opcje. Na początku wpadłem na pomysł, żeby pole można zapisać w postaci dziewięciocyfrowej liczby, np. to samo pole, które wyświetliliśmy na ekranie, można zapisać tak: 220012111, wyjaśnię na palcach, co to jest to... Kod jest taki sam, 1 - „X”, 2 - „O” , 0 – „ „, co oznacza 220012111 = „OO__XOXX”, lub jeśli po co trzeciej cyfrze wstawisz podział wiersza i dodasz spacje dla przejrzystość:
О О _
_ Х О
Х Х Х
tutaj znowu, wygodne do przechowywania, wynaleziono urządzenie do wyświetlania, ale niewygodne do porównania! Rozwiązanie znaleziono, gdy ponumerowałem komórki 1-9, pomyślałem, bo w programowaniu odliczanie zaczyna się od 0 i numeruje je jak na obrazku.Nie Gra Java dla początkujących - 3zauważyłeś jakichś osobliwości? jeśli spojrzysz na powyższy obrazek, stanie się jasne, że rozwiązania z 2 kombinacjami mają nieparzysty numer seryjny, 4 kombinacje mają numer seryjny 4, 3 kombinacje mają resztę. Doszedłem więc do wniosku, że trzeba zachować pole gry w postaci regularnej tablicy liczb: prosta iteracja między liczbami, możliwość porównania zgodnie z wybranym algorytmem, proste wyjście na ekran. Jeśli chodzi o sam algorytm porównania. Przejdźmy do porządku: we wszystkich opcjach sprawdzany jest wiersz i kolumna, sprawdzamy tylko je. jeśli wyszukiwanie nie daje rezultatu, sprawdzamy numer komórki pod kątem parzystym/nieparzystym, jeśli jest nieparzysty, to wracamy do gry, nie ma sensu dalej sprawdzać, jeśli jest parzysty, sprawdzamy, czy ta komórka leży na lewej przekątnej, liczby tej przekątnej przy dzieleniu przez 4 mają resztę 0. Jeśli leży, sprawdzamy dopasowania, jeśli nie znaleziono dopasowań, sprawdzamy cyfrę 4, jeśli nie, wracamy do gry, jeśli tak, idziemy dalej przez kod i zwracamy wynik sprawdzenia ostatniej przekątnej. Prawdopodobnie dla nieprzygotowanej osoby jest to trudne do zrozumienia po przeczytaniu powyższego zestawu liter, ale ktoś może powiedzieć, że w samym kodzie jest dużo liter, co może być prostsze, chętnie to omówię. Wyjaśniliśmy pole, teraz mamy do czynienia z dwoma użytkownikami, którzy na zmianę i każdy z nich ma swój własny znak, X lub O. Na pierwszym etapie nie mamy żadnej funkcjonalności dla wielu użytkowników, więc najłatwiejszym sposobem byłoby użycie ikon jedna po drugiej. X zawsze wykonuje pierwszy ruch, O zawsze wykonuje drugi, potem znowu X i tak dalej. Aż prosi się o sprawdzenie ( prawda/fałsz ), a jeśli prawda – to aktualny gracz to X, jeśli fałsz – to O i na początku każdego ruchu flag=!flaga Pozostaje jakoś otrzymać sygnał od graczy o którym wybrana przez nich komórka. Tutaj będziemy potrzebować naszych niezapomnianych BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); Graczy, wprowadzimy numery komórek do konsoli, a naciśnięcie Enter spowoduje wykonanie ruchu. Komórka odpowiadająca wprowadzonej liczbie zmieni swoją wartość z 0 na 1 lub 2 w zależności od aktualnego stanu checkboxa, co zostało omówione w powyższym akapicie. W tym miejscu ważne jest, aby sprawdzić poprawność danych wejściowych, aby nikt nie mógł zmienić X na O, gdy komórka jest już wypełniona :) Co gracz może wpisać w konsoli?
  1. pusta linia
  2. litery, znaki interpunkcyjne, nawiasy... jednym słowem, niecyfry
  3. nieprawidłowe liczby - ujemne lub poza rozmiarem tablicy, zajęte komórki.
Standardową metodą uzyskania cyfry z ciągu znaków jest metoda statyczna parseInt klasy Integer. Integer.parseInt("2");Zgłasza wyjątek NumberFormatException, jeśli nie może pobrać cyfry z danego ciągu. Możemy zapewnić ochronę od pierwszych dwóch punktów, przechwytując ten wyjątek. W punkcie trzecim stworzyłbym inną metodę sprawdzającą wpisaną wartość, jednak najrozsądniej byłoby przenieść żądanie stringa do osobnej metody, w której zostanie przeprowadzona walidacja, a zwróci ona tylko liczbę. Podsumowując, stworzyliśmy pole, zrobiliśmy metodę wyświetlającą je, stworzyliśmy metodę sprawdzającą „czy ten gracz wygrał o godzinę?” i zwalidowaliśmy wprowadzone liczby. Niewiele pozostało do zrobienia, sprawdź remis - osobna metoda, która przegląda tablicę, szuka 0 i wyświetla wyniki gry. To wszystko, kod jest gotowy, gra okazała się mała, tylko jedna klasa, więc twardzi kopiujący-wklejający mogą bez zrozumienia po prostu skopiować wszystko do swojego projektu i uruchomić go samodzielnie, sam taki byłem, ale teraz staram się tego nie robić i nikomu tego nie polecam :) Życzę wszystkim powodzenia w nauce JAVA! ps reszta punktów - multi i baza danych dojdą później, materiał już zacząłem studiować :)
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;
        //мы здесь, значит, первый поиск не положительного результата
        //если oznaczający n находится на одной из граней - возвращаем false
        if (n%2!=0) return false;
        //проверяем принадлежит ли к левой диагонали oznaczający
        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;
    }
}
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION