Elegante Lösung zum Ausmalen von Schachplättchen

19

Ich entwickle ein Schachspiel, das ich in Java geschrieben habe, neu und frage mich, ob es einen eleganten Algorithmus gibt, um Schachplättchen auf einem nummerierten Schachbrett zu färben.

Im Moment verwendet meine Lösung if else-Anweisungen, um zu bestimmen, ob sich die Kachel in einer geraden oder ungeraden Reihe befindet, und basierend darauf, ob es sich um ein helles oder dunkles Quadrat handeln soll.

Amir Afghani
quelle
Warum brauchen Sie einen eleganteren Algorithmus, um etwas so Grundlegendes zu tun? Nur Neugierde oder ...?
ssb
5
Ehrlich gesagt bin ich nur neugierig.
Amir Afghani

Antworten:

40

Die eleganteste Art und Weise, die mir in Anbetracht der Tatsache, dass Sie die Indizes rowund haben, in den columnSinn kommt, ist die folgende:

bool isLight = (row % 2) == (column % 2);

oder umgekehrt:

bool isDark = (row % 2) != (column % 2);

Grundsätzlich ist eine Kachel auf einem Schachbrett hell, wenn sowohl die Spalte als auch die Zeile gerade oder ungerade sind, und ansonsten dunkel.

kevintodisco
quelle
4
Sehr schöne Lösung. Obwohl Ihr Kommentar irreführend ist: "Eine Kachel auf einem Schachbrett ist überall dort hell, wo sowohl die Spalte als auch die Zeile gerade sind". Das ist nicht wahr. Angenommen, Zeile ist 3 und Spalte ist 5 (beide sind ungerade ) 3 % 2 == 1und 5 % 2 == 1. Also sind beide ungerade, aber sie haben die Farbe "hell". Nicht zu sagen, dass Ihre Lösung falsch ist (es ist gut, da es das Muster abwechselt), aber Ihr Kommentar / Ihre Erklärung scheint falsch zu sein.
Bummzack
Hoppla, danke, dass du diesen @bummzack entdeckt hast. Die Antwort wurde aktualisiert.
kevintodisco
Eine nette Art, es auszudrücken, könnte sein, dass ein Plättchen leicht ist, solange seine Koordinaten dieselbe Parität haben.
Ver
34
bool isLight = ((row ^ column) & 1) == 0;

XOR-Verknüpfung der Zeilen- und Spaltenindizes und Betrachtung des niedrigstwertigen Bits. Wenn Sie den Zeilen- oder Spaltenindex um eins ändern, wird das Ergebnis invertiert und ein Prüfmuster generiert.

Nathan Reed
quelle
5
^ist in Ordnung, +funktioniert aber genauso gut. :)
Chris Burt-Brown
2
Funktioniert übrigens -auch. :)
Trevor Powell
2
Bit-Operationen ftw :)
Mike Cluck
3
bevorzugen Sie die anderen, da dies "unlesbar" ist (ich weiß, Bit-Operationen, bedeutet nicht, es ist wartbar)
Matsemann
22

Ein weiterer Vorschlag, sehr einfach:

isLight = (row + column) % 2 == 0;

Durch Hinzufügen von Zeile und Spalte wird die Anzahl der horizontalen und vertikalen Schritte angegeben, die von der oberen linken Kachel entfernt sind.

Eine gerade Anzahl von Schritten ergibt eine helle Farbe.
Eine ungerade Anzahl von Schritten ergibt eine dunkle Farbe.

