Несколько слов о шахматном конкурсе от 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
А где же превращение пешки? Знающий человек написал мне о максимальных 1020 очках. Но это другая история и здесь не будет этого разбора.
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}
1, 2, 3, 9, 10 - это партии с превращением пешки, а вот остальные разберем детально.
У нас есть общие баллы и количество ходов, поэтому можно найти в партии полученные очки за фигуры:
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]
1. d4 d5 2. c4 e6 3. Nf3 Be7 4. Nc3 dxc4 5. e4 b5 6. Nxb5 a6 7. Nc3 Bb7 8. Bxc4 c5 9. Qb3 cxd4 10. Qxb7 Ra7 11. Qxa7 dxc3 12. bxc3 Nf6 13. Rb1 O-O 14. O-O Nxe4 15. Rxb8 Qxb8 16. Qxe7 Qb6 17. Qh4 Qc6 18. Ne5 Qxc4 19. Nxc4 Nxc3 20. Bh6 gxh6 21. Re1 Nxa2 22. Qxh6 Rc8 23. Re3 Rxc4 24. Rg3+ Rg4 25. Rxg4+ Kh8 26. Qg7# 1-0\n
И что нам это дает? Только некоторое представление о партии. Потому, что бот не тупой, и мало съесть эти фигуры, надо еще поставить ему мат.
Короче, я не решил эту задачу.
За эту неделю узнал много интересного. А шахматный софт превосходен (lc0 > stockfish 17).
Но понял одно: все онлайн шахматные конкурсы в наше время неактуальны.
750
от яндекса яндексу (9 ходов и две пешки):
[10, 10]
1. e4 Nf6 2. e5 Ng8 3. d4 d6 4. Nf3 dxe5 5. Nxe5 Nd7 6. Qf3 f6 7. Qh5+ g6 8. Nxg6 Nb6 9. Ne5# 1-0\n
1650
lc0 в 10 ходов:
[30, 10, 10, 10]
1. Nc3 d5 2. d4 Nf6 3. Bf4 c5 4. e3 a6 5. dxc5 e5 6. Bxe5 Nc6 7. Bxf6 Qxf6 8. Nxd5 Qxb2 9. Nc7+ Ke7 10. Qd6# 1-0\n
1235
Stockfish 17 (17 ходов):
[30, 30, 10]
1. d4 d5 2. c4 e6 3. Nc3 Nf6 4. Bg5 Bb4 5. Qa4+ Nc6 6. e3 Bd7 7. Qc2 h6 8. Bxf6 Qxf6 9. a3 Bxc3+ 10. Qxc3 O-O-O 11. Nf3 Rhe8 12. cxd5 Nb8 13. Rc1 c6 14. d6 Qf5 15. Ne5 f6 16. Qa5 fxe5 17. Qc7# 1-0\n