Solution élégante pour colorer les tuiles d'échecs

19

Je suis en train de re-développer un jeu d'échecs que j'ai écrit en Java et je me demandais s'il y avait un algorithme élégant pour colorer les tuiles d'échecs sur un échiquier numéroté.

À l'heure actuelle, ma solution utilise les instructions if else pour déterminer si la tuile est sur une ligne paire ou impaire, et sur cette base, si elle doit être un carré clair ou sombre.

Amir Afghani
la source
Pourquoi avez-vous besoin d'un algorithme plus élégant pour faire quelque chose d'aussi basique? Juste de la curiosité ou ...?
ssb
5
Honnêtement, je suis juste curieux.
Amir Afghani

Réponses:

40

La façon la plus élégante à laquelle je peux penser, étant donné que vous avez les indices rowet column, est la suivante:

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

ou, inversement:

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

Fondamentalement, une tuile sur un échiquier est claire partout où la colonne et la ligne sont mutuellement impaires ou paires, et est sombre sinon.

kevintodisco
la source
4
Très belle solution. Bien que votre commentaire soit trompeur: "une tuile sur un échiquier est claire partout où la colonne et la ligne sont égales". Ce n'est pas vrai .. supposons que la ligne est 3 et la colonne est 5 (les deux sont inégales ) 3 % 2 == 1et 5 % 2 == 1.. donc les deux sont inégales mais seront colorées "claires". Ne pas dire que votre solution est mauvaise (c'est bien, car cela alternera le modèle) mais votre commentaire / explication semble faux.
bummzack
Oups, merci d'avoir repéré ce @bummzack. Mis à jour la réponse.
kevintodisco
Une bonne façon de le dire pourrait être de dire qu'une tuile est légère tant que ses coordonnées ont la même parité.
ver
34
bool isLight = ((row ^ column) & 1) == 0;

XOR ensemble les indices de ligne et de colonne et regardez le bit le moins significatif. La modification de l'index d'une ligne ou d'une colonne inversera le résultat, d'où la génération d'un motif de vérificateur.

Nathan Reed
la source
5
^est bien, mais +fonctionne aussi bien. :)
Chris Burt-Brown
2
D'ailleurs, ça -marche aussi. :)
Trevor Powell
2
Bit operations ftw :)
Mike Cluck
3
préférez les autres, car c'est "illisible" (je sais que les opérations sur bits ne signifient pas qu'il est maintenable)
Matsemann
22

Une autre suggestion, très simple:

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

L'ajout de la ligne et de la colonne donne le nombre de pas horizontaux et verticaux à l'écart de la tuile supérieure gauche.

Un nombre pair d'étapes donne une couleur claire.
Un nombre impair d'étapes donne une couleur sombre.

Chris Burt-Brown
la source
Fondamentalement, la même chose que la réponse de Nathan vient d'être écrite différemment.
API-Beast
@ Mr.Beast: & 1va être beaucoup plus efficace que % 2, sauf si ce dernier est spécialement optimisé. Mais en général, je suis d'accord.
LarsH
1
@LarsH Le compilateur s'occupe de ce genre de choses (ou du moins, il devrait)
neeKo
@LarsH Je visais la lisibilité, pas la vitesse. Mais il n'y a pas grand-chose dedans. Je ne suis pas sûr que la différence de vitesse entre les deux puisse être considérée "beaucoup" quand on sait qu'elle ne sera appelée que 64 fois, et j'aimerais penser qu'un compilateur moderne générerait de toute façon des binaires identiques.
Chris Burt-Brown
@Chris: Je parlais de l'efficacité de l'opération%, qui n'est pas affectée par le nombre de fois qu'elle est appelée. Mais je suis d'accord, il est peu probable que cela fasse une différence pratique dans la vitesse du programme, et je suis également d'accord sur l'importance de la lisibilité par rapport aux améliorations potentielles de la vitesse.
LarsH
4

Celui-ci suppose que nos carrés sont numérotés dans la plage [0..63].

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

Comprendre pourquoi cela fonctionne est la moitié du plaisir. :)

