Announcement: The Bozochess Project

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Source release 2011.11.08 available

Post by sje »

The BozoChess Pascal source release 2011.11.08 is available for free; just send me a PM or email and it's on its way.

This release does not yet have a general search, so it can't function as an engine at this point. I plan to have a basic search up and running within a week, but not today.

There is only one source file:

Code: Select all

$ wc bozochess.pas
    8225   29038  269365 bozochess.pas
If you don't have a Pascal compiler, you can get one for free at http://www.freepascal.org/download.var
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

After five weeks, some emendations

Post by sje »

sje wrote:3) Bozochess supports the modern chess data interchange formats (SAN, FEN, EPD, PGN); it does not support the EDN (English Descriptive Notation) used in Chess 0.5.
The EPD code is not yet there; when implemented, it will use the "New EPD" format. This is the same as the original EPD format except that the the half move clock and full move number always appear as the fifth and sixth tokens on a line. So the first six fields of an EPD record are exactly the same as the six fields of the corresponding FEN record.
sje wrote:4) Bozochess routine names are not constrained to the six character limit from the Old Days Pascal. There has been considerable effort to use meaningful and somewhat long names which can be decoded without help from a cheat sheet.
A minor correction: having reviewed the original Pascal compiler, I note that all non-external identifiers had TEN characters of significance. (Sixty bits per word, six bits per character.)
sje wrote:7) There is not a single damn variant field list in the entire program. No machine dependency kludges here.
No longer entirely true; there are three exceptions: the bitboard type, the board type, and the search limit type. None of these has any machine dependencies. The code gives further details.
sje wrote:8) The poor string processing of the Old Days Pascal is gone. Exactly how this will be replaced is still uncertain, but might use the ansistring type seen in Free Pascal.
The code works with either old style Borland strings (max 255 character length) or the newer ansistrings (unrestricted length).
jwes
Posts: 778
Joined: Sat Jul 01, 2006 7:11 am

Re: After five weeks, some emendations

Post by jwes »

sje wrote:
sje wrote:3) Bozochess supports the modern chess data interchange formats (SAN, FEN, EPD, PGN); it does not support the EDN (English Descriptive Notation) used in Chess 0.5.
The EPD code is not yet there; when implemented, it will use the "New EPD" format. This is the same as the original EPD format except that the the half move clock and full move number always appear as the fifth and sixth tokens on a line. So the first six fields of an EPD record are exactly the same as the six fields of the corresponding FEN record.
Any chance the ep field could be there only if there is an en passant capture?
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: After five weeks, some emendations

Post by sje »

jwes wrote:
sje wrote:
sje wrote:3) Bozochess supports the modern chess data interchange formats (SAN, FEN, EPD, PGN); it does not support the EDN (English Descriptive Notation) used in Chess 0.5.
The EPD code is not yet there; when implemented, it will use the "New EPD" format. This is the same as the original EPD format except that the the half move clock and full move number always appear as the fifth and sixth tokens on a line. So the first six fields of an EPD record are exactly the same as the six fields of the corresponding FEN record.
Any chance the ep field could be there only if there is an en passant capture?
YES This has already been implemented. A superfluous en passant target square can't even make it into the program; the search won't produce one at any node, and any user FEN input is similarly filtered.

Transcript:

Code: Select all

[] f4 e6 Kf2 Qf6 f5 g5
Playing: 1 f4
Playing: 1... e6
Playing: 2 Kf2
Playing: 2... Qf6
Playing: 3 f5
Playing: 3... g5
[] dfen
FEN: rnb1kbnr/pppp1p1p/4pq2/5Pp1/8/8/PPPPPKPP/RNBQ1BNR w kq - 0 4
Note the lack of an en passant target square in the resulting FEN.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Revised mate finder routine

Post by sje »

The main purpose of the mate finder routine in BozoChess is not to find checkmates; instead, it is to test the data structures and control flow of a general search without the need to get all of the many pieces of a general search running. The mate finder also serves a teaching function as it is a near minimal tree search. I suppose perft() can be the minimal search depending on one's viewpoint; like the mate finder, it also serves a teaching function.

Code: Select all

