ChessPro online

Шахматные программы. Общие вопросы

вернуться в форум

29.10.2007 | 19:10:30

Главная  -  Поговорим?  -  Железный марш

62

Rom77

29.05.2026 | 04:20:06

все его сообщения:
за день, за месяц,
за все время


*****
Перед тринадцатым шагом производится инициализация некоторых параметров, в том числе параметров для сортировки.

996 	MovePicker mp(pos, ttData.move, depth, &mainHistory, &lowPlyHistory, &captureHistory, contHist,

997 &sharedHistory, ss->ply);
999 value = bestValue;
1001 int moveCount = 0;

Step 13. Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs

На тринадцатом шаге запускается цикл генерации ходов (move = mp.next_move) из рассматриваемой позиции. По мере его выполнения будут рассмотрены все возможные ходы из позиции, если конечно в какой-то момент не произойдет отсечение и выход из цикла.

Ходы выдаются по одному за раз. Но пока они только определяются, а не выполняются на "внутренней доске" программы - эта операция будет произведена на 16 шаге. Выполнять ходы не нужно, поскольку программа сначала попытается их отсечь максимально быстро, используя только информацию о ходе. Перебор начнется позже.

Класс MovePicker, в том числе генератор ходов mp.next_move можно найти в файлах movepick. Как уже упоминалось ранее, для экономии ресурсов ходы генерируются "порционно", по стадиям сортировки.

1003 	// Step 13. Loop through all pseudo-legal moves until no moves remain

1004 // or a beta cutoff occurs.
1005 while ((move = mp.next_move()) != Move::none())
1006 {
... ...
1009 if (move == excludedMove)
1010 continue;

1012 // Check for legality
1013 if (!pos.legal(move))
1014 continue;
... ...
1022 ss->moveCount = ++moveCount;
... ...
1029 if (PvNode)
1030 (ss + 1)->pv = nullptr;

1032 extension = 0;
1033 capture = pos.capture_stage(move);
1034 movedPiece = pos.moved_piece(move);
1035 givesCheck = pos.gives_check(move);

1037 // Calculate new depth for this move
1038 newDepth = depth - 1;

Одновременно с генерацией производится сортировка ходов. Методы сортировки разобраны нами во II части. Стокфиш в основном использует те же самые методы, кроме киллеров. Многочисленные таблицы сортировки по истории вполне заменили их, и необходимость в киллерах просто отпала.

Таблицы истории вынесены в отдельный файл history.h. Сам процесс сортировки можно посмотреть в файле movepick.cpp.

Таблицы ButterflyHistory и PieceToHistory в принципе одинаковы и отражают классическое представление таблиц истории. Различия между ними только в характере записи ходов. Аналогично как в записи шахматной партии, в одном случае используется запись хода координатами начального и конечного полей, а в другом запись фигура - конечное поле.

Конечно сейчас система сортировок стала более разнообразной, чем в старых программах. Кроме обычных таблиц истории здесь накапливается статистика отдельно для пешечных структур PawnHistory, для перебора вблизи корня LowPlyHistory, для взятий CapturePieceToHistory и таблицы отражающие связь предшествующих и последующих ходов ContinuationHistory. PawnHistory определяет пешечные формации с помощью хеш-ключей, аналогично тому как поступают таблицы корректировки оценки (см. Step 6).

После определения хода, который будет рассматриваться далее, проверяется его корректность с помощью функции pos.legal(move). Дело в том, что генератор не всегда выдает корректные ходы с точки зрения правил. Поэтому ходы выходящие из под генератора ходов называют псевдолегальными.

Некоторые ходы слишком трудоемко полноценно вычислять на этапе генерации ходов. Например взятия на проходе или рокировки. Можно проверить их на корректность позже, когда они уже будут "заиграны". Проще сделать такой ход и проверить, подвергается ли король атаке. Аналогичным образом функция pos.legal(move) проверяет также ходы королем под шах и связки.

Таким образом сначала генерируются так называемые псевдолегальные ходы, а недопустимые среди них отсеиваются уже после генерации, по ходу цикла. Еще раз напомню, что функции начинающиеся с pos. можно найти в файле position.cpp
номер сообщения: 54-72-8289

63

Rom77

29.05.2026 | 04:21:24

все его сообщения:
за день, за месяц,
за все время
Step 14. Pruning at shallow depth

На четырнадцатом шаге снова выполняются отсечения на предельных глубинах (низких depth). Но на этот раз для их выполнения требуется знать ход в позиции. Генерация хода производилась на предыдущем шаге.

1049 	    // Step 14. Pruning at shallow depths.

1051 if (!rootNode && pos.non_pawn_material(us) && !is_loss(bestValue))
1052 {
1053 // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold
1054 if (moveCount >= (3 + depth * depth) / (2 - improving))
1055 mp.skip_quiet_moves();

1057 // Reduced depth of the next LMR search
1058 int lmrDepth = newDepth - r / 1024;

1060 if (capture || givesCheck)
1061 {
... ...
1081 }
1082 else
1083 {
1084 int history = (*contHist[0])[movedPiece][move.to_sq()]
1085 + (*contHist[1])[movedPiece][move.to_sq()]
1086 + sharedHistory.pawn_entry(pos)[movedPiece][move.to_sq()];

1088 // Continuation history based pruning
1089 if (history < -4083 * depth)
1090 continue;

1092 history += 69 * mainHistory[us][move.raw()] / 32;
1095 lmrDepth += history / 3208;
1097 Value futilityValue = ss->staticEval + 42 + 161 * !bestMove + 127 * lmrDepth
1098 + 85 * (ss->staticEval > alpha);

1100 // Futility pruning: parent node
1103 if (!ss->inCheck && lmrDepth < 13 && futilityValue < = alpha)
1104 {
1105 if (bestValue < = futilityValue && !is_decisive(bestValue)
1106 && !is_win(futilityValue))
1107 bestValue = futilityValue;
1108 continue;
1109 }

1111 lmrDepth = std::max(lmrDepth, 0);

1113 // Prune moves with negative SEE
1114 if (!pos.see_ge(move, -25 * lmrDepth * lmrDepth))
1115 continue;
1116 }
1117 }

Вблизи предельной/заданной глубины не обязательно отсекать только по оценке, как это делали Razoring и Futility Pruning ранее. Можно сформулировать правила отсечения и на основании иных принципов. Поэтому помимо Futility pruning здесь производятся отсечения поздних ходов по сортировке, ходов с плохой историей, отсечения по SEE.

Отсечения производятся раздельно для взятий и тихих ходов.
номер сообщения: 54-72-8290

64

Rom77

29.05.2026 | 04:22:44

все его сообщения:
за день, за месяц,
за все время
Step 15. Extensions

На пятнадцатом шаге мы обратимся к методам продления вариантов.

1119 	    // Step 15. Extensions

1120 // Singular extension search
1129 if (!rootNode && move == ttData.move && !excludedMove && depth >= 6 + ss->ttPv
1130 && is_valid(ttData.value) && !is_decisive(ttData.value) && (ttData.bound & BOUND_LOWER)
1131 && ttData.depth >= depth - 3 && !is_shuffling(move, ss, pos))
1132 {
1133 Value singularBeta = ttData.value - (53 + 75 * (ss->ttPv && !PvNode)) * depth / 60;
1134 Depth singularDepth = newDepth / 2;

1136 ss->excludedMove = move;
1137 value = search< NonPV>(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode);
1138 ss->excludedMove = Move::none();

1140 if (value < singularBeta)
1141 {
1142 int corrValAdj = std::abs(correctionValue) / 230673;
1143 int doubleMargin = -4 + 199 * PvNode - 201 * !ttCapture - corrValAdj
1144 - 897 * ttMoveHistory / 127649 - (ss->ply > rootDepth) * 42;
1145 int tripleMargin = 73 + 302 * PvNode - 248 * !ttCapture + 90 * ss->ttPv - corrValAdj
1146 - (ss->ply * 2 > rootDepth * 3) * 50;

1148 extension =
1149 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin);

1151 depth++;
1152 }

1154 // Multi-cut pruning
1160 else if (value >= beta && !is_decisive(value))
1161 {
1162 ttMoveHistory < < std::max(-400 - 100 * depth, -4000);
1163 return value;
1164 }

1166 // Negative extensions

1173 // If the ttMove is assumed to fail high over current beta
1174 else if (ttData.value >= beta)
1175 extension = -3;

1177 // If we are on a cutNode but the ttMove is not assumed to fail high over current beta
1179 else if (cutNode)
1180 extension = -2;
1181 }

