Autor Wątek: [Java] Wzorzec projektowy do gry Saper  (Przeczytany 4657 razy)

Offline Xenox93

  • Użytkownik

# Wrzesień 27, 2015, 01:03:28
Witam,

mam do Was dość niecodzienną prośbę. Otóż na III roku studiów do końca I semestru będę musiał zaliczyć programowanie w języku Java. Jako pracę zaliczeniową wybrałem najbardziej ambitny projekt jaki był, tzn. taki, który pozwoli mi na poznanie i nauczenie się nowego języka oraz wielu bibliotek/pakietów, które mogą się przydać w przyszłych projektach, m.in. GUI, Bazy danych oraz sieci, a to wszystko musi być oparte na wątkach. Z racji, że Java jest językiem obiektowym również chciałbym napisać kod poprawnie a zarazem wykorzystać prostotę logiki aplikacji i nauczyć się wykorzystywać wzorce projektowe. Ogólnie rzecz biorąc całą grę mam już napisaną, więc nie martwię się tym, czy skończę grę ;)

Jednakże, z racji, że nie miałem okazji odbywać stażu/praktyk w studiu programistycznym, tak więc przydałaby mi się refaktoryzacja kodu tak aby zrozumieć i wykorzystać w pełni zalety OOP oraz nauczyć się kiedy i w jaki sposób wykorzystywać wzorce projektowe.

Wcześniej dużo czytałem o wzorcach projektowych i z grubsza mam pojęcie, ale teoretyczne. Ciężko znaleźć mi praktyczne i racjonalne zastosowanie ich. Wiem, że Saper wykorzystujący GUI-Swing jest banalny a użycie OOP a już na pewno wykorzystanie wzorców projektowych mija się z celem, ale pomimo tego jest to raczej najlepsza okazja, żeby nie zrobić pomyłki i poprawnie napisać oraz wykorzystać dany wzorzec.

Niżej zamieszczam założenie projektu, które zamieścił wykładowca:
Cytuj
Tu gramy z serwerem, który generuje plansze i sprawdza każdy ruch, oraz zapewnia ranking użytkowników (można wybrać sobie rozmiar planszy)

Ogólnie założenie jest dziwne, gdyż Saper jest grą single player, ale skoro takie jest zadanie to trzeba napisać grę, która będzie miała logikę gry na serwerze.

Jak każdy z Was zauważył, od razu na myśl przychodzi wzorzec architektoniczny:MVC.

Model - cała logika gry oraz wszelkiego rodzaju dane, np. statystyki oraz konta graczy, które będą się znajdowały tylko na serwerze. Klient będzie posiadał jedynie wysyłanie zapytań/wiadomości/pakietów do serwera oraz odbiór wyników/odpowiedzi.
View - wszystkie okna oraz słuchacze zdarzeń znajdujące się tylko u klienta.
Controller - będzie pozwalał na komunikację klient <-> serwer oraz będzie wywoływał odpowiednie metody.


Teraz przyszła kolej na wzorce projektowe:
State - przejście z okna Menu do okna Statystyki będzie posiadało pewien stan.
Memento - pozwoli za pomocą stanu przełączać/przemieszczać się pomiędzy oknami, np. za pomocą przycisku "Wróć" znajdującego się w oknie Statystyk wrócimy do Menu.
Observer - obserwator pozwoli nam w pewien sposób połączyć oba powyższe wzorce. Ostatecznie można go będzie zastąpić innym wzorcem lub całkowicie inaczej zaprojektować architekturę aplikacji.
Decorator - zmiana zawartości okna runtime opierając się na poprzednim stanie, nadal łączenie poprzednich wzorców w jedną spójną całość.
Strategy - zmiana klasy tworzącej dane okno, np. przekazanie klasy Game (tworzącej planszę gry oraz wiele innych elementów) jako parametr konstruktora dekoratora oraz wykorzystanie w wielu innych miejscach kodu, które będą wykorzystywały inne wzorce znajdujące się poniżej.

Command - logika klienta oraz serwera będzie w postaci tego wzorca. Odczytywanie rekordów z baz danych może trwać długo, aby nie czekać można posłużyć się tym wzorcem, który polecenie traktuje jako obiekt, który jest wysyłany od klienta do odbiorcy, dzięki temu można kolejkować polecenia oraz rozdzielać je na wątki.
Interpreter - wiadomości/pakiety będą wysyłany do serwera/klienta, który musi odpowiedni sparsować wiadomości, np: "checkLogin login password", a następnie wykorzysta odpowiednią klasę wzorca Command.

