Forum Coders' city Strona Główna Coders' city
Nasza pasja to programowanie!
 

 PomocPomoc   SzukajSzukaj   UżytkownicyUżytkownicy   GrupyGrupy  RejestracjaRejestracja 
Archiwum starego forum + teoria    RSS & Panel/SideBar
 ProfilProfil   Zaloguj się, by sprawdzić wiadomościZaloguj się, by sprawdzić wiadomości   ZalogujZaloguj 

Potrzebuję szybkiej odpowiedzi na moje pytanie... Zasady

Gra w statki. Prośba o sugestie i sprawdzenie kodu w javie



 
Odpowiedz do tematu    Forum Coders' city Strona Główna -> Java
Zobacz poprzedni temat :: Zobacz następny temat  
Autor Wiadomość
ninja
Gość





PostWysłany: Sob Paź 17, 2015 2:00 pm  OP    Temat postu: Gra w statki. Prośba o sugestie i sprawdzenie kodu w javie Odpowiedz z cytatem Pisownia

Postanowiłem sobie, że w ramach nauki i ćwiczeń programowania w Javie napiszę prostą grę w statki. W tym celu wstępnie stworzyłem 3 metody: metodę przygotowującą grę (setGame()), metodę pobierającą wybór pola przez gracza (gamerChoice()) i metodę sprawdzającą czy trafił czy nie (checkChoice()). Na początku przed przystąpieniem do dalszego pisania chciałem sprawdzić czy wszystko działa jak należy i czy to co stworzyłem ma jakiś sens. Okazało się jednak, że ostatnia metoda nie działa i nie ważne jakie współrzędne się wpiszę zawsze wyświetla, że gracz spudłował!. W związku z tym prosiłbym kogoś mądrzejszego i dobrego o pomoc w rozwiązaniu tego problemu, sprawdzeniu czy to co napisałem w ogóle do czegoś się nadaje. Dodam jeszcze, że dopiero co zaczynam naukę programowania więc proszę o wyrozumiałość ;) Z góry dziękuje za poświęcony czas, pomoc i odpowiedź :).

P.S: Domyślam się również, że istnieje wiele lepszych sposobów na napisanie tej gry, ale nie bardzo rozumiem dlaczego nie działa mi to co napisałem, nie wiem w czym jest problem co jest trochę de-motywujące przy nauce. Myślę też, że zostawianie nie rozwiązanych problemów też nie jest dobre, dlatego bardzo proszę kogoś kto znalazł rozwiązanie o wytłumaczenie mi dlaczego to nie działa.

Poniżej kod klasy z metodami:
Kod:

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

public class BattleShip {
    
    Random random = new Random();

    public int i,j;
    int[][] matrix = new int[10][10];
    int ship1[] = new int[3];
    
    
    // metoda ustawiajaca na planszy jeden statek zajmujacy 3 pola
    public void setGame() {
    
        ship1[0] = 1;
        ship1[1] = 2;
        ship1[2] = 3;
        
        i = random.nextInt(10);
        j = random.nextInt(10);
        
        if(i == 8 || i == 9 && j != 8 || j != 9) {
            matrix[i][j] = ship1[0];
            System.out.println("tabela["+i+"]["+j+"]");
            j++;
            matrix[i][j] = ship1[1];
            System.out.println("tabela["+i+"]["+j+"]");
            j++;
            matrix[i][j] = ship1[2];
            System.out.println("tabela["+i+"]["+j+"]");
            
        } else if(j == 8 || j == 9 && i != 8 || i != 9) {
            matrix[i][j] = ship1[0];
            System.out.println("tabela["+i+"]["+j+"]");
            i++;
            matrix[i][j] = ship1[1];
            System.out.println("tabela["+i+"]["+j+"]");
            i++;
            matrix[i][j] = ship1[2];
            System.out.println("tabela["+i+"]["+j+"]");
            
        } else if(i == 8 || i == 9 && j == 8 || j == 9) {
            int x = random.nextInt(1);
            if(x == 0) {
                matrix[i][j] = ship1[0];
                System.out.println("tabela["+i+"]["+j+"]");
                i--;  
                matrix[i][j] = ship1[1];
                System.out.println("tabela["+i+"]["+j+"]");
                i--;  
                matrix[i][j] = ship1[2];
                System.out.println("tabela["+i+"]["+j+"]");
                
            } else {
                matrix[i][j] = ship1[0];
                System.out.println("tabela["+i+"]["+j+"]");
                j--;  
                matrix[i][j] = ship1[1];
                System.out.println("tabela["+i+"]["+j+"]");
                j--;  
                matrix[i][j] = ship1[2];
                System.out.println("tabela["+i+"]["+j+"]");            
            }                
        } else {
            matrix[i][j] = ship1[0];
            System.out.println("tabela["+i+"]["+j+"]");
            i++;
            matrix[i][j] = ship1[1];
            System.out.println("tabela["+i+"]["+j+"]");
            i++;
            matrix[i][j] = ship1[2];            
        }
        gamerChoice(); // wywoluje metode proszaca gracza o wybranie pol
    }
    
