Solución elegante para colorear fichas de ajedrez

19

Estoy volviendo a desarrollar un juego de ajedrez que escribí en Java, y me preguntaba si hay un algoritmo elegante para colorear fichas de ajedrez en un tablero de ajedrez numerado.

En este momento, mi solución usa declaraciones if else para determinar si el mosaico está en una fila par o impar, y en función de eso, si debería ser un cuadrado claro u oscuro.

Amir Afghani
fuente
¿Por qué necesitas un algoritmo más elegante para hacer algo tan básico? ¿Solo curiosidad o ...?
ssb
55
Sinceramente, solo tengo curiosidad.
Amir Afghani

Respuestas:

40

La forma más elegante que se me ocurre, dado que tiene los índices rowe column, es la siguiente:

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

o, por el contrario:

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

Básicamente, un mosaico en un tablero de ajedrez es claro donde tanto la columna como la fila son mutuamente impares o pares, y de lo contrario es oscuro.

kevintodisco
fuente
44
Muy buena solución. Aunque su comentario es engañoso: "un mosaico en un tablero de ajedrez es liviano donde sea que la columna y la fila sean iguales". Eso no es cierto ... suponga que la fila es 3 y la columna es 5 (ambas son desiguales ) 3 % 2 == 1y 5 % 2 == 1... entonces ambas son desiguales pero serán de color "claro". No digo que su solución es incorrecta (es buena, ya que alternará el patrón) pero su comentario / explicación parece incorrecta.
bummzack
Vaya, gracias por ver a ese @bummzack. Se actualizó la respuesta.
kevintodisco
Una buena manera de decirlo podría ser decir que un mosaico es ligero siempre que sus coordenadas tengan la misma paridad.
ver
34
bool isLight = ((row ^ column) & 1) == 0;

XOR juntos los índices de fila y columna y mira el bit menos significativo. Cambiar el índice de fila o columna por uno invertirá el resultado, por lo tanto, genera un patrón de verificación.

Nathan Reed
fuente
55
^está bien, pero +funciona igual de bien. :)
Chris Burt-Brown
2
Para el caso, también -funciona. :)
Trevor Powell
2
Bit operaciones ftw :)
Mike Cluck
3
prefiero los otros, ya que esto es "ilegible" (sé que las operaciones de bit no significa que sea mantenible)
Matsemann
22

Otra sugerencia, muy sencilla:

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

Agregar la fila y la columna da el número de pasos horizontales y verticales alejados del mosaico superior izquierdo.

Un número par de pasos da color claro.
Un número impar de pasos da color oscuro.

Chris Burt-Brown
fuente
Básicamente lo mismo que la respuesta de Nathan, escrita de manera diferente.
API-Beast
@ Mr.Beast: & 1va a ser mucho más eficiente que % 2, a menos que este último esté especialmente optimizado. Pero en general estoy de acuerdo.
LarsH
1
@LarsH El compilador se encarga de ese tipo de cosas (o al menos debería)
neeKo
@LarsH Estaba apuntando a la legibilidad, no a la velocidad. Pero no hay mucho en eso. No estoy seguro de que la diferencia de velocidad entre los dos pueda considerarse "mucho" cuando sabemos que solo se llamará 64 veces, y me gustaría pensar que un compilador moderno generaría binarios idénticos de todos modos.
Chris Burt-Brown
@ Chris: Estaba hablando de la eficiencia de la operación%, que no se ve afectada por la cantidad de veces que se llama. Pero estoy de acuerdo, no es probable que haga una diferencia práctica en la velocidad del programa, y ​​también estoy de acuerdo con la importancia de la legibilidad en relación con las posibles mejoras de velocidad.
LarsH
4

Éste supone que nuestros cuadrados están numerados en el rango [0..63].

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

Averiguar por qué funciona es la mitad de la diversión. :)

