ChessLisp for everyone!

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Tord Romstad
Posts: 1808
Joined: Wed Mar 08, 2006 9:19 pm
Location: Oslo, Norway

Re: ChessLisp for everyone!

Post by Tord Romstad »

sje wrote:ChessLisp, unlike just about all older Lisps, is case specific throughout.
Pedantic remark:

Common Lisp is case-sensitive, too. It just happens that the reader with the default readtable settings converts all symbols read to uppercase. If you just do

Code: Select all

(setf (readtable-case *readtable*) :preserve)
you get case-sensitivity.

You already knew this, of course, but perhaps some other readers didn't. :)
Tord Romstad
Posts: 1808
Joined: Wed Mar 08, 2006 9:19 pm
Location: Oslo, Norway

Re: ChessLisp for everyone!

Post by Tord Romstad »

sje wrote:Also, I haven't found any Lisp that has support for native 64 bit integers.
I know that SBCL has, and I think many others have, too. In order to get native 64-bit arithmetic, just wrap all arithmetic expressions in a LOGAND with #xFFFFFFFFFFFFFFFF. A sufficiently smart compiler will realize that all the computations inside the LOGAND form can be optimized to native 64-bit arithmetic.

Having to type "(logand <whatever> #xFFFFFFFFFFFFFFFF)" everywhere quickly becomes tedious, but with a little work you should be able to write a WITH-64BIT-ARITHMETIC macro that walks through the code and inserts logand where required.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: ChessLisp for everyone!

Post by sje »

ChessLisp design notes: monadic predicates

A Lisp monadic predicate is a function that takes a single operand and deterministically returns either the symbol t (predicate satisfied) or the symbol nil (predicate not satisfied).

Due to historical kludge accumulation, the monadic predicate names in Common Lisp are not entirely consistent; the names in Scheme are better. In ChessLisp, the Common Lisp functionality is retained, but with Scheme-like names.

Monadic predicates coded and tested (so far; this is actual dispatch source):

Code: Select all

    /* atom?         */ case sysfuncAtomQ&#58;        EvalSysFuncAtomQ&#40;);        break;
    /* character?    */ case sysfuncCharacterQ&#58;   EvalSysFuncCharacterQ&#40;);   break;
    /* cons?         */ case sysfuncConsQ&#58;        EvalSysFuncConsQ&#40;);        break;
    /* eof?          */ case sysfuncEofQ&#58;         EvalSysFuncEofQ&#40;);         break;
    /* eq?           */ case sysfuncEqQ&#58;          EvalSysFuncEqQ&#40;);          break;
    /* eql?          */ case sysfuncEqlQ&#58;         EvalSysFuncEqlQ&#40;);         break;
    /* even?         */ case sysfuncEvenQ&#58;        EvalSysFuncEvenQ&#40;);        break;
    /* float?        */ case sysfuncFloatQ&#58;       EvalSysFuncFloatQ&#40;);       break;
    /* integer?      */ case sysfuncIntegerQ&#58;     EvalSysFuncIntegerQ&#40;);     break;
    /* list?         */ case sysfuncListQ&#58;        EvalSysFuncListQ&#40;);        break;
    /* literal?      */ case sysfuncLiteralQ&#58;     EvalSysFuncLiteralQ&#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;
    /* number?       */ case sysfuncNumberQ&#58;      EvalSysFuncNumberQ&#40;);      break;
    /* odd?          */ case sysfuncOddQ&#58;         EvalSysFuncOddQ&#40;);         break;
    /* one?          */ case sysfuncOneQ&#58;         EvalSysFuncOneQ&#40;);         break;
    /* positive?     */ case sysfuncPositiveQ&#58;    EvalSysFuncPositiveQ&#40;);    break;
    /* string?       */ case sysfuncStringQ&#58;      EvalSysFuncStringQ&#40;);      break;
    /* symbol?       */ case sysfuncSymbolQ&#58;      EvalSysFuncSymbolQ&#40;);      break;
    /* vector?       */ case sysfuncVectorQ&#58;      EvalSysFuncVectorQ&#40;);      break;
    /* zero?         */ case sysfuncZeroQ&#58;        EvalSysFuncZeroQ&#40;);        break;