    public int tab1Choice, tab2Choice;
    
    void gamerChoice() {
        
    
            Scanner read1 = new Scanner(System.in);
            System.out.print("Wspolrzedna tablicy pionowej: ");
            tab1Choice = read1.nextInt();
            Scanner read2 = new Scanner(System.in);
            System.out.print("\t, a teraz poziomej: ");
            tab2Choice = read2.nextInt();

            checkChoice(tab1Choice, tab2Choice);
    }    
    // metoda sprawdzająca wybór gracza
    int choice1, choice2;
    
    public void checkChoice(int a, int b) {
        
        a = choice1;
        b = choice2;
        
        
        if(matrix[choice1][choice2] == ship1[0] ||
                matrix[choice1][choice2] == ship1[1] ||
                matrix[choice1][choice2] == ship1[2]) {
            int hitNumber1 = 0;
            System.out.println("Trafiony!");
            hitNumber1++;
            if(hitNumber1 == 3) System.out.println("Zatopiony!");
        } else {
            System.out.print("Pudło!");
        }        
    }
}



A oto klasa testująca:
Kod:

public class BattleShipDemo {

    public static void main(String[] args) {
        
        BattleShip gamer = new BattleShip();
        
        gamer.setGame();
    }
}
Powrót do góry
marcin_an



Dołączył: 26 Maj 2005
Posty: 18813

PostWysłany: Sob Paź 17, 2015 9:48 pm      Temat postu: Odpowiedz z cytatem Pisownia