При изложении различных методов отсечений и сокращений (reductions) в предыдущих частях не раз упоминались и продления (extensions). Но за исключением QS, который традиционно не принято к ним относить, ни одного примера продлений так и не было приведено. Тем не менее, хотя в программах продления и не играют решающей роли, они очень важны. Всегда полезно продлить определенную ветку, досчитаться до конкретных последствий некоторых ходов и тем самым уточнить ее оценку.

По тексту программы можно заметить, что небольшие продления вариантов периодически происходят то здесь, то там. Но единственным "глобальным" методом продлений, выделенным отдельным пунктом, являются Singular Extensions - продления единственных ходов.

Те, кому приходилось играть против обычных шахматных программ, знают что они могут быть чрезвычайно изворотливы, особенно если попадают в сложную ситуацию. Раз за разом они отыскивают неочевидные ходы, которые только на время отодвигают материальные потери. Иногда даже чрезмерно отодвигают (так называемый "эффект горизонта"). Но против человека такой подход иногда срабатывает.

Если программа считает на фиксированную глубину, то она может неадекватно высоко оценивать такие варианты, тогда как это всего лишь отсрочка неизбежного. Поэтому логично их немного продлить, чтобы получить истинную оценку. Идея Singular Extensions заключается в продлении одного из ходов, если его оценка намного выше, чем у других из данной позиции. Тем более, что если такой ход единственный, то продлить его будет не слишком затратно.