Note: Not all run time error checking has been implemented -- yet.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

ChessLisp design notes: old style basic list accessors

Post by sje »

ChessLisp design notes: old style basic list accessors

A Lisp old style basic list accessor is a function that takes a single operand (a list) and returns the indicated component of the list. There are thirty of these defined in Common Lisp, and ChessLisp implements the whole set for compatibility purposes.

The car (contents of address register) and cdr (contents of data register) functions are from the first version of Lisp (i.e., Lisp 1.0 or Year Zero Lisp or Lisp 1958). Better are the more recent, Common Lisp equivalents first and rest.

Likewise, cadr == second, caddr = third, cadddr = fourth.

ChessLisp observes the Common Lisp semantics of the basement cases (car nil) == nil and (cdr nil) == nil.

Old style basic list accessors coded and tested (this is actual dispatch source):

Code: Select all

    /* caaaar        */ case sysfuncCaaaar&#58;       EvalSysFuncCaaaar&#40;);       break;
    /* caaadr        */ case sysfuncCaaadr&#58;       EvalSysFuncCaaadr&#40;);       break;
    /* caaar         */ case sysfuncCaaar&#58;        EvalSysFuncCaaar&#40;);        break;
    /* caadar        */ case sysfuncCaadar&#58;       EvalSysFuncCaadar&#40;);       break;
    /* caaddr        */ case sysfuncCaaddr&#58;       EvalSysFuncCaaddr&#40;);       break;
    /* caadr         */ case sysfuncCaadr&#58;        EvalSysFuncCaadr&#40;);        break;
    /* caar          */ case sysfuncCaar&#58;         EvalSysFuncCaar&#40;);         break;
    /* cadaar        */ case sysfuncCadaar&#58;       EvalSysFuncCadaar&#40;);       break;
    /* cadadr        */ case sysfuncCadadr&#58;       EvalSysFuncCadadr&#40;);       break;
    /* cadar         */ case sysfuncCadar&#58;        EvalSysFuncCadar&#40;);        break;
    /* caddar        */ case sysfuncCaddar&#58;       EvalSysFuncCaddar&#40;);       break;
    /* cadddr        */ case sysfuncCadddr&#58;       EvalSysFuncCadddr&#40;);       break;
    /* caddr         */ case sysfuncCaddr&#58;        EvalSysFuncCaddr&#40;);        break;
    /* cadr          */ case sysfuncCadr&#58;         EvalSysFuncCadr&#40;);         break;
    /* car           */ case sysfuncCar&#58;          EvalSysFuncCar&#40;);          break;
    /* cdaaar        */ case sysfuncCdaaar&#58;       EvalSysFuncCdaaar&#40;);       break;
    /* cdaadr        */ case sysfuncCdaadr&#58;       EvalSysFuncCdaadr&#40;);       break;
    /* cdaar         */ case sysfuncCdaar&#58;        EvalSysFuncCdaar&#40;);        break;
    /* cdadar        */ case sysfuncCdadar&#58;       EvalSysFuncCdadar&#40;);       break;
    /* cdaddr        */ case sysfuncCdaddr&#58;       EvalSysFuncCdaddr&#40;);       break;
    /* cdadr         */ case sysfuncCdadr&#58;        EvalSysFuncCdadr&#40;);        break;
    /* cdar          */ case sysfuncCdar&#58;         EvalSysFuncCdar&#40;);         break;
    /* cddaar        */ case sysfuncCddaar&#58;       EvalSysFuncCddaar&#40;);       break;
    /* cddadr        */ case sysfuncCddadr&#58;       EvalSysFuncCddadr&#40;);       break;
    /* cddar         */ case sysfuncCddar&#58;        EvalSysFuncCddar&#40;);        break;
    /* cdddar        */ case sysfuncCdddar&#58;       EvalSysFuncCdddar&#40;);       break;
    /* cddddr        */ case sysfuncCddddr&#58;       EvalSysFuncCddddr&#40;);       break;
    /* cdddr         */ case sysfuncCdddr&#58;        EvalSysFuncCdddr&#40;);        break;
    /* cddr          */ case sysfuncCddr&#58;         EvalSysFuncCddr&#40;);         break;
    /* cdr           */ case sysfuncCdr&#58;          EvalSysFuncCdr&#40;);          break;
