Poniższy kod działa tak, jak powinienem, ale jest brzydki, przesadny lub zawiera wiele innych rzeczy. Patrzyłem na formuły i próbowałem napisać kilka rozwiązań, ale ostatecznie otrzymałem podobną liczbę instrukcji.
Czy istnieje jakiś wzór matematyczny, który przydałby mi się w tym przypadku, czy 16, jeśli stwierdzenia są dopuszczalne?
Aby wyjaśnić kod, jest to rodzaj gry opartej na jednoczesnych turach. Dwóch graczy ma po cztery przyciski akcji, a wyniki pochodzą z tablicy (0-3), ale zmienne „one” i „two” mogą być przypisał cokolwiek, jeśli to pomoże. Wynik jest następujący: 0 = żadna wygrana, 1 = wygrana p1, 2 = wygrana p2, 3 = oba wygrane.
public int fightMath(int one, int two) {
if(one == 0 && two == 0) { result = 0; }
else if(one == 0 && two == 1) { result = 0; }
else if(one == 0 && two == 2) { result = 1; }
else if(one == 0 && two == 3) { result = 2; }
else if(one == 1 && two == 0) { result = 0; }
else if(one == 1 && two == 1) { result = 0; }
else if(one == 1 && two == 2) { result = 2; }
else if(one == 1 && two == 3) { result = 1; }
else if(one == 2 && two == 0) { result = 2; }
else if(one == 2 && two == 1) { result = 1; }
else if(one == 2 && two == 2) { result = 3; }
else if(one == 2 && two == 3) { result = 3; }
else if(one == 3 && two == 0) { result = 1; }
else if(one == 3 && two == 1) { result = 2; }
else if(one == 3 && two == 2) { result = 3; }
else if(one == 3 && two == 3) { result = 3; }
return result;
}
if-statement
math
formula
TomFirth
źródło
źródło
f(a, b)
która daje odpowiedź w ogólnym przypadku? Nie wyjaśniłeś logiki obliczeń, dlatego wszystkie odpowiedzi są tylko pomadką na świni. Zacznę od poważnego przemyślenia logiki programu, używanieint
flag do akcji jest bardzo przestarzałe.enum
mogą zawierać logikę i mają charakter opisowy, dzięki czemu możesz pisać swój kod w bardziej nowoczesny sposób.Odpowiedzi:
Jeśli nie możesz opracować formuły, możesz użyć tabeli dla tak ograniczonej liczby wyników:
źródło
IndexOutOfBoundsException
jeśli jeden lub więcej indeksów jest poza zakresem.i
/j
/k
dla zmiennych pętli), nazwanych stałych, porządkowania kodu w czytelny sposób itp., Ale kiedy nazwy zmiennych i funkcji zaczynają zajmować więcej niż 20 znaków każdy, co znajduję, prowadzi do mniej czytelnego kodu. Moje zwykłe podejście polega na wypróbowaniu zrozumiałego, ale zwięzłego kodu z komentarzami tu i tam, aby wyjaśnić, dlaczego kod ma taką strukturę (a nie jak). Umieszczając dlaczego w nazwach po prostu zaśmieca wszystko.Ponieważ twój zestaw danych jest tak mały, możesz spakować wszystko w 1 długą liczbę całkowitą i przekształcić go w formułę
Bardziej wariant bitowy:
Wykorzystuje to fakt, że wszystko jest wielokrotnością liczby 2
Geneza magicznej stałej
Co mogę powiedzieć? Świat potrzebuje magii, czasem możliwość czegoś wymaga jej stworzenia.
Istotą funkcji, która rozwiązuje problem OP, jest mapa od 2 liczb (jedna, dwie), dziedzina {0,1,2,3} do zakresu {0,1,2,3}. Każda z odpowiedzi dotyczyła sposobu wdrożenia tej mapy.
Ponadto w wielu odpowiedziach można zobaczyć powtórzenie problemu jako mapę 1 2-cyfrowej podstawy 4 liczby N (jeden, dwa), gdzie jedna to cyfra 1, dwie to cyfra 2, a N = 4 * jeden + dwa; N = {0,1,2, ..., 15} - szesnaście różnych wartości, to ważne. Wyjściem funkcji jest jedna 1-cyfrowa liczba podstawowa 4 {0,1,2,3} - 4 różne wartości, również ważne.
Teraz 1-cyfrowy numer podstawowy 4 można wyrazić jako 2-cyfrowy numer podstawowy 2; {0,1,2,3} = {00,01,10,11}, więc każde wyjście może być zakodowane tylko za pomocą 2 bitów. Z góry możliwe jest tylko 16 różnych wyjść, więc 16 * 2 = 32 bity to wszystko, co jest konieczne do zakodowania całej mapy; to wszystko może zmieścić się w 1 liczbie całkowitej.
Stała M jest kodowaniem mapy m, gdzie m (0) jest kodowany w bitach M [0: 1], m (1) jest kodowany w bitach M [2: 3], a m (n) jest kodowany w bitach M [n * 2: n * 2 + 1].
Pozostaje tylko indeksowanie i zwracanie prawej części stałej, w tym przypadku możesz przesunąć M w prawo 2 * N razy i wziąć 2 najmniej znaczące bity, czyli (M >> 2 * N) i 0x3. Wyrażenia (jedno << 3) i (dwa << 1) po prostu mnożą rzeczy, zauważając, że 2 * x = x << 1 i 8 * x = x << 3.
źródło
Nie podoba mi się żadne z przedstawionych rozwiązań oprócz JAB. Żadne z pozostałych nie ułatwia odczytania kodu i zrozumienia, co jest obliczane .
Oto jak napisałbym ten kod - znam tylko C #, a nie Java, ale otrzymujesz obraz:
Teraz jest o wiele bardziej jasne, co jest tutaj obliczane: podkreśla to, że obliczamy, kto zostanie trafiony jakim atakiem, i zwracamy oba wyniki.
Jednak może być jeszcze lepiej; ta tablica boolowska jest nieco nieprzejrzysta. Podoba mi się podejście do wyszukiwania tabel, ale chciałbym napisać to w taki sposób, aby było jasne, jaka jest zamierzona semantyka gry. Oznacza to, że zamiast „zero ataku i obrona jednego nie daje żadnego trafienia”, zamiast tego znajdź sposób, aby kod wyraźniej oznaczał „atak niskim kopnięciem i obrona bloku niskim skutkiem braku trafienia”. Spraw, by kod odzwierciedlał logikę biznesową gry.
źródło
Możesz utworzyć macierz, która zawiera wyniki
Kiedy chcesz uzyskać wartość, skorzystasz
źródło
Inne osoby już zasugerowały mój początkowy pomysł, metodę matrycową, ale oprócz konsolidacji instrukcji if można uniknąć części tego, co masz, upewniając się, że dostarczone argumenty znajdują się w oczekiwanym zakresie i używając zwrotów w miejscu (niektóre kodowanie standardy, które widziałem, egzekwują jeden punkt wyjścia dla funkcji, ale zauważyłem, że wielokrotne zwroty są bardzo przydatne do unikania kodowania strzałek, a przy powszechności wyjątków w Javie i tak nie ma sensu ścisłe egzekwowanie takiej reguły ponieważ każdy niewyłapany wyjątek zgłoszony w metodzie jest możliwym punktem wyjścia). Zagnieżdżanie instrukcji przełączania jest możliwe, ale dla małego zakresu wartości, które tu sprawdzasz, stwierdzam, czy instrukcje są bardziej zwarte i raczej nie spowodują dużej różnicy wydajności,
W efekcie jest to mniej czytelne, niż mogłoby to wynikać z nieregularności części mapowania danych wejściowych i wyników. Zamiast tego preferuję styl matrycy ze względu na jego prostotę i sposób, w jaki można ustawić matrycę, aby miała sens wizualny (choć częściowo na to wpływ mają moje wspomnienia map Karnaugh):
Aktualizacja: Biorąc pod uwagę Twoją wzmiankę o blokowaniu / trafianiu, oto bardziej radykalna zmiana funkcji, która korzysta z wyliczonych typów własności / posiadania atrybutów dla danych wejściowych i wyniku, a także nieco modyfikuje wynik w celu uwzględnienia blokowania, co powinno skutkować większym funkcja czytelna.
Nie musisz nawet zmieniać samej funkcji, jeśli chcesz dodawać bloki / ataki o większej wysokości, tylko wyliczenia; dodanie dodatkowych rodzajów ruchów prawdopodobnie będzie wymagać modyfikacji funkcji. Ponadto,
EnumSet
s może być bardziej rozszerzalne niż użycie dodatkowych wyliczeń jako właściwości głównego wyliczenia, np.EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);
A następnieattacks.contains(move)
zamiastmove.type == MoveType.ATTACK
, chociaż użycieEnumSet
s będzie prawdopodobnie nieco wolniejsze niż bezpośrednie sprawdzanie równości.Dla przypadku, gdy udany blok skutkuje w liczniku, można zastąpić
if (one.height == two.height) return LandedHit.NEITHER;
zRównież zastąpienie niektórych
if
instrukcji użyciem operatora trójskładnikowego (boolean_expression ? result_if_true : result_if_false
) może uczynić kod bardziej zwartym (na przykład kod w poprzednim bloku stałby sięreturn one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
), ale może to prowadzić do trudniejszych do odczytania onelinerów, więc nie Polecam go do bardziej skomplikowanych rozgałęzień.źródło
one
itwo
być ponownie użyty jako punkty początkowe w moim arkuszu sprite. Chociaż nie wymaga to dużo dodatkowego kodu, aby to umożliwić.EnumMap
klasa, której można użyć do odwzorowania wyliczeń na przesunięcia liczb całkowitych (możesz także użyć wartości porządkowych członków wyliczenia bezpośrednio, np.Move.ATTACK_HIGH.ordinal()
Byłby0
,Move.ATTACK_LOW.ordinal()
byłby1
itp., Ale jest to bardziej kruche / mniej elastyczne niż jawnie kojarzenie każdego członka z wartością jako dodawanie wartości wyliczeniowych pomiędzy istniejącymi pomniejszyłoby liczbę, co nie byłoby tak w przypadkuEnumMap
.)attack(against)
metodę doMove
wyliczenia, zwracając HIT, gdy ruch jest udanym atakiem, POWRÓT, gdy ruch jest zablokowanym atakiem, i NIC, gdy nie jest atakiem. W ten sposób możesz zaimplementować go ogólnie (public boolean attack(Move other) { if this.isAttack() return (other.isAttack() || other.height != this.height) ? HIT : BACKFIRE; return NOTHING; }
) i zastąpić go w razie potrzeby konkretnymi ruchami (słabe ruchy, które może zablokować każdy blok, ataki, które nigdy nie odpalają itp.)Dlaczego nie skorzystać z tablicy?
Zacznę od początku. Widzę wzór, wartości zaczynają się od 0 do 3 i chcesz złapać wszystkie możliwe wartości. To jest twój stół:
patrząc na ten sam plik binarny tabeli, widzimy następujące wyniki:
Być może już widzisz jakiś wzorzec, ale kiedy połączę wartość pierwszą i drugą, widzę, że używasz wszystkich wartości 0000, 0001, 0010, ..... 1110 i 1111. Teraz połączmy wartość pierwszą i drugą, aby utworzyć jeden 4-bitowa liczba całkowita.
Kiedy tłumaczymy to z powrotem na wartości dziesiętne, widzimy bardzo możliwą tablicę wartości, w której jeden i dwa połączone mogą być użyte jako indeks:
Tablica jest wtedy
{0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3}
, gdzie jej indeks to po prostu jeden i dwa razem.Nie jestem programistą Java, ale możesz pozbyć się wszystkich instrukcji if i zapisać to w następujący sposób:
Nie wiem, czy przesunięcie bitów o 2 jest szybsze niż mnożenie. Ale warto spróbować.
źródło
To używa trochę bitmagii (już to robisz, trzymając dwa bity informacji (niski / wysoki i atak / blok) w jednej liczbie całkowitej):
Nie uruchomiłem go, wpisałem tylko tutaj, proszę dwukrotnie sprawdzić.Pomysł na pewno działa. EDYCJA: Jest teraz testowany dla każdego wejścia, działa dobrze.Czy powinienem zasugerować rozdzielenie dwóch bitów informacji na osobne zmienne? Kod oparty głównie na operacjach bitowych takich jak ten powyżej jest zwykle bardzo trudny do utrzymania.
źródło
return ((one ^ two) & 2) == 0 ? (one & 2) / 2 * 3 : ((one & 2) / 2 ^ ((one ^ two) & 1)) + 1;
Szczerze mówiąc, każdy ma swój własny styl kodu. Nie sądziłbym, że wpłynie to zbytnio na wydajność. Jeśli rozumiesz to lepiej niż przy użyciu wersji skrzynki przełączników, kontynuuj korzystanie z tego.
Możesz zagnieżdżać ifs, więc potencjalnie może wystąpić niewielki wzrost wydajności ostatniego sprawdzania, ponieważ nie przejdzie tak wielu instrukcji if. Ale w kontekście podstawowego kursu java prawdopodobnie nie przyniesie to korzyści.
Więc zamiast ...
Zrobiłbyś ...
I po prostu sformatuj go tak, jak wolisz.
Nie poprawia to wyglądu kodu, ale moim zdaniem może go nieco przyspieszyć.
źródło
Zobaczmy, co wiemy
1: twoje odpowiedzi są symetryczne dla P1 (gracz pierwszy) i P2 (gracz drugi). Ma to sens w grze walki, ale jest również coś, co możesz wykorzystać, aby poprawić swoją logikę.
2: 3 uderzenia 0 uderzeń 2 uderzenia 1 uderzenia 3. Jedynymi przypadkami nieobjętymi tymi przypadkami są kombinacje 0 vs 1 i 2 vs 3. Innymi słowy, unikalna tabela zwycięstwa wygląda następująco: 0 uderzeń 2, 1 uderzeń 3, 2 uderzenia 1, 3 uderzenia 0.
3: Jeśli 0/1 zmierzy się ze sobą, wówczas nastąpi remis bez trafień, ale jeśli 2/3 pójdzie przeciwko sobie, oba trafią
Po pierwsze, zbudujmy funkcję jednokierunkową, która mówi nam, czy wygraliśmy:
Następnie możemy użyć tej funkcji do skomponowania końcowego wyniku:
Chociaż jest to prawdopodobnie bardziej skomplikowane i prawdopodobnie wolniejsze niż wyszukiwanie tabel oferowane w wielu odpowiedziach, uważam, że jest to doskonała metoda, ponieważ faktycznie zawiera logikę twojego kodu i opisuje go każdemu, kto czyta twój kod. Myślę, że dzięki temu jest to lepsze wdrożenie.
(Minęło trochę czasu, odkąd zrobiłem jakąkolwiek Javę, więc przepraszam, jeśli składnia jest wyłączona, mam nadzieję, że nadal jest zrozumiała, jeśli źle ją popełniam)
Nawiasem mówiąc, 0-3 wyraźnie coś znaczy ; nie są to wartości arbitralne, więc warto je nazwać.
źródło
Mam nadzieję, że poprawnie rozumiem logikę. Co powiesz na coś takiego:
Sprawdzanie jednego wysokiego trafienia lub jednego niskiego trafienia nie jest blokowane i to samo dla gracza drugiego.
Edycja: Algorytm nie został w pełni zrozumiany, „trafienie” przyznawane podczas blokowania, którego nie zdawałem sobie sprawy (Thx elias):
źródło
Nie mam doświadczenia z Javą, więc mogą występować literówki. Proszę traktować kod jako pseudo-kod.
Poszedłbym z prostym przełącznikiem. W tym celu potrzebujesz oceny pojedynczego numeru. Jednak w tym przypadku, ponieważ
0 <= one < 4 <= 9
i0 <= two < 4 <= 9
, możemy przekonwertować obie inty na proste int, mnożącone
przez 10 i dodająctwo
. Następnie użyj przełącznika w wynikowej liczbie w następujący sposób:Jest jeszcze jedna krótka metoda, którą chcę wskazać jako kod teoretyczny. Jednak nie użyłbym tego, ponieważ ma dodatkową złożoność, z którą normalnie nie chcesz sobie poradzić. Dodatkowa złożoność wynika z podstawy 4 , ponieważ liczenie wynosi 0, 1, 2, 3, 10, 11, 12, 13, 20, ...
Naprawdę tylko dodatkowa uwaga, na wypadek, gdyby coś mi brakowało w Javie. W PHP zrobiłbym:
źródło
Ponieważ wolisz zagnieżdżone
if
warunki warunkowe, oto inny sposób.Zauważ, że nie używa on
result
członka i nie zmienia żadnego stanu.źródło
else
łańcuchów, ale to nie miało znaczenia.else if
instrukcji, możemy przyspieszyć kod za pomocąswitch
lub tabel przeglądowych.one==0
uruchomi kod, będzie musiał sprawdzić, czyone==1
wtedy,one==2
a jeśli w końcu jeszczeone==3
- Jeśli byłyby jeszcze inne, gdyby tam był, nie wykonałby trzech ostatnich kontroli, ponieważ zamknąłby instrukcję po pierwszym dopasowaniu. I tak, możesz dalej optymalizować, używając instrukcji switch zamiastif (one...
instrukcji, a następnie używając innego przełącznika w obudowieone's
. To jednak nie moje pytanie.Wypróbuj z obudową przełącznika. ..
Zajrzyj tutaj lub tutaj, aby uzyskać więcej informacji na ten temat
Możesz dodać do niego wiele warunków (nie jednocześnie), a nawet mieć domyślną opcję której żadne inne przypadki nie zostały spełnione.
PS: Tylko jeśli jeden warunek ma być spełniony ..
Jeśli jednocześnie wystąpią 2 warunki .. Nie sądzę, aby można było użyć przełącznika. Ale możesz zmniejszyć swój kod tutaj.
Instrukcja Java przełączania wielu przypadków
źródło
Pierwszą rzeczą, która przyszła mi do głowy, była zasadniczo ta sama odpowiedź udzielona przez Francisco Presencia, ale nieco zoptymalizowana:
Możesz dalej go zoptymalizować, ustawiając ostatni przypadek (dla 3) jako domyślny:
Zaletą tej metody jest to, że łatwiej jest zobaczyć, które wartości
one
itwo
które wartości zwracają, niż niektóre inne sugerowane metody.źródło
źródło
; --unicorns
Możesz użyć skrzynki przełączników zamiast wielu
if
Należy również wspomnieć, że ponieważ masz dwie zmienne, musisz połączyć te dwie zmienne, aby użyć ich w przełączniku
Sprawdź tę instrukcję Java, aby obsłużyć dwie zmienne?
źródło
Gdy rysuję tabelę między jednym a dwoma, a wynikiem, widzę jeden wzór,
Powyższe ograniczyłoby co najmniej 3, jeśli stwierdzenia. Nie widzę ustalonego wzorca ani nie jestem w stanie wiele wyciągnąć z podanego kodu - ale jeśli taka logika może zostać wyprowadzona, zmniejszyłoby to liczbę instrukcji if.
Mam nadzieję że to pomoże.
źródło
Dobrym pomysłem byłoby zdefiniowanie reguł jako tekstu, wówczas łatwiej będzie uzyskać prawidłową formułę. Jest to wyodrębnione z ładnej reprezentacji tablicy Laalto:
A oto kilka ogólnych uwag, ale powinieneś opisać je w kategoriach:
Można oczywiście zmniejszyć to do mniejszej ilości kodu, ale ogólnie dobrym pomysłem jest zrozumienie tego, co piszesz, niż znalezienie kompaktowego rozwiązania.
Wyjaśnienie skomplikowanych trafień p1 / p2 byłoby świetne, wygląda interesująco!
źródło
Najkrótsze i wciąż czytelne rozwiązanie:
lub nawet krócej:
Nie zawiera żadnych „magicznych” liczb;) Mam nadzieję, że to pomaga.
źródło
static int val(int i, int u){ int q = (i & 1) ^ (u & 1); return ((i >> 1) << (1 ^ q))|((u >> 1) << q); }
źródło
Osobiście lubię kaskadować operatorów trójskładnikowych:
Ale w twoim przypadku możesz użyć:
Lub możesz zauważyć wzór w bitach:
Możesz więc użyć magii:
źródło
Oto dość zwięzła wersja, podobna do odpowiedzi JAB . Wykorzystuje mapę do przechowywania, która porusza się triumfalnie nad innymi.
Przykład:
Wydruki:
źródło
static final Map<Move, List<Move>> beats = new java.util.EnumMap<>();
zamiast tego, powinien być nieco bardziej wydajny.Użyłbym mapy, HashMap lub TreeMap
Zwłaszcza jeśli parametry nie są w formularzu
0 <= X < N
Jak zestaw losowych liczb całkowitych dodatnich ..
Kod
źródło
Dzięki @Joe Harper, kiedy skończyłem, używając odmiany jego odpowiedzi. Aby jeszcze bardziej zmniejszyć, ponieważ 2 wyniki na 4 były takie same, zmniejszyłem ją jeszcze bardziej.
W pewnym momencie mogę do tego wrócić, ale jeśli nie będzie poważnego oporu spowodowanego wieloma stwierdzeniami
if
, zatrzymam to na razie. Przeanalizuję macierz tabel i przejdę dalej do rozwiązań instrukcji.źródło
Oto sugestia, jak może to wyglądać, ale używanie ints jest nadal trochę brzydkie:
Lepiej byłoby użyć typu strukturalnego dla danych wejściowych i wyjściowych. Dane wejściowe faktycznie mają dwa pola: pozycję i typ (blok lub atak). Dane wyjściowe mają również dwa pola: player1Wins i player2Wins. Kodowanie tego w jedną liczbę całkowitą utrudnia odczytanie kodu.
Niestety Java nie jest zbyt dobra w wyrażaniu tego rodzaju typów danych.
źródło
Zamiast tego zrób coś takiego
źródło