Mediator – komunikacja między klasami, zamiast bezpośrednio to wszystko za pomocą klasy pośredniej. Idealnie pasuje to roli Controller'a.

Factory Method - tworzy jeden obiekt - inicjalizacja programu w zależności od przeznaczenia, np.: klient lub serwer.


To byłoby właściwie tyle, zastanawiałem się nad użyciem Composite oraz Singleton (z racji, że zawsze widoczne będzie tylko jedno okno). Pewnie przestraszycie się że wpadłem na tak głupi pomysł, żeby wykorzystać tyle wzorców w tak banalnej aplikacji, ale tak jak wcześniej napisałem, chciałbym wykorzystać jak najwięcej możliwych wzorców aby je jakoś przyswoić. Zdaję sobie sprawę, że mogłem niektóre wzorce wykorzystać niepotrzebnie gdyż po prostu nie ma dla nich zastosowania w tej aplikacji, dlatego zwracam się do Was o pomoc, abyście mnie naprowadzili.

Krótki opis prostego działania aplikacji:
Inicjalizacja aplikacji: na jednym wątku odpala się inicjalizacja serwera a na drugim klienta.
Gotowość: serwer oczekuje na klienta/ów i jest cały czas gotowy. Klient łączy się z serwerem(na innym wątku) i tworzy oraz wyświetla pierwsze okno logowania.
Teraz klient podaje login i hasło, gdy kliknie na przycisk: "Zaloguj", ActionListener znajdujący się w klasie okna i nasłuchujący zdarzeń, wywołuje metodę Controller'a - checkLogin, która z kolei odwołuje się do Model'u - CheckLogin, który tak na prawdę jest klasą wykorzystującą wzorzec Command. Model u klienta nie posiada danych, więc wysyła wiadomość "checkLogin login password" na serwer.
Serwer odbiera wiadomość i zaczyna ją parsować za pomocą klasy wykorzystującą wzorzec Interpreter. Gdy w końcu sparsuje wiadomość, wywoła metodę checkLogin Controller'a, który z kole wywoła klasę wzorca Command, która wykorzysta klasę CheckLogin z metodą execute(), która sprawdzi czy istnieje konto w bazie danych. Jeżeli tak lub nie, to wywoła odpowiednią klasę, która będzie posiadała metodę execute(), która wyśle wiadomość do klienta "checkLogin OK".
Klient to sparsuje i jeżeli dane logowania są poprawne to zmieni stan( wzorzec State) na Menu, a do historii doda poprzedni stan LogIn(wzorzec Memento). W przeciwnym wypadku wyświetli komunikat.

Tak w skrócie wygląda koncepcja w miarę poprawnej architektury gry. Zdaję sobie sprawę, że można byłoby jakoś uprościć komunikację lub pozbyć się Controller'a wszędzie lub u klienta zrezygnować z Model'u bądź zastąpić go od razu bezpośrednim wysyłaniem/odbieraniem pakietów oraz wywoływaniem odpowiednich metod od razu.

Jestem otwarty na każde propozycje oprócz napisania kodu w banalny sposób, bez żadnych wzorców i różnych zalet OOP. Tym bardziej, że nie jest to gra napisana dla siebie, a na zaliczenie przedmiotu, który chcąc nie chcąc będzie sprawdzany pod kątem obiektowości itp. Zależy mi na dobrej ocenie oraz na nauczeniu się czegoś, tak aby można byłoby się bez wstydu również pochwalić przed przyszłym pracodawcą. Wiem, że refaktoryzacja jest ciągłym procesem, który się nie kończy, ale chciałbym mieć bardziej zorganizowany kod niż w obecnej postaci.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Xion

  • Redaktor
    • xion.log

  • +10
# Wrzesień 27, 2015, 08:57:25

Zadania tego typu tylko potwierdzają potoczne przekonanie, że tam gdzie kończy się rozsądek zaczyna się uczelnia...