Чтобы не производить лишнего перебора посмотрим в хеш-таблице, не отсекался ли по бете (BOUND_LOWER) данный ход из данной позиции ранее, с приличной глубиной. Если так, то его оценка должна быть высока. Исключаем его из рассмотрения (excludedMove) и проводим перебор с сокращенной глубиной и с понижением альфа-бета границ на определенную величину. Если отсечения не произошло, то наш исключенный ход был единственным с высокой оценкой и его можно продлить.

Напротив, если отсечение на предварительном переборе все же случилось, то значит мы имеем уже минимум два хода с высокой оценкой. В таком случае никаких продлений выполнять нельзя.

Здесь вступает в действие метод MultiCut, который наоборот, производит отсечение узла. Его основной принцип заключается в том, что мы по сути уже дважды отсекли этот узел на сокращенной глубине, сначала исключенным ходом из хеш-таблиц, затем вторым ходом на предварительном переборе. Если позиция отсекалась уже несколькими ходами на сокращенной глубине, то почти наверняка хотя бы один из таких ходов отсечет ее и на полной (заданной) глубине. Производим отсечение.

В этом отношении MultiCut очень похож на ProbCut и нулевой ход. Точно так же он отсекает по бете с перебором на сокращенную глубину, но с некоторым условием. Это условие у каждого метода разное - пропуск хода у Null Move, большой запас в превышении беты у ProbCut, и многократные отсечения у MultiCut.

Так как методы похожи, то и отсекают они примерно одни и те же узлы. Нулевой ход наиболее эффективен в шахматах, поэтому он выполняется раньше. Два других метода вынуждены отсекать уже на "очищенном" дереве и соответственно дают не слишком большую прибавку к силе игры, а потому имеют вспомогательный характер.

Если же и MultiCut не сработает, то продолжим далее. Теперь мы можем если не отсечь, то хотя бы сократить глубину перебора.

Таким образом, на самом деле 15-й шаг представляет собой очень гибкую систему, как продлений, так и сокращений. Действительно, если в позиции единственный ход с высокой оценкой, то его надо продлить. Но если таких ходов два или больше, то напротив, весь вариант надо отсекать, а если отсечение не проходит, то можно хотя бы подсократить перебор по ветке хода.
номер сообщения: 54-72-8291

65

Rom77

29.05.2026 | 04:24:31

все его сообщения:
за день, за месяц,
за все время
Step 16. Make the move

Здесь собственно делается ход. Позиция на "внутренней доске" программы меняется. Производится углубление по дереву вариантов. Попутно обновляется счетчик позиций.

1183 	    // Step 16. Make the move

1184 do_move(pos, move, st, givesCheck, ss);

Между 16 и 17 шагом, в зависимости от некоторых условий вводятся различные поправки (r) к глубине на основании типа и статистики узлов. Это дополнительные величины сокращений и продлений для хода из нашей позиции. Они потребуются в основном на следующем шаге, для LMR.

1037 	    // Calculate new depth for this move

1038 newDepth = depth - 1;
1040 int delta = beta - alpha;
1042 Depth r = reduction(improving, depth, moveCount, delta);
... ...
1186 // Add extension to new depth
1187 newDepth += extension;
... ...
1199 // Increase reduction for cut nodes
1200 if (cutNode)
1201 r += 3372 + 997 * !ttData.move;

1203 // Increase reduction if ttMove is a capture
1204 if (ttCapture)
1205 r += 1119;
... ...
1226 // Scale up reductions for expected ALL nodes
1227 if (allNode)
1228 r += r / (depth + 1);

Step 17. Late moves reduction (LMR)

Метод LMR описан во II и III частях. Согласно нему, на ходах после первого по сортировке (moveCount > 1) производится перебор на сокращенную глубину. При этом используется минимальное окно по методу PVS. Если альфа превышена, то для проверки корректности оценки перебор повторяется, уже на полную глубину.

Сокращения (r, reduction) для LMR вычисляются выше, отдельной функцией в строке 1042. В Стокфише при вычислении сокращений учитываются уже четыре параметра, а не один лишь номер хода (moveCount), как практиковалось в старых программах.

1230 	    // Step 17. Late moves reduction / extension (LMR)

1231 if (depth >= 2 && moveCount > 1)
1232 {
1238 Depth d = std::max(1, std::min(newDepth - r / 1024, newDepth + 2)) + PvNode;

1240 ss->reduction = newDepth - d;
1241 value = -search< NonPV>(pos, ss + 1, -(alpha + 1), -alpha, d, true);
1242 ss->reduction = 0;

1244 // Do a full-depth search when reduced LMR search fails high
1246 if (value > alpha)
1247 {
1248 // Adjust full-depth search based on LMR results - if the result was
1249 // good enough search deeper, if it was bad enough search shallower.
1250 const bool doDeeperSearch = d < newDepth && value > bestValue + 50;
1251 const bool doShallowerSearch = value < bestValue + 9;

1253 newDepth += doDeeperSearch - doShallowerSearch;

1255 if (newDepth > d)
1256 value = -search< NonPV>(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode);

1258 // Post LMR continuation history updates
1259 update_continuation_histories(ss, movedPiece, move.to_sq(), 1365);
1260 }
1261 }
номер сообщения: 54-72-8292

66

Rom77

