Der folgende Code funktioniert zwar so, wie ich es brauche, aber er ist hässlich, übertrieben oder eine Reihe anderer Dinge. Ich habe mir Formeln angesehen und versucht, einige Lösungen zu schreiben, aber am Ende habe ich eine ähnliche Anzahl von Aussagen.
Gibt es eine Art mathematische Formel, die mir in diesem Fall zugute kommen würde, oder sind es 16, wenn Aussagen akzeptabel sind?
Um den Code zu erklären, handelt es sich um eine Art Spiel auf der Basis von simultanen Runden. Zwei Spieler haben jeweils vier Aktionsschaltflächen und die Ergebnisse stammen aus einem Array (0-3), aber die Variablen 'eins' und 'zwei' können sein etwas zugewiesen, wenn dies hilft. Das Ergebnis ist, 0 = kein Sieg, 1 = p1 gewinnt, 2 = p2 gewinnt, 3 = beide gewinnen.
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;
}
quelle
f(a, b)
, die im allgemeinen Fall die Antwort liefert? Sie haben die Logik der Berechnung nicht erklärt, daher sind alle Antworten nur Lippenstift auf einem Schwein. Ich würde damit beginnen, Ihre Programmlogik ernsthaft zu überdenken. Die Verwendung vonint
Flags für Aktionen ist sehr veraltet.enum
s können Logik enthalten und sind beschreibend. Auf diese Weise können Sie Ihren Code auf modernere Weise schreiben.Antworten:
Wenn Sie keine Formel erstellen können, können Sie eine Tabelle für eine so begrenzte Anzahl von Ergebnissen verwenden:
quelle
IndexOutOfBoundsException
sowieso eine, wenn ein oder mehrere Indizes außerhalb der Grenzen liegen.i
/j
/k
Konvention für Schleifenvariablen), benannte Konstanten, meinen Code in eine lesbaren Art und Weise der Anordnung, etc., aber wenn die Variable und Funktionsnamen mehr starten Aufnahme als 20 Zeichen Jedes, was ich finde, führt tatsächlich zu weniger lesbarem Code. Mein üblicher Ansatz ist es, hier und da nach verständlichem, aber prägnantem Code mit Kommentaren zu suchen, um zu erklären, warum der Code so strukturiert ist, wie er ist (und wie). Das Warum in die Namen zu setzen, macht einfach alles unübersichtlich.Da Ihr Datensatz so klein ist, können Sie alles in eine lange Ganzzahl komprimieren und in eine Formel umwandeln
Bitweise Variante:
Dies nutzt die Tatsache, dass alles ein Vielfaches von 2 ist
Der Ursprung der magischen Konstante
Was kann ich sagen? Die Welt braucht Magie, manchmal erfordert die Möglichkeit, dass etwas erschaffen wird.
Die Essenz der Funktion, die das Problem von OP löst, ist eine Zuordnung von 2 Zahlen (eins, zwei), Domäne {0,1,2,3} zum Bereich {0,1,2,3}. Jede der Antworten hat sich mit der Implementierung dieser Karte befasst.
Außerdem können Sie in einer Reihe von Antworten eine Wiederholung des Problems als Karte von 1 2-stelligen Basis 4-Nummer N (eins, zwei) sehen, wobei eins Ziffer 1, zwei Ziffer 2 und N = 4 * eins ist + zwei; N = {0,1,2, ..., 15} - 16 verschiedene Werte, das ist wichtig. Die Ausgabe der Funktion ist eine 1-stellige Basis-4-Zahl {0,1,2,3} - 4 verschiedene Werte, ebenfalls wichtig.
Nun kann eine 1-stellige Basis-4-Nummer als 2-stellige Basis-2-Nummer ausgedrückt werden. {0,1,2,3} = {00,01,10,11}, sodass jeder Ausgang mit nur 2 Bits codiert werden kann. Von oben sind nur 16 verschiedene Ausgänge möglich, sodass nur 16 * 2 = 32 Bit erforderlich sind, um die gesamte Karte zu codieren. Dies kann alles in eine ganze Zahl passen.
Die Konstante M ist eine Codierung der Abbildung m, wobei m (0) in Bits M [0: 1] codiert ist, m (1) in Bits M [2: 3] codiert ist und m (n) in Bits codiert ist M [n * 2: n * 2 + 1].
Alles, was bleibt, ist das Indizieren und Zurückgeben des rechten Teils der Konstante. In diesem Fall können Sie M 2 * N-mal nach rechts verschieben und die 2 niedrigstwertigen Bits nehmen, dh (M >> 2 * N) & 0x3. Die Ausdrücke (eins << 3) und (zwei << 1) multiplizieren nur die Dinge, während festgestellt wird, dass 2 * x = x << 1 und 8 * x = x << 3.
quelle
Ich mag keine der vorgestellten Lösungen außer JABs. Keiner der anderen macht es einfach, den Code zu lesen und zu verstehen, was berechnet wird .
So würde ich diesen Code schreiben - ich kenne nur C #, nicht Java, aber Sie bekommen das Bild:
Jetzt ist viel klarer, was hier berechnet wird: Dies unterstreicht, dass wir berechnen, wer von welchem Angriff getroffen wird, und beide Ergebnisse zurückgeben.
Dies könnte jedoch noch besser sein; das Boolesche Array ist etwas undurchsichtig. Ich mag den Table-Lookup-Ansatz, aber ich würde ihn gerne so schreiben, dass klar wird, wie die Semantik des Spiels aussehen soll. Das heißt, anstatt "ein Angriff von Null und eine Verteidigung von Eins führt zu keinem Treffer", finden Sie stattdessen einen Weg, den Code deutlicher zu machen, was impliziert, dass "ein Angriff mit geringem Tritt und eine Verteidigung mit niedrigem Block zu keinem Treffer führen". Stellen Sie sicher, dass der Code die Geschäftslogik des Spiels widerspiegelt.
quelle
Sie können eine Matrix erstellen, die Ergebnisse enthält
Wenn Sie Wert erhalten möchten, werden Sie verwenden
quelle
Andere Leute haben bereits meine ursprüngliche Idee, die Matrixmethode, vorgeschlagen, aber zusätzlich zur Konsolidierung der if-Anweisungen können Sie einige Ihrer Probleme vermeiden, indem Sie sicherstellen, dass die angegebenen Argumente im erwarteten Bereich liegen, und indem Sie direkte Rückgaben verwenden (einige Codierungen) Standards, bei denen ich gesehen habe, dass One-Point-of-Exit für Funktionen erzwungen wird, aber ich habe festgestellt, dass Mehrfachrückgaben sehr nützlich sind, um die Pfeilcodierung zu vermeiden, und angesichts der Verbreitung von Ausnahmen in Java macht es ohnehin nicht viel Sinn, eine solche Regel strikt durchzusetzen da jede nicht erfasste Ausnahme, die innerhalb der Methode ausgelöst wird, ohnehin ein möglicher Austrittspunkt ist). Das Verschachteln von switch-Anweisungen ist eine Möglichkeit, aber für den kleinen Wertebereich, den Sie hier überprüfen, finde ich, dass Anweisungen kompakter sind und wahrscheinlich nicht zu einem großen Leistungsunterschied führen.
Dies ist letztendlich weniger lesbar als es sonst aufgrund der Unregelmäßigkeit von Teilen der Eingabe-> Ergebniszuordnung sein könnte. Ich bevorzuge den Matrixstil stattdessen aufgrund seiner Einfachheit und der Art und Weise, wie Sie die Matrix so einrichten können, dass sie visuell sinnvoll ist (obwohl dies teilweise durch meine Erinnerungen an Karnaugh-Karten beeinflusst wird):
Update: In Anbetracht Ihrer Erwähnung von Blockieren / Schlagen ist hier eine radikalere Änderung der Funktion, die aufgezählte Typen mit Eigenschaften / Attributen für Eingaben und das Ergebnis verwendet und das Ergebnis ein wenig ändert, um das Blockieren zu berücksichtigen, was zu mehr führen sollte lesbare Funktion.
Sie müssen nicht einmal die Funktion selbst ändern, wenn Sie Blöcke / Angriffe mit mehr Höhen hinzufügen möchten, nur die Aufzählungen. Das Hinzufügen zusätzlicher Arten von Zügen erfordert jedoch wahrscheinlich eine Änderung der Funktion. Außerdem ist
EnumSet
s möglicherweise erweiterbarer als die Verwendung zusätzlicher Aufzählungen als Eigenschaften der Hauptaufzählung, z. B.EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);
und dannattacks.contains(move)
eher alsmove.type == MoveType.ATTACK
, obwohl die Verwendung vonEnumSet
s wahrscheinlich etwas langsamer ist als direkte Gleichheitsprüfungen.Für den Fall, dass ein erfolgreicher Block in einem Zähler ergibt, können Sie ersetzen
if (one.height == two.height) return LandedHit.NEITHER;
mitDas Ersetzen einiger
if
Anweisungen durch die Verwendung des ternären Operators (boolean_expression ? result_if_true : result_if_false
) könnte den Code kompakter machen (zum Beispiel würde der Code im vorhergehenden Block werdenreturn one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
), aber das kann zu schwer lesbaren Oneliner führen, so dass ich es nicht tun würde. Ich empfehle es für komplexere Verzweigungen.quelle
one
undtwo
als Startpunkte auf meinem spritesheet wiederverwendet werden. Obwohl es nicht viel zusätzlichen Code erfordert, um dies zu ermöglichen.EnumMap
Klasse, mit der Sie die Aufzählungen Ihren ganzzahligen Offsets zuordnen können (Sie können auch die Ordnungswerte der Aufzählungselemente direkt verwenden, z. B.Move.ATTACK_HIGH.ordinal()
wäre0
,Move.ATTACK_LOW.ordinal()
wäre1
usw.), aber das ist fragiler / weniger flexibel als explizit Das Zuordnen jedes Mitglieds zu einem Wert als Hinzufügen von Aufzählungswerten zwischen vorhandenen Werten würde die Zählung beeinträchtigen, was bei einem nicht der Fall wäreEnumMap
.)attack(against)
derMove
Aufzählung sogar eine Methode hinzufügen , die HIT zurückgibt, wenn der Zug ein erfolgreicher Angriff ist, BACKFIRE, wenn der Zug ein blockierter Angriff ist, und NICHTS, wenn es kein Angriff ist. Auf diese Weise können Sie es allgemein implementieren (public boolean attack(Move other) { if this.isAttack() return (other.isAttack() || other.height != this.height) ? HIT : BACKFIRE; return NOTHING; }
) und es bei Bedarf bei bestimmten Zügen überschreiben (schwache Züge, die jeder Block blockieren kann, Angriffe, die niemals nach hinten losgehen usw.)Warum nicht ein Array verwenden?
Ich werde von vorne beginnen. Ich sehe ein Muster, die Werte gehen von 0 bis 3 und Sie möchten alle möglichen Werte abfangen. Das ist dein Tisch:
Wenn wir uns dieselbe Tabellenbinärdatei ansehen, sehen wir die folgenden Ergebnisse:
Vielleicht sehen Sie bereits ein Muster, aber wenn ich den Wert eins und zwei kombiniere, sehe ich, dass Sie alle Werte 0000, 0001, 0010, ..... 1110 und 1111 verwenden. Nun kombinieren wir den Wert eins und zwei, um ein einziges zu erstellen 4-Bit-Ganzzahl.
Wenn wir dies zurück in Dezimalwerte übersetzen, sehen wir ein sehr mögliches Array von Werten, bei denen eins und zwei zusammen als Index verwendet werden können:
Das Array ist dann
{0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3}
, wo sein Index einfach eins und zwei kombiniert ist.Ich bin kein Java-Programmierer, aber Sie können alle if-Anweisungen loswerden und sie einfach so aufschreiben:
Ich weiß nicht, ob eine Bitverschiebung um 2 schneller ist als eine Multiplikation. Aber es könnte einen Versuch wert sein.
quelle
Dies verwendet ein wenig Bitmagie (Sie tun dies bereits, indem Sie zwei Informationsbits (niedrig / hoch & Angriff / Block) in einer einzigen Ganzzahl speichern):
Ich habe es nicht ausgeführt, sondern nur hier eingegeben. Bitte überprüfen Sie es noch einmal.Die Idee funktioniert sicherlich. EDIT: Es wird jetzt für jede Eingabe getestet, funktioniert gut.Oder sollte ich vorschlagen, die beiden Informationsbits in separate Variablen zu trennen? Code, der hauptsächlich auf Bitoperationen wie der oben genannten basiert, ist normalerweise sehr schwer zu pflegen.
quelle
return ((one ^ two) & 2) == 0 ? (one & 2) / 2 * 3 : ((one & 2) / 2 ^ ((one ^ two) & 1)) + 1;
Um ganz ehrlich zu sein, hat jeder seinen eigenen Codestil. Ich hätte nicht gedacht, dass die Leistung zu stark beeinträchtigt würde. Wenn Sie dies besser verstehen als die Verwendung einer Switch-Case-Version, verwenden Sie diese weiterhin.
Sie könnten die ifs verschachteln, sodass sich möglicherweise eine leichte Leistungssteigerung für Ihre letzten if-Prüfungen ergeben würde, da nicht so viele if-Anweisungen durchlaufen worden wären. Aber in Ihrem Kontext eines Java-Grundkurses wird es wahrscheinlich keinen Nutzen bringen.
Also, anstatt ...
Du würdest tun ...
Und formatieren Sie es einfach neu, wie Sie es bevorzugen.
Dies lässt den Code nicht besser aussehen, beschleunigt ihn aber möglicherweise ein wenig, glaube ich.
quelle
Mal sehen, was wir wissen
1: Ihre Antworten sind symmetrisch für P1 (Spieler eins) und P2 (Spieler zwei). Dies ist für ein Kampfspiel sinnvoll, kann aber auch genutzt werden, um Ihre Logik zu verbessern.
2: 3 Schläge 0 Schläge 2 Schläge 1 Schläge 3. Die einzigen Fälle, die von diesen Fällen nicht abgedeckt werden, sind Kombinationen von 0 gegen 1 und 2 gegen 3. Anders ausgedrückt sieht die einzigartige Siegertabelle folgendermaßen aus: 0 Schläge 2, 1 Schläge 3, 2 Schläge 1, 3 Schläge 0.
3: Wenn 0/1 gegeneinander antreten, gibt es ein unentschiedenes Unentschieden, aber wenn 2/3 gegeneinander antreten, treffen beide
Lassen Sie uns zunächst eine Einwegfunktion erstellen, die uns sagt, ob wir gewonnen haben:
Mit dieser Funktion können wir dann das Endergebnis zusammenstellen:
Obwohl dies wohl komplexer und wahrscheinlich langsamer ist als die in vielen Antworten angebotene Tabellensuche, halte ich es für eine überlegene Methode, da sie tatsächlich die Logik Ihres Codes kapselt und sie jedem beschreibt, der Ihren Code liest. Ich denke, das macht es zu einer besseren Implementierung.
(Es ist schon eine Weile her, dass ich Java gemacht habe, also entschuldige mich, wenn die Syntax nicht stimmt. Hoffentlich ist es immer noch verständlich, wenn ich etwas falsch verstanden habe.)
0-3 bedeutet übrigens eindeutig etwas; Sie sind keine willkürlichen Werte, daher wäre es hilfreich, sie zu benennen.
quelle
Ich hoffe ich verstehe die Logik richtig. Wie wäre es mit so etwas wie:
Das Aktivieren eines Treffers hoch oder eines Treffers niedrig ist nicht blockiert und das Gleiche gilt für Spieler zwei.
Bearbeiten: Algorithmus wurde nicht vollständig verstanden, "Treffer" beim Blockieren vergeben, was ich nicht realisiert habe (Thx elias):
quelle
Ich habe keine Erfahrung mit Java, daher kann es zu Tippfehlern kommen. Bitte betrachten Sie den Code als Pseudocode.
Ich würde mit einem einfachen Schalter gehen. Dafür benötigen Sie eine einzelne Zahlenbewertung. In diesem Fall können wir jedoch seit
0 <= one < 4 <= 9
und0 <= two < 4 <= 9
beide Ints in ein einfaches int konvertieren, indem wirone
mit 10 multiplizieren und addierentwo
. Verwenden Sie dann einen Schalter in der resultierenden Zahl wie folgt:Es gibt noch eine andere kurze Methode, auf die ich nur als theoretischen Code hinweisen möchte. Ich würde es jedoch nicht verwenden, da es eine zusätzliche Komplexität aufweist, mit der Sie normalerweise nicht umgehen möchten. Die zusätzliche Komplexität ergibt sich aus der Basis 4 , da die Zählung 0, 1, 2, 3, 10, 11, 12, 13, 20, ... ist.
Wirklich nur ein zusätzlicher Hinweis, falls mir etwas in Java fehlt. In PHP würde ich tun:
quelle
Da Sie verschachtelte
if
Bedingungen bevorzugen , gibt es hier einen anderen Weg.Beachten Sie, dass das
result
Mitglied nicht verwendet wird und kein Status geändert wird.quelle
else
Ketten benutzen können, aber es hatte keinen Unterschied gemacht.else if
Anweisungen haben, können wir den Code mitswitch
oder Nachschlagetabellen beschleunigen .one==0
der Code ausgeführt wird, muss geprüft werden, obone==1
dann obone==2
und schließlich obone==3
- Wenn noch weitere if vorhanden wären, würden die letzten drei Überprüfungen nicht durchgeführt, da die Anweisung nach der ersten Übereinstimmung beendet würde. Und ja, Sie könnten weiter optimieren, indem Sie anstelle derif (one...
Anweisungen eine switch-Anweisung und dann im Fall von eine weitere switch-Anweisung verwendenone's
. Das ist jedoch nicht meine Frage.Versuchen Sie es mit Schaltergehäuse. ..
Schauen Sie hier oder hier nach, um weitere Informationen zu erhalten
Sie können mehrere Bedingungen (nicht gleichzeitig) hinzufügen und sogar eine Standardoption haben, bei der keine anderen Fälle erfüllt wurden.
PS: Nur wenn eine Bedingung erfüllt sein soll ..
Wenn 2 Bedingungen gleichzeitig auftreten, kann der Schalter nicht verwendet werden. Aber Sie können Ihren Code hier reduzieren.
Java-Switch-Anweisung mehrere Fälle
quelle
Das erste, was mir einfiel, war im Wesentlichen die gleiche Antwort von Francisco Presencia, aber etwas optimiert:
Sie können es weiter optimieren, indem Sie den letzten Fall (für 3) zum Standardfall machen:
Der Vorteil dieser Methode besteht darin, dass leichter zu erkennen ist, welche Werte für welche Rückgabewerte gelten
one
undtwo
welchen entsprechen, als bei einigen anderen vorgeschlagenen Methoden.quelle
quelle
; --unicorns
Sie können anstelle eines Mehrfachschalters einen Schalter verwenden
if
Um zu erwähnen, dass Sie, da Sie zwei Variablen haben, die beiden Variablen zusammenführen müssen, um sie im Schalter zu verwenden
Überprüfen Sie diese Java-switch-Anweisung, um zwei Variablen zu verarbeiten.
quelle
Wenn ich eine Tabelle zwischen eins / zwei und dem Ergebnis zeichne, sehe ich ein Muster:
Das Obige würde mindestens 3 if-Aussagen reduzieren. Ich sehe weder ein festgelegtes Muster noch kann ich viel aus dem angegebenen Code lernen - aber wenn eine solche Logik abgeleitet werden kann, würde dies eine Reihe von if-Anweisungen reduzieren.
Hoffe das hilft.
quelle
Ein guter Punkt wäre, die Regeln als Text zu definieren. Sie können dann leichter die richtige Formel ableiten. Dies wird aus Laaltos netter Array-Darstellung extrahiert:
Und hier gehen wir mit einigen allgemeinen Kommentaren, aber Sie sollten sie in Regelbegriffen beschreiben:
Sie könnten dies natürlich auf weniger Code reduzieren, aber es ist im Allgemeinen eine gute Idee, zu verstehen, was Sie codieren, anstatt eine kompakte Lösung zu finden.
Eine Erklärung zu den komplizierten p1 / p2-Treffern wäre toll, sieht interessant aus!
quelle
Die kürzeste und noch lesbare Lösung:
oder noch kürzer:
Enthält keine "magischen" Zahlen;) Hoffe es hilft.
quelle
static int val(int i, int u){ int q = (i & 1) ^ (u & 1); return ((i >> 1) << (1 ^ q))|((u >> 1) << q); }
quelle
Ich persönlich mag es, ternäre Operatoren zu kaskadieren:
In Ihrem Fall können Sie jedoch Folgendes verwenden:
Oder Sie können ein Muster in Bits bemerken:
So können Sie Magie verwenden:
quelle
Hier ist eine ziemlich prägnante Version, ähnlich der Antwort von JAB . Dabei wird eine Karte zum Speichern verwendet, die den Triumph über andere bewegt.
Beispiel:
Drucke:
quelle
static final Map<Move, List<Move>> beats = new java.util.EnumMap<>();
stattdessen empfehlen , es sollte etwas effizienter sein.Ich würde eine Map verwenden, entweder eine HashMap oder eine TreeMap
Vor allem, wenn die Parameter nicht im Formular enthalten sind
0 <= X < N
Wie eine Reihe von zufälligen positiven ganzen Zahlen.
Code
quelle
Vielen Dank an @Joe Harper, als ich eine Variation seiner Antwort verwendete. Um es weiter abzunehmen, da 2 Ergebnisse pro 4 gleich waren, habe ich es weiter abgenommen.
Ich werde vielleicht irgendwann darauf zurückkommen, aber wenn es keinen größeren Widerstand gibt, der durch mehrere
if
Aussagen verursacht wird, werde ich dies vorerst beibehalten. Ich werde mich weiter mit der Tabellenmatrix und den Switch-Anweisungslösungen befassen.quelle
Hier ist ein Vorschlag, wie dies aussehen könnte, aber die Verwendung eines Ints hier ist immer noch etwas hässlich:
Es wäre schöner, einen strukturierten Typ für die Eingabe und die Ausgabe zu verwenden. Die Eingabe hat tatsächlich zwei Felder: die Position und den Typ (Block oder Angriff). Die Ausgabe enthält außerdem zwei Felder: player1Wins und player2Wins. Das Codieren in eine einzelne Ganzzahl erschwert das Lesen des Codes.
Leider ist Java nicht sehr gut darin, solche Datentypen auszudrücken.
quelle
Mach stattdessen so etwas
quelle