ChessLisp for everyone!

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

ChessLisp for everyone!

Post by sje »

I'm working on a portable ChessLisp interpreter that will be released under a Creative Commons license.

An early beta should be available by the end of this year.

The ReadMe file:

This is the ReadMe file version v2010.07.22 for the ChessLisp interpreter.

Copyright (C) 2010 by chessnotation@me.com (Some rights reserved)

License: Creative Commons Attribution-Share Alike 3.0
See: http://creativecommons.org/licenses/by-sa/3.0/
Caution: No warranty; use at your own risk.


Abstract:

ChessLisp is a general purpose Lisp which also contains features designed for chess programming. The general purpose features of ChessLisp are taken largely from Common Lisp; some features of Scheme are also present. The chess specific features are intended for abstraction, encapsulation, and execution efficiency in the chess application domain. The ChessLisp interpreter is realized as a portable ANSI C++ application that's simple to install and to operate.


General language features:

ChessLisp features are adopted from Common Lisp; a programmer with just about any Lisp experience can become familiar with ChessLisp without excessive effort. Some Common Lisp programs can run under the ChessLisp interpreter without modification while others can be easily and sometimes automatically changed to run. In most cases where there are differences between Common Lisp and ChessLisp, the ChessLisp functionality is a subset of the corresponding Common Lisp features.

One set of differences between ChessLisp and Common Lisp is that the system function predicate names are revised; they are the more sensible, Scheme-like names with a trailing question mark. Examples: atom? list? null? zero? positive? etc. Those who prefer to use the old style predicate names can write wrapper functions or macros.

ChessLisp, unlike just about all older Lisps, is case specific throughout.

The numeric types in ChessLisp are a subset of the Common Lisp numeric types. ChessLisp does not have a built-in complex type, it does not have a rational type, and it does not have arbitrary precision integers. While these types could be added, they are not in the initial release as their general utility in the chess domain is limited or nonexistent. ChessLisp does have a single floating point numeric type that uses the host C++ "double" for its realization. The ChessLisp single integer type is the 64 bit signed integer that uses the host C++ "long long int" for its realization. As might be guessed, the 64 bit integer type is extensively used in the chess specific language feature set for efficient bitboard representation.

There is no ChessLisp compiler in the initial release.


Chess specific language features:

The chess specific functions are easily distinguished from the rest of the ChessLisp functions as the former all start with an upper case letter while the latter all start with a lowercase letter (or a digit or some punctuation character).

ChessLisp constants are present for enumerations of chess scalar types. These types include: color, piece, man (color/piece combinations), ranks, files, squares, castlings, flanks, quadrants, move special cases (regular, en passant, kingside castling, queenside castling, promotion to a knight, promotion to a bishop, promotion to a rook, promotion to a queen), game results, game terminations, draw conditions, annotation flags, and PGN tags. Other types may be added as needed. Composition and decomposition functions are included as appropriate.

ChessLisp also has a Score scalar type and associated functions. The basic score quantum is the millipawn. Special Score values include MateIn<n>, LoseIn<n> (LoseIn0 is a synonym for Checkmated), DrawScore, IllegalScore, PosInf, and NegInf.

ChessLisp has a 64 bit Hash type for Zobrist hashing along with the appropriate functions for mapping a position or an entire game to a hash value.

ChessLisp structures are present for all commonly used chess classes. These structures include: BasicMove (from-square, to-square, from-man, to-man, special-case, and move-flags), Move (BasicMove plus a score), Board, FenScalars (active-color, castlings, ep-target, halfmove-counter, fullmove-number), FenPosition (a Board plus FenScalars), Bitboard (realized as a 64 bit integer), BBDB (a bitboard database), BasicPosition (FenPosition plus a BBDB), Position (BasicPosition plus a move and hash histories), PgnTagPair, PgnTagSet (a list of PgnTagPairs), Game (PgnTagSet plus a Position), and GameCollection. All of these structures have appropriate read and write routines for accessing formatted storage.

The basic routines for playing and unplaying moves are ExecuteMove and RetractMove. A Position object may be scrolled back and forth as desired. The Position structure and its many routines handle all the low level details.

Predicate functions for Position objects include: IsInitialPosition? IsDraw? IsRepeated? IsDrawByRepetition? IsDrawByStalemate? IsDrawByInsufficientMaterial? IsDrawByFiftyMoveRule? IsIsGameOver? IsLegal? IsCheck? IsCheckmate? IsMateIn1? and others as needed.