29.05.2026 | 04:27:21

все его сообщения:
за день, за месяц,
за все время
Step 18. Full-depth search

Когда все предыдущие способы прекратить или сократить перебор исчерпаны, то выполняется основной перебор на заданную глубину. Если ход первый по сортировке (moveCount = 1) и является PV-node, он выполняется с полным альфа-бета окном. Для ходов после первого по сортировке выполняется перебор с минимальным окном. Аналогично предыдущему шагу, при превышении минимального окна, перебор может быть проведен повторно, с полным окном (см. PVS). При запуске задается тип узла < PV> или < NonPV>, Cut или All.

1263 	    // Step 18. Full-depth search when LMR is skipped

1264 else if (!PvNode || moveCount > 1)
1265 {
... ...
1270 // Note that if expected reduction is high, we reduce search depth here
1271 value = -search< NonPV>(pos, ss + 1, -(alpha + 1), -alpha,
1272 newDepth - (r > 3957) - (r > 5654 && newDepth > 2), !cutNode);
1273 }

1275 // For PV nodes only, do a full PV search on the first move or after a fail high,
1276 // otherwise let the parent node fail low with value < = alpha and try another move.
1277 if (PvNode && (moveCount == 1 || value > alpha))
1278 {
1279 (ss + 1)->pv = pv;
1280 (ss + 1)->pv[0] = Move::none();

1282 // Extend move from transposition table if we are about to dive into qsearch.
1283 // decisive score handling improves mate finding and retrograde analysis.
1284 if (move == ttData.move
1285 && ((is_valid(ttData.value) && is_decisive(ttData.value) && ttData.depth > 0)
1286 || ttData.depth > 1))
1287 newDepth = std::max(newDepth, 1);

1289 value = -search< PV>(pos, ss + 1, -beta, -alpha, newDepth, false);
1290 }

Step 19. Undo move

После того как ветка на предыдущем шаге исследована на заданную глубину, возвращаем ход назад. Поднимаемся выше по дереву вариантов.

1292 	    // Step 19. Undo move

1293 undo_move(pos, move);
номер сообщения: 54-72-8293

67

Rom77

29.05.2026 | 04:28:54

все его сообщения:
за день, за месяц,
за все время
Step 20. Check for a new best move

Здесь подводятся итоги основного перебора выполненного ранее, конечно если уже не произошло отсечения на одном из предыдущих шагов. Полученная оценка позиции последовательно сравнивается с оценкой текущего лучшего хода из позиции bestValue, альфой и бетой, по нарастающей.

Если оценка текущего рассматриваемого хода из позиции выше лучшей оценки, то значение лучшей оценки для позиции обновляется. Если выше альфы, то повышается альфа (кроме случая, когда превышена бета). Если выше беты, то происходит отсечение и выход из цикла - оставшиеся возможные ходы из позиции не рассматриваются.

1297 	    // Step 20. Check for a new best move

1298 // Finished searching the move. If a stop occurred, the return value of
1299 // the search cannot be trusted, and we return immediately without updating
1300 // best move, principal variation nor transposition table.
1301 if (threads.stop.load(std::memory_order_relaxed))
1302 return VALUE_ZERO;

1304 if (rootNode)
1305 {
... ...
1353 }

1355 // In case we have an alternative move equal in eval to the current bestmove,
1356 // promote it to bestmove by pretending it just exceeds alpha (but not beta).
1357 int inc = (value == bestValue && ss->ply + 2 >= rootDepth && (int(nodes) & 14) == 0
1358 && !is_win(std::abs(value) + 1));

1360 if (value + inc > bestValue)
1361 {
1362 bestValue = value;

1364 if (value + inc > alpha)
1365 {
1366 bestMove = move;

1368 if (PvNode && !rootNode) // Update pv even in fail-high case
1369 update_pv(ss->pv, move, (ss + 1)->pv);

1371 if (value >= beta)
1372 {

1374 ss->cutoffCnt += (extension < 2) || PvNode;
1375 assert(value >= beta); // Fail high
1376 break;
1377 }

1379 // Reduce other moves if we have found at least one score improvement
1380 if (depth > 2 && depth < 14 && !is_decisive(value))
1381 depth -= 2;
... ...
1384 alpha = value; // Update alpha! Always alpha < beta
1385 }
1386 }

После 20 шага цикл генерации и просмотра ходов из позиции, который начинался с 13 шага, заканчивается. Далее следует подведение итогов.

После 21 шага в основном обновляются хеш-таблицы и различная статистика по результатам прошедшего перебора. На этом основной перебор в данной позиции заканчивается. Функция search возвращает в дерево перебора оценку bestValue рассматриваемой позиции, предполагая некий лучший ход из нее, с перебором выполненным на заданную глубину.

Окончание следует…
номер сообщения: 54-72-8294

68

Rom77

02.06.2026 | 04:55:44

все его сообщения:
за день, за месяц,
за все время
Шахматные программы VIII. Заключение

Итак, мы подробно рассмотрели в статье, как работают важнейшие составляющие реальной шахматной программы по отдельности и вместе. Пора подвести некоторые итоги, сделать выводы, и попытаться оценить будущие перспективы.


