Code covered by the BSD License  

Highlights from
ChessPeace

image thumbnail
from ChessPeace by Zachary Danziger
Software for playing Chess and Chess Variants

PossibleMoves(B,noCheck)
function [pList state] = PossibleMoves(B,noCheck)
% generates a list of all possible moves given the board object, B
%
% m used throughout as a temporary storage variable for different functions
%
% The noCheck input is used to indicate that we wish to ignore move
% restrictions associated with check, typically done for speed. If noCheck
% equals 1, state will always be returned as 'play'.
%
% movement data: [r0 c0 r1 c0 flag]
% flag can take the following values:
% >0: indicates a promotion (supercedes all other flags)
%  0: move to an empty square
% -1: en passant
% -2: castle
% -3: indicates a piece is protecting itself
% -4: a capture
%
% %%% code for ChessPiece, v1.1 2012, ZCD %%%
%


%% movement and effect types initialization
% track different move types
pMoves = zeros(200,5);            % moves
pMi = 1;                          % counters
pCaptures = zeros(200,5);         % captures
pCi = 1;
pImmobile = zeros(50,3);          % immobilized pieces
pIi = 1;
pProtect = zeros(50,3);           % protected pieces
pPi = 1;


if nargin==1
    noCheck = 0;
end


%% obtain all possible moves w/o regard for checks

% a matrix of all colour values for the current board configuration
cmap1 = reshape([B.top.colour],size(B.top));
% only check possible moves starting from squares occupied by pieces
[ixRow ixCol] = find(cmap1<0 | cmap1>0);

% loop to check all pieces for their possible moves
for ii=1:length(ixRow)
    % only cycle through squares with pieces on them:
    i=ixRow(ii);
    j=ixCol(ii);
    
    % only check this piece if its owner has moving rights
    if B.top(i,j).colour==B.info.turn
        % proceed to check all effects of this piece on legal moves
        if iscell(B.top(i,j).move)
            for k=1:length(B.top(i,j).move)
                % get the available squares for this piece
                m = B.top(i,j).move{k}([i j],B);
                % add those squares to pMoves
                if ~isempty(m)
                    % put in the squares
                    inputSize = size(m,1);
                    pMoves(pMi:pMi+inputSize-1,:) = [repmat([i j],[inputSize 1]) m];
                    pMi = pMi + inputSize; % increment the counter
                end
            end
        end
        if iscell(B.top(i,j).capture)
            for k=1:length(B.top(i,j).capture)
                m = B.top(i,j).capture{k}([i j],B);
                if ~isempty(m)
                    inputSize = size(m,1);
                    pCaptures(pCi:pCi+inputSize-1,:) = [repmat([i j],[inputSize 1]) m];
                    pCi = pCi + inputSize; % increment the counter
                end
            end
        end
        if iscell(B.top(i,j).init)
            for k=1:length(B.top(i,j).init)
                m = B.top(i,j).init{k}([i j],B);
                if ~isempty(m)
                    % mark en passant with -1 in the flag column
                    if strcmp(B.top(i,j).type,'guard')
                        % hack - for each move, if it can be done regularly
                        % without the initial move, do not mark it,
                        % otherwise mark it
                        for ip=1:size(m,1)
                            if ~any(all(pMoves(:,3:4)==repmat(m(ip,1:2),[size(pMoves,1) 1]),2))
                                m(ip,end)=-1;
                            end
                        end
                    end
                    inputSize = size(m,1);
                    pMoves(pMi:pMi+inputSize-1,:) = [repmat([i j],[inputSize 1]) m];
                    pMi = pMi + inputSize; % increment the counter
                end
            end
        end
        if B.top(i,j).castle==1 && B.top(i,j).royal==1
            % check for legality of castling
            % locations of royal and major casteling pieces on the to-move
            % side
            yR=i; xR=j;
            ixM = find([B.top.castle]==1 & [B.top.royal]==0 & [B.top.colour]==B.info.turn);
            [yM xM] = ind2sub(size(B.top),ixM);
            % once for each combination of royal piece and major piece
            % with castle rights
            for im = 1:length(ixM)
                % castle rules:
                % 1) royal and major are on the same rank
                % 2) there are no pieces between the royal and major
                % 3) the royal does not land on a piece after a castle
                % 4) check restrictions (enforced in the check loop)
                
                canCastle = 1;
                % rule 1
                if yM(im)~=i
                    canCastle = 0;
                end
                
                % rule 3
                if xM(im)<xR
                    cf=xM(im)+1:xR-1;       % squares that must have no pieces
                    adj=-2;
                else
                    cf=xR+1:xM(im)-1;
                    adj=2;
                end
                
                if ~( strcmp(B.top(i,j+adj).type,'empty') || all([i j+adj]==[yM(im) xM(im)]) )
                    % if the landing square is empty OR the landing square
                    % is occupied by the castling major piece, allow castling
                    canCastle = 0;
                end
                
                % rule 2
                if all(strcmp({B.top(yR,cf).type},'empty')) && canCastle
                    pMoves(pMi,:) = [yR xR yM(im) xM(im) -2];
                    pMi = pMi+1;
                end
                
            end
        end
           
    end
    
    % special features require checking all pieces, not just the side to move
    if ~isempty(B.top(i,j).name)
        
    % check for immobilized squares
    if iscell(B.top(i,j).immobile)
        % note the squares that are frozen
        for k=1:length(B.top(i,j).immobile)
            m = B.top(i,j).immobile{k}([i j],B);
            if ~isempty(m)
            inputSize = size(m,1);
            pImmobile(pIi:pIi+inputSize-1,:) = m;
            pIi = pIi + inputSize; % increment the counter
            end
        end
    end
    % check for protected squares
    if iscell(B.top(i,j).protect)
        % note the squares that are protected
        for k=1:length(B.top(i,j).protect)
            m = B.top(i,j).protect{k}([i j],B);
            if ~isempty(m)
            inputSize = size(m,1);
            pProtect(pPi:pPi+inputSize-1,:) = m;
            pPi = pPi + inputSize; % increment the counter
            end
        end
    end
    
    end
 
