chess: procedure options(main);
/****************************************************************
* *
* This program has served as a timing test case throughout *
* the PL/I development. We expect that this program will be *
* extensively changed as various programmers work with it - *
* if you make any great improvements, let us know and we'll *
* send your updated version with our next release (you'll *
* also go in line in the list below, for eternal fame). *
* Feel free to distribute this program, or altered versions *
* thereof, but please keep the list of names intact. Oh, by *
* the way, CHESS currently plays against itself, and reads a *
* value to determine the search depth (don't make the value *
* too large, or you'll wait quite a while for the moves). *
* *
* Programmer Address Date *
* ---------- ------------------- ------ *
* JWB Digital Research 3/79 *
* *
* *
* (P.S., in its current state, this program takes 1:58 to *
* compile on a 4-mhz Z-80 with a hard disk attached, and 1:45 *
* if the $Q compile toggle is enabled.) *
****************************************************************/
declare (white initial (1), none initial (0), black initial (-1))
static fixed (1);
declare (empty_square initial (0), illegal_square initial (1),
white_pawn initial (2), white_knight initial (3),
white_bishop initial (4), white_rook initial (5),
white_queen initial (6), white_king initial (7),
black_pawn initial (8), black_knight initial (9),
black_bishop initial (10), black_rook initial (11),
black_queen initial (12), black_king initial (13))
static fixed (4);
declare piece_value (0 : 13) static fixed initial
(0,0,100,290,310,500,900,8000,-100,-290,-310,-500,-900,-8000);
declare piece_picture (0 : 13) static char (4) varying initial
(' |','___|',' P |',' N |',' B |',' R |',' Q |',' K |',
'
|','|','|','|','|','|');
declare bishop_like (0 : 13) static bit initial
('0','0','0','0','1','0','1','0','0','0','1','0','1','0');
declare rook_like (0 : 13) static bit initial
('0','0','0','0','0','1','1','0','0','0','0','1','1','0');
declare board (0 : 119) static fixed (4) initial
(01,01,01,01,01,01,01,01,01,01,
01,01,01,01,01,01,01,01,01,01,
01,11,09,10,12,13,10,09,11,01,
01,08,08,08,08,08,08,08,08,01,
01,00,00,00,00,00,00,00,00,01,
01,00,00,00,00,00,00,00,00,01,
01,00,00,00,00,00,00,00,00,01,
01,00,00,00,00,00,00,00,00,01,
01,02,02,02,02,02,02,02,02,01,
01,05,03,04,06,07,04,03,05,01,
01,01,01,01,01,01,01,01,01,01,
01,01,01,01,01,01,01,01,01,01);
declare center (0 : 119) static fixed (2) initial
(00,00,00,00,00,00,00,00,00,00,
00,00,00,00,00,00,00,00,00,00,
00,00,00,00,00,00,00,00,00,00,
00,00,01,01,01,01,01,01,00,00,
00,00,01,02,02,02,02,01,00,00,
00,00,01,02,03,03,02,01,00,00,
00,00,01,02,03,03,02,01,00,00,
00,00,01,02,02,02,02,01,00,00,
00,00,01,01,01,01,01,01,00,00,
00,00,00,00,00,00,00,00,00,00,
00,00,00,00,00,00,00,00,00,00,
00,00,00,00,00,00,00,00,00,00);
declare bonus (0 : 119) static fixed (4) initial
( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 3, 2, 1,-9, 3, 4, 1, 0,
0, 1, 1, 1, 6, 7, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 6, 7, 1, 1, 1, 0,
0, 1, 3, 2, 1,-9, 3, 4, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
declare (((max_stage,max_cap) initial (3), ply, max_ply) fixed (3),
((move_index, best_move, first_move, last_move) fixed,
(stage_lim, stage_lst) fixed (3),
move_color fixed (1), cur_piece fixed (4),
move_bonus fixed (4)) (0 : 5),
next fixed, /* index to next available of */
((est_score, move_score) fixed,
(move_from, move_to, move_dir) fixed (7))
(0 : 350)) static;
/****************************************************************
* *
* *
* *
****************************************************************/
display: procedure;
declare (i, j) fixed;
declare dashes static varying character (41) initial
(' +---+---+---+---+---+---+---+---+'),
spaces static varying character (9) initial (' |');
put skip(2);
do i = 20 to 90 by 10;
write from(dashes);
put skip;
write from(spaces);
do j = 1 to 8;
write from(piece_picture (board (i + j)));
end;
put skip;
end;
write from(dashes);
put skip(2);
end display;
/****************************************************************
* *
* *
* *
****************************************************************/
display_move: procedure (move);
declare move fixed;
declare spaces varying character (6) static initial (' '),
dash varying character (1) static initial ('-'),
takes varying character (1) static initial ('x'),
names (0 : 119) varying character (2) static initial
(' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
' ','a8','b8','c8','d8','e8','f8','g8','h8',' ',
' ','a7','b7','c7','d7','e7','f7','g7','h7',' ',
' ','a6','b6','c6','d6','e6','f6','g6','h6',' ',
' ','a5','b5','c5','d5','e5','f5','g5','h5',' ',
' ','a4','b4','c4','d4','e4','f4','g4','h4',' ',
' ','a3','b3','c3','d3','e3','f3','g3','h3',' ',
' ','a2','b2','c2','d2','e2','f2','g2','h2',' ',
' ','a1','b1','c1','d1','e1','f1','g1','h1',' ',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ');
write from(spaces);
write from(names (move_from (move)));
if cur_piece (ply) = empty_square then
write from(dash);
if cur_piece (ply) ~= empty_square then
write from(takes);
write from(names (move_to (move)));
end display_move;
/****************************************************************
* *
* *
* *
****************************************************************/
color: procedure (square) returns (fixed (1));
declare square fixed (7);
if board (square) >= black_pawn then return (black);
if board (square) <= illegal_square then return (none);
return (white);
end color;
/****************************************************************
* *
* *
* *
****************************************************************/
sort_moves: procedure;
declare ((i, j, t) fixed, s fixed (7), color fixed (1),
switched bit) static;
color = move_color (ply);
do switched = '1' while (switched);
switched = '0';
do i = first_move (ply) repeat (j)
while (i < last_move (ply));
j = i + 1;
t = move_score (i) - move_score (j);
if t < 0 & color = white | t > 0 & color = black then
do;
switched = '1';
t = est_score (i);
est_score (i) = est_score (j);
est_score (j) = t;
t = move_score (i);
move_score (i) = move_score (j);
move_score (j) = t;
s = move_from (i);
move_from (i) = move_from (j);
move_from (j) = s;
s = move_to (i);
move_to (i) = move_to (j);
move_to (j) = s;
s = move_dir (i);
move_dir (i) = move_dir (j);
move_dir (j) = s;
if i > first_move (ply) then j = j - 2;
end;
end;
end;
best_move (ply) = first_move (ply);
end sort_moves;
/****************************************************************
* *
* *
* *
****************************************************************/
generate_moves: procedure;
declare (move_piece fixed (4), from_square fixed (7),
castle_bonus fixed (4), move fixed) static;
/****************************************************************
* *
* *
* *
****************************************************************/
add_move: procedure (to_square, direction);
declare to_square fixed (7), direction fixed (5);
declare (current_piece fixed (4), score fixed) static;
current_piece = board (to_square);
if current_piece = illegal_square then return;
if color (to_square) = move_color (ply) then return;
move_from (next) = from_square;
move_to (next) = to_square;
move_dir (next) = direction;
score = center (to_square) - center (from_square) +
bonus (from_square);
if color (from_square) = black then score = - score;
score = score - piece_value (current_piece);
est_score (next) = score;
move_score (next) = score;
if move_color (ply) = white then
do;
if score > move_score (best_move (ply)) then
best_move (ply) = next;
end; else
do;
if score < move_score (best_move (ply)) then
best_move (ply) = next;
end;
last_move (ply) = next;
next = next + 1;
end add_move;
/****************************************************************
* *
* *
* *
****************************************************************/
multi_move: procedure (dir);
declare dir fixed (5);
declare ts fixed (7) static;
do ts = from_square + dir repeat (ts + dir)
while (board (ts) = empty_square);
call add_move (ts, dir);
end;
call add_move (ts, dir); /* adds captures */
end multi_move;
/****************************************************************
* *
* *
* *
****************************************************************/
generate_piece_moves: procedure;
move_piece = board (from_square);
if move_piece = white_pawn then
do;
if board (from_square - 10) = empty_square then
do; call add_move (from_square - 10, -10);
if board (from_square - 20) = empty_square then
if bonus (from_square) ~= 0 then
call add_move (from_square - 20, -10);
end;
if color (from_square - 9) = -move_color (ply) then
call add_move (from_square - 9, -9);
if color (from_square-11) = -move_color (ply) then
call add_move (from_square - 11, -11);
end; else
if move_piece = black_pawn then
do;
if board (from_square + 10) = empty_square then
do; call add_move (from_square + 10, 10);
if board (from_square + 20) = empty_square then
if bonus (from_square) ~= 0 then
call add_move (from_square + 20, 10);
end;
if color (from_square + 9) = -move_color (ply) then
call add_move (from_square + 9, 9);
if color (from_square+11) = -move_color (ply) then
call add_move (from_square + 11, 11);
end; else
if move_piece = white_knight |
move_piece = black_knight then
do;
call add_move (from_square - 21, -21);
call add_move (from_square - 19, -19);
call add_move (from_square - 12, -12);
call add_move (from_square - 8, -8);
call add_move (from_square + 8, 8);
call add_move (from_square + 12, 12);
call add_move (from_square + 19, 19);
call add_move (from_square + 21, 21);
end; else
if move_piece = white_king |
move_piece = black_king then
do;
call add_move (from_square - 11, -11);
call add_move (from_square - 10, -10);
call add_move (from_square - 9, -9);
call add_move (from_square - 1, -1);
call add_move (from_square + 1, 1);
call add_move (from_square + 9, 9);
call add_move (from_square + 10, 10);
call add_move (from_square + 11, 11);
castle_bonus = 0;
if bonus (from_square) ~= 0 then
do;
if move_piece = white_king then
if from_square = 95 then
castle_bonus = 15;
end; else
do;
if move_piece = black_king then
if from_square = 25 then
castle_bonus = -15;
end;
if castle_bonus ~= 0 then
do;
if bonus (from_square + 3) ~= 0 then
if board (from_square + 3) = move_piece-2 then
if board (from_square + 1) = empty_square then
if board (from_square + 2) = empty_square then
do;
call add_move (from_square + 2, 2);
est_score (next - 1) =
est_score (next - 1) + castle_bonus;
move_score (next - 1) =
move_score (next - 1) + castle_bonus;
end;
if bonus (from_square - 4) ~= 0 then
if board (from_square - 4) = move_piece-2 then
if board (from_square - 3) = empty_square then
if board (from_square - 2) = empty_square then
if board (from_square - 1) = empty_square then
do;
call add_move (from_square - 2, -2);
est_score (next - 1) =
est_score (next - 1) + castle_bonus;
move_score (next - 1) =
move_score (next - 1) + castle_bonus;
end;
end;
end; else
do;
if bishop_like (move_piece) then
do;
call multi_move (-11);
call multi_move (-9);
call multi_move (9);
call multi_move (11);
end;
if rook_like (move_piece) then
do;
call multi_move (-10);
call multi_move (10);
call multi_move (1);
call multi_move (-1);
end;
end;
end generate_piece_moves;
/****************************************************************
* *
* *
* *
****************************************************************/
move_prohibited: procedure (best_move, move) returns (bit);
declare (best_move, move) fixed;
declare to_sq fixed (7) static;
do to_sq = move_from (best_move)
repeat (to_sq + move_dir (best_move))
while (to_sq ~= move_to (best_move));
if move_to (move) = to_sq then return ('1');
end;
if move_to (move) = to_sq then return ('1');
return ('0');
end move_prohibited;
first_move (ply) = next;
best_move (ply) = next;
move_index (ply) = next - 1;
move = best_move (ply - 2);
if ply >= max_ply then
if ply >= 2 then
if move_from (move_index (ply - 2)) ~= move_from (move) then
if move_from (move) ~= move_to (move_index (ply - 1)) then
if move_to (move) ~= move_from (move_index (ply - 1)) then
if ~ move_prohibited (move, move_index (ply - 1)) then
if ~ move_prohibited (move, move_index (ply - 2)) then
do;
from_square = move_from (move);
call add_move (move_to (move), move_dir (move));
return;
end;
do from_square = 21 to 98;
if color (from_square) = move_color (ply) then
call generate_piece_moves();
if ply >= max_ply then
if alpha_beta_cutoff (best_move (ply)) then
do;
last_move (ply) = next - 1;
return;
end;
end;
return;
end generate_moves;
/****************************************************************
* *
* *
* *
****************************************************************/
alpha_beta_cutoff: procedure (move) returns (bit);
declare move fixed (7);
declare score fixed static;
if ply = 0 then return ('0');
if move_index (ply - 1) = first_move (ply - 1) then
return ('0');
if move_index (ply) < first_move (ply) then
return ('0');
score = move_score (move) +
move_score (move_index (ply - 1)) -
move_score (best_move (ply - 1));
if move_color (ply) = white then return (score > 0);
return (score < 0);
end alpha_beta_cutoff;
/****************************************************************
* *
* *
* *
****************************************************************/
select_next_move: procedure returns (bit);
declare (to, from) fixed (7) static;
if alpha_beta_cutoff (move_index (ply)) then return ('0');
move_index (ply) = move_index (ply) + 1;
if move_index (ply) > last_move (ply) then return ('0');
to = move_to (move_index (ply));
from = move_from (move_index (ply));
cur_piece (ply) = board (to);
if ply >= max_cap & cur_piece (ply) = empty_square then
return ('0');
board (to) = board (from);
board (from) = empty_square;
move_bonus (ply) = bonus (from);
bonus (from) = 0;
if board (to) = white_king | board (to) = black_king then
do;
if to = from + 2 then
do;
board (to - 1) = board (to) - 2;
board (to + 1) = empty_square;
end; else
if to = from - 2 then
do;
board (to + 1) = board (to) - 2;
board (to - 2) = empty_square;
end;
end;
return ('1');
end select_next_move;
/****************************************************************
* *
* *
* *
****************************************************************/
retract_move: procedure;
declare (to, from) fixed (7) static;
to = move_to (move_index (ply));
from = move_from (move_index (ply));
board (from) = board (to);
board (to) = cur_piece (ply);
bonus (from) = move_bonus (ply);
if board (from) = white_king | board (from) = black_king then
do;
if to = from + 2 then
do;
board (to + 1) = board (from) - 2;
board (to - 1) = empty_square;
end; else
if to = from - 2 then
do;
board (to - 2) = board (from) - 2;
board (to + 1) = empty_square;
end;
end;
end retract_move;
/****************************************************************
* *
* *
* *
****************************************************************/
score_ply_moves: procedure recursive;
do while (select_next_move ());
call stage();
move_score (move_index (ply)) =
move_score (move_index (ply)) +
move_score (best_move (ply + 1));
if move_color (ply) = white then
do;
if move_score (move_index (ply)) >
move_score (best_move (ply)) then
best_move (ply) = move_index (ply);
end; else
do;
if move_score (move_index (ply)) <
move_score (best_move (ply)) then
best_move (ply) = move_index (ply);
end;
call retract_move();
end;
end score_ply_moves;
/****************************************************************
* *
* *
* *
****************************************************************/
stage: procedure recursive;
declare i fixed static;
ply = ply + 1;
if ply ~= 0 then move_color (ply) = -move_color (ply - 1);
stage_lst (ply) = max_ply;
stage_lim (ply) = ply + 2;
if stage_lim (ply) > max_stage then
stage_lim (ply) = max_stage;
call generate_moves();
if ply ~= 0 then
if cur_piece (ply - 1) = white_king |
cur_piece (ply - 1) = black_king then
do;
move_score (best_move (ply)) = 0;
next = first_move (ply);
ply = ply - 1;
return;
end;
if ply < max_ply then
do max_ply = stage_lim (ply) repeat (stage_lim (ply) + 1)
while (max_ply <= stage_lst (ply));
stage_lim (ply) = max_ply;
do i = first_move (ply) to last_move (ply);
move_score (i) = est_score (i);
end;
move_index (ply) = first_move (ply) - 1;
call score_ply_moves();
call sort_moves();
end;
next = first_move (ply);
max_ply = stage_lst (ply);
ply = ply - 1;
end stage;
/****************************************************************
* *
* *
* *
****************************************************************/
make_move: procedure (color);
declare color fixed (1);
declare (to, from) fixed static;
next = 0;
ply = -1;
max_ply = max_stage;
move_color (0) = color;
call stage();
ply = 0;
to = move_to (best_move (0));
from = move_from (best_move (0));
cur_piece (0) = board (to);
board (to) = board (from);
board (from) = empty_square;
bonus (from) = 0;
bonus (to) = 0;
if board (to) = white_king | board (to) = black_king then
do;
if to = from + 2 then
do;
board (to - 1) = board (to) - 2;
board (to + 1) = empty_square;
bonus (to + 1) = 0;
end; else
if to = from - 2 then
do;
board (to + 1) = board (to) - 2;
board (to - 2) = empty_square;
bonus (to - 2) = 0;
end;
end;
call display_move (best_move (0));
end make_move;
declare (move_number, move_display)
fixed (7) static initial(1);
put skip list('Chess Program Version 1.0');
put skip list('Type Search Depth ');
get list(max_cap);
max_stage = max_cap;
put list('Type Number of Moves Between Displays ');
get list(move_display);
do while ('1');
call make_move (white);
call make_move (black);
put skip;
move_number = move_number + 1;
if move_number > move_display then
do;
move_number = 1;
call display();
end;
end;
end chess;