Napisz ten serwer dokładnie tak, jak ci się wydaje poprawnie. Część z tzw. wzorców wykorzystasz niemal nieświadomie (Interpreter, Command, pewnie Strategy, może inne). Wciskanie ich na siłę na pewno nie przyniesie rezultatu, którym warto się chwalić...

Offline Radomiej

  • Użytkownik
    • Blog

# Wrzesień 27, 2015, 11:43:03
Luknij na LibGDX, system ECS np. Ashley albo artemis-odb i zacznij pisać:)

Offline Kos

  • Użytkownik
    • kos.gd

  • +2
# Wrzesień 27, 2015, 12:25:39
Dodam jeszcze do wypowiedzi Xiona że jak robisz apkę na jakimś istniejącym frameworku (obojętnie czy mówimy o webie, okienkach...) to większość 'wzorców projektowych' znajdziesz już zaimplementowanych w tym frameworku. Wpływają na sposób w jaki z niego korzystasz.

W samej logice aplikacji (szczególnie jeśli to mała gra typu saper) jest dużo mniej miejsca na wzorce niż w całej infrastrukturze dookoła niej.

Pamiętam jak wpadło mi w ręce "Patterns of Enterprise Application Architecture" Martina Fowlera, liczyłem że coś z niej przyda mi się do webdevu. No i rzeczywiście, niezła książka, jakbym miał zamiar pisać własne railsy albo django to byłaby jak znalazł :).
« Ostatnia zmiana: Wrzesień 27, 2015, 12:27:41 wysłana przez Kos »

Offline matheavyk

  • Użytkownik

  • +4
# Wrzesień 27, 2015, 21:37:14
Wczoraj czytając ten temat już nie mogłem się doczekać przeczytania pierwszego komentarza. Dzięki Xion ;)

To prawda, że cały opis tego co się dzieje sprawia, że myśli się "nie tędy droga chłopie", ale przecież zrozumcie - Xenox93 chce się nauczyć wzorców, więc pomyślał, że najlepsza droga to zaimplementowanie kilku. I przecież nie jest to zły pomysł. Natomiast faktycznie złym pomysłem jest upychanie wszystkich na siłę w jednym projekcie. Mnie się wydaje, że lepiej osobno analizować sobie każdy wzorzec i pod niego wymyślać jakiś mały program, który by z niego korzystał. A kiedy już się przyswoi satysfakcjonującą ich liczbę, to zrozumie się, że prawdziwą umiejętnością jest, jak zwykle zresztą, dobranie właściwego narzędzia do wykonania pracy. Nie trzeba spycharki, żeby przesunąć fotel, ale głupio nie zauważyć, że 10 kilo piasku lepiej przesypać łopatą niż dłonią. Dlatego moim zdaniem ważne jest, żeby uczyć się wzorców projektowych tak, jak wzorzec opisuje np. wikipedia: wzorzec składa się z nazwy, problemu, rozwiązania, konsekwencji. Uczymy się po to, by w przyszłych projektach umieć rozpoznać problem i dopasować do niego rozwiązanie lub zdać sobie sprawę, że rozwiązanie musimy wymyślić sami, bo jeszcze nie istnieje. Dlatego szukanie tych problemów na siłę w saperze jest nie tylko szukaniem dziury w całym, ale na dodatek może wypaczyć rozumienie sensu stosowania wzorców (to takie moje przypuszczenie).

Xenox93, nie obrażaj się za to co napisałem. Jeśli chcesz praktyczną radę, to polecam zmniejszenie liczby wzorców, których chcesz użyć, co najmniej o połowę. Zaimplementowanie 6 zamiast 12 też da Ci dużo materiału do przemyśleń (głównie jak je tam upchnąć :D ), też będziesz mógł się pochwalić, że aż tyle użyłeś w pracy, a przynajmniej każdy z nich lepiej poznasz.

