#######################################################
##
## rules.eden
##
## This script defines the rules of the game. As each
## rule is a relatively simple definition, rules are
## easily changed or extended.
##
#######################################################
%eden
## Define a "forward" direction for current player in terms of grid y-coords
fwd is (current_player == W) ? 1 : -1;
## Short-hand for the current piece selected, and the square user is attempting to move into
x is current_square[1];
y is current_square[2];
px is selected_piece[1];
py is selected_piece[2];
four_ranks_empty is (is_empty([x,y]) && is_empty([x,y+fwd]) && is_empty([x,y+2*fwd]) && is_empty([x,y+3*fwd]));
is_onfirstrank is (current_player==W && py==1) || (current_player==B && py==8);
can_gofwd is (py>=1) && (py<=8) && (py!=rank(4,current_player) || can_passfourthrank);
can_passfourthrank is (py==rank(4,current_player) && exists_otherpieceonrank([px,py]));
## A pjawn can move one or two squares forward, depending on where it is.
can_movefwd_one is can_gofwd && is_empty([px,py+fwd]);
can_movefwd_two is (is_onfirstrank && is_empty([px,py+fwd]) && is_empty([px,py+2*fwd]));
/*
* A pjawn can capture a piece if it is capable of moving forward to the next rank
* (i.e. esp rank 4-5) and the piece being captured is on a square adjacent to the
* pjawn's current column.
*/
can_capture_left is can_gofwd && is_opponent_piece([px-1,py+fwd]);
can_capture_right is can_gofwd && is_opponent_piece([px+1,py+fwd]);
can_capture_enpassant is enpassant_capture_possible;
/*
* A piece may be captured on the next move according to the enpassant rule
* if it's on the first rank and advances two squares, avoiding capture where
* it would have been captured had it advnaced one square.
*/
enpassant_capture_possible = 0;
enpassant_capture_x = -1;
enpassant_capture_y = -1;
enpassant_capture_px = -1;
enpassant_capture_py = -1;
enpassant_enabled is can_movefwd_two && x==px && y==py+fwd*2 && (
is_opponent_piece([px-1,py+fwd*2]) ||
is_opponent_piece([px+1,py+fwd*2])
);
/* If enpassant is to be enabled on the next move, record the position of the
* piece that can be captured (enpassant_px,enpassant_py), and the square we're
* allowed to move into (enpassant_x,enpassant_y)
*/
enpassant_x is (enpassant_enabled ? px : -1);
enpassant_y is (enpassant_enabled ? py+fwd : -1);
enpassant_px is (enpassant_enabled ? x : -1);
enpassant_py is (enpassant_enabled ? y : -1);
/* A pjawn can be placed on any column on the first rank provided there
* are pieces available and the squares in the first 4 ranks of the column
* are empty
*/
can_placepiece is pieces_available &&
four_ranks_empty &&
is_empty(current_square);
can_movepiece is can_movefwd_one || can_movefwd_two;
can_capturepiece is can_capture_first || can_capture;
can_play is (can_placepiece || can_movepiece || can_capturepiece);
/*
* Define a list of the possible moves that can be made at any time
*/
possible_moves is [ can_movefwd_one,
can_movefwd_two,
can_capture_left,
can_capture_right,
can_capture_enpassant ];
/*
* Define a string name for each rule - used for hints
*/
move_names is [ "Can move forward one square",
"Can move forward two squares",
"Can capture opponent piece on left",
"Can capture opponent piece on right",
"Can capture piece [enpassant]" ];
/* These definitions enforce the rules on the player's chosen square.
*/
can_play_place is (can_placepiece &&
y==rank(1,current_player)
);
can_play_move is (can_movefwd_one && x==px && y==py+fwd) ||
(can_movefwd_two && x==px && y==py+fwd*2);
can_play_capture is can_gofwd && (
(x==px-1 && y==py+fwd && can_capture_left) ||
(x==px+1 && y==py+fwd && can_capture_right)
);
can_play_enpassant is can_gofwd &&
x==enpassant_capture_x &&
y==enpassant_capture_y;
/*
* The game ends when a a player has won, or if no moves are possible.
*/
game_over is black_win || white_win;
black_win is white_lose || (b1[2] == 1) || (b2[2] == 1) || (b3[2] == 1) || (b4[2] == 1) || (b5[2] == 1) || (b6[2] == 1) || (b7[2] == 1) || (b8[2] == 1);
white_win is black_lose || (w1[2] == 8) || (w2[2] == 8) || (w3[2] == 8) || (w4[2] == 8) || (w5[2] == 8) || (w6[2] == 8) || (w7[2] == 8) || (w8[2] == 8);
white_lose is (current_player == W) && (available_moves == 0);
black_lose is (current_player == B) && (available_moves == 0);
available_moves is poss_move_count + poss_place_count;
poss_move_count is poss_moves#; ## Number of moves that are currently possible
poss_place_count is poss_places#; ## Number of placements that are currently possible
poss_moves is pieces_can_move(current_player); ## Moves that are currently possible for pieces currently on the board
poss_places is squares_can_place(current_player,pieces_available); ## Squares into which a square can currently be placed
/*
* Returns a list of pieces that can be moved, and the moves possible
* for each piece.
*/
func pieces_can_move {
auto result,i,m,p;
result = [];
## Move the game into "testing mode" to allow us to test pieces and squares
## Also save any currently selected piece
testing_square = 1;
p = (selected_piece != @) ? selected_piece : [0,0];
for (i=1; i<=current_pieces#; i++) {
selected_piece = current_pieces[i];
if (selected_piece != [0,0]) {
## Test each possible move against the current piece
for (m=1; m<=possible_moves#; m++) {
if (possible_moves[m] != 0) {
## The current move is possible with this piece
result = result // [[selected_piece,m]];
}
}
}
}
selected_piece = p;
testing_square = 0;
return result;
}
/*
* Returns a list of squares into which a piece can currently be placed
*/
func squares_can_place {
auto result,p,x,y;
testing_square = 1;
result = [];
if (pieces_available) {
y = rank(1,current_player);
for (x=1; x<=8; x++) {
current_square = [x,y];
if (can_placepiece)
result = result // [[x,y]];
}
}
testing_square = 0;
return result;
}