end % end squares loop   


% get rid of all excess zeros from moves
pMoves(pMi:end,:)=[];
pCaptures(pCi:end,:)=[];
pImmobile(pIi:end,:)=[];
pProtect(pPi:end,:)=[];



% === combine captures and moves ===
% format: [x0 y0 x1 y1 pflag; ...]
pList = unique([pMoves; pCaptures],'rows');


% = Examine special cases (pieces with protection or immobilization) =
% Pieces moving FROM immobile squares is illegal
ix = zeros(size(pList,1),1);
for i2=1:size(pImmobile,1)
    % mark each time we find an immobilized square
    ix = ix + all(repmat(pImmobile(i2,1:2),[size(pList,1) 1])==pList(:,1:2),2);
end
% eliminate these moves as possibilities
pList(ix>0,:) = [];

% Pieces moving TO protected squares is illegal (unless its a castle)
ix = zeros(size(pList,1),1);
for i2=1:size(pProtect,1)
    % if the square being protected is itself a piece with protection
    % capability (and the piece isn't protecting itself) do not allow it
    %if isnan(B.top(pProtect(i2,1),pProtect(i2,1)).protect) 
        % mark each time we find a protected square
        ix = ix + all(repmat(pProtect(i2,1:2),[size(pList,1) 1])==pList(:,3:4),2) & pList(:,end)~=-2;
    %end
end
% eliminate these moves as possibilities
pList(ix>0,:) = [];




%% introduce legality of checks

if ~noCheck

% check all psuedo moves for check
ix = ones(1,size(pList,1));
for pm=1:size(pList,1)
    % make the ith pseudomove
    pseudoB = MakeMove(pList(pm,1:2),pList(pm,3:4),B,pList(pm,5));

    % if this is a castling move
    if pList(pm,5)==-2
        % note which squares must not be "in check" for castle to be
        % permitted
        if pList(pm,2)<pList(pm,4), cf2=1; else cf2=-1; end
        % original square k was on and square the king passed through (and
        % square where k landed, but he is already there now in pseudoB)
        ixC = [pList(pm,1:2) pList(pm,3) pList(pm,2)+cf2];
        pseudoB.top(ixC(1),ixC(2)) = NewPiece('king',-pseudoB.info.turn);
        pseudoB.top(ixC(3),ixC(4)) = NewPiece('king',-pseudoB.info.turn);
    end
    
    
    % rebuild immobilizing squares after pseudomove
    [imobRow imobCol] = ind2sub(size(pseudoB.top),find(cellfun(@iscell,{pseudoB.top.immobile})));
    ixI = zeros(1,3);
    for ii=1:length(imobRow)
        i=imobRow(ii); j=imobCol(ii);
        % note the squares that are frozen
        for k=1:length(pseudoB.top(i,j).immobile)
            ixI = [ixI; pseudoB.top(i,j).immobile{k}([i j],pseudoB)];
        end
    end
    % rebuild protected squares after pseudomove
    [protRow protCol] = ind2sub(size(pseudoB.top),find(cellfun(@iscell,{pseudoB.top.protect})));
    ixP = zeros(1,3);
    for ii=1:length(protRow)
        i=protRow(ii); j=protCol(ii);
        % note the protected squares
        for k=1:length(pseudoB.top(i,j).protect)
            ixP = [ixP; pseudoB.top(i,j).protect{k}([i j],pseudoB)];
        end
    end     
    
    % get the royal piece positions of the previously moving side
    [x y] = ind2sub(size(pseudoB.top),find([pseudoB.top.royal] & ...
        [pseudoB.top.colour]==-pseudoB.info.turn));
    rlocs = [x' y'];
    
    % remove all protected squares because capture is not allowed
    ixr = zeros(size(rlocs,1),1);
    for i2=1:size(ixP,1)
        % mark each time we find a protected square
        ixr = ixr + all(repmat(ixP(i2,1:2),[size(rlocs,1) 1])==rlocs,2);
    end
    rlocs(ixr>0,:) = [];
    
    
    % only check possible moves starting from squares occupied by pieces
    cmap2 = reshape([pseudoB.top.colour],size(B.top));
    [ixRow2 ixCol2] = find(cmap2<0 | cmap2>0);
    for ii=1:length(ixRow2)
        % only cycle through squares with pieces on them:
        i=ixRow2(ii);
        j=ixCol2(ii);

        % check all capture possibilities on the royal squares
        if pseudoB.top(i,j).colour==pseudoB.info.turn && ~any(all(repmat([i j],[size(ixI,1) 1])==ixI(:,1:2),2))
            mi=1;
            chCaps = zeros(100,3);
            if iscell(pseudoB.top(i,j).capture)  % non-capturing pieces exist
            for k = 1:length(pseudoB.top(i,j).capture)
                
                m = pseudoB.top(i,j).capture{k}([i j],pseudoB);
                if ~isempty(m)
                    inputSize = size(m,1);
                    chCaps(mi:mi+inputSize-1,:) = m;
                    mi = mi+inputSize;
                end

            end
            end
            % lets also check initial move possibilities (which could be
            % royal captures)
            if iscell(pseudoB.top(i,j).init)
            for k = 1:length(pseudoB.top(i,j).init)
                m = pseudoB.top(i,j).init{k}([i j],pseudoB);
                if ~isempty(m)
                    inputSize = size(m,1);
                    chCaps(mi:mi+inputSize-1,:) = m;
                    mi = mi+inputSize;
                end
            end
            end
            
            % if a capture is possible...
            if mi>1
                chCaps(mi:end,:)=[];
                for k=1:size(rlocs,1)
                    if any(all(chCaps(:,1:2)==repmat(rlocs(k,:),[size(chCaps,1) 1]),2))
                        % note moves that result in check
                        ix(pm) = 0;
                        break;
                    end
                end
            end
        end
        if ix(pm)==0; break; end % no need to look farther at this move
    end % end loop for this move
end
% === remove the all ix (illegal due to check) moves ===
pList = pList(ix==1,:);


%% check victory conditions       

% draw by repitition?
if B.info.draw50>5
    % check if there are 3 position repeats
    numReps = sum(all(strcmp(repmat(B.info.hist(end,:),[size(B.info.hist,1) 1]),B.info.hist),2));
    if numReps == 3
        state = '1/2-1/2, draw by repitition';
        return;
    end
end

% 50 moves draw?
if B.info.draw50==100
    state = '1/2-1/2, draw by 50 moves';
    return;
end

% check for checkmate and stalemate when there are no legal moves
if isempty(pList)
    
    % get the royal piece positions of the previously moving side
    [x y] = ind2sub(size(B.top),find([B.top.royal] & ...
        [B.top.colour]==B.info.turn));
    rlocs = [x' y'];    % and remove all protected squares because capture is not allowed
    
    % remove all protected squares because capture is not allowed
    ixr = zeros(size(rlocs,1),1);
    for i2=1:size(pProtect,1)
        % mark each time we find a protected square
        ixr = ixr + all(repmat(pProtect(i2,1:2),[size(rlocs,1) 1])==rlocs,2);
    end
    rlocs(ixr>0,:) = [];
    
    % make sure we aren't referencing empty arrays
    if isempty(pImmobile), pImmobile=zeros(1,3); end
    
    % assume stalemate unless check is found
    mflag = 0;
    for i=1:size(B.top,1)
    for j=1:size(B.top,2)
        % look at pieces that are to move next AND that are not immobile
        if B.top(i,j).colour==-B.info.turn && ...
                ~any(all(repmat([i j],[size(pImmobile,1) 1])==pImmobile(:,1:2),2))
            % get captures for this pieces
            m = [];
            for k = 1:length(B.top(i,j).capture)
                if iscell(B.top(i,j).capture)  % non-capturing pieces exist
                m = [m; B.top(i,j).capture{k}([i j],B)];
                end
            end
            for k = 1:length(B.top(i,j).init)   % in case a piece makes an initial capture on the royal piece
                if iscell(B.top(i,j).init) 
                m = [m; B.top(i,j).init{k}([i j],B)];
                end
            end
            % if this piece can make captures (not empty) test to see if
            % any are royal captures
            if ~isempty(m)
                for ii=1:size(rlocs,1)
                for jj=1:size(m,1)
                    if all(rlocs(ii,:)==m(jj,1:2))
                        % this is a checkmate condition, stop searching
                        mflag = 1;
                    end
                end
                end
            end
        end
        if mflag; break; end
    end
    if mflag; break; end
    end
    % final game outcome
    if mflag
        if B.info.turn==1
            state = '0-1, White checkmated';
        else
            state = '1-0, Black checkmated';
        end
    else
        state = '1/2-1/2, Stalemate';
    end
else
    state = 'play';
end
        
else
% when checks are not considered
    % check to see if royal pieces are on the board
    
    state = 'play';

    % if there are no royal pieces on the board for this side, consider it
    % a null (this is useful for computer evaluations, because the
    % computer may capture royal pieces during move consideration)
    [x y] = ind2sub(size(B.top),find([B.top.royal] & ...
        [B.top.colour]==B.info.turn));
    rlocs = [x' y'];
    if isempty(rlocs)
        state = '0-0, No royals';
        pList = zeros(0,5);
    end
end






Contact us