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 |',
                     '<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;
