Несколько слов о шахматном конкурсе от Yandex Cloud. Он завершился, а результаты будут 27 января.
На старте конкурса пару раз играл, но так как не умею, то никакого результата и не было. Первое место было у Public Name с каким то рейтингом 3xxx.
И вот неделю назад пришло время вернуться. Рейтинг перестроился, первые три места с одинаковыми очками (5726).
Не столько важно (да-да, я проиграл, и решения у меня нет), важно поэкспериментировать с ботом и движками Stockfish 17
и Leela Chess Zero (lс0)
. Это обычные консольные считалки ходов. Хорошо просчитывают, качественно.
Но для того чтобы коммуницировать с ними обычное используют GUI и протокол UCI (Universal Chess Interface). А хочется иметь прямой доступ к движку. Послать ход, получить ход.
Сюда врывается превосходный websocketd (для многих OS, в том числе Windows), который создает WebSocket сервер для любого консольного (STDIN/STDOUT) приложения, например cmd:
websocketd.exe --port=9999 cmd.exe
И на порту 9999 через websocket доступен интерпретатор командной строки. websocketd --port=9999 stockfish
ws.send('position startpos'); ws.send('go nodes 10000');
bestmove <ход>
yandex.cloud/api/chess/moveHuman
ws.send('position fen ' + fen); ws.send('go nodes 10000');
game_status == 'ongoing'
Из забавного. В возвращаемых данных от moveHuman
в последнем элементе массива analysis_history
есть подсказка для нас, для людей, какой ход будет для нас более выйгрышным: Move.from_uci('g1f3')
. По сути это тот же bestmove
.
А еще забавнее, что с этими данными получаеся самый быстрый мат.
Бот с данными от Stockfish и lc0 великолепно отрабатывает, постоянные победы. Но очки далеки от топа.
Возможно стоит почитать правила? Да ну, бред какой-то. Но почитаем.
Пункт про электронных помощников не интересный.
А вот подсчет рейтинга интересный:
Очки (ход или за съестное) * 5 (победа, другое нас не интересует) * 50 / количество ходов.
За каждый ход дается один балл. За поедание такая картина, всего 380
:
45 30 30 90 30 30 45\n10 10 10 10 10 10 10 10
5726
5726
5726
5450
5078
4983
4857
4826
4756
4698
Так, максимально и максимально популярно 5726. Что нужно сделать, чтобы заработать столько очков? У нас есть формула, поэтому сгенерим все варианты:
var board = [];\nfor (let i = 1, steps = 50; i <= steps; i++) {\n\tfor (let j = 1; j <= 380 + steps; j++) {\n\t\tboard.push(j + ', ходов: ' + i + ' | ' + (j * 5 * 50 / i));\n\t}\n}
Оке, ищем 5726. Ииии... Нет такого варианта. То есть, математика не знает как по этой формуле получить 5726.
4-8 - имеется, а вот 9 и 10 - отсутствуют.
Проверил все мои партии и рейтинг моих соседей, все имеются в сгенерированном списке. Так же видно, что итоговый подсчет округляется в меньшую сторону (floor). А значит никаких -1.
Какой-то косяк. Ну да ладно. Дождемся итоговых результатов.
Идем дальше. У нас есть общие баллы и количество ходов, поэтому можно найти в партии полученные очки за фигуры:
function eaten(score, steps) {\n\t// очень неочень функция, много дублей\n\tfunction powerset(arr, prefix=[], i=0){\n\t\tif (i === arr.length)\n\t\t\treturn [prefix];\n\n\t\treturn powerset(arr, prefix.concat(arr[i]), i + 1)\n\t\t\t.concat(powerset(arr, prefix, i + 1));\n\t}\n\n\t// очки\n\tvar points = [\n\t\t45, 30, 30, 90, 30, 30, 45,\n\t\t10, 10, 10, 10, 10, 10, 10, 10,\n\t];\n\t\n\t// генерируем все вохможные варианты съестных очков\n\tvar possible = powerset(points);\n\n\t// несъестное\n\tvar turns = [];\n\tfor (let i = 1; i <= steps; i++) {\n\t\tturns.push(1);\n\t}\n\n\tvar data = [];\n\n\tfor (let i = 0; i < possible.length; i++) {\n\t\tif (turns.length - 1 > possible[i].length) {\n\t\t\t// соединяем съеденные с пустыми ходами\n\t\t\tlet s = Object.values(Object.assign({}, turns, possible[i]));\n\t\t\ts = eval(p.join('+'));\n\t\t\t// если в score очки за ходы\n\t\t\tif (s === score) {\n\t\t\t\tdata.push(possible[i]);\n\t\t\t}\n\t\t\t// если в score итоговые баллы\n\t\t\telse if (Math.floor(s * 5 * 50 / steps) === score) {\n\t\t\t\tdata.push(possible[i]);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn data;\n}\n
Анализируем какие очки были получены:
5450
[45, 30, 30, 90, 30, 30, 10, 10, 10, 10, 10, 10, 10]
5078
[45, 30, 30, 90, 30, 45, 10, 10, 10, 10, 10]
3134
Мой результат полученный с помощью lс0 имеет два варианта, но в данном случае был второй:
[30, 30, 30, 10, 10, 10, 10, 10, 10, 10]
[45, 30, 30, 90, 30, 45, 10, 10, 10, 10]
И что нам это дает? Только некоторое представление о партии. Потому, что бот не тупой, и мало съесть эти фигуры, надо еще поставить ему мат.
Короче, я не решил эту задачу.
За эту неделю узнал много интересного. А шахматный софт превосходен (lc0 > stockfish 17).
Но понял одно: все онлайн шахматные конкурсы в наше время неактуальны.
750
от яндекса яндексу (9 ходов и две пешки):
[10, 10]
1650
lc0 в 10 ходов:
[30, 10, 10, 10]
1235
Stockfish 17 (17 ходов):
[30, 30, 10]