Если попробовать измерить прогресс каждой важной составляющей движка по отдельности, за всю историю компьютерных шахмат, то можно предложить следующие, приблизительные (!) цифры:

- поиск по дереву вариантов (направленный перебор), начиная с полного перебора ~ 2000 пунктов;

- рукописная оценочная функция, считая от оценки только по материалу ~ 700 - 1000 пунктов;
- нейросетевая оценочная функция на базе NNUE, плюсом к ручной ~ 300 - 400 пунктов;
- железо, считая от машин 1960-х, производительностью до 1 млн. опер/сек ~ 1000 пунктов.

Прибавки от использования дебютных книг и эндшпильных баз можно считать незначительными и оценить их соответственно как 80 и 20 пунктов в прошлом, и около нуля сейчас.

Эти цифры приводятся только для ориентировки, чтобы было понятно "что почем". Хотя указанные составляющие сами по себе и малосвязаны, но они все-таки влияют друг на друга. Их нельзя использовать слишком прямолинейно и уж тем более механически складывать между собой. Для самостоятельной проверки данных вводных рекомендую обратиться к программе, которая предлагалась в конце IV части и ранним версиям Стокфиша.
номер сообщения: 54-72-8299

69

Rom77

02.06.2026 | 04:56:47

все его сообщения:
за день, за месяц,
за все время
*****
История компьютерных шахмат и соответственно совершенствования методов, началась в 50-е годы с попыток построить селективный перебор типа Б по Шеннону, с мощными отсечениями на базе шахматных принципов и мудрёной оценочной функцией.

Но шахматные программы "категории Б" так и не взлетели. Шахматные знания в виде определенных шахматных правил не получалось вносить, потому что в игре слишком много исключений из таких правил, а обобщения часто не применимы в конкретных ситуациях. В то же время одна - две серьезные ошибки обычно решают исход партии. Внесение изощренных знаний в оценочную функцию, тоже не приводило к улучшениям по причинам уже озвученным выше.

Имея высокую долю ошибки спекулятивных отсечений, программы слишком часто выбрасывали из дерева перебора очевидные варианты, которые легко опровергали все их глубокие построения в других ветках, тем самым делая их бессмысленными.

Действительно, если программа зевает каждым вторым ходом, то совершенно неважно насколько гениальны остальные ходы. Пусть бы даже их делал современный Стокфиш. Все равно программа проиграет.

Хуже всего, что даже если постепенно совершенствовать надежность каждого хода, допустим до 90 % (что само по себе большой прогресс) то в процессе партии, делая ход за ходом, программа все равно свои одну-две ошибки найдет и, так или иначе, проиграет. Таким образом в ранние годы никак не получалось добиться прогресса разработки, как бы ни старались авторы подобных "умных" программ. Но это отдельная и очень поучительная история.

Зато с середины 70-х (а кое-где и раньше) вперед продвинулись шахматные программы с так называемым перебором "на полную ширину". То есть программы отсекающие как правило без потерь, в основном альфа-бетой.

В последующие годы поверх альфа-беты "навесили" методы отсечений с потерями, но все же с высокой надежностью, на базе универсальных, уже не шахматных правил. Их мы как раз и рассматривали в статье.

Эффективность современных методов отсечений, помимо того что они реально полезны, подкрепляется сокращенным перебором. Когда-то резкое разрастание дерева с глубиной считалось препятствием. Но современные программы сумели обернуть это препятствие в свою пользу. Ведь верно и обратное - небольшое сокращение глубины перебора резко уменьшает число рассматриваемых позиций и соответственно требуемый вычислительный ресурс. В то же время надежность остается на уровне и потому в совокупности с хорошим методом, например нулевым ходом, обеспечит качество и надежность, аналогичное перебору на большую глубину.

Таким образом новые программы победили, потому что сначала они создали базу на отсечениях без потерь, и только потом, на этой базе стали добавлять эвристики, которые реально приносили пользу. При относительно медленном улучшении оценочной функции, прогресс перебора стал основным локомотивом продвижения шахматных программ вперед по шкале силы игры. Продолжается он и сейчас.
номер сообщения: 54-72-8300

70

Rom77

02.06.2026 | 04:58:01

все его сообщения:
за день, за месяц,
за все время
*****
Здесь неизбежно возникает вопрос - ну и к чему все это? Зачем еще больше улучшать шахматные программы, если уровень человека уже давно и намного превзойден? Какова цель этих усилий?

Но, честно сказать, уровень человека оказался довольно невысок. Это в прошлые времена он виделся как сияющая и недостижимая вершина. В наши дни для многих история компьютерных шахмат начинается уже с поражения Каспарова от Дип Блю. А для некоторых даже с появления программы Рыбка. А если кто и вспоминает что было до нее, то либо редко, либо очень кратко.

Здесь уместно вспомнить известное выражение, что у разработчиков "нет цели, только путь". Стремление к совершенству всегда достойно, даже если идеальная цель в виде решения шахмат недостижима. Основная задача сильнейших программ оставаться сильнейшими, тем самым открывая перед нами новые возможности.