Trevor Powell
fuente
Enfoque interesante Pero, ¿no tiene que hacer algo con el valor de retorno para ponerlo en una bool, p return (i>>3 ^ i) & 1 != 0. ¿Java permite la conversión implícita de entero a booleano?
LarsH
Ah, tienes razón; Leí directamente sobre el bit "Java" y escribí la respuesta pensando en C ++. Editando mi respuesta.
Trevor Powell
Esta es claramente la mejor tabla.
Marca Thomas el
1
Este enfoque me atrae de la misma manera que Perl me atrae. Este tipo de incomprensibilidad sucinta siempre es divertido de escribir. Menos divertido de depurar.
Trevor Powell
2
  1. Numera las fichas. Podría derivar esta información calculando la fila * 8 + columna o algo similar.

  2. Tome el módulo 16 del número de cuadrícula. (Hay 16 posiciones antes de que se repitan las fichas).

  3. Colorea el mosaico en función de si tiene un número par o impar. Voltee el color del mosaico si el resultado es mayor que 7.

Código para índices de base cero:

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
fuente
¿Por qué seleccionas la segunda fila? Esto debería funcionar para las 8 filas
Amir Afghani
1
No entiendo tu pregunta. La modulus 16operación reduce el problema a dos filas. La segunda fila sigue un patrón diferente al primero. La ifdeclaración solo se evalúa como verdadera si se trata de un XOR de mosaico par y no en la segunda fila. Si ambos son verdaderos, se evalúa como falso. Revise el operador XOR: msdn.microsoft.com/en-us/library/zkacc7k1.aspx
Jim
1
IsSecondRowRealmente debería haber sido nombrado IsEvenRow. Es una forma bastante complicada de obtener el bit bajo de la fila: primero desplace los bits de las posiciones de la fila 3 a la derecha, luego descarte todos menos el LSB de la fila, luego verifique si el cuarto bit de cellnum está configurado.
MSalters
Veo. +1 por la respuesta.
Amir Afghani
Quizás un buen ejemplo de por qué la elegancia no siempre es la mejor solución. ;)
Jim
0

Aunque este enfoque no es realmente necesario para algo tan simple como un tablero de ajedrez, cuando pienso en una forma elegante de representar algo relacionado con la vista, quiero que sea lo más fácil posible cambiar la vista renderizada como sea posible. Por ejemplo, supongamos que decidió alternar blanco y negro en cada fila, pero no en cada columna. Las frases utilizadas en las respuestas hasta ahora tendrían que reescribirse.

Si tuviera que ir tan lejos como pudiera con esto y hacer que sea tan fácil rediseñar el patrón en el tablero de Ajedrez como sea posible, esto es lo que haría:

1) Haría un archivo que indica de qué color es cada cuadrado en el tablero de ajedrez.

Por ejemplo, podría hacer un archivo chess_board_pattern.configque se vea así:

bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb

2) Escribiría una clase / componente / lo que sea que pueda leer este archivo y crear algún tipo de objeto que represente el patrón del tablero:

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) Entonces usaría esa clase en la función que realmente dibuja el tablero.

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

De nuevo, esto es mucho más difícil de lo necesario para un tablero de ajedrez. Sin embargo, creo que, en general, cuando se trabaja en proyectos más complicados, es mejor encontrar soluciones generalizadas como esta en lugar de escribir código que es difícil de cambiar más adelante.

Kevin
fuente
8
Debe publicar esto en thedailywtf.com . :)
avakar
11
No es lo suficientemente empresarial, necesita más XML.
Maximus Minimus
3
Hola Kevin. Usted escribió, The one-liners used in answers so far would have to be re-written.pero también it's best to come up with generalized solutions like this instead of writing code that's difficult to change later.Pero debe comprender que este código es mucho más difícil de eliminar y reescribir que una sola línea. Así que te rechacé porque no es elegante ni aconsejable hacer esto.
Chris Burt-Brown
1
+1 - La elegancia no solo es breve. Si poder cambiar las configuraciones de la placa es uno de los requisitos, esta es una buena manera de hacerlo. He hecho cosas similares en algunos programas de rompecabezas. Sin embargo, no esperaría que un programa de ajedrez tuviera este requisito. Y no estaría de acuerdo en que las soluciones generalizadas sean siempre las mejores. No hay FIN para las generalizaciones que podrían hacerse, de modo que no pueda escribir Hello World sin implementar un analizador LALR y un intérprete OpenGL. La clave es saber cuándo YAGNI.
LarsH
2
Me gusta esta respuesta ¡Es la forma más elegante de maximizar sus ganancias si le facturan por hora!
Panda Pyjama