Note: Not all run time error checking has been implemented -- yet.
Dan Andersson
Posts: 442
Joined: Wed Mar 08, 2006 8:54 pm

Re: ChessLisp for everyone!

Post by Dan Andersson »

Not considering Scheme a Lisp? :) I guess it would certainly be possible if one purposefully ignored mostly everything about the languages ;)

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

More design notes: relationals

Post by sje »

A Lisp relational function returns t (or nil) if the specified relation is satisfied (or not) for its operands.

ChessLisp includes a full set of relational functions:

Relationals coded and tested (this is actual dispatch source):

Code: Select all

    // 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; ChessLispSysFunc04.cpp&#41;

    /* char/=         */ case sysfuncCharRelNE&#58;   EvalSysFuncCharRelNE&#40;);    break;
    /* char<          */ case sysfuncCharRelLT&#58;   EvalSysFuncCharRelLT&#40;);    break;
    /* char<=         */ case sysfuncCharRelLE&#58;   EvalSysFuncCharRelLE&#40;);    break;
    /* char=          */ case sysfuncCharRelEQ&#58;   EvalSysFuncCharRelEQ&#40;);    break;
    /* char>          */ case sysfuncCharRelGT&#58;   EvalSysFuncCharRelGT&#40;);    break;
    /* char>=         */ case sysfuncCharRelGE&#58;   EvalSysFuncCharRelGE&#40;);    break;

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

    /* str/=          */ case sysfuncStrRelNE&#58;    EvalSysFuncStrRelNE&#40;);     break;
    /* str<           */ case sysfuncStrRelLT&#58;    EvalSysFuncStrRelLT&#40;);     break;
    /* str<=          */ case sysfuncStrRelLE&#58;    EvalSysFuncStrRelLE&#40;);     break;
    /* str=           */ case sysfuncStrRelEQ&#58;    EvalSysFuncStrRelEQ&#40;);     break;
    /* str>           */ case sysfuncStrRelGT&#58;    EvalSysFuncStrRelGT&#40;);     break;
    /* str>=          */ case sysfuncStrRelGE&#58;    EvalSysFuncStrRelGE&#40;);     break;
Note: Not all run time error checking has been implemented -- yet.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Bit functions

Post by sje »

ChessLisp has a full set of bit functions; these work with natively supported 64 bit integers and allow fast bitboard operations. I guarantee that these are much faster than the equivalent Common Lisp hacks (e.g., logand, sbit, etc.).

Bit functions coded and tested (this is actual dispatch source):