На самом деле можно поставить перед собой более достижимую задачу. Можно ли решить шахматы в практическом смысле? Если бы можно было разыграть из какой-либо позиции партию и получить результат совпадающий с ее "решением" хотя бы в 95 % случаев, то на практике это нас бы вполне устроило.

Но не слишком ли это фантастические ожидания? Насколько далеко от нас подобное решение? Как узнать, совпадает ли оно с идеальным, ведь никаких 32-фигурных таблиц "окончаний" нет, и не предвидится? Такое "решение шахмат" тоже кажется малореальным, но на самом деле оно уже где-то просматривается, и в современных технических условиях не так уж невозможно, как кажется.

Здесь можно предложить кое-какие признаки, указывающие на возможный итог розыгрыша позиции. Начать с того, что на самом деле мы фактически знаем итог всех относительно сбалансированных позиций, включая начальную. Это ничья. То же самое можно сказать и про так называемые "выигранные позиции", оценка которых не вызывает сомнений. Это победа одной из сторон.

Современные программы на приличном железе и контроле, разыгрывая подобные позиции, практически всегда оканчивают их, соответственно, ничьей и победой. Так что для них мы можем утверждать, что эти позиции в практическом смысле уже решены.

В настоящее время остается только решить так называемые позиции "на два результата". Пласт таких позиций все еще огромен. Как узнать какое же истинное решение у каждой такой позиции?

От себя могу предложить по крайней мере один признак, который может свидетельствовать о приближении к идеалу. Это процент ничьих в парах партий из таких позиций.

Обычно чтобы обеспечить равные условия, данные позиции разыгрываются шахматными программами дважды, со сменой цвета. Если в каком-либо наборе таких позиций ничейность в парах партий превысит 90 - 95 % процентов случаев, то мы можем утверждать, что решение шахмат, по крайней мере в нашем определении, уже где-то не за горами. Иначе говоря, количество результатов 1,5 - 0,5 в парах партий должно быть меньше 5 %.

Лучше всего проводить такие тесты в наиболее тяжелых условиях, когда одному из соперников дается гораздо больше времени или, например, свести принципиально разные, но равные по силам программы. В первом случае можно проверить достаточна ли глубина, чтобы не было ошибок на основных ветках, а во втором, не много ли преждевременно отсеченных ходов-кандидатов на побочных.

Конечно сравнение результатов матчей старых и матчей новых программ обязательно должно производиться в тех же самых условиях контроля времени и набора позиций, поскольку эти стартовые условия сильно влияют на ничейность пар. Ускорение контроля и извлечение части позиций из набора может серьезно понизить процент ничейности.

К сожалению, а может быть к счастью, в настоящее время на практике процент ничьих в наборах стартовых позиций "на два результата" достигает всего 60 - 70 % и относительно идеальный результат все еще кажется недостижимым. Кроме того, если по мере совершенствования программ извлекать из подобного набора позиции, которые будут склоняться к победной или ничейной оценке, то такой процент можно поддерживать еще долго. По крайней мере, пока большая часть позиций не перейдут в статус определенных.

Тем не менее, при соблюдении равных условий тестов, важность самого показателя ничейности пар сохраняется, ведь доля позиций "на два результата" в общем числе возможных позиций в шахматах будет сокращаться.

Между тем, намечаются кое-какие технические тенденции, которые в итоге могут привести к искомому "практическому решению".
номер сообщения: 54-72-8301

71

Rom77

02.06.2026 | 04:59:33

все его сообщения:
за день, за месяц,
за все время


*****
Одна из ключевых причин быстрого роста программ в последние 15 - 20 лет, это эволюционный процесс совершенствования кода поиска. Накопление относительно небольших усовершенствований на протяжении одного - двух десятилетий, привело к огромному усилению программ – на 600 - 800 пунктов общей силы игры.

В первую очередь тому поспособствовали статистически достоверные тесты для проверки каждой вносимой идеи, важность которых первым понял наверное автор программы Рыбка в 2005 году.

Далее помогли оупенсорс и краудфандинг сообщества компьютерных шахмат, с которого стартовал проект Стокфиш в 2011-м. Они оказались сильнее креативных и материальных ресурсов частных коммерческих разработчиков (как уже упоминалось во вступлении, крупные компании видимо не особо привлекала емкость шахматного рынка, а только его потенциал общественной значимости).

Также некоторый вклад внесло определение действенного метода тюнинга параметров поиска - SPSA. И конечно широкое распространение метода распараллеливания LasySMP в 2014 - 2015 годах решило одну из ключевых проблем компьютерных шахмат.

Процесс постепенных улучшений поиска в движках продолжается и в наши дни. Все идеи в современных проектах проверяются на оселке опыта на тестовых платформах по типу OpenBench для шахмат.
номер сообщения: 54-72-8302

72

Rom77

02.06.2026 | 05:00:24

все его сообщения:
за день, за месяц,
за все время
*****
Что же можно ожидать в будущем? Какие пути совершенствования программ, помимо эволюционного прогресса поиска, просматриваются уже сейчас?