No i na koniec, jak zwykle, wystąpię jako obrońca uczelni :).
Po pierwsze, nikt nie kazał "w programie użyj 10 wzorców projektowych". Z tego, co widzę Xenox93 sam chce skorzystać na projekcie i dodatkowo nauczyć się wzorców.
Po drugie, projekt zmobilizował do nauki. Po trzecie kiedyś wypada, dla samego siebie, tych wzorców się nauczyć, a jak to zrobić, jeśli nie poprzez wymyślanie problemów i implementowanie wzorców? W ostateczności, to nawet jeśli ktoś przy pisaniu projektu pomyśli "przecież to bez sensu mi kazali, że mam tu tego używać", to i tak znaczy, że wyciągnął naukę, bo zrozumiał o co chodzi. Nie wyobrażam sobie nauki innej niż poprzez głupie zadania do kolejnych wzorców. Bo jeśli miałbym się uczyć ich dopiero w praktyce, to jestem pewien, że nie jeden raz zdarzyłoby mi się przegapić miejsce, w którym użycie wzorca oszczędziłoby mi mnóstwo czasu i cierpienia.
« Ostatnia zmiana: Wrzesień 27, 2015, 21:40:46 wysłana przez matheavyk »

Offline Xenox93

  • Użytkownik

# Wrzesień 27, 2015, 21:44:03
Zadania tego typu tylko potwierdzają potoczne przekonanie, że tam gdzie kończy się rozsądek zaczyna się uczelnia...
W samej logice aplikacji (szczególnie jeśli to mała gra typu saper) jest dużo mniej miejsca na wzorce niż w całej infrastrukturze dookoła niej.
Dokładnie, też mi to przyszło namyśl jak przeczytałem treść zadania, ale był to jeden z najlepszych możliwych projektów do wyboru, czyli nauka nowego języka i większości przydatnych rzeczy oraz co jest z tym związane poznanie nowych bibliotek oraz możliwość napisania gry ;)

Dodam jeszcze do wypowiedzi Xiona że jak robisz apkę na jakimś istniejącym frameworku (obojętnie czy mówimy o webie, okienkach...) to większość 'wzorców projektowych' znajdziesz już zaimplementowanych w tym frameworku. Wpływają na sposób w jaki z niego korzystasz.
Dokładnie taka sytuacja ma miejsce w przypadku Swing'a.

Napisz ten serwer dokładnie tak, jak ci się wydaje poprawnie. Część z tzw. wzorców wykorzystasz niemal nieświadomie (Interpreter, Command, pewnie Strategy, może inne). Wciskanie ich na siłę na pewno nie przyniesie rezultatu, którym warto się chwalić...
Już napisałem i mój kod wygląda jakby został napisany przez dziecko, które dopiero uczy się programować, czyli wszystko byle jak aby działało. Niestety, z tego powodu poziom kodu nie satysfakcjonuje oraz wstydziłbym się go pokazać jakiemukolwiek pracodawcy :/ Prawie, że wszystkie klasy to singleton'y, aby uniknąć ciągłego przekazywania referencji w parametrach konstruktorów/metod. Były w moim przypadku przydatne, gdyż w danym momencie aktywne mogło być tylko jedno okno, miałem tylko jeden socket itd. Więc w sam raz singleton dobry na napisanie kodu byle działał.

Teraz, gdy mam napisaną aplikację, chciałbym zrobić code refactoring. Na pewno nie podobają mi się switch'e  w metodzie pobierającej pakiet i jednocześnie interpretującej/parsującej ten pakiet... Co więcej, nie podoba mi się ciągłe na sztywno zmiana zawartości okna z ona logowania do Menu w obsłudze zdarzenia przycisku "Zaloguj się" oraz wiele innych niedociągnięć. Dodatkowo jakbym miał korzystać z ECS to wolałbym sam go napisać, żebym mógł w tak prostym projekcie poznać jego zasadę działania w celu lepszej nauki oraz lepszego wykorzystania dostępnych już bibliotek/framework'ów. Gdyż pamiętam, że gdy pierwszy raz napisałem okno w C++ za pomocą Qt po prostu było to dla mnie idiotyczne i nielogiczne, dziś gdy poznałem podstawy OOP (niestety bez wzorców) to patrzę na biblioteki wykorzystujące OOP już z innej perspektywy.

Tak więc, prosiłbym Was o kilka wskazówek dot. wykorzystania kilku wzorców z kilkunastu możliwych, które poukładają i ułatwią późniejszą modyfikację kodu. MVC wydawał mi się dobrym wyborem, gdyż na studiach nie mieliśmy bibliotek graficznych DX/OGL, więc LibGDX odpada na rzecz Swing'a, a w przyszłości chciałbym również nauczyć się LibGDX, w tym celu wystarczyłoby zmienić jedynie View ze Swing'a na w/w bibliotekę graficzną pozostawiając resztę bez zmian.

