Пролог движения шахматных фигур

#prolog #chess

Вопрос:

Я создаю шахматную игру с искусственным интеллектом, используя пролог. У меня есть эта доска:

 Board = [piece(white, rook, 1, 1), piece(white, knight, 2, 1), piece(white, bishop, 3, 1), piece(white, queen, 4, 1), piece(white, king, 5, 1), piece(white, bishop, 6, 1), piece(white, knight, 7, 1), piece(white, rook, 8, 1), piece(white, pawn, 1, 2), piece(white, pawn, 2, 2), piece(white, pawn, 3, 2), piece(white, pawn, 4, 2), piece(white, pawn, 5, 2), piece(white, pawn, 6, 2), piece(white, pawn, 7, 2), piece(white, pawn, 8, 2), piece(black, rook, 1, 8), piece(black, knight, 2, 8), piece(black, bishop, 3, 8), piece(black, queen, 4, 8), piece(black, king, 5, 8), piece(black, bishop, 6, 8), piece(black, knight, 7, 8), piece(black, rook, 8, 8), piece(black, pawn, 1, 7), piece(black, pawn, 2, 7), piece(black, pawn, 3, 7), piece(black, pawn, 4, 7), piece(black, pawn, 5, 7), piece(black, pawn, 6, 7), piece(black, pawn, 7, 7), piece(black, pawn, 8, 7)]
 

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

правка: вот ходы пешки:

 in_boundaries(Col,Row):-
    between(1,8,Col),
    between(1,8,Row).

create_board(Board) :-
    findall(Piece, initial(Piece), Board).
nonmember(X,[Y|Ys]):-
    X=Y,
    nonmember(X,Ys).
nonmember(_,[]).
%% Pawn possible moves
movement(Board,piece(white,pawn,Col,Row),piece(white,pawn,Col,NewRow)):-
    NewRow is Row 1,
    in_boundaries(Col,NewRow),
    nonmember(piece(_,_,Col,NewRow),Board). %%Check if (Col,Row 1) is empty.
movement(Board,piece(black,pawn,Col,Row),piece(black,pawn,Col,NewRow)):-
    NewRow is Row-1,
    in_boundaries(Col,NewRow),
    nonmember(piece(_,_,Col,NewRow),Board). %%Check if (Col,Row 1) is empty.
movement(Board,piece(white,pawn,Col,Row),piece(white,pawn,NewCol,NewRow)):-
    NewCol is Col-1,
    NewRow is Row 1,
    in_boundaries(Col,NewRow),
    member(piece(black,_,NewCol,NewRow),Board). %%Check if the pawn could eat in (Col-1,Row 1).
movement(Board,piece(white,pawn,Col,Row),piece(white,pawn,NewCol,NewRow)):-
    NewCol is Col 1,
    NewRow is Row 1,
    in_boundaries(Col,NewRow),
    member(piece(black,_,NewCol,NewRow),Board). %%Check if the pawn could eat in (Col 1,Row 1).
movement(Board,piece(black,pawn,Col,Row),piece(black,pawn,NewCol,NewRow)):-
    NewCol is Col-1,
    NewRow is Row-1,
    in_boundaries(Col,NewRow),
    member(piece(black,_,NewCol,NewRow),Board). %%Check if the pawn could eat in (Col-1,Row-1).
movement(Board,piece(black,pawn,Col,Row),piece(black,pawn,NewCol,NewRow)):-
    NewCol is Col-1,
    NewRow is Row-1,
    in_boundaries(Col,NewRow),
    member(piece(black,_,NewCol,NewRow),Board). %%Check if the pawn could eat in (Col 1,Row-1).
 

Ответ №1:

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

Мы напишем отношение, которое ведет себя следующим образом:

 ?- piece_moved(piece(white, rook, 2, 7), Moved).
Moved = piece(white, rook, 1, 7) ;
Moved = piece(white, rook, 3, 7) ;
Moved = piece(white, rook, 4, 7) ;
Moved = piece(white, rook, 5, 7) ;
Moved = piece(white, rook, 6, 7) ;
Moved = piece(white, rook, 7, 7) ;
Moved = piece(white, rook, 8, 7) ;
Moved = piece(white, rook, 2, 1) ;
Moved = piece(white, rook, 2, 2) ;
Moved = piece(white, rook, 2, 3) ;
Moved = piece(white, rook, 2, 4) ;
Moved = piece(white, rook, 2, 5) ;
Moved = piece(white, rook, 2, 6) ;
Moved = piece(white, rook, 2, 8) ;
false.
 

Впоследствии, если вам действительно нужен список, вы можете получить его с помощью findall/3 :

 ?- findall(Moved, piece_moved(piece(white, rook, 2, 7), Moved), Moves).
Moves = [piece(white, rook, 1, 7), piece(white, rook, 3, 7), piece(white, rook, 4, 7), piece(white, rook, 5, 7), piece(white, rook, 6, 7), piece(white, rook, 7, 7), piece(white, rook, 8, 7), piece(white, rook, 2, 1), piece(..., ..., ..., ...)|...].
 

(Кстати, не помешало бы посмотреть, как вы реализовали свои пешечные ходы.)

Я буду использовать следующий предикат, чтобы помочь проверить, является ли перемещение допустимым:

 valid(Coordinate) :-
    between(1, 8, Coordinate).
 

Тогда перемещение ладьи по одному измерению является:

 piece_moved(piece(Color, rook, X, Y), piece(Color, rook, X1, Y)) :-
    between(-7, 7, DeltaX),
    dif(DeltaX, 0),
    X1 is X   DeltaX,
    valid(X1).
 

Это сохраняет постоянную Y координату и перемещается X на произвольное ненулевое число до 7 квадратов влево или вправо, пока результирующая позиция верна.

Движение по другому измерению в основном то же самое:

 piece_moved(piece(Color, rook, X, Y), piece(Color, rook, X, Y1)) :-
    between(-7, 7, DeltaY),
    dif(DeltaY, 0),
    Y1 is Y   DeltaY,
    valid(Y1).
 

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

1. Разве это select(Y, [1, 2, 3, 4, 5, 6, 7, 8], Choices), member(Y1, Choices) не было бы более эффективным?

2. Это выделяет промежуточный список для выражения по существу dif(Y, Y1) . Да, вы могли бы сделать это более эффективным для этой конкретной части, но я думаю, что подход с добавлением смещений к позиции проще для OP обобщить на другие части.