Начиная с 2017 года, новое видение перспектив оценки позиции и архитектуры всей шахматной программы предложили нейросети глубокого обучения Альфа Зеро и Лилы. А с 2020-го, в более практическом смысле для программ на классической базе, прорыв принесло появление неглубоких полносвязных сетей NNUE. Даже несмотря на свой относительно небольшой размер они позволили обычным программам шагнуть вперед. Но такие сети по-прежнему гораздо слабее сравнительно сетей Альфа Зеро и даже младших сетей Лилы.

Вынужденные использовать относительно слабые вычислительные возможности векторов современных центральных процессоров, сети NNUE производят обычно только около 200 тысяч математических операций для вычисления оценки отдельной позиции. В то же время сеть Альфа Зеро производила 1,5 миллиарда вычислений на позицию, используя TPU, а крупнейшая сеть Лилы производит их как минимум еще на порядок больше на тензорных ядрах современных видеокарт. Естественно, сама по себе оценка позиции из таких сетей в разы лучше.

А ведь всего несколько сантиметров отделяет классические программы от мощных вычислительных ресурсов матричных умножителей на современных GPU. Если бы не большая задержка передачи данных на видеокарту...

Но именно слабость нейросети NNUE оставляет нам надежду на огромный потенциал ее дальнейшего усовершенствования. Кроме эволюционных изменений в поиске, вполне возможны поистине революционные изменения во второй важной части шахматных программ на классической базе - в оценке. И на то в настоящее время появляются технические предпосылки.

Появление встроенных матричных вычислителей NPU в массовых моделях центральных процессоров с производительностью от 50 TOPS и выше, позволит резко увеличить возможные объемы вычислений на единицу времени непосредственно в CPU. При, можно надеяться, небольшой latency. Или все-таки реально уменьшение величины задержки передачи данных на различные внешние ускорители? По крайней мере на этот параметр сейчас обратили внимание производители соответствующего железа, в связи с резким ростом популярности мультиагентных систем.

Таким образом, уже существуют готовые процессорные архитектуры, которые позволят резко увеличить производительность CPU. Если/как только, они станут мейнстримом, мечты об идеальных шахматах могут реализоваться.

В свою очередь увеличение вычислительных возможностей позволит нарастить размеры нейросетей шахматных программ на классической базе до относительно нормальных размеров. А с ними резко подтянется и их общий уровень силы оценки, при сохранении скорости перебора таких программ. Можно надеяться, что уровень поднимется даже до более-менее сопоставимого с качеством оценки позиции младших нейросетей Лилы.

В то же время лучшие архитектуры сетей и методы их обучения уже известны, в том числе и применительно к шахматам. Сверточные, трансформерные и многие другие системы - все можно попробовать. Не исключены и различные гибридные сочетания с полносвязными сетями архитектуры NNUE, особенно на первых порах.

То есть здесь даже не надо каких-то особых алгоритмических прорывов, достаточно наладить взаимодействие с тем железом, которое уже существует и использовать те архитектуры сетей и методы обучения, которые уже известны.

Помимо всего прочего, при переходе на более глубокую сеть напрашивается использование выхода policy, который пока не применим на архитектуре NNUE, но с успехом используется Альфа Зеро и Лилой. Этот выход нейросети, кроме той оценки позиции, которую производит выход value, дополнительно дает ходы-кандидаты с оценкой их "перспективности".

Такие ходы можно с пользой применить при сортировке в классических программах вместо (а лучше вместе с) History. Так же их можно применить при распараллеливании для "отлавливания" ходов-кандидатов на ветках преждевременно отсеченных основным перебором, что крайне важно в современных условиях. Это дает альтернативный взгляд на ходы-кандидаты на рано отсекаемых побочных вариантах, чем занимается пока только History и, непроизвольно, несинхронизированная выборка потоков (см. соображения по распараллеливанию в VI части статьи).
номер сообщения: 54-72-8303

73

Rom77

02.06.2026 | 05:01:37

все его сообщения:
за день, за месяц,
за все время
*****
Между тем, мы так и не коснулись методов используемых Альфа Зеро и ее последователями. А все, что написано в данной статье, ни в одном месте не подходит для объяснения принципов работы таких архитектур. Поэтому тем, кто все же интересуется этой постепенно уходящей в прошлое программой, придется разбираться отдельно. Благо в интернете можно найти подробные блок-схемы, разъясняющие принцип ее работы.

Программа Лила (Leela Chess Zero, Lc0) значительно улучшила принципы работы Альфа Зеро и усилила всю систему, но желающих последовать ее примеру в топе программ по-прежнему очень немного. Пожалуй что, можно назвать еще программу Ceres, и все. Программисты движков, которые врываются в топ в настоящее время, предпочитают отталкиваться от классической базы.

Хотя у программ на базе Альфа Зеро наблюдается некоторый прогресс, тем не менее он вовсе не такой как хотелось бы. Так, в последнее время Лила начала отставать от Стокфиша и других движков. Старые организационные и технические проблемы разработки по-прежнему требуют разрешения. А потому перспективы всех подобных программ выглядят достаточно туманно.

