Eleganckie rozwiązanie do barwienia płytek szachowych

19

Ponownie opracowuję grę w szachy napisaną w Javie i zastanawiałem się, czy istnieje elegancki algorytm do kolorowania płytek szachowych na numerowanej szachownicy.

W tej chwili moje rozwiązanie używa instrukcji if else, aby ustalić, czy kafelek znajduje się w parzystym czy nieparzystym rzędzie, i na tej podstawie, czy powinien to być jasny czy ciemny kwadrat.

Amir Afghani
źródło
Dlaczego potrzebujesz bardziej eleganckiego algorytmu, aby zrobić coś tak podstawowego? Po prostu ciekawość czy ...?
ssb
5
Szczerze mówiąc, jestem po prostu ciekawy.
Amir Afghani,

Odpowiedzi:

40

Najbardziej elegancki sposób, jaki mogę wymyślić, biorąc pod uwagę, że masz wskaźniki rowi column, to:

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

lub odwrotnie:

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

Zasadniczo kafelek na szachownicy jest jasny wszędzie tam, gdzie kolumna i rząd są wzajemnie nieparzyste lub parzyste, a poza tym jest ciemny.

kevintodisco
źródło
4
Bardzo fajne rozwiązanie. Chociaż twój komentarz jest mylący: „płytka na szachownicy jest lekka wszędzie tam, gdzie kolumna i rząd są równe”. To nieprawda. Załóżmy, że wiersz to 3, a kolumna to 5 (oba są nierówne ) 3 % 2 == 1i 5 % 2 == 1.. więc oba są nierówne, ale będą miały kolor „jasny”. Nie mówię, że twoje rozwiązanie jest złe (jest dobre, ponieważ zmieni naprzemiennie wzór), ale twój komentarz / wyjaśnienie wydaje się błędny.
bummzack
Ups, dzięki za wykrycie tego @bummzack. Zaktualizowałem odpowiedź.
kevintodisco
Dobrym sposobem na określenie tego może być stwierdzenie, że płytka jest lekka, o ile jej współrzędne mają tę samą parzystość.
wer
34
bool isLight = ((row ^ column) & 1) == 0;

XOR razem indeksy wierszy i kolumn i spójrz na najmniej znaczący bit. Zmiana indeksu wiersza lub kolumny o jeden spowoduje odwrócenie wyniku, a zatem generuje wzorzec kontrolny.

Nathan Reed
źródło
5
^jest w porządku, ale +działa równie dobrze. :)
Chris Burt-Brown
2
Jeśli o to chodzi, to też -działa. :)
Trevor Powell,
2
Operacje bitowe ftw :)
Mike Cluck
3
wolę inne, ponieważ jest to „nieczytelne” (wiem, że operacje bitowe nie oznaczają, że można je utrzymać)
Matsemann
22

Kolejna sugestia, bardzo prosta:

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

Dodanie wiersza i kolumny daje liczbę kroków poziomych i pionowych od lewej górnej płytki.

Parzysta liczba kroków daje jasny kolor.
Dziwna liczba kroków daje ciemny kolor.

Chris Burt-Brown
źródło
Zasadniczo to samo, co odpowiedź Nathana, napisana inaczej.
API-Beast
@ Mr.Beast: & 1będzie znacznie wydajniejszy niż % 2, chyba że ten ostatni zostanie specjalnie zoptymalizowany. Ale generalnie się zgadzam.
LarsH,
1
@LarsH Kompilator zajmuje się takimi rzeczami (a przynajmniej powinien)
neeKo
@LarsH Dążyłem do czytelności, a nie prędkości. Ale nie ma w tym wiele. Nie jestem pewien, czy różnicę prędkości między tymi dwoma można uznać za „dużo”, kiedy wiemy, że będzie to wywoływane tylko 64 razy, i chciałbym myśleć, że nowoczesny kompilator i tak wygenerowałby identyczne pliki binarne.
Chris Burt-Brown,
@Chris: Mówiłem o wydajności operacji%, na którą nie ma wpływu liczba wywołań. Ale zgadzam się, że raczej nie będzie to miało praktycznej różnicy w szybkości programu, a także zgadzam się co do znaczenia czytelności w stosunku do potencjalnej poprawy prędkości.
LarsH,
4

Ten zakłada, że ​​nasze kwadraty są ponumerowane w zakresie [0..63].

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

Zrozumienie, dlaczego to działa, to połowa zabawy. :)