Chris Burt-Brown
quelle
Grundsätzlich das Gleiche wie Nathans Antwort, die nur anders geschrieben wurde.
API-Beast
@ Mr.Beast: & 1wird wesentlich effizienter als % 2, es sei denn, letzteres ist speziell optimiert. Aber im Allgemeinen stimme ich zu.
LarsH
1
@LarsH Der Compiler kümmert sich um solche Dinge (oder sollte es zumindest)
neeKo
@LarsH Ich wollte die Lesbarkeit, nicht die Geschwindigkeit. Aber da ist nicht viel drin. Ich bin mir nicht sicher, ob der Geschwindigkeitsunterschied zwischen den beiden als "viel" angesehen werden kann, wenn wir wissen, dass er nur 64-mal aufgerufen wird, und ich würde gerne glauben, dass ein moderner Compiler sowieso identische Binärdateien erzeugen würde.
Chris Burt-Brown
@ Chris: Ich habe über die Effizienz der% -Operation gesprochen, die nicht von der Anzahl der Aufrufe abhängt. Ich stimme jedoch zu, dass es wahrscheinlich keinen praktischen Unterschied in der Geschwindigkeit des Programms gibt, und ich stimme auch der Bedeutung der Lesbarkeit im Verhältnis zu möglichen Geschwindigkeitsverbesserungen zu.
LarsH
4

Diesmal wird davon ausgegangen, dass unsere Quadrate im Bereich [0..63] nummeriert sind.

bool IsLight(int i)
{
    return 0!=(i>>3^i)&1;
}

Herauszufinden, warum es funktioniert, ist der halbe Spaß. :)

Trevor Powell
quelle
Interessanter Ansatz. Aber müssen Sie nicht etwas mit dem Rückgabewert tun, um ihn in einen Bool zu bekommen, z return (i>>3 ^ i) & 1 != 0. Ermöglicht Java die implizite Konvertierung von Ganzzahlen in Boolesche Werte?
LarsH
Ah, du hast recht; Ich las direkt über das "Java" -Bit und schrieb die Antwort unter Berücksichtigung von C ++. Meine Antwort bearbeiten.
Trevor Powell,
Dies ist eindeutig das beste Board.
Marcks Thomas
1
Dieser Ansatz spricht mich genauso an wie Perl. Diese prägnante Unverständlichkeit macht immer Spaß zu schreiben. Weniger Spaß beim Debuggen.
Trevor Powell
2
  1. Nummerieren Sie die Kacheln. Sie können diese Informationen ableiten, indem Sie Zeile * 8 + Spalte oder ähnliches berechnen.

  2. Nehmen Sie Modul 16 der Gitternummer. (Es gibt 16 Positionen, bevor sich die Kacheln wiederholen.)

  3. Färben Sie die Kachel basierend darauf, ob sie eine gerade oder eine ungerade Zahl hat. Kippen Sie die Kachelfarbe, wenn das Ergebnis größer als 7 ist.

Code für nullbasierte Indizes:

int cellNum = (row*8+column) % 16;
bool isSecondRow = cellNum > 7;
if(cellNum % 2 == 0 ^ isSecondRow){ //XOR operator
    setColor(Color.White);
}else{
    setColor(Color.Charcoal);
}
Jim
quelle
Warum heben Sie die zweite Reihe hervor? Dies sollte für alle 8 Zeilen funktionieren
Amir Afghani
1
Ich verstehe deine Frage nicht. Die modulus 16Operation reduziert das Problem auf zwei Zeilen. Die zweite Reihe folgt einem anderen Muster als die erste. Die ifAnweisung wird nur dann als wahr ausgewertet, wenn es sich um eine gerade Kachel handelt, die sich nicht in der zweiten Zeile befindet. Wenn beides wahr ist, wird es als falsch gewertet. Überprüfen Sie den XOR-Operator: msdn.microsoft.com/en-us/library/zkacc7k1.aspx
Jim
1
IsSecondRowhätte wirklich genannt werden sollen IsEvenRow. Es ist eine ziemlich verworrene Methode, um das niedrige Bit der Reihe zu erhalten: Verschieben Sie zuerst die Bits der Reihe 3 nach rechts, verwerfen Sie dann alle bis auf das niedrigste Bit der Reihe und überprüfen Sie dann, ob das vierte Bit der Zellennummer gesetzt ist.
MSalters
Aha. +1 für die Antwort.
Amir Afghani
Vielleicht ein gutes Beispiel dafür, warum Eleganz nicht immer die beste Lösung ist. ;)
Jim
0