procedure CpcFindMate(var cpc: cpctype; fullmovecount: Integer);
    var
      ply: plytype;
      depth: Integer;

    function MateNode(var mywindow: windowtype; var mypv: variationtype): svtype;
      var
        isdefending: Boolean;
        gms: gmstype;
        index: Integer;
        subwindow: windowtype;
        subsv: svtype;
        subpv: variationtype;

      function AnotherTry: Boolean; inline;
        var
          myresult: Boolean;
      begin
        if isdefending then
          myresult := IsSvLosing(mywindow.alfa)
        else
          myresult := not IsSvMating(mywindow.alfa);
        AnotherTry := myresult
      end; { AnotherTry }

    begin
      with cpc, ssc, gms do
        begin
          {CpcTraceCV(cpc);}
          Inc(nodecount);
          if depth = 0 then
            if PosIsCheckmate(currpos) then
              mywindow.alfa := svlosein0
            else
              mywindow.alfa := sveven
          else
            begin
              VariationInit(subpv);
              if depth = 1 then
                PosGenOnlyChecks(currpos, gms)
              else
                begin
                  PosGenerate(currpos, gms);
                  PosApplyOrderAntiMobility(currpos, gms);
                  GmsSortByScore(gms)
                end;
              isdefending := Odd(ply);
              index := 0;
              Inc(ply); Dec(depth);
              while &#40;index < movecount&#41; and WindowIsOpen&#40;mywindow&#41; and AnotherTry do
                begin
                  WindowShiftDn&#40;mywindow, subwindow&#41;;
                  VariationRecycle&#40;subpv&#41;;
                  VariationAppendMove&#40;currvar, moves&#91;index&#93;);
                  PosExecute&#40;currpos, moves&#91;index&#93;);
                  subsv &#58;= CalcSvUp&#40;MateNode&#40;subwindow, subpv&#41;);
                  PosRetract&#40;currpos&#41;;
                  VariationRecycleTail&#40;currvar&#41;;
                  if subsv > mywindow.alfa then
                    begin
                      mywindow.alfa &#58;= subsv;
                      if subsv < mywindow.beta then
                        begin
                          VariationRecycle&#40;mypv&#41;;
                          VariationAppendMove&#40;mypv, moves&#91;index&#93;);
                          VariationAppend&#40;mypv, subpv&#41;
                        end
                    end;
                  Inc&#40;index&#41;
                end;
              Dec&#40;ply&#41;; Inc&#40;depth&#41;;
              VariationTerm&#40;subpv&#41;
            end
        end;
      MateNode &#58;= mywindow.alfa
    end; &#123; MateNode &#125;

  begin
    with cpc, ssc do
      begin
        ply &#58;= 0; depth &#58;= &#40;fullmovecount * 2&#41; - 1;
        predsv &#58;= MateNode&#40;rootwindow, predvar&#41;;
        TimerStop&#40;ssctimer&#41;; usedusec &#58;= TimerCurrent&#40;ssctimer&#41;;
        if IsSvMating&#40;predsv&#41; then
          begin
            VariationNotate&#40;predvar, rootpos&#41;;
            st &#58;= stforcedmate
          end
        else
          begin
            VariationRecycle&#40;predvar&#41;;
            st &#58;= stlimitdepth
          end
      end
  end; &#123; CpcFindMate &#125;
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

The split/merge score sort routine

Post by sje »

The split/merge score sort routine is used to sort an array of moves in descending order of scores where such a sorting is needed. The number of comparisons is O(N log N) vs O(N*N) for a bubble sort. A bubble sort can still be faster if the number of entries is small (say, N < 5). But even then, a bubble sort is not that much faster.

Because a move takes more storage than just a score, this sort sorts an index array instead and then applies the sorted index array to the moves to get a sorted array of moves.

A similar routine is used to sort an array of moves by ascending SAN order.

