Indeed it works now. And I changed the plans a little. As e.p. capture cannot be done without creating e.p. rights, and this creation is a side effect of regular leaps and rides, implementing generation of the latter is the logical next step. To get some idea of whether the code is working, it will be used for highlighting pseudo-legal moves. Because such highlighting has to be done from the Post-Game sections, we first have to fill those, with gosub GameEnd false; and gosub GameEnd true; for white and black, respectively. As Game Courier wants to have the complete list of moves for all pieces in advance, we also need a routine GenAll that calls GenMove for all pieces of a given player (which is the opponent of the player passed to GameEnd). This is pretty much standard:
sub GenAll player:
my from piece;
set hit false;
if #player:
def friends onlylower;
else:
def friends onlyupper;
endif;
for (from piece) fn friends:
gosub GenMoves #piece #from 1;
next;
endsub;
For now the only task of the GameEnd routine is to use this 'all-moves' generator for filling the array of legal moves; a new task (2) is defined in GotMove to do a setlegal #orisqr #destsqr there. In a later stage this would only have to be done for moves that need explicit specification of side effects; we don't want to encourage the user to click the destination of such moves without typing the side effects! The routine GameEnd just specifies the task, and inverts the player.
sub GameEnd player:
set task 2;
set side not #player;
gosub GenAll #side;
drawn;
endsub;
That is all pretty straightforward. The real work is done by NextLeg, which is now expanded to:
sub NextLeg togo legindex startsqr cursqr locustsqr dropsqr iso:
my range dx dy mode to tosqrs k len newindex hx hy;
// retrieve leg description (next 4 elements in 'legdefs')
set rng elem #legindex #legdefs;
set dx elem + 1 #legindex #legdefs;
set dy elem + 2 #legindex #legdefs;
set mode elem + 3 #legindex #legdefs;
verify not flag #startsqr or not & 64 #mode; // 64 = initial
set tosqrs ray #cursqr #dx #dy; // squares along ray in this direction
set r count #tosqrs; // view distance
verify #r; // stop if nowhere to go
// here we can shorten the range for non-jumping moves
dec togo; // count this leg done
// here we can set dropsqr for legs that unload
set r min #rng #r; // farthest we can get (at least 1!)
// here we can treat the 'iso' length request, and else
if & 1 #mode: // leg includes free ride
set k 0;
do while < #k dec #r: // for non-final (and thus empty) squares
set to elem #k #tosqrs;
if not #togo: // no more legs follow
gosub GotMove #startsqr #to #locustsqr #dropsqr 0 0;
endif;
inc k;
loop;
endif;
// at this point only the move with exactly r steps can be left
set len cond == 1 #rng #iso #r; // if leg is slide, remember its length
set to elem dec #r #tosqrs; // target square, r steps away along ride
set stopper space #to; // what is on it
if == @ stopper: // target is empty
verify & 1 #mode; // 1 = non-capture
else: // target is occupied
// here we can handle hopping
set side islower space #startsqr; // whether we are black
set fratricide cond #side islower #stopper isupper #stopper;
if #fratricide: // target is friend
if & 8 #mode: // 8 = first leg of castling
verify match #to #partners; // ends at castling partner
verify not flag #to; // partner is virgin
set locustsqr #to; // order its removal
set to where #startsqr elem + 5 #legindex #legdefs 0;
set dropsqr where #to - 0 #dx #dy; // make it appear on other side of King
gosub GotMove #startsqr #to #locustsqr #dropsqr #stopper 1;
return;
endif;
// here we can handle move induction
verify & 4096 #mode; // 4096 = destroy (friendly capture)
else: // target is foe
verify & 2 #mode: // 2 = capture
// TODO: check-only moves
endif;
endif;
if not #togo:
gosub GotMove #startsqr #to #locustsqr #dropsqr #stopper 0;
endif;
endsub;
The task is to generate all possible realizations of one leg of the move, where the leg is a ride that is specified in the legdefs table (at legindex) as its range, x-y step and 'mode'. The latter is a set of bit flags packed into an integer, which specifies what the leg can do (e.g. capture). For most legs the destination will be unique, e.g. because they are leaps, or because the mode requires a certain kind of occupant on the destination. And by definition a ride can only have one occupied square in it, at which it must terminate. The only case where a leg as several alternative destinations is thus when a true rider makes a non-capture move. In this case NextLeg has to loop over the destinations, and generate a move for each of them.
The algorithm of NextLeg roughly has three parts. First it determines the farthest square of the ride, taking into account the board size and occupation, the range of the leg, and whether a specific length was requested (to match that of an earlier leg). Then, if the mode allows non-captures, it loops over all squares up to that final square, which are guaranteed to be empty. Finally it determines if a move to the final square is possible, given the occupant and the mode. This can (in a non-final leg) still lead to two different realizations, one that captures the occupant, and one that uses it as a screen to hop over.
In phase 3 of the project we only handle simple moves, consisting of a single leg. For every realization of the leg NextLeg then calls GotMove to process the move. In multi-leg moves it might have to call itself recursively at those points, to attempt the remaining legs, but for now we can leave out these recursive calls. The only modes we handle is capture, non-capture and friendly capture. (The castling code of phase 2 goes into that latter section.) It is ignored whether a leap is lame, and the more exotic features of XBetza, such as move induction and unloading of victims are not implemented either. Hopping, locust capture, and bent riders are intrinsically two-leg phenomena, and won't work either.
Indeed it works now. And I changed the plans a little. As e.p. capture cannot be done without creating e.p. rights, and this creation is a side effect of regular leaps and rides, implementing generation of the latter is the logical next step. To get some idea of whether the code is working, it will be used for highlighting pseudo-legal moves. Because such highlighting has to be done from the Post-Game sections, we first have to fill those, with gosub GameEnd false; and gosub GameEnd true; for white and black, respectively. As Game Courier wants to have the complete list of moves for all pieces in advance, we also need a routine GenAll that calls GenMove for all pieces of a given player (which is the opponent of the player passed to GameEnd). This is pretty much standard:
For now the only task of the GameEnd routine is to use this 'all-moves' generator for filling the array of legal moves; a new task (2) is defined in GotMove to do a setlegal #orisqr #destsqr there. In a later stage this would only have to be done for moves that need explicit specification of side effects; we don't want to encourage the user to click the destination of such moves without typing the side effects! The routine GameEnd just specifies the task, and inverts the player.
That is all pretty straightforward. The real work is done by NextLeg, which is now expanded to:
The task is to generate all possible realizations of one leg of the move, where the leg is a ride that is specified in the legdefs table (at legindex) as its range, x-y step and 'mode'. The latter is a set of bit flags packed into an integer, which specifies what the leg can do (e.g. capture). For most legs the destination will be unique, e.g. because they are leaps, or because the mode requires a certain kind of occupant on the destination. And by definition a ride can only have one occupied square in it, at which it must terminate. The only case where a leg as several alternative destinations is thus when a true rider makes a non-capture move. In this case NextLeg has to loop over the destinations, and generate a move for each of them.
The algorithm of NextLeg roughly has three parts. First it determines the farthest square of the ride, taking into account the board size and occupation, the range of the leg, and whether a specific length was requested (to match that of an earlier leg). Then, if the mode allows non-captures, it loops over all squares up to that final square, which are guaranteed to be empty. Finally it determines if a move to the final square is possible, given the occupant and the mode. This can (in a non-final leg) still lead to two different realizations, one that captures the occupant, and one that uses it as a screen to hop over.
In phase 3 of the project we only handle simple moves, consisting of a single leg. For every realization of the leg NextLeg then calls GotMove to process the move. In multi-leg moves it might have to call itself recursively at those points, to attempt the remaining legs, but for now we can leave out these recursive calls. The only modes we handle is capture, non-capture and friendly capture. (The castling code of phase 2 goes into that latter section.) It is ignored whether a leap is lame, and the more exotic features of XBetza, such as move induction and unloading of victims are not implemented either. Hopping, locust capture, and bent riders are intrinsically two-leg phenomena, and won't work either.