Obwohl dieser Ansatz für etwas so Einfaches wie ein Schachbrett nicht unbedingt erforderlich ist, möchte ich, wenn ich mir eine elegante Möglichkeit überlege, etwas mit der Ansicht zu rendern, es so einfach wie möglich machen, die gerenderte Ansicht so einfach wie möglich zu ändern. Angenommen, Sie möchten in jeder Zeile, jedoch nicht in jeder Spalte, Schwarz und Weiß abwechseln. Die bisher in Antworten verwendeten Einzeiler müssten neu geschrieben werden.

Wenn ich so weit wie möglich gehen und es so einfach wie möglich machen würde, das Muster auf dem Schachbrett neu zu gestalten, würde ich Folgendes tun:

1) Ich würde eine Datei erstellen, die angibt, welche Farbe jedes Quadrat im Schachbrett hat.

Zum Beispiel könnte ich eine Datei erstellen chess_board_pattern.config, die ungefähr so ​​aussieht:

bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb

2) Ich würde eine Klasse / Komponente / was auch immer schreiben, die diese Datei lesen und eine Art Objekt erstellen kann, das das Brettmuster darstellt:

public class BoardPattern {
    private Color[][] pattern;

    public BoardPattern(File patternFile)
    {
        pattern = new Color[8][8];
        //Parse the file and fill in the values of pattern
    }

    public Color[][] getPattern {
        return pattern;
    }
}

3) Ich würde dann diese Klasse in der Funktion verwenden, die die Tafel tatsächlich zeichnet.

File patternFile = new File("chess_board_pattern.ini");
Color[][] pattern = new BoardPattern(patternFile).getPattern();
ChessBoardDrawable chessBoard = new ChessBoardDrawable();

for(int row = 0; row < 8; row++) {
    for(int column; column < 8; column++) {
        chessBoard.drawSquare(row, column, Color[row][column]);
    }
}

Auch dies ist viel schwieriger als es für ein Schachbrett notwendig ist. Ich denke jedoch, dass es bei der Arbeit an komplizierteren Projekten am besten ist, allgemeine Lösungen wie diese zu finden, anstatt Code zu schreiben, der später nur schwer zu ändern ist.

Kevin - Setzen Sie Monica wieder ein
quelle
8
Sie sollten dies auf thedailywtf.com posten . :)
Avakar
11
Nicht unternehmerisch genug, braucht mehr XML.
Maximus Minimus
3
Hallo Kevin. Sie haben The one-liners used in answers so far would have to be re-written.aber auch geschrieben it's best to come up with generalized solutions like this instead of writing code that's difficult to change later.Aber Sie müssen verstehen, dass dieser Code viel schwieriger abzureißen und neu zu schreiben ist als eine einzelne Zeile. Also habe ich Sie herabgestimmt, weil es nicht elegant oder ratsam ist, dies zu tun.
Chris Burt-Brown
1
+1 - Eleganz ist nicht nur in der Kürze. Wenn die Möglichkeit besteht, die Platinenkonfiguration zu ändern, ist dies ein guter Weg. Ich habe ähnliche Dinge in einigen Puzzle-Programmen gemacht. Ich würde allerdings nicht erwarten, dass ein Schachprogramm diese Anforderung erfüllt. Und ich würde nicht zustimmen, dass verallgemeinerte Lösungen immer am besten sind. Es gibt KEIN ENDE für Verallgemeinerungen, die vorgenommen werden könnten, sodass Sie Hello World nicht schreiben können, ohne einen LALR-Parser und einen OpenGL-Interpreter zu implementieren. Der Schlüssel ist zu wissen, wann YAGNI.
LarsH
2
Ich mag diese Antwort. Dies ist die eleganteste Möglichkeit, Ihre Gewinne zu maximieren, wenn Sie stundenweise abgerechnet werden!
Panda Pyjama