Code: Select all

  procedure GmsSortByScore&#40;var gms&#58; gmstype&#41;;
    var
      index&#58; Integer;
      tempmoves&#58; array &#91;gitype&#93; of movetype;
      altindex, saveindex&#58; array &#91;gitype&#93; of Integer;
      altsv, savesv&#58; array &#91;gitype&#93; of svtype;

    procedure SortScoreSegment&#40;start, count&#58; Integer&#41;;
      var
        start0, start1&#58; Integer;
        count0, count1&#58; Integer;
        tempindex&#58; Integer;
        tempsv&#58; svtype;
        fill&#58; Integer;
        pick0, pick1&#58; Integer;
        pick0limit, pick1limit&#58; Integer;
    begin
      if count > 1 then
        begin

          &#123; At least two records need sorted &#125;

          if count = 2 then
            begin

              &#123; Sort two records &#125;

              if altsv&#91;start&#93; < altsv&#91;start + 1&#93; then
                begin

                  &#123; Simple exchange &#125;

                  tempindex &#58;= altindex&#91;start&#93;;
                  altindex&#91;start&#93; &#58;= altindex&#91;start + 1&#93;;
                  altindex&#91;start + 1&#93; &#58;= tempindex;
                  tempsv &#58;= altsv&#91;start&#93;;
                  altsv&#91;start&#93; &#58;= altsv&#91;start + 1&#93;;
                  altsv&#91;start + 1&#93; &#58;= tempsv
                end
            end
          else
            begin

              &#123; Sort more than two records; start with split calculation &#125;

              count0 &#58;= count div 2; count1 &#58;= count - count0;
              start0 &#58;= start; start1 &#58;= start0 + count0;

              &#123; Sort the two segments &#125;

              SortScoreSegment&#40;start0, count0&#41;;
              SortScoreSegment&#40;start1, count1&#41;;

              &#123; Set up to merge the results of two segments &#125;

              fill &#58;= start;
              pick0 &#58;= start0; pick1 &#58;= start1;
              pick0limit &#58;= pick0 + count0; pick1limit &#58;= pick1 + count1;

              &#123; Fill while at least one unpicked record in each segment &#125;

              while &#40;pick0 < pick0limit&#41; and &#40;pick1 < pick1limit&#41; do
                begin
                  if altsv&#91;pick0&#93; > altsv&#91;pick1&#93; then
                    begin
                      savesv&#91;fill&#93; &#58;= altsv&#91;pick0&#93;; saveindex&#91;fill&#93; &#58;= altindex&#91;pick0&#93;;
                      Inc&#40;pick0&#41;
                    end
                  else
                    begin
                      savesv&#91;fill&#93; &#58;= altsv&#91;pick1&#93;; saveindex&#91;fill&#93; &#58;= altindex&#91;pick1&#93;;
                      Inc&#40;pick1&#41;
                    end;
                  Inc&#40;fill&#41;
                end;

              &#123; Add any remaining records from the first segment &#125;

              while pick0 < pick0limit do
                begin
                  savesv&#91;fill&#93; &#58;= altsv&#91;pick0&#93;; saveindex&#91;fill&#93; &#58;= altindex&#91;pick0&#93;;
                  Inc&#40;pick0&#41;; Inc&#40;fill&#41;
                end;

              &#123; Add any remaining records from the second segment &#125;

              while pick1 < pick1limit do
                begin
                  savesv&#91;fill&#93; &#58;= altsv&#91;pick1&#93;; saveindex&#91;fill&#93; &#58;= altindex&#91;pick1&#93;;
                  Inc&#40;pick1&#41;; Inc&#40;fill&#41;
                end;

              &#123; Copy to result &#125;

              for fill &#58;= start to &#40;start + count - 1&#41; do
                begin
                  altsv&#91;fill&#93; &#58;= savesv&#91;fill&#93;; altindex&#91;fill&#93; &#58;= saveindex&#91;fill&#93;
                end
            end
        end
    end; &#123; SortScoreSegment &#125;

  begin
    with gms do
      if movecount > 1 then
        begin
          for index &#58;= 0 to movecount - 1 do
            begin
              tempmoves&#91;index&#93; &#58;= moves&#91;index&#93;;
              altindex&#91;index&#93; &#58;= index; altsv&#91;index&#93; &#58;= moves&#91;index&#93;.sv
            end;
          SortScoreSegment&#40;0, movecount&#41;;
          for index &#58;= 0 to movecount - 1 do
            moves&#91;index&#93; &#58;= tempmoves&#91;altindex&#91;index&#93;&#93;
        end
  end; &#123; GmsSortByScore &#125;
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Revesed command set

Post by sje »

Bozo has a revised command set as the program slowly yet surely progresses towards having a full move selection search.