Code: Select all

    /* bf-and        */ case sysfuncBfAnd&#58;        EvalSysFuncBfAnd&#40;);        break;
    /* bf-andnot     */ case sysfuncBfAndnot&#58;     EvalSysFuncBfAndnot&#40;);     break;
    /* bf-contract   */ case sysfuncBfContract&#58;   EvalSysFuncBfContract&#40;);   break;
    /* bf-count      */ case sysfuncBfCount&#58;      EvalSysFuncBfCount&#40;);      break;
    /* bf-empty?     */ case sysfuncBfEmptyQ&#58;     EvalSysFuncBfEmptyQ&#40;);     break;
    /* bf-eql?       */ case sysfuncBfEqlQ&#58;       EvalSysFuncBfEqlQ&#40;);       break;
    /* bf-expand     */ case sysfuncBfExpand&#58;     EvalSysFuncBfExpand&#40;);     break;
    /* bf-first      */ case sysfuncBfFirst&#58;      EvalSysFuncBfFirst&#40;);      break;
    /* bf-last       */ case sysfuncBfLast&#58;       EvalSysFuncBfLast&#40;);       break;
    /* bf-not        */ case sysfuncBfNot&#58;        EvalSysFuncBfNot&#40;);        break;
    /* bf-notempty?  */ case sysfuncBfNotemptyQ&#58;  EvalSysFuncBfNotemptyQ&#40;);  break;
    /* bf-or         */ case sysfuncBfOr&#58;         EvalSysFuncBfOr&#40;);         break;
    /* bf-reset      */ case sysfuncBfReset&#58;      EvalSysFuncBfReset&#40;);      break;
    /* bf-reset?     */ case sysfuncBfResetQ&#58;     EvalSysFuncBfResetQ&#40;);     break;
    /* bf-set        */ case sysfuncBfSet&#58;        EvalSysFuncBfSet&#40;);        break;
    /* bf-set?       */ case sysfuncBfSetQ&#58;       EvalSysFuncBfSetQ&#40;);       break;
    /* bf-toggle     */ case sysfuncBfToggle&#58;     EvalSysFuncBfToggle&#40;);     break;
    /* bf-xor        */ case sysfuncBfXor&#58;        EvalSysFuncBfXor&#40;);        break;
Note: Not all run time error checking has been implemented -- yet.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: ChessLisp for everyone!

Post by sje »

On Common Lisp readtables:

If the Common Lisp language specification had been done right in the fist place, there would be no need for user massaging of readtables. Case insensitivity is a dinosaur and should have been retired long ago, about the same time that most ASR-33 Teletypes wound up in the junk heap.

Note the high correlation between case insensitivity and the deadness of a programming language (e.g., Fortran, Pascal, and Ada).

BUT IF SOMEONE WANTS TO CODE AND GET OUTPUT IN UPPERCASE, THAT'S OKAY. Well, as long as I don't have to look at it.


On 64 bit native support:

Unless things have changed, I know that cmucl, gcl, and clisp (among others) have very inefficient handing of 64 bit integer values. They have taken the general approach to handle everything adequately and so handle nothing excellently.


On Scheme:

I consider Scheme to be a Lisp. It is certainly a descendant of Lisp (mostly from MacLisp). But it's not my first choice for just about anything.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

The very first benchmark

Post by sje »

This is the first ever benchmark. It's ChessLisp vs GNU Common Lisp (a.k.a., gcl).

Platform: 3 GHz P4 Ubuntu Linux

Test function:

(defun fib (n) (cond ((= n 0) 1) ((= n 1) 1) (t (+ (fib (- n 1)) (fib (- n 2))))))

Function call and result:

(fib 40) => 165580141

ChessLisp: 573 sec => 288971 Hz
gcl: 368 sec => 449946 Hz

So ChessLisp is running at about 2/3 the speed of gcl. Not too bad for the very first test; I'm glad just to get the correct answers.

After the chess run time is connected, I expect ChessLisp to beat gcl by at least a factor of ten on domain specific tests.

If you are running Ubuntu, you can use the Synaptic package manager to download and install gcl. On any Debian Linux, the corresponding apt-get call does the same thing. Eventually, ChessLisp will also be available by the same mechanisms.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: The very first benchmark

Post by sje »

I added a few lines of code to the top level bindings list scanner to have the list reorganize on each successful probe. When a binding is located, it's moved to the front of the list. The superstition is that recently located bindings will be frequently probed bindings.

Old ChessLisp: 573 sec => 288971 Hz
New ChessLisp: 465 sec => 356085 Hz

Yes, it's low hanging fruit.

I'll take a guess and suggest that gcl likely uses a hash table. I might do the same in ChessLisp at a later date.