Справка по регулярным выражениям для шахматных ходов (SAN)

#java #regex

#java #регулярное выражение

Вопрос:

Я пишу программу, которая должна уметь читать и анализировать шахматные ходы (SAN).

Вот пример возможных принятых ходов:

 e4
Nf3
Nbd2
Nb1c3
R1a3
d8=Q
exd5
Nbxd2
...
  

Сначала я написал NFA, затем преобразовал его в грамматику, а затем преобразовал в регулярное выражение.

С моими соглашениями это выглядит так

 pln   plxln   plnxln   plnln   plln   pxln   lxln=(B R Q N)   lxln   lnxln=(B R Q N)   lnxln   lnln=(B R Q N)   lnln   ln=(B R Q N)   ln   pnxln   pnln
  

где:

p является символом set {B,R,Q,N,K} (или думать об этом как (B R Q N K) = [BRQNK]

l является символом среди [a-h] интервалов (чувствителен к регистру)

n является числом среди [1-8] интервалов

представляет операцию объединения … если я правильно понял, (B R Q N) это [BRQN] на языках программирования регулярных выражений.

= это просто обычный символ… в шахматных ходах он используется при продвижении (например, e8 = Q)

x это тоже обычный символ … используется, когда, перемещая свою фигуру в этом месте, вы берете фигуру противника.

( / ) : Как в математике

Я попытался разобрать первую часть pln как: [BRQN][a-h][1-8] в онлайн-тестере регулярных выражений java и работал на ход, подобный Nf3 . Я не понял, как сделать объединение для составного выражения (например pln plxln ) … также как я могу пометить части регулярного выражения, чтобы при его обнаружении я получал всю информацию? Я пытался прочитать документы об этом, но не понял.

Любой совет?

Комментарии:

1. Интересный вопрос. Вам нужно позаботиться о рокировке, проверке и мате?

2. Мне еще нужно выполнить рокировку (хотя это может быть записано в выражении как строка «O-O» или «O-O-O».. Проверка и мат могут быть приятной функцией, но не являются строго обязательными в моей программе … хотя их, кажется, не сложно добавить… но это сделало бы выражение длиннее. Я вижу, пишу ли я выражение более полно

3. Обновление: я видел, что ChessBase игнорирует такие символы, как или # put out place (не проверка / мат), как ненужные, действительно, программа должна быть в состоянии понять, когда выполняется проверка / мат.

Ответ №1:

В вашей нотации | указаны регулярные выражения. Таким образом, вы могли бы использовать регулярное выражение

 [BRQNK][a-h][1-8]|[BRQNK][a-h]x[a-h][1-8]|[BRQNK][a-h][1-8]x[a-h][1-8]|[BRQNK][a-h][1-8][a-h][1-8]|[BRQNK][a-h][a-h][1-8]|[BRQNK]x[a-h][1-8]|[a-h]x[a-h][1-8]=(B R Q N)|[a-h]x[a-h][1-8]|[a-h][1-8]x[a-h][1-8]=(B R Q N)|[a-h][1-8]x[a-h][1-8]|[a-h][1-8][a-h][1-8]=(B R Q N)|[a-h][1-8][a-h][1-8]|[a-h][1-8]=(B R Q N)|[a-h][1-8]|[BRQNK][1-8]x[a-h][1-8]|[BRQNK][1-8][a-h][1-8]
  

Это, очевидно, немного некрасиво. Я могу придумать 2 возможных способа сделать это лучше:

  • С COMMENTS помощью флага вы можете добавлять пробелы.
  • Объедините возможности вместе более приятным способом. Например, [BRQNK][a-h]x[a-h][1-8]|[BRQNK][a-h][1-8]x[a-h][1-8] может быть переписан как [BRQNK][a-h][1-8]?x[a-h][1-8] .

Я также знаю об одном другом улучшении, которое недоступно в java. (И, возможно, не так много языков, но вы можете сделать это на Perl.) Подвыражение (?1) (аналогично (?2) и т.д.) немного похоже 1 , за исключением того, что вместо сопоставления точной строки, соответствующей первой группе захвата, оно соответствует любой строке, которая могла соответствовать этой группе захвата. Другими словами, это эквивалентно повторной записи группы захвата. Таким образом, вы могли бы (в Perl) заменить первое [BRQNK] на ([BRQNK]) , а затем заменить все последующие вхождения на (?1) .

Комментарии:

1. Да! Это действительно распознает ходы! Мне также нужно проанализировать ходы, и мне было интересно, поможет ли это именование в группах. Я подумал что-то вроде (?<P>[BRQNK])(?<dl>[a-h])(?<dn>[1-8])|(?<P>[BRQNK]..... , где P стоит как фигура, dl как буква назначения, dn как номер назначения, а также для других типов ходов ol и od (o = origin). Проблема в том, что я не могу дважды указывать одно и то же имя для групп, даже если в матче они никогда не будут найдены вместе… Как я могу это сделать? В конце мне понадобится не менее 3 переменных на ход и больше, когда будут добавлены детали (например, спецификация столбца)

2. Я не могу придумать лучшего способа сделать это на java. Конечно, вы могли бы использовать несколько другие имена для эквивалентных групп захвата. Все еще немного лучше, чем нумерованные группы. (В Perl я бы сказал, что вы могли бы использовать (?|...) группу, за исключением того, что она плохо сочетается с именованными группами захвата, если имена групп не совпадают во всех альтернативах.)

Ответ №2:

/^([NBRQK])?([a-h])?([1-8])?(x)?([a-h][1-8])(=[NBRQK])?( |#)?$|^O-O(-O)?$/


.

.

.

.

Это было протестировано на 2599 случаях. Модульные тесты см. Ниже

 describe('Importer/Game', function() {
    let Importer, Game;
    beforeEach(function() {
        Importer = require(`${moduleDir}/import`).Importer;
        Game = require(`${moduleDir}/import`).Game;
    });
    describe('moveRegex', function() {
        describe('non-castling', function() {
            //  ([NBRQK])?  ([a-h])?   ([1-8])?   (x)?     ([a-h][1-8]) (=[NBRQK])? ( |#)?/
            //  unitType?   startFile? startRank? capture? end          promotion?  checkState?
            for(let unitType of ['', 'N', 'B', 'R', 'Q', 'K']) {
                for(let startFile of ['', 'b']) {
                    for(let startRank of ['', '3']) {
                        for(let capture of ['', 'x']) {
                            for(let promotion of ['', '=Q']) {
                                for(let checkState of ['', ' ', '#']) {
                                    //TODO: castling
                                    const dest = 'e4';
                                    const san = unitType   startFile   startRank   capture   dest   promotion   checkState;
                                    testPositive(san);
                                    //TODO: negative substitutions here.
                                    testNagative('Y'        startFile   startRank   capture   dest   promotion   checkState);
                                    testNagative(unitType   'i'         startRank   capture   dest   promotion   checkState);
                                    testNagative(unitType   startFile   '9'         capture   dest   promotion   checkState);
                                    testNagative(unitType   startFile   startRank   'X'       dest   promotion   checkState);
                                    testNagative(unitType   startFile   startRank   capture   'i9'   promotion   checkState);
                                    // testNagative(unitType   startFile   startRank   capture   ''     promotion   checkState);
                                    testNagative(unitType   startFile   startRank   capture   dest   '='         checkState);
                                    testNagative(unitType   startFile   startRank   capture   dest   'Q'         checkState);
                                    testNagative(unitType   startFile   startRank   capture   dest   promotion   '  ');
                                }
                            }
                        }
                    }
                }
            }
        });
        describe('castling', function() {
            testPositive('O-O');
            testPositive('O-O-O');
            testNagative('OOO');
            testNagative('OO');
            testNagative('O-O-');
            testNagative('O-O-O-O');
            testNagative('O');
        });
        function testPositive(san) {
            it(`should handle this san: ${san}`, function(done) {
                const matches = san.match(Importer.moveRegex);
                assert(matches);
                done();
            });
        }
        function testNagative(san) {
            it(`should not match this: ${san}`, function(done) {
                const matches = san.match(Importer.moveRegex);
                assert(!matches);
                done();
            });
        }
    });
});
  

Ответ №3:

 Re: /^([NBRQK])?([a-h])?([1-8])?(x)?([a-h][1-8])(=[NBRQK])?( |#)?$|^O-O(-O)?$/
  

Это как недостаточно, так и чрезмерно.

Это исключает возможные законные ходы O-O , O-O-O , O-O#, and O-O-O#.

Она включает в себя множество строк, которые никогда не могут быть законными: e8=K, Kaa4, Nf5=B, Qa1xb7

и так далее.

Ответ №4:

Я сделал это:

 /(^([PNBRQK])?([a-h])?([1-8])?(x|X|-)?([a-h][1-8])(=[NBRQ]| ?e.p.)?|^O-O(-O)?)( |#|$)?$/
  

Включает в себя: O-O , O-O-O , O-O# и O-O-O#

Также: e.p. , N-f6 или NXf6 и Pe4 или Pe5xd6

Обновить:

Спасибо @Toto за улучшение моей версии регулярного выражения выше:

 ^([PNBRQK]?[a-h]?[1-8]?[xX-]?[a-h][1-8](=[NBRQ]| ?e.p.)?|^O-O(?:-O)?)[ #$]?$
  

Комментарии:

1. Зачем все эти группы захвата?

2. @Toto я использую его в своей программе. Это необязательно, вы можете сказать e4 или явно указать пешку: Pe4

3. Нет, ваше регулярное выражение не является необязательным , оно требует 2.0 ms и 268 steps для соответствия вашим 8 примерам. Когда вы удаляете бесполезные группы захвата, требуется 1.0 ms и 146 steps для завершения. Доказательство

4. @Toto Мне все еще нужны были эти группы захвата, если ввод был, например, длинной алгебраической нотацией

5. Вы смотрели на ссылку, которую я предоставил выше? Упрощенное регулярное выражение соответствует всем вашим примерам и в два раза эффективнее.

Ответ №5:

Я уже некоторое время использую это на своем веб-портале.

 [BRQNK][a-h][1-8]| [a-h][1-8]|[BRQNK][a-h][a-h][1-8]|O-O|0-0-0|[BRQNK]x[a-h][1-8]|[a-h]x[a-h][1-8]|1/2-1/2|1/-O|O-/1