Trevor Powell
źródło
Ciekawe podejście Ale czy nie musisz coś robić z wartością zwracaną, aby uzyskać bool, np. return (i>>3 ^ i) & 1 != 0? Czy Java pozwala na niejawną konwersję liczby całkowitej na wartość logiczną?
LarsH,
Ach, masz rację; Przeczytałem prosto nad bitem „Java” i napisałem odpowiedź, myśląc o C ++. Edycja mojej odpowiedzi.
Trevor Powell,
To zdecydowanie najlepsza deska.
Marcks Thomas
1
To podejście przemawia do mnie tak samo, jak Perl do mnie. Tego rodzaju zwięzłe niezrozumienie zawsze sprawia przyjemność pisanie. Mniej zabawy w debugowaniu.
Trevor Powell,
2
  1. Numeruj kafelki. Możesz uzyskać te informacje, obliczając kolumnę wiersz * 8 + lub coś podobnego.

  2. Weź moduł 16 numeru siatki. (Przed powtórzeniem płytek jest 16 pozycji).

  3. Pokoloruj kafelek na podstawie tego, czy ma on liczbę parzystą czy nieparzystą. Odwróć kolor kafelka, jeśli wynik jest większy niż 7.

Kod indeksów zerowych:

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
źródło
Dlaczego wyróżniasz drugi rząd? Powinno to działać dla wszystkich 8 wierszy
Amir Afghani,
1
Nie rozumiem twojego pytania. modulus 16Działanie zmniejsza problem dwóch rzędach. Drugi rząd ma inny wzór niż pierwszy. ifSprawozdanie ocenia tylko prawda, jeśli jest to albo o numerach parzystych płytki XOR nie w drugim rzędzie. Jeśli oba są prawdziwe, wartość jest fałszywa. Przejrzyj operator XOR: msdn.microsoft.com/en-us/library/zkacc7k1.aspx
Jim
1
IsSecondRownaprawdę powinienem był nazwać IsEvenRow. Jest to dość skomplikowany sposób na uzyskanie niskiego bitu rzędu: najpierw przesuń bity pozycji rzędu 3 w prawo, następnie odrzuć wszystkie oprócz LSB rzędu, a następnie sprawdź, czy ustawiony jest 4. bit cellnum.
MSalters
Widzę. +1 za odpowiedź.
Amir Afghani,
Być może dobry przykład, dlaczego elegancja nie zawsze jest najlepszym rozwiązaniem. ;)
Jim
0

Chociaż takie podejście nie jest tak naprawdę konieczne w przypadku czegoś tak prostego jak szachownica, gdy myślę o eleganckim sposobie renderowania czegoś związanego z widokiem, chcę maksymalnie ułatwić zmianę renderowanego widoku. Załóżmy na przykład, że zdecydowałeś, że chcesz naprzemiennie czarno-biały w każdym rzędzie, ale nie w każdej kolumnie. Jednowierszowe używane do tej pory odpowiedzi musiałyby zostać przepisane.

Jeśli miałbym posunąć się tak daleko, jak to tylko możliwe, i maksymalnie ułatwić przeprojektowanie wzoru na szachownicy, oto co bym zrobił:

1) Zrobiłbym plik wskazujący, jaki kolor ma każdy kwadrat na szachownicy.

Na przykład mogę utworzyć plik, chess_board_pattern.configktóry wygląda mniej więcej tak:

bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb

2) Napisałbym klasę / komponent / cokolwiek, co może odczytać ten plik i stworzyć jakiś obiekt reprezentujący wzorzec płytki:

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) Użyłbym tej klasy w funkcji, która faktycznie rysuje planszę.

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]);
    }
}

Ponownie, jest to o wiele trudniejsze niż jest to konieczne dla szachownicy. Myślę jednak ogólnie, że pracując nad bardziej skomplikowanymi projektami, najlepiej jest wymyślić takie ogólne rozwiązania zamiast pisać kod, który później trudno zmienić.

Kevin
źródło
8
Powinieneś opublikować to na thedailywtf.com . :)
avakar
11
Nie dość przedsiębiorczy, potrzebuje więcej XML.
Maximus Minimus,
3
Witaj Kevin. Napisałeś, The one-liners used in answers so far would have to be re-written.ale także it's best to come up with generalized solutions like this instead of writing code that's difficult to change later.Ale musisz zrozumieć, że ten kod jest o wiele trudniejszy do zerwania i przepisania niż pojedynczy wiersz. Dlatego głosowałem za tobą, ponieważ nie jest to eleganckie ani wskazane.
Chris Burt-Brown,
1
+1 - Elegancja to nie tylko zwięzłość. Jeśli jednym z wymagań jest możliwość zmiany konfiguracji płyty, jest to dobra droga. Zrobiłem podobne rzeczy w niektórych programach układanek. Jednak nie spodziewałbym się, że program szachowy będzie miał ten wymóg. I nie zgodziłbym się, że uogólnione rozwiązania są zawsze najlepsze. Nie ma ŻADNEJ KOŃCA dla uogólnień, które mogłyby być wykonane, tak że nie można napisać Hello World bez implementacji parsera LALR i interpretera OpenGL. Kluczem jest wiedzieć, kiedy YAGNI.
LarsH,
2
Podoba mi się ta odpowiedź. To najbardziej elegancki sposób na zmaksymalizowanie zysków, jeśli naliczane są opłaty za godzinę!
Panda Pajama,