Trevor Powell
la source
Approche intéressante. Mais n'avez-vous pas à faire quelque chose à la valeur de retour pour la mettre dans un bool, par exemple return (i>>3 ^ i) & 1 != 0? Java permet-il la conversion implicite d'entier en booléen?
LarsH
Ah, tu as raison; J'ai lu directement sur le bit "Java" et j'ai écrit la réponse en pensant au C ++. Modification de ma réponse.
Trevor Powell
C'est clairement la meilleure planche.
Marcks Thomas
1
Cette approche me plaît de la même manière que Perl me plaît. Ce genre d'incompréhensibilité succincte est toujours amusant à écrire. Moins amusant à déboguer.
Trevor Powell
2
  1. Numérotez les tuiles. Vous pouvez obtenir ces informations en calculant la ligne * 8 + colonne ou quelque chose de similaire.

  2. Prenez le module 16 du numéro de grille. (Il y a 16 positions avant la répétition des tuiles.)

  3. Colorez la tuile en fonction de son nombre pair ou impair. Inversez la couleur de la tuile si le résultat est supérieur à 7.

Code pour les indices de base zéro:

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
la source
Pourquoi distinguez-vous la deuxième rangée? Cela devrait fonctionner pour les 8 lignes
Amir Afghani
1
Je ne comprends pas ta question. L' modulus 16opération réduit le problème à deux lignes. La deuxième ligne suit un modèle différent de la première. L' ifinstruction ne prend la valeur true que si c'est une tuile XOR de nombre pair ne se trouvant pas dans la deuxième ligne. Si les deux sont vrais, il est évalué comme faux. Examinez l'opérateur XOR: msdn.microsoft.com/en-us/library/zkacc7k1.aspx
Jim
1
IsSecondRowaurait vraiment dû être nommé IsEvenRow. C'est un moyen plutôt compliqué d'obtenir le bit de ligne le plus bas: déplacez d'abord les bits de la ligne 3 vers la droite, puis jetez tout sauf le LSB de la ligne, puis vérifiez si le 4e bit de numéro de cellule est défini.
MSalters
Je vois. +1 pour la réponse.
Amir Afghani
Peut-être un bon exemple de pourquoi l'élégance n'est pas toujours la meilleure solution. ;)
Jim
0

Bien que cette approche ne soit pas vraiment nécessaire pour quelque chose d'aussi simple qu'un échiquier, quand je pense à une manière élégante de rendre quelque chose lié à la vue, je veux rendre aussi simple que possible la modification de la vue rendue. Par exemple, supposons que vous ayez décidé d'alterner le noir et le blanc sur chaque ligne, mais pas sur chaque colonne. Les lignes simples utilisées jusqu'à présent dans les réponses devraient être réécrites.

Si je devais aller aussi loin que possible avec cela et rendre aussi facile que possible la refonte du motif sur l'échiquier, voici ce que je ferais:

1) Je créerais un fichier indiquant la couleur de chaque case de l'échiquier.

Par exemple, je pourrais créer un fichier chess_board_pattern.configqui ressemble à ceci:

bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb
bwbwbwbw
wbwbwbwb

2) J'écrirais une classe / composant / tout ce qui peut lire ce fichier et créer une sorte d'objet qui représente le modèle de carte:

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) J'utiliserais alors cette classe dans la fonction qui dessine réellement le tableau.

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

Encore une fois, c'est beaucoup plus difficile que nécessaire pour un échiquier. Je pense qu'en général, cependant, lorsque vous travaillez sur des projets plus compliqués, il est préférable de proposer des solutions généralisées comme celle-ci au lieu d'écrire du code difficile à modifier plus tard.

Kevin
la source
8
Vous devez poster ceci sur thedailywtf.com . :)
avakar
11
Pas assez d'entreprise, nécessite plus de XML.
Maximus Minimus
3
Bonjour, Kevin. Vous avez écrit The one-liners used in answers so far would have to be re-written.mais aussi it's best to come up with generalized solutions like this instead of writing code that's difficult to change later.Mais vous devez comprendre que ce code est beaucoup plus difficile à déchirer et à réécrire qu'une seule ligne. Je vous ai donc rétrogradé car il n'est ni élégant ni conseillé de le faire.
Chris Burt-Brown
1
+1 - L'élégance n'est pas seulement dans la brièveté. Si la possibilité de modifier les configurations de carte est l'une des exigences, c'est une bonne façon de procéder. J'ai fait des choses similaires dans certains programmes de puzzle. Je ne m'attendrais pas à ce qu'un programme d'échecs ait cette exigence. Et je ne suis pas d'accord pour dire que les solutions généralisées sont toujours les meilleures. Il n'y a PAS de FIN aux généralisations qui pourraient être faites, de sorte que vous ne pouvez pas écrire Hello World sans implémenter un analyseur LALR et un interpréteur OpenGL. La clé est de savoir quand YAGNI.
LarsH
2
J'aime cette réponse. C'est la façon la plus élégante de maximiser vos profits si vous êtes facturé à l'heure!
Panda Pyjama