La programmation des moteurs d'échecs est un territoire très compliqué, donc dès le départ, je vous pointerai vers le wiki de programmation des échecs , qui contient beaucoup d'informations sur ce sujet.
Contexte
Les calculs d'échecs (et bien d'autres choses similaires) sont généralement modélisés et considérés comme des «arbres de jeu» ou des « arbres de décision ». Globalement, cet arbre est un graphique dirigé, avec un nœud en haut (la position actuelle), menant à un nœud pour chaque mouvement possible, chacun conduisant à plus de nœuds pour chaque prochain mouvement possible , et ainsi de suite.
Dans leur forme de force brute la plus simpliste, les moteurs d'échecs génèrent toutes les positions sur cet arbre jusqu'à une certaine limite de profondeur ("ply"), évaluant chaque position résultante sur la base de certains critères complexes 1 . Ensuite, il joue le coup qui semble conduire au meilleur résultat. De nos jours, beaucoup de techniques vraiment compliquées ont été développées pour limiter le nombre de positions que le moteur doit regarder, mais je vais les ignorer pour cette réponse, car elles ne changent pas le vrai problème à main.
Math Tangent
La raison fondamentale pour laquelle les moteurs prennent généralement environ la même quantité de temps pour considérer chaque mouvement est que la taille de l'arbre de décision augmente de façon exponentielle avec depth ( k
).
Considérez la position de départ. Le sommet de l'arborescence ( k=0
) est un nœud. Il y a vingt premiers mouvements possibles pour les Blancs, il y a donc vingt nœuds en profondeur k=1
. Ensuite, les Noirs ont également vingt coups disponibles pour chacune des options des Blancs: donc k=2
, il y a 20 * 20 = 400
des positions possibles! Et cela ne fait qu'empirer à mesure que les joueurs développent leurs pièces!
Par exemple, supposons qu'il y ait toujours vingt coups possibles pour chaque joueur à un moment donné 2 . Vous demandez à l'ordinateur d'anticiper cinq coups pour chaque joueur (dix plis). Regardons la taille de l'arbre de force brute à chaque niveau. Pour le plaisir, nous allons également regarder le nombre total de positions dans l'arbre (du haut au niveau donné).
Ply | Positions | Total Tree Size
----------------------------------------
0 | 1 | 1
1 | 20 | 21
2 | 400 | 421
3 | 8000 | 8421
4 | 160000 | 168421
5 | 3200000 | 3368421
6 | 64000000 | 67368421
7 | 1280000000 | 1347368421
8 | 25600000000 | 26947368421
9 | 512000000000 | 538947368421
10 | 10240000000000 | 10778947368421
Le résultat de chaque niveau étant exponentiellement plus grand que le niveau précédent est que la taille de l'arbre entier est dominée par le niveau inférieur . Prenons l'exemple ci-dessus: le dernier niveau contient à lui seul dix mille milliards de nœuds. Le reste de l'arbre ne contient que cinq cent milliards. Le dixième pli contient environ 95% des nœuds de l'arbre entier (c'est en fait vrai à chaque niveau). En pratique, cela signifie que tout le temps de recherche est consacré à l'évaluation du "dernier" mouvement.
Répondre
Alors, comment cela se rapporte-t-il à votre question? Eh bien, disons que l'ordinateur est réglé sur dix plis, comme ci-dessus, et en outre, il "se souvient" des résultats de ses évaluations. Il calcule un coup, le joue, puis vous faites un coup. Maintenant, deux mouvements ont été effectués, donc il taille toutes les positions de la mémoire liées aux mouvements qui n'ont pas eu lieu, et il reste un arbre qui descend les huit mouvements restants qu'il a déjà calculés: 26 947 368 421 positions!
D'accord! Il nous suffit donc de calculer les deux derniers plis! En utilisant notre estimation de 20 mouvements à chaque profondeur, le nombre total de mouvements que nous devons calculer ici est toujours supérieur à 10 000 milliards. Les positions que nous avons déjà calculées ne représentent que 2,5% des possibilités! Donc, même en mettant en cache les résultats du dernier mouvement, le mieux que nous puissions espérer est une augmentation de 2,5% de la vitesse! Au fond, c'est pourquoi même si votre programme met en cache les résultats précédents, vous ne voyez généralement pas d'accélération significative entre les mouvements (sauf dans les cas où l'ordinateur trouve un partenaire forcé ou quelque chose, bien sûr!).
Clause de non-responsabilité relative à la simplification
Il y a beaucoup de complexité impliquée dans cette question, c'est pourquoi j'ai lié au wiki de programmation tout en haut et j'ai seulement essayé d'expliquer la réponse en termes mathématiques généraux. En réalité, les programmes mettent généralement en cache des parties de l'arbre d'un mouvement à l'autre, et il y a d'autres raisons pour lesquelles cela est insuffisant en soi - quelques raisons simples (par exemple, une certaine ligne peut sembler bonne à huit mouvements, mais se termine par un retour) -mate mate on move nine!) et de nombreux très compliqués (généralement liés à diverses méthodes d'élagage intelligentes). L'ordinateur doit donc continuer à regarder plus loin pour éviter de faire de mauvaises hypothèses en fonction de la profondeur de coupure du mouvement précédent.
1 Je ne vais pas entrer ici dans les fonctions heuristiques, car c'est sa propre zone incroyablement complexe, mais il y a souvent des gains qui peuvent également être obtenus via des schémas de mise en cache de position.
2 Un facteur de ramification moyen de 20 est probablement beaucoup trop faible .