The basic move generator is GenerateMoves. Variants include: GenerateGainers (captures and promotions), GenerateHolders (non-gainers), and GenerateChecks. The result of a generation is a simple list of moves.

The number of moves in a position can be calculated by:

Code: Select all

  &#40;length &#40;GenerateMoves position&#41;)
and also by the faster:

Code: Select all

  &#40;CountMoves position&#41;
A simple pathway enumerator (i.e., "perft"):

Code: Select all

  &#40;defun Perft &#40;position plydepth&#41;
    &#40;cond
      (&#40;zero? plydepth&#41; 1&#41;
      (&#40;one? plydepth&#41; &#40;CountMoves position&#41;)
      &#40;t
        &#40;let (&#40;sum 0&#41; &#40;newplydepth &#40;1- plydepth&#41;))
          &#40;dolist &#40;move &#40;GenerateMoves position&#41;)
            &#40;ExecuteMove position move&#41;
            &#40;incf sum &#40;Perft position newplydepth&#41;)
            &#40;RetractMove position&#41;)
          sum&#41;)))
Calling the above with:

Code: Select all

  &#40;Perft &#40;LoadPosition "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" 4&#41;)
produces:

Code: Select all

  197281
as expected, and the result appears rather quickly as nearly all the work is done by the optimized C++ routines that form the chess specific portion of the ChessLisp interpreter.

The real fun starts with patterns and pattern matching. More on this later.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: ChessLisp for everyone!

Post by sje »

I should mention that most of the code has been written. However, there is a significant need for integration and testing, and this will take months at my current sloth-like rate of progress.

Also, my current Lisp evaluator is implemented recursively but it really needs to be re-written to operate iteratively. This will take a bit of effort, but this will be worth the work.

The current fully tested system function feature set:

Code: Select all

void ChessLisp&#58;&#58;SysFuncDispatch&#40;const sysfuncType sysfunc&#41;
&#123;
  switch &#40;sysfunc&#41;
  &#123;
    // System function evaluation routines &#40;file&#58; ChessLispSysFunc01.cpp&#41;

    /* *             */ case sysfuncOpMultiply&#58;   EvalSysFuncOpMultiply&#40;);   break;
    /* +             */ case sysfuncOpAdd&#58;        EvalSysFuncOpAdd&#40;);        break;
    /* -             */ case sysfuncOpSubtract&#58;   EvalSysFuncOpSubtract&#40;);   break;
    /* /             */ case sysfuncOpDivide&#58;     EvalSysFuncOpDivide&#40;);     break;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc02.cpp&#41;

    /* /=            */ case sysfuncRelNE&#58;        EvalSysFuncRelNE&#40;);        break;
    /* <             */ case sysfuncRelLT&#58;        EvalSysFuncRelLT&#40;);        break;
    /* <=            */ case sysfuncRelLE&#58;        EvalSysFuncRelLE&#40;);        break;
    /* =             */ case sysfuncRelEQ&#58;        EvalSysFuncRelEQ&#40;);        break;
    /* >             */ case sysfuncRelGT&#58;        EvalSysFuncRelGT&#40;);        break;
    /* >=            */ case sysfuncRelGE&#58;        EvalSysFuncRelGE&#40;);        break;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc03.cpp&#41;

    /* 1+            */ case sysfuncOpOneInc&#58;     EvalSysFuncOpOneInc&#40;);     break;
    /* 1-            */ case sysfuncOpOneDec&#58;     EvalSysFuncOpOneDec&#40;);     break;
    /* 2*            */ case sysfuncOpTwoMul&#58;     EvalSysFuncOpTwoMul&#40;);     break;
    /* 2/            */ case sysfuncOpTwoDiv&#58;     EvalSysFuncOpTwoDiv&#40;);     break;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc04.cpp&#41;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc05.cpp&#41;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc06.cpp&#41;

    /* cons          */ case sysfuncCons&#58;         EvalSysFuncCons&#40;);         break;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc07.cpp&#41;

    /* car           */ case sysfuncCar&#58;          EvalSysFuncCar&#40;);          break;
    /* cdr           */ case sysfuncCdr&#58;          EvalSysFuncCdr&#40;);          break;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc08.cpp&#41;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc09.cpp&#41;

    /* atom?         */ case sysfuncAtomQ&#58;        EvalSysFuncAtomQ&#40;);        break;
    /* cons?         */ case sysfuncConsQ&#58;        EvalSysFuncConsQ&#40;);        break;
    /* eq?           */ case sysfuncEqQ&#58;          EvalSysFuncEqQ&#40;);          break;
    /* even?         */ case sysfuncEvenQ&#58;        EvalSysFuncEvenQ&#40;);        break;
    /* list?         */ case sysfuncListQ&#58;        EvalSysFuncListQ&#40;);        break;
    /* nonnegative?  */ case sysfuncNonnegativeQ&#58; EvalSysFuncNonnegativeQ&#40;); break;
    /* nonpositive?  */ case sysfuncNonpositiveQ&#58; EvalSysFuncNonpositiveQ&#40;); break;
    /* nonzero?      */ case sysfuncNonzeroQ&#58;     EvalSysFuncNonzeroQ&#40;);     break;
    /* null?         */ case sysfuncNullQ&#58;        EvalSysFuncNullQ&#40;);        break;
    /* odd?          */ case sysfuncOddQ&#58;         EvalSysFuncOddQ&#40;);         break;
    /* positive?     */ case sysfuncPositiveQ&#58;    EvalSysFuncPositiveQ&#40;);    break;
    /* symbol?       */ case sysfuncSymbolQ&#58;      EvalSysFuncSymbolQ&#40;);      break;
    /* zero?         */ case sysfuncZeroQ&#58;        EvalSysFuncZeroQ&#40;);        break;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc10.cpp&#41;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc11.cpp&#41;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc12.cpp&#41;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc13.cpp&#41;

    // System function evaluation routines &#40;file&#58; ChessLispSysFunc14.cpp&#41;

    /* gc            */ case sysfuncGc&#58;           EvalSysFuncGc&#40;);           break;
    /* quit          */ case sysfuncQuit&#58;         EvalSysFuncQuit&#40;);         break;

    default&#58;
      Fatal&#40;"ChessLisp&#58;&#58;SysFuncDispatch missing dispatch");
      break;
  &#125;;
&#125;
Brief demo:

Code: Select all

ChessLisp ready
&#91;&#93; nil
nil
&#91;&#93; t
t
&#91;&#93; 123
123
&#91;&#93; 'abc
abc
&#91;&#93; '&#40;a b c&#41;
&#40;a b c&#41;
&#91;&#93; &#40;setq abc '&#40;a b c&#41;)
&#40;a b c&#41;
&#91;&#93; abc
&#40;a b c&#41;
&#91;&#93; &#40;car abc&#41;
a
&#91;&#93; &#40;cdr abc&#41;
&#40;b c&#41;
&#91;&#93; &#40;cons 'a '&#40;b c&#41;)
&#40;a b c&#41;
&#91;&#93; &#40;cons &#40;car abc&#41; &#40;cdr abc&#41;)
&#40;a b c&#41;
&#91;&#93; (+ 1 2 3 4 5&#41;
15
&#91;&#93; (* 1 2 3 4 5&#41;
120
&#91;&#93; (- 5 4&#41; 
1
&#91;&#93; (/ 120 24&#41;
5
&#91;&#93; (+ (* 2 3&#41; (- 8 4&#41; (/ 123 3&#41;)
51
&#91;&#93; &#40;gc&#41;    
&#40;32768 2582 30188&#41;
&#91;&#93; &#40;quit&#41;
nil
User avatar
Greg Strong
Posts: 388
Joined: Sun Dec 21, 2008 6:57 pm
Location: Washington, DC

Re: ChessLisp for everyone!

Post by Greg Strong »

This is interesting, but I'm not clear on exactly what the overall goal is. A framework for expirementation? Building GUIs? Mate problem solving? I'd love to hear what you'd like to ultimately accomplish (even if it's a long way off...)
LucenaTheLucid
Posts: 197
Joined: Mon Jul 13, 2009 2:16 am

Re: ChessLisp for everyone!

Post by LucenaTheLucid »

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

Re: ChessLisp for everyone!

Post by sje »

The ChessLisp project is a general Lisp interpreter along with extensive chess specific functionality. Unlike the work done with Symbolic, ChessLisp is licensed via Creative Commons and its source is available. Unlike some other free (as in free beer) Lisps, ChessLisp is easy to build on any Unix-like system:

To make: g++ -o ChessLisp *.cpp

Or better: g++ -O3 -Wall -pipe -o ChessLisp *.cpp

No package management, no auxiliary libraries, no tricky installation configuration, and so no general arse-pain. The ReadMe file and the Examples file along with a pointer to Common Lisp documentation are all that's needed to get started.

Actually, the chess specific functionality can be ignored and the interpreter can be used just for Lisp programming. It will take some time to get (nearly) all of Common Lisp's functionality in place, but enough will be available in the initial release for fairly advanced programming.

One motivation here is to provide a platform for AI experimentation in the chess domain. Earlier work has shown that representing chess structures such as bitboards, bitboard databases, packed move representations, and the like in a portable format on free Lisps has been most disappointing with respect to run time efficiency. I'm skipping over a lot of history here, so you'll have to trust me on this. Much of the problem is that while Common Lisp has a bit vector type, the implementations leave much to be desired. Also, I haven't found any Lisp that has support for native 64 bit integers. Some only handle 22 bit values directly with all larger integers handed off to general but slow routines. Coding for efficiency in one Lisp can make for sloth code on a different Lisp.

Side note: the support for 64 bit integer values can make for efficient programs in related domains like checkers (draughts), Othello, Qubic, and some chess variants.

Another problem with some Lisps is that they were designed back in the day when RAM was far more limited than it is today. ChessLisp deliberately trades space efficiency for time efficiency as is appropriate for modern architectures.

More later.
User avatar
mhull
Posts: 13447
Joined: Wed Mar 08, 2006 9:02 pm
Location: Dallas, Texas
Full name: Matthew Hull

Re: ChessLisp for everyone!

Post by mhull »

Hello Steven,

It's been awhile since the last Symbolic Status Report. Are you still actively developing it?

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

Re: ChessLisp for everyone!

Post by sje »

mhull wrote:It's been awhile since the last Symbolic Status Report. Are you still actively developing it?
Symbolic has been on the back burner for some time. I did do Myopic over the winter and I'll be trying to work on publicly releasable projects for the present.
Pradu
Posts: 287
Joined: Sat Mar 11, 2006 3:19 am
Location: Atlanta, GA

Re: ChessLisp for everyone!

Post by Pradu »

Lisp is a great language but many implementations seem to have to trade off performance for functionality. Have you looked into Lisp dialects intended fast execution such as PreScheme?
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: ChessLisp for everyone!

Post by sje »

Gosh, I looked at plenty of Lisps and some Schemes. All had some drawbacks: most in the area of run time inefficiencies, some with portability problems, some that cost money, some with license issues, etc.

ChessLisp avoids all these problems, although there is the personal cost of my time and effort to make it come to life.

To be honest, I'm not a big Scheme fan although I do admit that it has some good points: it doesn't have the Common Lisp feature bloat, it can be quickly implemented, it has better predicate names, and it can be very good for instructional purposes (see SICP). But there are some parts of it that I'd rather not have to use like the artificial (to me) differences between nil, the empty list, and #f. I'll stick with the McCarthy Lisp heritage instead.

Of course, if someone wanted to make a ChessScheme, that would be fine with me. Also, they would be welcome to use ChessLisp to help if they were to abide by the Creative Commons license.
Tord Romstad
Posts: 1808
Joined: Wed Mar 08, 2006 9:19 pm
Location: Oslo, Norway

Re: ChessLisp for everyone!

Post by Tord Romstad »

Pradu wrote:Lisp is a great language but many implementations seem to have to trade off performance for functionality. Have you looked into Lisp dialects intended fast execution such as PreScheme?
Most of us wouldn't consider Scheme a Lisp at all. There are superficial similarities in syntax, but the languages are vastly different. Calling Scheme a dialect of Lisp is roughly equivalent to calling C a dialect of Java.

Steven and I evidently have quite different opinions and preferences concerning Lisp in general (which isn't surprising, as Lisp programmers tend to be very individualistic), I share his feelings about Scheme. It's a useful language for theoretical research and for teaching beginning programmers, but I wouldn't ever consider using it for any sort of large or medium-scale practical program.

Personally, I am currently not attracted to any form of Lisp apart from standard Common Lisp. It isn't perfect, but for someone like me it is still far better than any other language for almost every possible task. I use and enjoy lots of languages for fun and educational purposes, but always turn to Common Lisp for serious work. Although a better Lisp would be possible in theory, it would involve a tremendous amount of work to get there, and I feel that this work would be better spent designing Common Lisp libraries. That's the only place we're really lagging behind languages like Python and Ruby.