I'll have a new release ready on Monday, November 21st.

Code: Select all

&#91;&#93; help
Enter a command, or a sequence of one or more SAN chess moves 
  Commands&#58;
    bench      Run the benchmark
    d          Display everything
    db         Display board &#40;ANSI color&#41;
    dbbdb      Display bitboard database
    dbmono     Display board &#40;monochrome&#41;
    dfen       Display FEN
    dm         Display moves
    dp         Display position
    dpgn       Display PGN
    dpi        Display program identification
    echo       Echo parameters
    exit       Exit program
    ffperft    Summing over FEN <file> data, run perft to <depth>
    g          Search for a move and then play it
    gg         Play both sides until the end of the game
    help       Show help
    loadfen    Load FEN from a <file>
    loadpgn    Load PGN from a <file>
    mate       Search for a checkmate in <n> full moves
    new        New game
    noop       No operation
    perftbulk  Run perft to <depth> with bulk counting
    perftfull  Run perft to <depth> with each node visited
    perfttran  Run perft to <depth> with transposition help
    rg         Generate and display a single random game
    rgdump     Dump to a <file> <n> random games
    rgstat     Generate a report for <number> random game&#40;s&#41;
    rm         Retract move
    rmp        Retract move pair
    s          Search for a move but do not play it
    savefen    Save FEN to a <file>
    savepgn    Save PGN to a <file>
    selftest   Run the self test
    sfen       Set FEN <mpd> <good> <cavs> <epsq> <hmvc> <fmvn>
    stag       Set PGN <tagname> to <tagvalue>
    test       Run developer test
&#91;&#93; d
BozoChess 2011.11.17
bRbNbBbQbKbBbNbR
bPbPbPbPbPbPbPbP
  &#58;&#58;  &#58;&#58;  &#58;&#58;  &#58;&#58;
&#58;&#58;  &#58;&#58;  &#58;&#58;  &#58;&#58;  
  &#58;&#58;  &#58;&#58;  &#58;&#58;  &#58;&#58;
&#58;&#58;  &#58;&#58;  &#58;&#58;  &#58;&#58;  
wPwPwPwPwPwPwPwP
wRwNwBwQwKwBwNwR
FEN&#58; rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
Played moves count&#58; 0
Move count&#58; 20
Moves&#58; Na3 Nc3 Nf3 Nh3 a3 a4 b3 b4 c3 c4 d3 d4 e3 e4 f3 f4 g3 g4 h3 h4
Material&#58; White&#58; +40.600   Black&#58; +40.600   Total&#58; +81.200
Hashes&#58; Main&#58; a8932a4a2da7cb33   Pawn&#58; 031e4f8f595d6880   Game&#58; a8932a4a2da7cb33
Elapsed time&#58; 000.00&#58;00&#58;03.473

&#91;Event "Unknown event"&#93;
&#91;Site "Unknown site"&#93;
&#91;Date "2011.11.17"&#93;
&#91;Round "-"&#93;
&#91;White "Unknown player"&#93;
&#91;Black "Unknown player"&#93;
&#91;Result "*"&#93;

*

&#91;&#93; sfen 1B1bk3/7R/1r5p/1Pp1p3/2NP1N2/1RP2Pp1/2QK4/8 w - - 2 52 
&#91;&#93; mate 4
Found mate in 4
PV&#58; 52 Rh8+ Ke7 53 Qh7+ Kf6 54 Qg6+ Ke7 55 Qg7#
Count&#58; 5361   Time&#58; 000.00&#58;00&#58;00.098   Frequency&#58; 54704
&#91;&#93; sfen 1B6/2r5/8/p1bQ4/2p1P1Pp/P2P3P/1P1k2BK/R2b1R2 w - - 5 49 
&#91;&#93; mate 4
Found mate in 4
PV&#58; 49 Raxd1+ Ke2 50 Bf3+ Ke3 51 Rde1+ Kd2 52 Re2#
Count&#58; 2570   Time&#58; 000.00&#58;00&#58;00.037   Frequency&#58; 69459
&#91;&#93; sfen 1B6/6k1/8/4pB2/7Q/3r4/2K5/3Q2q1 b - - 3 99 
&#91;&#93; mate 4
Found mate in 4
PV&#58; 99... Qxd1+ 100 Kb2 Qb3+ 101 Ka1 Rd1+ 102 Bb1 Rxb1#
Count&#58; 169   Time&#58; 000.00&#58;00&#58;00.007   Frequency&#58; 24142
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Slaying the bogus en passant target square