Co do ECS również mnie interesuje z racji lepszego wykorzystania i napisania kodu OOP. Gdyż pisanie rozległych klas o hierarchii, która zamiast się rozszerzać to się wydłuża, często powtarzając kod/metody/klasy. Tyle, czy ma inne zastosowanie niż Entity? Czy można użyć go do tworzenia GUI? Czy ma gdzieś indziej zastosowanie?

Również zaciekawiła mnie możliwość wykorzystania jednej klasy do wszystkich jednostek w grze, ale to raczej w C/C++, nie w języku JAVA ;)

Z tego co wiem, na obecną chwilę, na pewno skorzystam z:
Interpreter - parsowanie pakietu od serwera/klienta
Command - wszystkie klasy wykonujące logikę, np. sprawdzanie rekordu w bazie danych będą wykorzystywać ten wzorzec, mając tylko jedną metodę: execute();
State + Visitor lub Observer + Memento - całe GUI, tj. zdarzenia itp.
Strategy - na pewno nie raz w Model i View
Mediator(?) - może w przypadku Controller'a, ale to okaże się w praktyce.
Factory method lub Abstract Factory - inicjalizacja/tworzenie aplikacji w zależności czy to klient czy serwer.

Na obecną chwilę nie widzę zastosowania dla innych wzorców i nie zamierzam szukać na siłę. Tak więc, czy powyższe założenia są akceptowalne?

Mam dodatkowe pytanie, ale odnośnie już praktyki: otóż lepiej pisać kod lokalnie/etapami/modułami czy lepiej  od razu pisać tak jak być powinno? Chodzi mi o to, że wcześniej napisałem Sapera lokalnie, tzn. wszystkie klasy i logika wykonywały się u klienta. Nie pisałem w ogóle kodu serwera, więc nie było komunikacji klient <-> serwer. Jednakże słyszałem od innych, że lepiej od razu napisać komunikację sieciową, później parsowanie pakietów, zapytania do baz danych i całą resztę etapami. Czy mieli rację? Przyznam szczerze, że gdzieś czytałem, że moje podejście jest antywzorcem, tak nie powinno się robić, po ciągle pojawiają się trudności przez co mój kod wygląda jak wygląda, a nawet mogło się to skończyć nie ukończeniem gry w ogóle :/

@matheavyk: Masz zupełną rację. Właśnie chodziło mi o taką rzeczową odpowiedź, którą uzyskałem od każdego, dzięki ;) A tak w ogóle za co mam się gniewać? Przecież napisałeś prawdę jednocześnie nie wyśmiewając mnie. To co napisałeś, to były moje wcześniejsze obawy, że upycham większość na siłę.

Byłbym wdzięczny, gdyby ktoś miał chwilę i ochotę na wstawienie linków do stron z zadaniami, gdzie mógłbym nauczyć się wzorców oraz kiedy z nich korzystać. Jeśli nie ma takowych to z chęcią po refaktoryzacji kodu Sapera, napisałbym kilka aplikacji nawet prostych lub trochę trudniejszych w C++ i/lub Java ucząc się i poznając każdy wzorzec.

Ze szczególnym uwzględnieniem ECS, SoA i podejścia chyba  Krzysiek K., który namawia do pisania jednej klasy dla wszystkich jednostek, co ma sens, ale czy ma zastosowanie w aplikacji biznesowej? Jeśli tak to tym bardziej warto. Dlaczego wolałbym pisać kod jedynie w OOP? Otóż pisze się w tym łatwiej, logiczniej, szybciej  oraz łatwo wprowadzać zmiany, ale kosztem wydajności. Tak więc, gdy aplikacja będzie wymagać optymalizacji korzystałbym z rozwiązania SoA lub stosując jedną klasę ^^

Ogólnie rzecz biorąc mam trochę braków, np. muszę poznać kolejny język C# oraz języki programowania web. Właśnie w tym ostatnim dość często przydają się i korzysta się z wzorców. Fakt faktem, że są gotowe frameworki, ale aby nie robić copy-paste warto poznać jak one działają.
« Ostatnia zmiana: Wrzesień 27, 2015, 22:06:16 wysłana przez Xenox93 »

