Solução elegante para colorir azulejos de xadrez

19

Estou re-desenvolvendo um jogo de xadrez que escrevi em Java e fiquei imaginando se existe um algoritmo elegante para colorir peças de xadrez em um tabuleiro de xadrez numerado.

No momento, minha solução usa instruções if else para determinar se o bloco está em uma linha par ou ímpar e, com base nisso, se deve ser um quadrado claro ou escuro.

Amir Afghani
fonte
Por que você precisa de um algoritmo mais elegante para fazer algo tão básico? Apenas curiosidade ou ...?
Ss12
5
Honestamente, estou apenas curioso.
Amir Afghani

Respostas:

40

A maneira mais elegante em que consigo pensar, considerando que você tem os índices rowe column, é a seguinte:

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

ou, inversamente:

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

Basicamente, um ladrilho em um tabuleiro de xadrez é leve onde quer que a coluna quanto a linha sejam mutuamente ímpares ou pares e, caso contrário, é escuro.

kevintodisco
fonte
4
Solução muito boa. Embora seu comentário seja enganador: "um bloco em um tabuleiro de xadrez é leve onde quer que a coluna e a linha sejam pares". Isso não é verdade .. assuma que a linha é 3 e a coluna é 5 (ambos são desiguais ) 3 % 2 == 1e 5 % 2 == 1.. então ambos são desiguais, mas serão coloridos como "claros". Não dizendo que sua solução está errada (é bom, pois alternará o padrão), mas seu comentário / explicação parece errado.
bummzack
Opa, obrigado por descobrir isso @bummzack. Atualizado a resposta.
Kevintodisco
Uma boa maneira de colocá-lo pode estar dizendo que um bloco é leve desde que suas coordenadas tenham a mesma paridade.
ver
34
bool isLight = ((row ^ column) & 1) == 0;

XOR juntos os índices de linha e coluna e observe o bit menos significativo. Alterar o índice de linha ou coluna por um irá inverter o resultado, portanto, gera um padrão de verificador.

Nathan Reed
fonte
5
^está bem, mas +funciona igualmente bem. :)
Chris Burt-Brown
2
Para esse assunto, -funciona também. :)
Trevor Powell
2
Bit operações ftw :)
Mike Cluck '
3
preferem os outros, como este é "ilegível" (eu sei operações de bit, não significa que de fácil manutenção)
Matsemann
22

Outra sugestão, muito direta:

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

Adicionar a linha e a coluna fornece o número de etapas horizontais e verticais do bloco superior esquerdo.

Um número par de etapas fornece cores claras.
Um número ímpar de etapas fornece cores escuras.

Chris Burt-Brown
fonte
Basicamente, o mesmo que a resposta de Nathan, apenas escrita de forma diferente.
API-Beast
@ Mr.Beast: & 1será muito mais eficiente do que % 2, a menos que o último seja especialmente otimizado. Mas, em geral, eu concordo.
LarsH #
1
@LarsH O compilador cuida daqueles tipo de coisas (ou, pelo menos, deveria)
Neeko
@LarsH Eu estava buscando legibilidade, não velocidade. Mas não há muito nisso. Não tenho certeza de que a diferença de velocidade entre as duas possa ser considerada "muito" quando sabemos que ela será chamada apenas 64 vezes, e eu gostaria de pensar que um compilador moderno geraria binários idênticos de qualquer maneira.
Chris Burt-Brown
@ Chris: Eu estava falando sobre a eficiência da operação%, que não é afetada pelo número de vezes que é chamada. Mas concordo que não é provável que faça uma diferença prática na velocidade do programa e também concordo com a importância da legibilidade em relação a possíveis melhorias na velocidade.
LarsH,
4

Este assume que nossos quadrados são numerados no intervalo [0..63].

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

Descobrir por que funciona é metade da diversão. :)