Post by sje »

Can you see what's wrong with the following checkmates?

Code: Select all

1nb2b1n/rp4r1/2B5/p2p1pP1/1p1Pp1Pp/1RP4K/P1k1Q3/R1B3N1 b - d3 0 38
1nb2bkr/7p/2P5/p3pP1n/P2P1P1P/K5p1/2q5/1NQ1N2R w - e6 0 40
3R2n1/5pr1/p6b/2P3pk/PrP1Q1Pp/1P2pP2/4P3/3K1BNR b - g3 0 41
3n4/p4p2/1pb1R3/1rNk1P2/1PPp2P1/b2Q2P1/P2P4/1RBK4 b - c3 0 48
8/n3R3/5p2/p4krP/P3Pp1P/1b3Q2/1pP4N/4RK2 b - e3 0 55
rn3bnr/pb1q2p1/3p1pPk/2p1p2p/2pP4/PP3P1P/4P1R1/RNBQKB2 b Q d3 0 17
rnbq2nr/1p1p4/1Ppp4/p4p1p/2kPPpPb/PRP5/5P1P/3QKB1R b - e3 0 23
Or with the following mate-in-1 position?

Code: Select all

rn1q2nr/5pB1/p7/PppP1kpP/6b1/1Q1K3N/1PP1Pb2/RN3B1R w - c6 0 20
Yes, that's right. In each case the en passant target square is bogus as no legal en passant capture is possible. (Note that no checkmate or stalemate can have a legal en passant target.)

The latest revision of Bozo has a new command: "ffnormal" which does FEN file normalization at the rate of about 50,000 positions per second. Part of this normalization is to detect and eliminate any bogus en passant target squares.

A future command "efnomal" will do the same (and more) for EPD files.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

The first 33 lines of the program source

Post by sje »

The license text contained in the first 33 lines of the BozoChess Pascal source are taken directly from the "Modified BSD License" as it appears on the http://en.wikipedia.org/wiki/BSD_licenses Wikipedia page.

Code: Select all

&#123; BozoChess&#58; a simple bitboard chess program in Pascal &#125;

&#123;
  The following BSD License governs the distribution and use of this program&#58;
  
  Copyright &#40;c&#41; 2011 by the author, S. J. Edwards
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met&#58;

      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.

      * Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.

      * Neither the name of the author nor the names of any contributors may
        be used to endorse or promote products derived from this software
        without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  &#40;INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION&#41; HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  &#40;INCLUDING NEGLIGENCE OR OTHERWISE&#41; ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
&#125;
The sad fact that UPPER CASE has become customary in legal disclaimers further lowers my general opinion of the shyster profession.

"The first thing we do, let's kill all the lawyers."

From Henry The Sixth, Part 2, Act 4, Scene 2
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Monday progress report

Post by sje »

Monday progress report:

There will be no release this Monday as not much has been added in the terms of externally visible functionality. Internally, there has been progress in several areas such as with source reformatting (now a maximum of one statement per line) and with more source commentary.

BozoChess now has full EPD formation and export capability. However, as is with the case of PGN, importing and parsing is not yet implemented. These import tasks are on the to-do list, I promise. Having EPD import opens the way for running test suites, and PGN import not only allows restoration of a saved game, but is also needed for building an opening book from a PGN collection.

The program now understands about killer moves with its killerstype record containing most recent and second most recent killer moves at a ply. A killers record lives inside a pirtype (ply indexed) record, and a vector of plytype (0..255) pirtype records is used to store all the ply indexed items during a search. This is all working for the currently implemented checkmate search.

How big is the Pascal source?

Code: Select all

gail&#58;bozochess sje$ wc *.pas
   11473   33848  333207 bozochess.pas
gail&#58;bozochess sje$ grep \; *.pas | wc -l
    4841
gail&#58;bozochess sje$ grep procedure *.pas | wc -l
     330
gail&#58;bozochess sje$ grep function *.pas | wc -l
     177