Так или иначе, наверное не надо доказывать, как важно для компьютерных шахмат наличие программ на полностью альтернативной основе. Поэтому будем надеяться на какой-то прорыв и здесь.

Если вернуться обратно к перспективам обычных движков, то следует напротив, несколько поумерить пыл. Конечно, обозначенные выше варианты развития событий выглядят действительно интересно. Но следует все же иметь в виду некоторые моменты. Все те многообещающие возможности совсем не обязательно реализуются в ближайшем будущем, пусть они и напрашиваются. И вовсе не обязательно дадут реально полезный и применимый выход, хотя это и выглядит возможным.

Тем не менее, поживем и увидим. Возможно появится что-либо еще, какие-то методы или технологии, которые сейчас даже представить невозможно.

Таково состояние дел в области компьютерных шахмат. Еще десять с небольшим лет назад казалось, что ситуация будет развиваться по нисходящей, с постепенным затуханием прогресса. Но любые прогнозы имеют свойство не сбываться. Постоянно появляются новые, невиданные ранее сущности. Предсказать их появление трудно, но не смотря на заветы классиков, учитывать в прогнозах необходимо. Поэтому будем смотреть в будущее с оптимизмом. В общем, следите за новостями и не теряйте компьютерные шахматы из виду.

номер сообщения: 54-72-8304

74

Vizvezdenec

Ниже нуля
Севастополь

02.06.2026 | 14:47:08

все его сообщения:
за день, за месяц,
за все время
Даже Торд Ромстад, который, собственно, сделал Глаурунг, всегда думал, что прогресс сойдёт на нет. И вообще не думал, что программа такая опенсорсная сможет так высоко зайти, он скорее думал, что она сможет показать ценность кооперации и прочего.
Но вышло так, что она взлетела на самый топ, при этом состав разработчиков активный уже несколько раз поменялся, из ветеранов осталось всего ничего людей, и то "ветераны" эти уже времён первого фиштеста, то есть не основатели.
А проект всё живёт и всё развивается - и прогресс в целом идёт практически в одном и том же темпе, не считая мега-скачков вроде NNUE.
номер сообщения: 54-72-8305

75

Rom77

02.06.2026 | 20:11:58

все его сообщения:
за день, за месяц,
за все время
Меня тоже удивляет, что оупенсорс переиграл коммерческий сектор. Наверное, один из редчайших случаев.
номер сообщения: 54-72-8306

76

romm

Мастер USCF
Колумбус, Огайо

02.06.2026 | 22:37:11
Сайт

все его сообщения:
за день, за месяц,
за все время
Rom77: Меня тоже удивляет, что оупенсорс переиграл коммерческий сектор. Наверное, один из редчайших случаев.
Линукс?

__________________________
Полюбите нас черненькими, а беленькими нас всякий полюбит.
номер сообщения: 54-72-8307

77

Vizvezdenec

Ниже нуля
Севастополь

02.06.2026 | 23:57:02

все его сообщения:
за день, за месяц,
за все время
romm:
Rom77: Меня тоже удивляет, что оупенсорс переиграл коммерческий сектор. Наверное, один из редчайших случаев.
Линукс?

И какой процент пк сейчас на линуксе? Даже на макосе больше, хе.
номер сообщения: 54-72-8308

78

romm

Мастер USCF
Колумбус, Огайо

03.06.2026 | 00:01:15
Сайт

все его сообщения:
за день, за месяц,
за все время
Vizvezdenec:
romm:
Rom77: Меня тоже удивляет, что оупенсорс переиграл коммерческий сектор. Наверное, один из редчайших случаев.
Линукс?

И какой процент пк сейчас на линуксе?

Я про сервера. Где все солярисы, эйчпуксы, тру-64 и прочие коммерческие юниксы?

__________________________
Полюбите нас черненькими, а беленькими нас всякий полюбит.
номер сообщения: 54-72-8309

79

Rom77

03.06.2026 | 05:31:40

все его сообщения:
за день, за месяц,
за все время
romm: Линукс?

Конечно если поискать, то примеры найдутся. Хотя в случае линукса, все же он нашел себя в специализированной нише. Тогда как Стокфиш именуют чуть ли не "народным". (Здесь не имеется в виду число установок, понятно что емкость рынка разная)
номер сообщения: 54-72-8310

80

romm

Мастер USCF
Колумбус, Огайо

03.06.2026 | 05:46:21
Сайт

все его сообщения:
за день, за месяц,
за все время
Rom77:
romm: Линукс?

Конечно если поискать, то примеры найдутся. Хотя в случае линукса, все же он нашел себя в специализированной нише. Тогда как Стокфиш именуют чуть ли не "народным". (Здесь не имеется в виду число установок, понятно что емкость рынка разная)

Фигассе, специализированная.

А что, ведущие веб-сервера - апач, энжинекс - это не опенсорс? Почти все популярные интерпретируемые языки - опенсорс.

__________________________
Полюбите нас черненькими, а беленькими нас всякий полюбит.
номер сообщения: 54-72-8311

81

Rom77

03.06.2026 | 05:48:47

все его сообщения:
за день, за месяц,
за все время
Хорошо, пусть так.
номер сообщения: 54-72-8312