Sam sposób zamodelowania gry nie jest najlepszy - mieszasz rzeczy, które nie mają ze sobą związku. Zanim jednak przejdę do tego, uwagi do obecnego kodu:
  1. Linie 7-11, 78 i 93: masz pola, które są publiczne lub, bez żadnego uzasadnienia, publiczne dla danej paczki (aka "chronione"). Jakim prawem twoja klasa ma publiczne pola?
  2. Linia 7: naprawdę generator pseudolosowy jest związany z instancją gry i musisz trzymać jedną jego instancę przez cały czas życia gry? Szczególnie, że używasz tego obiektu tylko w jednej metodzie.
  3. Linie 78 i 93: czym są pola tab1Choice, tab2Choice, choice1 i choice2? Co one reprezentują z punktu widzenia całego obiektu gry? Bo ja nie widzę żadnego ich zastosowania - wyglądają jak zmienne lokalne metod, odpowiednio, gamerChoice oraz checkChoice. Zatem jakim prawem są to pola?
  4. Linia 80: dlaczego metoda gamerChoice jest chroniona? Szczególnie, że w programie nie ma innych klas (co bynajmniej i tak nie stanowiłoby uzasadnienia dla takiej widoczności, ale przynajmniej mogłoby móc kiedykolwiek zostać wykorzystanym).
  5. Linie 95, 97 i 98: po co metoda checkChoice ma argumenty a oraz b, skoro natychmiast po wywołaniu ich wartość jest niszczona?
  6. W klasie istnieją absurdalne zależności kolejności wywoływania metod. Np. wywołanie checkChoice przed gamerChoice nie ma sensu. To dyskwalifikuje program z jakiejkolwiek dalszej analizy, bo z założenia jego działanie jest przypadkowe.
  7. W ogóle te metody nie są - z punktu widzenia modelu programu - metodami, lecz wycinkami jednej procedury wykrojonymi poza nią.
  8. Linie 24-74: masz całą serię szczególnych warunków, których znaczenia nie jestem w stanie rozgryźć. Dlaczego np. jeśli i jest równe 0 lub j jest różne od 9 kod ma się zachować w dany, szczególny sposób? Z jakiego powodu wszystkie pola wiersza 8, oprócz ostatniego, są inne od wszystkich pól wiersza np. 7? Początkowo sądziłem, że chodzi o obsługę brzegów, ale nie - warunki nie mają większego związku z brzegami, a kod przed niczym nie chroni...
  9. ... bo masz tam wyjścia poza tablicę. Np. dla współrzędnych (8, 8) j w linii 31 będzie miał wartość 10.
  10. Linie 83 i 86: dwie instancje Scanner operujące na jednym strumieniu?
  11. Linia 87: znak tabulacji służy do tabulacji, a nie robienia kilku spacji. Jeżeli chcesz zrobić kilka spacji, to zrób kilka spacji.
  12. Instancje klasy BattleShip nie są całkowicie zainicjalizowane po utworzeniu, bo konstruktor nie ustawia części pól na sensowne wartości. Nie ustawia głównie dlatego, że w ogóle go nie ma.
  13. Linie 101-103: sprawdzasz zawsze matrix[0][0] - skutek całej serii wcześniej wymienionych błędów. Masz szanse rzędu 3%, że na (0, 0) pojawi się kawałek twojego trójmasztowca. Zrobiłeś tutaj kiepskiej jakości generator pseudolosowy, którego wynik działania nie ma nic wspólnego z jakimkolwiek wyborem gracza, bo wybór gracza nigdy nie jest brany pod uwagę.
  14. Widzę pole ship1 reprezentujące trójmasztowiec. A jeśli będę chciał dodać drugi trójmasztowiec, to dodasz pole ship2, wraz z przepisaniem jeszcze raz całego kodu - tym razem dla drugiego okrętu? W tej grze jest 10 statków. Będziesz robił 10 pól i 10 kopii prawie identycznego kodu? A jeśli zechcę zagrać na planszy 100x100 z setką statków? Od czegoś masz kolekcje.
  15. Właśnie: jeśli zmienię rozmiar planszy, to ten kod trzeba przepisywać od zera. Nie to, żeby zmiana rozmiaru planszy była normalnym elementem tej gry, ale dla tak trywialnego przypadku naprawdę nie powinno to stanowić żadnego problemu, jeśli tylko plansza będzie dostatecznie duża, by rozmieścić na niej okręty, i nie będzie miała jakiś skrajnie małych rozmiarów (3x3 lub mniej).
  16. Linia 107: hitNumber1 jest zawsze równe 1, (bo w poprzednich linijkach jest ustawiane na 0, a potem zwiększane do 1), więc ten warunek nigdy nie zostanie spełniony.
  17. W jaki w ogóle sposób miałeś zamiar sprawdzać zatopienie okrętu?


Przede wszystkim rozdziel ten program na klasy. Zrób sobie klasę, która reprezentuje stan gry, i klasę kontrolującą przebieg gry. To są dwa niezależne zagadnienia!

Na stan gry składa się rozmieszczenie okrętów na planszy oraz liczba trafień w każdy z nich. Skoro musisz przechowywać informacje o okrętach, to oznacza, że potrzebujesz kolejnej klasy, reprezentującej takowe. Zwróć przy tym uwagę na fakt, że nigdzie nie ma mowy o tym, by na stan gry składała się "graficzna" reprezentacja planszy - a taką próbowałeś zrobić, tworząc niepotrzebnie tablicę 10x10. Tablica taka może być przydatna, ale dopiero jako późniejsza optymalizacja kodu, która przy tak malutkich danych i braku prezentacji planszy nie będzie miała zastosowania*.

Obiekt reprezentujący statek musi być w stanie zrealizować dwie operacje: ustalić, czy trafienie w daną pozycję dotyczy go i, jeśli tak, przyjąć to uderzenie; oraz odpowiedzieć na pytanie, czy został całkowicie zniszczony. Masz zatem dwie metody: takeHit oraz isDestroyed. Reszta gry musi zostać poinformowana, czy nastąpiło trafienie. Tutaj są dwie opcje: obserwator dla każdego okrętu albo zwracanie wartości boolean z takeHit. Trzecia wersja to rozbicie takeHit na dwie metody: sprawdzającą oraz obsługującą faktyczne trafienie. Nie widzę jednak podstaw do takiego zamodelowania tego, podobnie jak obserwatorzy zdają się zbyt ciężkim rozwiązaniem dla tak prostego przypadku - szczególnie, że i tak nie masz klasy widoku, więc nie bardzo co ma być tym obserwatorem. Zatem przyjmijmy, że metoda takeHit zwraca boolean, informując o powodzeniu lub niepowodzeniu trafienia**.

