J'ai écrit un jeu de tic-tac-toe en Java, et ma méthode actuelle de détermination de la fin du jeu tient compte des scénarios possibles suivants pour la fin du jeu:
- Le plateau est plein et aucun gagnant n'a encore été déclaré: le jeu est un match nul.
- Cross a gagné.
- Circle a gagné.
Malheureusement, pour ce faire, il lit un ensemble prédéfini de ces scénarios à partir d'une table. Ce n'est pas forcément mauvais étant donné qu'il n'y a que 9 espaces sur un plateau, et donc la table est un peu petite, mais y a-t-il une meilleure façon algorithmique de déterminer si le jeu est terminé? La détermination de savoir si quelqu'un a gagné ou non est la viande du problème, car vérifier si 9 espaces sont pleins est trivial.
La méthode de la table pourrait être la solution, mais sinon, quelle est-elle? Et si la planche n'était pas de taille n=9
? Et si elle était un conseil beaucoup plus, disons n=16
, n=25
et ainsi de suite, ce qui provoque le nombre d'éléments placés consécutivement à gagner à être x=4
, x=5
etc? Un algorithme général à utiliser pour tous n = { 9, 16, 25, 36 ... }
?
la source
3
). Ainsi, vous pouvez suivre les décomptes de chacun et ne commencer à vérifier les gains que s'ils sont plus élevés.Réponses:
Vous savez qu'un coup gagnant ne peut se produire qu'après que X ou O ait effectué son coup le plus récent, vous ne pouvez donc rechercher que la ligne / colonne avec un diag optionnel contenu dans ce coup pour limiter votre espace de recherche lorsque vous essayez de déterminer un tableau gagnant. De plus, comme il y a un nombre fixe de coups dans un jeu de tirage au sort tic-tac-toe une fois que le dernier coup est effectué s'il ne s'agissait pas d'un coup gagnant, il s'agit par défaut d'un jeu de tirage au sort.
edit: ce code est pour un tableau n par n avec n dans une rangée pour gagner (le tableau 3x3 nécessite 3 dans une rangée, etc.)
edit: code ajouté pour vérifier l'anti diag, je ne pouvais pas trouver un moyen sans boucle pour déterminer si le point était sur l'anti diag, c'est pourquoi cette étape est manquante
la source
vous pouvez utiliser un carré magique http://mathworld.wolfram.com/MagicSquare.html si une ligne, une colonne ou un diag ajoute jusqu'à 15, alors un joueur a gagné.
la source
Que diriez-vous de ce pseudocode:
Après qu'un joueur pose une pièce à la position (x, y):
J'utiliserais un tableau de char [n, n], avec O, X et espace pour vide.
la source
i==1
etn==3
,rdiag
doit être vérifié à(1, 3)
et(1, 3-1+1)
est égal aux coordonnées correctes, mais(1, 3-(1+1))
non.Ceci est similaire à la réponse d'Oussama ALASSIRY , mais il échange un espace constant et un temps linéaire contre un espace linéaire et un temps constant. Autrement dit, il n'y a pas de boucle après l'initialisation.
Initialisez une paire
(0,0)
pour chaque ligne, chaque colonne et les deux diagonales (diagonale et anti-diagonale). Ces paires représentent l'accumulation(sum,sum)
des pièces dans la ligne, la colonne ou la diagonale correspondante, oùLorsqu'un joueur place une pièce, mettez à jour la paire de lignes, la paire de colonnes et les paires diagonales correspondantes (si sur les diagonales). Si une ligne, une colonne ou une paire diagonale nouvellement mise à jour est égale à
(n,0)
ou(0,n)
alors A ou B a gagné, respectivement.Analyse asymptotique:
Pour l'utilisation de la mémoire, vous utilisez des
4*(n+1)
entiers.Exercice: Pouvez-vous voir comment tester un match nul en O (1) fois par coup? Si tel est le cas, vous pouvez terminer le jeu tôt sur un match nul.
la source
O(sqrt(n))
temps, mais doit être fait après chaque mouvement, où n est la taille du plateau. Alors vous vous retrouvez avecO(n^1.5)
. Pour cette solution, vous obtenez duO(n)
temps global.Voici ma solution que j'ai écrite pour un projet sur lequel je travaille en javascript. Si le coût de la mémoire de quelques baies ne vous dérange pas, c'est probablement la solution la plus rapide et la plus simple que vous trouverez. Cela suppose que vous connaissez la position du dernier coup.
la source
Je viens d'écrire ceci pour ma classe de programmation C.
Je le publie car aucun des autres exemples ici ne fonctionnera avec une grille rectangulaire de toute taille , et n'importe quel nombre de marques consécutives N -in-a-row pour gagner.
Vous trouverez mon algorithme, tel quel, dans la
checkWinner()
fonction. Il n'utilise pas de nombres magiques ou quoi que ce soit de fantaisie pour rechercher un gagnant, il utilise simplement quatre boucles for - Le code est bien commenté donc je vais le laisser parler de lui-même, je suppose.la source
Si la carte est n × n alors il y a n lignes, n colonnes et 2 diagonales. Vérifiez chacun de ceux-ci pour tous les X ou tous les O pour trouver un gagnant.
S'il ne faut que x < n carrés consécutifs pour gagner, c'est un peu plus compliqué. La solution la plus évidente est de vérifier chaque x × x carré pour un gagnant. Voici un code qui le démontre.
(Je n'ai pas réellement testé cette * toux *, mais elle s'est compilée du premier coup, yay moi!)
la source
Je ne connais pas très bien Java, mais je connais C, alors j'ai essayé l'idée de carré magique d'adk (avec la restriction de recherche de Hardwareguy ).
Il compile et teste bien.
C'était amusant, merci!
En fait, en y réfléchissant, vous n'avez pas besoin d'un carré magique, juste un décompte pour chaque ligne / colonne / diagonale. C'est un peu plus facile que de généraliser un carré magique aux matrices
n
×n
, car il suffit de compter jusqu'àn
.la source
On m'a posé la même question lors d'une de mes interviews. Mes pensées: Initialisez la matrice avec 0. Gardez 3 tableaux 1) sum_row (taille n) 2) sum_column (taille n) 3) diagonale (taille 2)
Pour chaque mouvement de (X), décrémentez la valeur de la boîte de 1 et pour chaque mouvement de (0), incrémentez-la de 1. À tout moment si la ligne / colonne / diagonale qui a été modifiée dans le mouvement actuel a une somme de -3 ou + 3 signifie que quelqu'un a gagné la partie. Pour un tirage au sort, nous pouvons utiliser l'approche ci-dessus pour conserver la variable moveCount.
Pensez-vous que je manque quelque chose?
Edit: la même chose peut être utilisée pour la matrice nxn. La somme doit être paire +3 ou -3.
la source
un moyen sans boucle pour déterminer si le point était sur l'anti-diag:
la source
Je suis en retard à la fête, mais je voulais souligner un avantage que j'ai trouvé à utiliser un carré magique , à savoir qu'il peut être utilisé pour obtenir une référence au carré qui causerait la victoire ou la perte au tour suivant, plutôt que juste utilisé pour calculer quand une partie est terminée.
Prenez ce carré magique:
Tout d'abord, configurez un
scores
tableau qui est incrémenté à chaque fois qu'un déplacement est effectué. Voir cette réponse pour plus de détails. Maintenant, si nous lisons illégalement X deux fois de suite à [0,0] et [0,1], alors lescores
tableau ressemble à ceci:Et le tableau ressemble à ceci:
Ensuite, tout ce que nous avons à faire pour obtenir une référence sur la case à gagner / bloquer est:
En réalité, la mise en œuvre nécessite quelques astuces supplémentaires, comme la gestion des touches numérotées (en JavaScript), mais je l'ai trouvée assez simple et j'ai apprécié les mathématiques récréatives.
la source
J'ai fait quelques optimisations dans les contrôles de ligne, de col et de diagonale. C'est principalement décidé dans la première boucle imbriquée si nous devons vérifier une colonne ou une diagonale particulière. On évite ainsi de vérifier les colonnes ou les diagonales pour gagner du temps. Cela a un impact important lorsque la taille de la carte est supérieure et qu'un nombre important de cellules ne sont pas remplies.
Voici le code java pour cela.
la source
J'aime cet algorithme car il utilise une représentation 1x9 vs 3x3 de la carte.
la source
Autre option: générer votre table avec du code. Jusqu'à la symétrie, il n'y a que trois façons de gagner: la rangée de bord, la rangée du milieu ou la diagonale. Prenez ces trois et faites-les tourner de toutes les manières possibles:
Ces symétries peuvent avoir plus d'utilisations dans votre code de jeu: si vous arrivez sur un tableau dont vous avez déjà vu une version pivotée, vous pouvez simplement prendre la valeur mise en cache ou le meilleur mouvement mis en cache à partir de celle-ci (et la annuler). C'est généralement beaucoup plus rapide que d'évaluer le sous-arbre du jeu.
(Le basculement à gauche et à droite peut aider de la même manière; ce n'était pas nécessaire ici car l'ensemble des rotations des motifs gagnants est symétrique en miroir.)
la source
Voici une solution que j'ai trouvée, cela stocke les symboles sous forme de caractères et utilise la valeur int du char pour déterminer si X ou O a gagné (regardez le code de l'arbitre)
Voici également mes tests unitaires pour valider que cela fonctionne réellement
Solution complète: https://github.com/nashjain/tictactoe/tree/master/java
la source
Que diriez-vous d'une approche suivante pour 9 emplacements? Déclarez 9 variables entières pour une matrice 3x3 (a1, a2 .... a9) où a1, a2, a3 représentent la ligne 1 et a1, a4, a7 formeraient la colonne 1 (vous voyez l'idée). Utilisez «1» pour indiquer le joueur 1 et «2» pour indiquer le joueur 2.
Il y a 8 combinaisons de gains possibles: Win-1: a1 + a2 + a3 (la réponse peut être 3 ou 6 selon le joueur qui a gagné) Win-2: a4 + a5 + a6 Win-3: a7 + a8 + a9 Win-4 : a1 + a4 + a7 .... Win-7: a1 + a5 + a9 Win-8: a3 + a5 + a7
Maintenant, nous savons que si le joueur 1 traverse a1, nous devons réévaluer la somme de 3 variables: Win-1, Win-4 et Win-7. Quel que soit «Win-?» variables atteint 3 ou 6 remporte la première partie. Si la variable Win-1 atteint d'abord 6, le joueur 2 gagne.
Je comprends que cette solution n'est pas évolutive facilement.
la source
C'est un moyen très simple de vérifier.
}
la source
Si vous avez le champ pensionnaire 5 * 5 pour exemple, j'ai utilisé la méthode suivante de vérification:
Je pense que c'est plus clair, mais ce n'est probablement pas le moyen le plus optimal.
la source
Voici ma solution en utilisant un tableau à 2 dimensions:
la source
Solution à temps constant, s'exécute en O (8).
Stockez l'état de la carte sous forme de nombre binaire. Le plus petit bit (2 ^ 0) est la rangée supérieure gauche de la carte. Ensuite, il va vers la droite, puis vers le bas.
C'EST À DIRE
Chaque joueur a son propre nombre binaire pour représenter l'état (car tic-tac-toe) a 3 états (X, O et vide) donc un seul nombre binaire ne fonctionnera pas pour représenter l'état du plateau pour plusieurs joueurs.
Par exemple, un tableau comme:
Notez que les bits pour le joueur X sont disjoints des bits pour le joueur O, c'est évident car X ne peut pas mettre un morceau où O a un morceau et vice versa.
Pour vérifier si un joueur a gagné, nous devons comparer toutes les positions couvertes par ce joueur à une position que nous savons être une position gagnante. Dans ce cas, le moyen le plus simple de le faire serait de passer ET de la position du joueur et de la position gagnante et de voir si les deux sont égaux.
par exemple.
Remarque:
X & W = W
donc X est dans un état gagnant.C'est une solution à temps constant, cela ne dépend que du nombre de positions gagnantes, car l'application de la porte ET est une opération à temps constant et le nombre de positions gagnantes est fini.
Cela simplifie également la tâche d'énumération de tous les états valides de la carte, leur juste tous les nombres représentables par 9 bits. Mais bien sûr, vous avez besoin d'une condition supplémentaire pour garantir qu'un numéro est un état de carte valide (par exemple.
0b111111111
est un nombre valide de 9 bits, mais ce n'est pas un état de carte valide car X vient de prendre tous les tours).Le nombre de positions gagnantes possibles peut être généré à la volée, mais les voici quand même.
Pour énumérer toutes les positions de la carte, vous pouvez exécuter la boucle suivante. Bien que je laisse à quelqu'un d'autre déterminer si un numéro est un état de carte valide.
REMARQUE: (2 ** 9 - 1) = (2 ** 8) + (2 ** 7) + (2 ** 6) + ... (2 ** 1) + (2 ** 0)
la source
Je ne sais pas si cette approche est encore publiée. Cela devrait fonctionner pour n'importe quel tableau m * n et un joueur est censé occuper une position consécutive " winnerPos ". L'idée est basée sur la fenêtre en cours d'exécution.
la source
J'ai développé un algorithme pour cela dans le cadre d'un projet scientifique une fois.
Vous divisez le plateau de manière récursive en un tas de rects 2x2 qui se chevauchent, en testant les différentes combinaisons possibles pour gagner sur un carré 2x2.
Il est lent, mais il a l'avantage de fonctionner sur n'importe quelle carte de taille, avec des besoins en mémoire assez linéaires.
J'aimerais pouvoir trouver ma mise en œuvre
la source