Trevor Powell
fonte
Abordagem interessante. Mas você não precisa fazer algo com o valor de retorno para obter um valor, por exemplo return (i>>3 ^ i) & 1 != 0? O java permite a conversão implícita de número inteiro em booleano?
LarsH
Ah, você está certo; Eu li diretamente o bit "Java" e escrevi a resposta pensando em C ++. Editando minha resposta.
Trevor Powell
Este é claramente o melhor conselho.
Marcks Thomas
1
Essa abordagem me atrai da mesma maneira que Perl me atrai. Esse tipo de incompreensibilidade sucinta é sempre divertido de se escrever. Menos divertido para depurar.
Trevor Powell
2
  1. Numere as peças. Você pode derivar essas informações calculando a linha * 8 + coluna ou algo semelhante.

  2. Tome o módulo 16 do número da grade. (Existem 16 posições antes da repetição das peças.)

  3. Pinte o ladrilho com base se ele tiver um número par ou ímpar. Vire a cor do ladrilho se o resultado for maior que 7.

Código para índices baseados em zero:

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
fonte
Por que você destaca a segunda linha? Isso deve funcionar para todos os 8 linhas
Amir Afghani
1
I don't understand your question. The modulus 16 operation reduces the problem to two rows. The second row follows a different pattern than the first. The if statement only evaluates to true if either it is an even-numbered tile XOR not in the second row. If both are true, it evaluates to false. Review the XOR operator: msdn.microsoft.com/en-us/library/zkacc7k1.aspx
Jim
1
IsSecondRow realmente deveria ter sido nomeado IsEvenRow. It's a rather convoluted way to get the low bit of row: first shift the bits of row 3 positions to the right, then discard all but the LSB of row, then check if the 4th bit of cellnum is set.
MSalters
Entendo. +1 para a resposta.
Amir Afghani
Talvez um bom exemplo de por que a elegância nem sempre seja a melhor solução. ;) #
11557 Jim
0

Embora essa abordagem não seja realmente necessária para algo tão simples quanto um tabuleiro de xadrez, quando penso em uma maneira elegante de renderizar algo relacionado à vista, desejo facilitar o máximo possível a alteração da exibição renderizada. Por exemplo, suponha que você tenha decidido alternar preto e branco em cada linha, mas não em cada coluna. Os one-liners usados ​​nas respostas até agora teriam que ser reescritos.

Se eu fosse o mais longe possível e tornasse mais fácil redesenhar o padrão possível no tabuleiro de xadrez, eis o que eu faria:

1) Eu faria um arquivo que indica qual a cor de cada quadrado no tabuleiro de xadrez.

Por exemplo, eu poderia criar um arquivo chess_board_pattern.configparecido com este:

bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb

2) Eu escreveria uma classe / componente / qualquer coisa que possa ler este arquivo e criaria algum tipo de objeto que represente o padrão do quadro:

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) Eu usaria essa classe na função que realmente chama a atenção.

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

Novamente, isso é muito mais difícil do que o necessário para um tabuleiro de xadrez. Penso que, em geral, ao trabalhar em projetos mais complicados, é melhor apresentar soluções generalizadas como essa, em vez de escrever código difícil de mudar mais tarde.

Kevin - Restabelecer Monica
fonte
8
Você deve postar isso em thedailywtf.com . :)
avakar
11
Não é suficiente empresa, precisa de mais XML.
Maximus Minimus
3
Olá Kevin. Você escreveu, The one-liners used in answers so far would have to be re-written.mas também it's best to come up with generalized solutions like this instead of writing code that's difficult to change later.Mas deve entender que esse código é muito mais difícil de desmontar e reescrever do que uma única linha. Por isso, diminuí o seu voto porque não é elegante ou aconselhável fazer isso.
Chris Burt-Brown
1
+1 - Elegância não é apenas por uma questão de brevidade. Se conseguir alterar as configurações da placa é um dos requisitos, é um bom caminho a percorrer. Eu fiz coisas semelhantes em alguns programas de quebra-cabeça. Eu não esperaria que um programa de xadrez tivesse esse requisito. E não concordo que as soluções generalizadas sejam sempre as melhores. Não há fim para generalizações que podem ser feitas, de modo que você não pode escrever o Hello World sem implementar um analisador LALR e um interpretador OpenGL. A chave é saber quando YAGNI.
Larsh
2
Eu gosto desta resposta. É a maneira mais elegante de maximizar seus lucros se você estiver sendo cobrado a cada hora!
Panda Pajama