Stan gry będzie zatem reprezentował tylko kolekcję okrętów. Nawet nie musisz rozdzielać na zatopione i niezatopione lub mieć licznika zatopionych, bo - kolejny raz - przejrzenie za każdym razem 10-elementowej kolekcji to żadne obciążenie dla komputera. Czyli wystarczy nam metoda getShips udostępniająca kolekcję statków***.

Kontroler musi mieć tylko jedną metodę: runStep. W każdym wywołaniu pyta ona gracza o pozycję do strzału, wywołuje takeHit dla każdego okrętu, sprawdza trafienie i informuje, czy było trafienie, czy nie, i czy było zatopienie (isDestroyed). Następnie sprawdza, czy zostały jakieś niezatopione jednostki i jeśli nie zostały - zawiadamia o tym kod wywołujący metodę runStep. Co oznacza, że runStep musi zwracać boolean (ponownie uwaga analogiczna jak przy takeHit). A ten "kod wywołujący" to zwykła pętla, która kończy się, gdy runStep poinformuje, że gra się zakończyła.

W załączniku diagram UML, jeśli w czymś ci to pomoże. Zaproponowany model zawiera wiele rzeczy, które w praktyce powinny wyglądać inaczej, ale całkowicie celowo upraszczam, żebyś zaczął od czegoś prostego. Zauważ też, że nie poruszyłem w ogóle kwestii pól poszczególnych klas. To nie przypadek - pola to szczegół implementacyjny i rzadko kiedy jest sen rozważać go na poziomie wyższym niż klasa. Nie zdefiniowałem konstruktora dla klasy Ship, chociaż będzie taki potrzebny - zastanów się, jaki, żeby był w stanie obsłużyć dowolny n-masztowiec i żeby okręt wiedział, na jakich polach stoi. Pominąłem też kwestię przygotowania okrętów: to załatw na tym samym poziomie, na którym masz główną pętlę, wywołującą runStep, przed tą pętlą. Zauważ, żę jeśli wybierzesz algorytm polegający na losowym wstawianiu okrętów i sprawdzaniu, czy się mieszczą, to będziesz potrzebował wiedzieć, które pola są już zajęte i tutaj masz co najmniej trzy opcje: lista zajętych pól, tymaczasowa tablica 10x10 lub dodanie do Ship metody pozwalającej stwierdzić, jakie pola zajmuje. Twój wybór, podobnie jak wybranie innego algorytmu. Jeśli jednak wybierzesz ten, to pamiętaj, że ta pętla losująca może się nigdy nie skończyć - warto zatem dodać jakiś dodatkowy warunek, gwarantujący jej zakończenie po zbyt wielu próbach lub zbyt długim czasie pracy.
____
* Co nie oznacza, że w innych grach lub innych założeniach użycie reprezentacji planszy nie będzie miało sensu - np. jako element wzorca antyobiektu, będącego naturalnym rozwiązaniem dla większości symulacji i gier z elementami symulacji świata gry.
** Rozwiązanie przedstawione tutaj jest niezgodne z MVC, ale nie próbujemy użyć tego wzorca. Zbyt trywialny projekt, a na twoim poziomie implementacja widoku byłaby nadmiernym zaciemnieniem kodu. Możesz potem spróbować przerobić to na MVC.
*** Lepiej byłoby zrobić niemutowalny model, ale - po raz kolejny - sądzę, że na tym poziomie to nadmierna komplikacja. Szczególnie, że sensowna wersja wymagałaby użycia np. wzorca odwiedzającego, a to może być z punktu początkującego mocno niejasne.



cc-battleships-400d7c562bbaa471341ad15bb3b2095c.png
 Opis:

Pobierz
 Nazwa pliku:  cc-battleships-400d7c562bbaa471341ad15bb3b2095c.png
 Wielkość pliku:  1.79 KB
 Pobierano:  157 raz(y)

Powrót do góry
Zobacz profil autora Wyślij prywatną wiadomość
Wyświetl posty z ostatnich:   
Odpowiedz do tematu    Forum Coders' city Strona Główna -> Java Wszystkie czasy w strefie CET (Europa)

Strona 1 z 1

 
Skocz do:  
Możesz pisać nowe tematy
Możesz odpowiadać w tematach
Nie możesz zmieniać swoich postów
Nie możesz usuwać swoich postów
Nie możesz głosować w ankietach
Możesz dodawać załączniki na tym forum
Możesz pobierać pliki z tego forum




Debug: strone wygenerowano w 0.03590 sekund, zapytan = 13
contact

| Darmowe programy i porady Jelcyna | Tansze zakupy w Helionie | MS Office Blog |