Offline topik92

  • Użytkownik

# Wrzesień 28, 2015, 00:14:06
Jak nowicjusz nowicjuszowi powiem Ci ze przekazywanie referencji(z głową) jest jak najbardziej okej, bo wymusza nie korzystanie  z singletonów, zmiennych globalnych, utrudnia pisanie metod co robią "wszystko" i generalnie wymusza utrzymywanie porządku.

Offline Kos

  • Użytkownik
    • kos.gd

# Wrzesień 28, 2015, 08:19:59
Już napisałem i mój kod wygląda jakby został napisany przez dziecko, które dopiero uczy się programować, czyli wszystko byle jak aby działało. Niestety, z tego powodu poziom kodu nie satysfakcjonuje oraz wstydziłbym się go pokazać jakiemukolwiek pracodawcy :/

Jeśli masz już jakiś działający kod, to jesteś w bardzo dobrej sytuacji.
Przeczytaj go sobie jeszcze raz, zastanów się co Ci w nim przeszkadza, jakie "code smells" potrafisz nazwać i opisać. Wybierz sobie któryś i go napraw przepisując możliwie małą część kodu (i ew. stosując jakiś wzorzec który akurat pasuje). Powtarzaj do skutku. :)

Takie podejście jest bliskie temu co się dzieje w 'prawdziwych' wieloosobowych projektach, gdzie nie ma mowy o przepisywaniu od zera. Powinno też być dobre na Twój projekt, bo do każdego użytego wzorca będziesz miał historyjkę oraz porównanie "przed" i "po".

Offline Xenox93

  • Użytkownik

# Wrzesień 28, 2015, 16:01:00
W sumie masz rację, chyba będzie to najlepsza opcja. Tylko mam jeszcze pytanie, które ostatnio zadałem. Czy lepiej od razu brać się za sieć, później za bazę danych, a na końcu za dopieszczanie GUI? Bo na obecną chwilę moja implementacji komunikacji klient <-> serwer jest gorsza niż GUI.

W sumie to pytanie dot. nie tylko tego projektu, ale projektów w ogóle. Czy warto zaczynać programowanie od interfejsu-szczegółów czy lepiej od ogólnych założeń?

Offline ArekBal

  • Użytkownik

# Wrzesień 28, 2015, 20:20:32
Ogólnych założeń...
zawsze IMHO idziesz w stronę
MVP Minimum Viable Product

Potem powinno być  z górki dopóki nie natrafisz na jakąś bombę.
Jak zrobisz te MVP szczupłe, to potem łatwo to rozłożyć.

Offline topik92

  • Użytkownik

# Wrzesień 29, 2015, 00:05:16
Wstawiłbyś tu/wysłał mi kod ? Ja bym Ci wysłał swojego sapera im mogli byśmy porównać błędy :).

Offline Xenox93

  • Użytkownik

# Wrzesień 29, 2015, 21:09:23
http://sendfile.pl/pobierz/512167---8848.html

Wstawiam kod Sapera lokalnego, tzn. brak w nim modułu sieciowego, który jest zaczęty, ale nie jest ukończony. W najbliższym czasie zamierzam zrobić refaktoryzację kodu wybierając te wzorce, które w danym czasie mi się przydadzą.
Parę rzeczy może w nim nie działać, ale ogólna logika aplikacji funkcjonuje. Tak w ogóle z mojego kodu niczego się nie nauczysz.

Z góry uprzedzam, że ten kod jest drogą przez mękę oraz pełen grzechów typu singleton ^^

Offline topik92

  • Użytkownik

# Wrzesień 29, 2015, 23:10:54
[ed]http://sendfile.pl/pokaz/512341---0mii.html licznik min w momencie wygranej w skazuje 1 a zero dopiero po wcisnieciu ok. nie implementowałem skalowania. poza tym raczej wsio działa :).


Z rzeczy ważnych jesteś pewny że poprawnie liczy Ci miny w rogach i na bokach? bo tak na mój rozum będziesz miał tam zła wartość. Z mniej ważnych to że środkowy warunek jest bez sensu, dla poprawnych danych, dla nie poprawnych nie powinno się wykonać. swój wstawie za chwilke :)
           
           // u gory są for'y po i ,j i rozmiarach planszy // to liczy krawedzie.
               else if( i == 0 )
                {
                    if( i < (height-1) )
                    {
                        if( board.get( login )[i+1][j] == -1 )
                            board.get( login )[i][j]++;
                    }
                }
                else if( i == (height-1) )
                {
                    if( i > 0 )
                    {
                        if( board.get( login )[i-1][j] == -1 )
                            board.get( login )[i][j]++;
                    }
                }
                   
                if( j == 0 )
                {
                    if( j < (width-1) )
                    {
                        if( board.get( login )[i][j+1] == -1 )
                            board.get( login )[i][j]++;
                    }
                }
                else if( j == (width-1) )
                {
                    if( j > 0 )
                    {
                        if( board.get( login )[i][j-1] == -1 )
                            board.get( login )[i][j]++;
                    }
                }
« Ostatnia zmiana: Wrzesień 30, 2015, 00:07:04 wysłana przez topik92 »

Offline Xenox93

  • Użytkownik

# Wrzesień 30, 2015, 18:13:36
Trochę zmieniłem, np. usunąłem/dodałem 'else', przeniosłem warunek sprawdzania rogów na przedostatnie miejsce itp., gdyż kod był trochę nieczytelny, ale warunków nie zmieniałem, gdyż są poprawne.
Z kolei kod zawsze będzie działał, nie ważne jakie by nie były podane wartości pól, gwarantuje to wcześniejsza inicjalizacja tablicy dwuwymiarowej wartością 0 ^^

// Górna krawędź
if( i == 0 )
    if( i < (height-1) )
        if( board.get( login )[i+1][j] == -1 )
            board.get( login )[i][j]++;
// Dolna krawędź
else if( i == (height-1) )
    if( i > 0 )
        if( board.get( login )[i-1][j] == -1 )
            board.get( login )[i][j]++;

// Lewa krawędź
if( j == 0 )
    if( j < (width-1) )
        if( board.get( login )[i][j+1] == -1 )
            board.get( login )[i][j]++;
// Prawa krawędź
else if( j == (width-1) )
    if( j > 0 )
        if( board.get( login )[i][j-1] == -1 )
            board.get( login )[i][j]++;

// Rogi
if( (i == 0) && (j == 0) )
{
    if( (i < (height-1)) && (j < (width-1)) )
        if( board.get( login )[i+1][j+1] == -1 )
            board.get( login )[i][j]++;
}
else if( (i == 0) && (j == (width-1)) )
{
    if( (i < (height-1)) && (j > 0) )
        if( board.get( login )[i+1][j-1] == -1 )
            board.get( login )[i][j]++;
}
else if( (i == (height-1)) && (j == (width-1)) )
{
    if( (i > 0) && (j > 0) )
        if( board.get( login )[i-1][j-1] == -1 )
            board.get( login )[i][j]++;
}
else if( (i == (height-1)) && (j == 0) )
{
    if( (i > 0) && (j > (width-1)) )
        if( board.get( login )[i-1][j+1] == -1 )
            board.get( login )[i][j]++;
}
// Środek i pozostałe pola nie przylegające do krawędzi
else
{
    if( board.get( login )[i-1][j-1] == -1 )
        board.get( login )[i][j]++;

    if( board.get( login )[i-1][j] == -1 )
        board.get( login )[i][j]++;

    if( board.get( login )[i-1][j+1] == -1 )
        board.get( login )[i][j]++;

    if( board.get( login )[i][j+1] == -1 )
        board.get( login )[i+1][j+1]++;

    if( board.get( login )[i+1][j+1] == -1 )
        board.get( login )[i+1][j]++;

    if( board.get( login )[i+1][j] == -1 )
        board.get( login )[i+1][j-1]++;

    if( board.get( login )[i+1][j-1] == -1 )
        board.get( login )[i][j-1]++;

    if( board.get( login )[i][j-1] == -1 )
        board.get( login )[i][j]++;
}

Offline topik92

  • Użytkownik

# Wrzesień 30, 2015, 18:36:15
Chodzi o to że w krawędziach możesz mieć maksymalnie wartość 5 (lewo prawo dól i na skosy dla górnej krawędzi) a twój algorytm tego nie uzyska. Poza tym nie sprawdzasz czy przypadkiem nie stawiasz miny 2 razy na te samo pole.