What is maximum rating per programmed line...

Discussion of chess software programming and technical issues.

Moderator: Ras

User avatar
Jim Ablett
Posts: 2390
Joined: Fri Jul 14, 2006 7:56 am
Location: London, England
Full name: Jim Ablett

Re: What is maximum rating per programmed line...

Post by Jim Ablett »

SamChess by Jonathan Kreuzer must have one of the smallest sources.

http://www.3dkingdoms.com/chess/sam/samchess.html

Jim.
ZirconiumX
Posts: 1361
Joined: Sun Jul 17, 2011 11:14 am
Full name: Hannah Ravensloft

Re: What is maximum rating per programmed line...

Post by ZirconiumX »

Jim Ablett wrote:SamChess by Jonathan Kreuzer must have one of the smallest sources.

http://www.3dkingdoms.com/chess/sam/samchess.html

Jim.
It is non-portable.

Code: Select all

matthew$ g++ -O3 -o sam *.cpp
movegen.cpp:38: error: '__int64' has not been declared
movegen.cpp:210: error: '__int64' does not name a type
movegen.cpp:211: error: expected ',' or '...' before 'HashKey'
movegen.cpp:211: error: ISO C++ forbids declaration of '__int64' with no type
movegen.cpp: In function 'int Repetition(int)':
movegen.cpp:212: error: 'nStart' was not declared in this scope
movegen.cpp:213: error: 'ahead' was not declared in this scope
movegen.cpp:214: error: 'ahead' was not declared in this scope
movegen.cpp:214: error: 'RepNum' was not declared in this scope
movegen.cpp:214: error: 'HashKey' was not declared in this scope
movegen.cpp: At global scope:
movegen.cpp:217: error: expected ',' or '...' before 'HashKey'
movegen.cpp:217: error: ISO C++ forbids declaration of '__int64' with no type
movegen.cpp: In function 'void AddRepBoard(int)':
movegen.cpp:217: error: 'RepNum' was not declared in this scope
movegen.cpp:217: error: 'ahead' was not declared in this scope
movegen.cpp:217: error: 'HashKey' was not declared in this scope
movegen.cpp: At global scope:
movegen.cpp:219: error: '__int64' does not name a type
movegen.cpp:260: error: '__int64' does not name a type
movegen.cpp:270: error: ISO C++ forbids declaration of 'AddRepBoard' with no type
movegen.cpp: In static member function 'static void TEntry::Create_HashFunction()':
movegen.cpp:254: error: 'HashFunction' was not declared in this scope
movegen.cpp:258: error: 'HashSTM' was not declared in this scope
movegen.cpp:258: error: 'HashFunction' was not declared in this scope
movegen.cpp: At global scope:
movegen.cpp:274: error: '__int64' does not name a type
movegen.cpp: In function 'int ABSearch(SBoard&, int, int, int, int, int&, int)':
movegen.cpp:283: error: '__int64' was not declared in this scope
movegen.cpp:283: error: expected `;' before 'n64Hash'
movegen.cpp:285: error: 'g_Nodes' was not declared in this scope
movegen.cpp:285: error: 'g_CheckNodes' was not declared in this scope
movegen.cpp:315: error: 'g_Nodes' was not declared in this scope
movegen.cpp:320: error: 'n64Hash' was not declared in this scope
movegen.cpp:320: error: 'HashBoard' is not a member of 'TEntry'
movegen.cpp:329: error: 'n64Hash' was not declared in this scope
movegen.cpp: In function 'void ComputerMove(SBoard&, int&, int&)':
movegen.cpp:346: error: 'g_Nodes' was not declared in this scope
movegen.cpp:347: error: 'g_CheckNodes' was not declared in this scope
winboard.cpp: At global scope:
winboard.cpp:45: error: '__int64' has not been declared
winboard.cpp: In function 'void SendThinking(int, int, int, int, int)':
winboard.cpp:48: error: invalid conversion from 'const char*' to 'char*'
winboard.cpp:48: error:   initializing argument 1 of 'void GetMoveString(char*, char*, int)'
winboard.cpp: In function 'void WinboardLoop()':
winboard.cpp:82: error: 'HashBoard' is not a member of 'TEntry'
winboard.cpp:95: error: 'g_Nodes' was not declared in this scope
winboard.cpp:103: error: 'HashBoard' is not a member of 'TEntry'
winboard.cpp: In function 'void LogOutput(char*, char*)':
winboard.cpp:6: error: 'FILE' was not declared in this scope
winboard.cpp:6: error: 'fp' was not declared in this scope
winboard.cpp:6: error: 'fopen' was not declared in this scope
winboard.cpp:8: error: 'fprintf' was not declared in this scope
winboard.cpp:9: error: 'fclose' was not declared in this scope
winboard.cpp: At global scope:
winboard.cpp:13: error: variable or field 'LogBoard' declared void
winboard.cpp:13: error: 'SBoard' was not declared in this scope
winboard.cpp:13: error: 'Board' was not declared in this scope
winboard.cpp:14: error: expected ',' or ';' before '{' token
winboard.cpp:30: error: variable or field 'ReplayGame' declared void
winboard.cpp:30: error: 'SBoard' was not declared in this scope
winboard.cpp:30: error: 'Board' was not declared in this scope
winboard.cpp:30: error: expected primary-expression before 'int'
winboard.cpp:30: error: initializer expression list treated as compound expression
winboard.cpp:30: error: expected ',' or ';' before '{' token
winboard.cpp: In function 'void GetMoveString(char*, char*, int)':
winboard.cpp:35: error: 'SRC' was not declared in this scope
winboard.cpp:35: error: 'DST' was not declared in this scope
winboard.cpp:36: error: 'File' was not declared in this scope
winboard.cpp:36: error: 'Rank' was not declared in this scope
winboard.cpp:36: error: 'sprintf' was not declared in this scope
winboard.cpp: In function 'void SendMove(int)':
winboard.cpp:41: error: 'printf' was not declared in this scope
winboard.cpp:42: error: 'stdout' was not declared in this scope
winboard.cpp:42: error: 'fflush' was not declared in this scope
winboard.cpp: At global scope:
winboard.cpp:45: error: '__int64' has not been declared
winboard.cpp: In function 'void SendThinking(int, int, int, int, int)':
winboard.cpp:48: error: invalid conversion from 'const char*' to 'char*'
winboard.cpp:48: error:   initializing argument 1 of 'void GetMoveString(char*, char*, int)'
winboard.cpp:49: error: 'clock' was not declared in this scope
winboard.cpp:49: error: 'g_start' was not declared in this scope
winboard.cpp:49: error: 'printf' was not declared in this scope
winboard.cpp:50: error: 'stdout' was not declared in this scope
winboard.cpp:50: error: 'fflush' was not declared in this scope
winboard.cpp: At global scope:
winboard.cpp:53: error: 'SBoard' has not been declared
winboard.cpp: In function 'int GetMove(char*, int&)':
winboard.cpp:57: error: 'SQ' was not declared in this scope
winboard.cpp: In function 'void WinboardLoop()':
winboard.cpp:66: error: 'SBoard' was not declared in this scope
winboard.cpp:66: error: expected `;' before 'Board'
winboard.cpp:67: error: 'BLACK' was not declared in this scope
winboard.cpp:71: error: 'FILE' was not declared in this scope
winboard.cpp:71: error: 'fp' was not declared in this scope
winboard.cpp:71: error: 'fopen' was not declared in this scope
winboard.cpp:72: error: 'fclose' was not declared in this scope
winboard.cpp:75: error: 'stdin' was not declared in this scope
winboard.cpp:75: error: 'fgets' was not declared in this scope
winboard.cpp:76: error: 'sscanf' was not declared in this scope
winboard.cpp:78: error: 'Board' was not declared in this scope
winboard.cpp:82: error: 'TEntry' has not been declared
winboard.cpp:82: error: 'HashBoard' was not declared in this scope
winboard.cpp:82: error: 'AddRepBoard' was not declared in this scope
winboard.cpp:83: error: 'LogBoard' cannot be used as a function
winboard.cpp:90: error: 'clock' was not declared in this scope
winboard.cpp:90: error: 'srand' was not declared in this scope
winboard.cpp:91: error: 'rand' was not declared in this scope
winboard.cpp:93: error: 'ComputerMove' was not declared in this scope
winboard.cpp:95: error: 'clock' was not declared in this scope
winboard.cpp:95: error: 'g_start' was not declared in this scope
winboard.cpp:95: error: 'g_Nodes' was not declared in this scope
winboard.cpp:96: error: 'fprintf' was not declared in this scope
winboard.cpp:97: error: 'fclose' was not declared in this scope
winboard.cpp:103: error: 'TEntry' has not been declared
winboard.cpp:103: error: 'HashBoard' was not declared in this scope
winboard.cpp:103: error: 'AddRepBoard' was not declared in this scope
winboard.cpp:104: error: 'LogBoard' cannot be used as a function
winboard.cpp:109: error: 'g_MaxTime' was not declared in this scope
winboard.cpp:109: error: 'CLOCKS_PER_SEC' was not declared in this scope
winboard.cpp:114: error: 'WHITE' was not declared in this scope
winboard.cpp:116: error: 'ReplayGame' cannot be used as a function
winboard.cpp:117: error: 'ReplayGame' cannot be used as a function
Micro-max is portable.

So I think Sam is out of the running, there Jim.

Matthew:out
tu ne cede malis, sed contra audentior ito
Uri Blass
Posts: 11150
Joined: Thu Mar 09, 2006 12:37 am
Location: Tel-Aviv Israel

Re: What is maximum rating per programmed line...

Post by Uri Blass »

Piotr Cichy wrote:
pocopito wrote:Well, I guess this is an "it depends" case.
Are you thinking of something like micro-max or toledo, that are written in a few more than 1000 _characters_? On the other hand if you're thinking about something written in a more readable way, I guess that TSCP itself has around 2600 lines of C code (I guess it implements alpha-beta + null move + qscent + move ordering, and many of the lines are coments), its strength isn't that bad and I guess there's a lot of possibilities to improve for example in the evaluation function.

Regards

E Diaz
TSCP is obviously not a program with short source code. For example, nanoSzachy 4.0 has about 1200 lines of code (38020 bytes) and pikoSzachy 4.0 about 800 lines (24247 bytes). And this is "clean" code, not "obfuscated" like in case of microMax (there are comments, long variable names etc). Both programs are optimized for size, but size of executable file and not size of source code. The size of exe files is: 30.5 KB (nano) and 9.5 KB (piko). And they are much stronger than TSCP or microMax:

CEGT 40/4:

Code: Select all

NanoSzachy 4.0 x64   2381
PikoSzachy 4.0       2289
TSCP 1.81            1734
CCRL 40/40:

Code: Select all

NanoSzachy 4.0 64-bit 	2563
Micro-Max 4.8 (DM-PII)	1699
It may be interesting also to optimize for smallest use of memory
that means playing strength relative to the memory that you need to run the program(that means that it is better if the program run with no hash
if you optimize to elo divided by size in bytes and not size of the code but size of the exe including the memory that the process is using).
lucasart
Posts: 3243
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: What is maximum rating per programmed line...

Post by lucasart »

Piotr Cichy wrote: For example, nanoSzachy 4.0 has about 1200 lines of code (38020 bytes) and pikoSzachy 4.0 about 800 lines (24247 bytes). And this is "clean" code, not "obfuscated" like in case of microMax (there are comments, long variable names etc). Both programs are optimized for size, but size of executable file and not size of source code. The size of exe files is: 30.5 KB (nano) and 9.5 KB (piko). And they are much stronger than TSCP or microMax:

CEGT 40/4:

Code: Select all

NanoSzachy 4.0 x64   2381
PikoSzachy 4.0       2289
TSCP 1.81            1734
That's quite impressive! Here's what I get with my engine DiscoCheck

Code: Select all

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
C                               17            636            582           3146
C/C++ Header                    17            120            304            439
-------------------------------------------------------------------------------
SUM:                            34            756            886           3585
-------------------------------------------------------------------------------
About 2600 elo on CEGT 40/20. Last version tested was 3.6.2 (2575 elo), and version 3.7.1 is about 30 elo better in self testing.
It is 100% portable and not obfuscated (MicroMax style). I'll try to clean up my code a little and see if I can reduce it a little, while making it more readable at the same time (since comments don't count).

The size of the Linux x86-64 executable is 82.2 KB, which is a lot larger than NanoSzachy :oops:
JBNielsen
Posts: 267
Joined: Thu Jul 07, 2011 10:31 pm
Location: Denmark

Re: What is maximum rating per programmed line...

Post by JBNielsen »


Indeed it is quite impressive. I think we both follow the same philosophy of trying to keep the code short at a reasonable strength, but my program is both bigger and weaker than yours. It's a pity that they aren't open source.
Yes, very impressive.

Thanks for the information.

What is the best open source program regarding rating/size of source/readability?
JBNielsen
Posts: 267
Joined: Thu Jul 07, 2011 10:31 pm
Location: Denmark

Re: What is maximum rating per programmed line...

Post by JBNielsen »

Lines of code don't make any sense, especially in C. Here's an example that should make it obvious:
You are right. Counting characters must be better.

But every variable should be (counted as) fx 10 char long.
User avatar
hgm
Posts: 28447
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: What is maximum rating per programmed line...

Post by hgm »

It depends how you define 'readability'.

I of course would say it is micro-Max. The slight inconvenience of having 1-character variable names is IMO more than compensated by the fact that every line is commented to say what it does. You catch on quickly enough as to what the variables mean, especially since there can be only 52.

And if the in-source comments are not enough, there is about 30 web pages of description on how it works...
User avatar
Jim Ablett
Posts: 2390
Joined: Fri Jul 14, 2006 7:56 am
Location: London, England
Full name: Jim Ablett

Re: What is maximum rating per programmed line...

Post by Jim Ablett »

Jim Ablett wrote:
SamChess by Jonathan Kreuzer must have one of the smallest sources.

http://www.3dkingdoms.com/chess/sam/samchess.html

Jim.


It is non-portable.
Only takes 10 mins to make it portable (add '-fpermissive' when compiling)

Code: Select all

// A small chess program by Jonathan Kreuzer
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <signal.h>
#include <stdint.h>


int GameMoves[ 1000 ];
const int HASH_SIZE = 400000;
const int INVALID=32, IV=32, EMPTY=0, WHITE=8, BLACK=16, WH=8, BL=16, PAWN=0, KNIG=1, BISH=2, ROOK=3, QUEE=4, KING=5;
const int TIME = 300000;
const int B_QS = 4, B_KS = 8, W_QS = 1, W_KS = 2;
const int N_dirs[8] = {-21, -19, -12, -8, 8, 12, 19, 21};
const int K_dirs[8] = {1, 9, 10, 11, -1, -9, -10, -11};
const int Q_dirs[8] = {1, -1, 9, -9, -10, 10, -11, 11};
const int R_dirs[4] = {1, -1,  -10, 10};
const int B_dirs[4] = {9, -9, -11, 11};
const int P_dirs[8] = {-10, -20, -9, -11, 10, 20, 9, 11};
int inline SRC   ( int Move ) {return (Move&127);}
int inline DST   ( int Move ) {return ((Move>>7)&127);}
int inline PROMO ( int Move ) {return ((Move>>14)&3);}
int inline VALUE ( int Move ) {return ((Move>>16)&16383);}
int inline SWITCH ( int Color ){return Color^(WHITE|BLACK);}
int inline File ( int Sq ) {return (Sq-20)%10;}
int inline Rank ( int Sq ) {return (Sq-10)/10;}
int inline SQ ( int file, int rank) {return 21+file+rank*10;}
int EvalSq [ 26 * 128 ];
int SetCastle[120];
const unsigned char StartBoard[120] = { IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV,
	IV, ROOK|BL, KNIG|BL, BISH|BL, QUEE|BL, KING|BL, BISH|BL, KNIG|BL, ROOK|BL, IV,
	IV, PAWN|BL, PAWN|BL, PAWN|BL, PAWN|BL, PAWN|BL, PAWN|BL, PAWN|BL, PAWN|BL, IV,
	IV, 0, 0, 0, 0, 0, 0, 0, 0, IV, IV, 0, 0, 0, 0, 0, 0, 0, 0, IV, IV, 0, 0, 0, 0, 0, 0, 0, 0, IV, IV, 0, 0, 0, 0, 0, 0, 0, 0, IV,
	IV, PAWN|WH, PAWN|WH, PAWN|WH, PAWN|WH, PAWN|WH, PAWN|WH, PAWN|WH, PAWN|WH, IV,
	IV, ROOK|WH, KNIG|WH, BISH|WH, QUEE|WH, KING|WH, BISH|WH, KNIG|WH, ROOK|WH, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV, IV };
const int PieceValues[8]    = { 100, 328, 330, 522, 952, 5000 };
const int MPieceValues[8]   = {  00, 328, 330, 522, 952,   00 };
const int KingEval[10] = { 0, 8, 12, 5, 0, 0, 5, 14, 9, 0};
const int CentEval[10] = { 0,-6, -3, -1, 0, 0, -1, -3, -6, 0};
const int Cent[10]	   = { 0, 1, 2, 2, 3, 3, 2, 1, 1, 0};
void SendThinking ( int move, int Depth, int Eval, int64_t Nodes, int bFinished );

struct SBoard
	{
	unsigned char m_Sqs[120];
	int stm, Eval, EPsq, Castling, WKsq, BKsq, WMat, BMat, nMoves, nLastCapOrPawn;

	void Init()	{
		int sq, pc;
		for (sq = 0; sq < 120; sq++) m_Sqs[sq] = StartBoard [ sq ];
		Eval = 0; nMoves = 0;
		Castling = 0;
		stm = WHITE;
		WKsq = 95; BKsq = 25;
		BMat = WMat = 2*PieceValues[BISH]+2*PieceValues[KNIG]+2*PieceValues[ROOK]+PieceValues[QUEE];
		for (sq = 0; sq < 120; sq++) SetCastle[sq] = 0;
		SetCastle[21] = B_QS; SetCastle[28] = B_KS; SetCastle[25] = B_QS|B_KS; SetCastle[91] = W_QS; SetCastle[98] = W_KS; SetCastle[95] = W_QS|W_KS;
		// Evaluation is incremental and uses the piece square table in EvalSq arrays.
		for (pc = 0; pc < 8; pc++)
			for (sq = 0; sq < 120; sq++) {
				EvalSq[ ((pc|WHITE)<<7)+sq ] = PieceValues[pc];
				EvalSq[ ((pc|BLACK)<<7)+sq ] = -PieceValues[pc];
				if (pc == PAWN)
					{EvalSq[ ((pc|WHITE)<<7)+sq ] += (9-Rank(sq)) * Cent[File(sq)];
					 EvalSq[ ((pc|BLACK)<<7)+sq ] -=   Rank(sq)   * Cent[File(sq)];}
				else if (pc == KING) {}
				else{if (pc!=ROOK && Rank(sq)==8) EvalSq[ ((pc|WHITE)<<7)+sq ] -= 8;
					 if (pc!=ROOK && Rank(sq)==1) EvalSq[ ((pc|BLACK)<<7)+sq ] += 8;
					 EvalSq[ ((pc|WHITE)<<7)+sq ] += CentEval[ File(sq) ];
					 EvalSq[ ((pc|BLACK)<<7)+sq ] -= CentEval[ File(sq) ];
					}
			EvalSq[ (0<<7)+sq ] = (Rank(sq)-9)*2 + KingEval[File(sq)]; // King Values depend on stage
			EvalSq[ (1<<7)+sq ] = (Rank(sq))*2 - KingEval[File(sq)];
			EvalSq[ (2<<7)+sq ] = 2*CentEval[ File(sq) ];
			EvalSq[ (3<<7)+sq ] = -2*CentEval[ File(sq) ];
			}
		}
	int CanCastleKS ( const int Color) {
		if (Color == WHITE && !(Castling&W_KS) && m_Sqs[ WKsq+1 ] == EMPTY && !ColorAttacksSq(BLACK, WKsq+1) && m_Sqs[ WKsq+2 ] == EMPTY ) return 1;
		if (Color == BLACK && !(Castling&B_KS) && m_Sqs[ BKsq+1 ] == EMPTY && !ColorAttacksSq(WHITE, BKsq+1) && m_Sqs[ BKsq+2 ] == EMPTY ) return 1;
		return 0; }
	int CanCastleQS ( const int Color) {
		if (Color == WHITE && !(Castling&W_QS) && m_Sqs[ WKsq-1 ] == EMPTY && !ColorAttacksSq(BLACK, WKsq-1) && m_Sqs[ WKsq-2 ] == EMPTY && m_Sqs[ WKsq-3 ] == EMPTY) return 1;
		if (Color == BLACK && !(Castling&B_QS) && m_Sqs[ BKsq-1 ] == EMPTY && !ColorAttacksSq(BLACK, BKsq-1) && m_Sqs[ BKsq-2 ] == EMPTY && m_Sqs[ BKsq-3 ] == EMPTY) return 1;
		return 0; }
	void AdjustMat ( int Dst, const int Mul )	{
		if (m_Sqs[ Dst ]&WHITE) WMat += Mul*MPieceValues[ (m_Sqs[ Dst ]&7) ];
		else BMat += Mul*MPieceValues[ (m_Sqs[ Dst ]&7) ];
		}
	void MovePiece (const int Src, const int Dst, const int Promo) {
		int Piece = m_Sqs [Src];
		Eval += EvalSq [ (Piece<<7) + Dst ] - EvalSq [ (Piece<<7) + Src ];
		if (m_Sqs[ Dst ] != EMPTY)
			{ Eval -= EvalSq [ (m_Sqs[ Dst ]<<7) + Dst ];
			  AdjustMat ( Dst, -1); }
		m_Sqs[Dst] = Piece;
		m_Sqs[Src] = EMPTY;
		if (Piece == (KING|WH) ) WKsq = Dst;
		if (Piece == (KING|BL) ) BKsq = Dst;
		if ((Piece&7) == PAWN ) {
			if (Dst < 30 || Dst > 90) {m_Sqs[Dst] += Promo + 1;
									   AdjustMat ( Dst, 1 );
									   Eval += EvalSq [ (m_Sqs[Dst]<<7) + Dst ] - EvalSq [ (Piece<<7) + Dst ]; }
			if ( Dst == EPsq ) {EPsq = Src+File(Dst)-File(Src);
								Eval -= EvalSq [ (m_Sqs[ EPsq ]<<7) + EPsq ];
								m_Sqs [ EPsq ] = EMPTY;}
			if ( abs(Src-Dst)==20) EPsq = ((Src + Dst)>>1); else EPsq = 0;
			}
		else EPsq = 0;
		}
	void DoMove ( const int Move )	{
		int Dst = DST(Move), Src = SRC(Move);
		Castling |= SetCastle[ Src ] | SetCastle[ Dst ];
		nMoves++;
		stm = SWITCH (stm);
		if ((m_Sqs [Src]&7) == KING) {
			if (Dst == Src-2) {MovePiece (Src, Src-2, 0); MovePiece (Src-4, Src-1, 0); return; }
			if (Dst == Src+2) {MovePiece (Src, Src+2, 0); MovePiece (Src+3, Src+1, 0); return; } }
		MovePiece (Src, Dst, PROMO(Move));
		}
	int CheckDirec ( int Sq, const int Dir, const int Piece1, const int Piece2) {
		Sq += Dir;
		while ( m_Sqs[ Sq ] == EMPTY ) Sq += Dir;
		if ( m_Sqs[ Sq ] == Piece1 || m_Sqs[ Sq ] == Piece2) return 1; else	return 0;
		}
	int ColorAttacksSq ( int Color, int Sq)	{
		int i;
		for (i = 0; i < 8; i++) if (m_Sqs[Sq+N_dirs[i]] == (KNIG|Color)) return 1;
		for (i = 0; i < 8; i++) if (m_Sqs[Sq+K_dirs[i]] == (KING|Color)) return 1;
		for (i = 0; i < 4; i++) if (CheckDirec (Sq, R_dirs[i], (QUEE|Color), (ROOK|Color)) ) return 1;
		for (i = 0; i < 4; i++) if (CheckDirec (Sq, B_dirs[i], (QUEE|Color), (BISH|Color)) ) return 1;
		int n = (Color == WHITE) ? 4:0;
		for (i = 2; i <= 3; i++) if (m_Sqs[Sq+P_dirs[i + n]] == (PAWN|Color)) return 1;
		return 0;
		}
	int IsCheck (int color) {return (color == WHITE) ? ColorAttacksSq(BLACK, WKsq) : ColorAttacksSq(WHITE, BKsq);}
	int Evaluate() {
		if (WMat < 1400 && BMat < 1400) return Eval + EvalSq[ (2<<7)+WKsq ] + EvalSq[ (3<<7)+BKsq ];
		return Eval + EvalSq[ (0<<7)+WKsq ] + EvalSq[ (1<<7)+BKsq ]; }
};

struct SMovelist
	{
	int m_Moves[256];
	int m_nMoves, m_nAttacks, m_bCaps;
	unsigned char *m_pSqs;

	void inline AddMove (int Src, int Dst) { if (m_bCaps) return;
											 m_Moves[ m_nMoves++ ] = Src + (Dst<<7) + (3<<14) + (200<<16); }
	void inline AddAtkMove (int Src, int Dst) {	m_Moves[ m_nMoves++ ] = Src + (Dst<<7)  + (3<<14) + ( (200+PieceValues[(m_pSqs[Dst]&7)]) << 16); }
	void inline GenPieceMoves ( const int MoveArray[], const int bSlide, const int nDirs, int Sq, SBoard &Board, const int COLOR ) {
		for (int i = 0; i < nDirs; i++) {
			int tempSq = Sq + MoveArray[i];
			if (bSlide)
				while ( Board.m_Sqs [ tempSq ] == EMPTY ) {
					AddMove (Sq, tempSq);
					tempSq += MoveArray[i];
					}
			if ( Board.m_Sqs[tempSq]&SWITCH(COLOR) ) AddAtkMove ( Sq, tempSq );
			else if (Board.m_Sqs[tempSq] == EMPTY )	AddMove (Sq, tempSq);
			}
		}
	void inline GenPawnMoves (  const int MoveArray[], int Sq, SBoard &Board, const int COLOR  ) 	{
		int n = (COLOR == BLACK) ? 4:0;
		if ( Board.m_Sqs[ Sq+P_dirs[n+0] ] == EMPTY ) {
			if ( StartBoard[Sq]==(PAWN|SWITCH(COLOR))) AddAtkMove (Sq, Sq+P_dirs[n+0]); else AddMove (Sq, Sq+P_dirs[n+0]);
			if ( StartBoard[Sq]==(PAWN|COLOR) && Board.m_Sqs[ Sq+P_dirs[n+1] ] == EMPTY) AddMove (Sq, Sq+P_dirs[n+1]);
			}
		if ( Sq+P_dirs[n+2] == Board.EPsq || (Board.m_Sqs[ Sq+P_dirs[n+2] ]&SWITCH(COLOR)) ) AddAtkMove (Sq, Sq+P_dirs[n+2]);
		if ( Sq+P_dirs[n+3] == Board.EPsq || (Board.m_Sqs[ Sq+P_dirs[n+3] ]&SWITCH(COLOR)) ) AddAtkMove (Sq, Sq+P_dirs[n+3]);
		}
	void Generate ( SBoard &Board, int bCaps )	{
		m_nMoves = 0;
		m_bCaps = bCaps;
		m_pSqs = Board.m_Sqs;
		int Color = Board.stm;
		for ( int Sq = 20; Sq < 100; Sq ++)
			switch (Board.m_Sqs[ Sq ]^Color )	{
				case PAWN: GenPawnMoves  ( P_dirs, Sq, Board, Color ); break;
				case KNIG: GenPieceMoves ( N_dirs, 0, 8, Sq, Board, Color ); break;
				case BISH: GenPieceMoves ( B_dirs, 1, 4, Sq, Board, Color ); break;
				case ROOK: GenPieceMoves ( R_dirs, 1, 4, Sq, Board, Color ); break;
				case QUEE: GenPieceMoves ( Q_dirs, 1, 8, Sq, Board, Color ); break;
				case KING: GenPieceMoves ( K_dirs, 0, 8, Sq, Board, Color );
					if ( !Board.IsCheck (Color) ) {
						if ( Board.CanCastleQS ( Color ) ) AddMove (Sq, Sq-2);
						if ( Board.CanCastleKS ( Color ) ) AddMove (Sq, Sq+2);}
					break;
				};
		}
	void ScoreMoves ( SBoard &Board, const int Color, int BestMove ) {
		for (int i = 0; i < m_nMoves; i++) {
			int Dst = DST (m_Moves[i]), Src = SRC(m_Moves[i]);
			int Piece = Board.m_Sqs[ Src ];
			if (Color == WHITE) m_Moves[i] += ((EvalSq [ (Piece<<7) + Dst ] - EvalSq [ (Piece<<7) + Src ])<<16);
			if (Color == BLACK) m_Moves[i] -= ((EvalSq [ (Piece<<7) + Dst ] - EvalSq [ (Piece<<7) + Src ])<<16);
			if ( (m_Moves[i]&65535) == (BestMove&65535) ) m_Moves[i] += (2048<<16);
			}
		}
	int GetNextMove ( int &nMove ) 	{
		int Max = -1, Next = -1;
		for (int i = 0; i < m_nMoves; i++)
			if (m_Moves[i] && VALUE(m_Moves[i]) > Max) {
				nMove = m_Moves[i];
				Next = i;
				Max = VALUE(nMove);}
		if (Next == -1) return 0;
		m_Moves[Next] = 0;
		return 1;
		}
};

int64_t RepNum[ 2000 ];
int inline Repetition ( const int64_t HashKey, int nStart, int ahead)  {
	int i = (nStart > 0) ? nStart : 0;
	if ((i&1) != (ahead&1)) i++;
    for ( ; i < ahead; i+=2) if (RepNum[i] == HashKey) return true;
    return false;
    }
void inline AddRepBoard ( const int64_t HashKey, int ahead ) {RepNum[ ahead ] = HashKey;}

uint64_t HashFunction [128][16], HashSTM;
struct TEntry
	{
	unsigned long m_checksum;
	short m_eval,  m_bestmove;
	char  m_depth, m_failtype, m_ahead, m_age;

	void inline Read ( unsigned long CheckSum, short alpha, short beta, int &bestmove, int &value, int depth, int ahead) {
		if (m_checksum == CheckSum ) {
			if (m_depth >= depth) {
				int tempVal = m_eval;
			    if (abs (m_eval) > 18000) {
					if (m_eval > 0) tempVal = m_eval - ahead + m_ahead;
					if (m_eval < 0) tempVal = m_eval + ahead - m_ahead;
					}
			   switch ( m_failtype ) {
					case 0: value = tempVal; break;
					case 1: if (tempVal <= alpha) value = tempVal; 	break;
					case 2: if (tempVal >= beta) value = tempVal;	break;
					}
               }
			if (depth > 2) bestmove = m_bestmove;
			}
		}
	void inline Write ( unsigned long CheckSum, short alpha, short beta, int &bestmove, int &value, int depth, int ahead) {
		m_checksum = CheckSum;
		m_eval = value;
		m_ahead = ahead;
		m_depth = depth;
		m_bestmove = (bestmove & 65535);
		if (value <= alpha) m_failtype = 1; else if (value >= beta)  m_failtype = 2; else m_failtype = 0;
		}
	static void Create_HashFunction () {
		for (int i = 0; i <128; i++)
        for (int x = 0; x < 16; x++)	{
			HashFunction [i][x]  = rand() + (rand()*256) + (rand()*65536);
			HashFunction [i][x] <<= 32;
			HashFunction [i][x]  += rand() + (rand()*256) + (rand()*65536);
			}
		HashSTM = HashFunction[0][0];
		}
	static uint64_t HashBoard (const SBoard &Board) {
		uint64_t CheckSum = 0;
	    for (int index = 21; index <= 99; index++) {
			int nPiece =  Board.m_Sqs [ index ];
			if (nPiece == INVALID) continue;
			if (nPiece != EMPTY) CheckSum ^= HashFunction [ index ][ (nPiece&15) ];
			}
		if (Board.stm == BLACK) CheckSum ^= HashSTM;
		return CheckSum;
		}
	//static AddRepBoard ( );
	};
TEntry TTable[ HASH_SIZE ];

int64_t g_Nodes, g_CheckNodes;
int g_MaxTime;
clock_t g_start;

int ABSearch ( SBoard &InBoard, int alpha, int beta, int depth, int ahead, int &BestMove, int bNull )
{
	SMovelist Moves;
	SBoard Board;
	int Color = InBoard.stm, Eval, NextBest = 0, nMove, bInCheck = InBoard.IsCheck (Color);
	int64_t n64Hash;

	if ( g_Nodes > g_CheckNodes ) {
		g_CheckNodes = g_Nodes+10000;
		if ((clock()-g_start) > g_MaxTime) return TIME;
		}
	if ( (!bInCheck && depth <= 0) || depth < 0 || ahead > 32 ) {
		int Eval = (InBoard.stm == WHITE) ? InBoard.Evaluate() : -InBoard.Evaluate();
		if (Eval > alpha) alpha = Eval;
		if (alpha >= beta) return beta;
		Moves.Generate ( InBoard, 1 );
		}
	else {
		if (bNull && depth > 2 && !bInCheck
			&& ((Color==WHITE && InBoard.WMat > 400) || (Color==BLACK && InBoard.BMat > 400))) {
			InBoard.stm = SWITCH (InBoard.stm);
			Eval = -ABSearch ( InBoard, -beta, -beta+1, depth-3, ahead, NextBest, false);
			InBoard.stm = SWITCH (InBoard.stm);
			if (Eval == -TIME) return TIME;
			if (Eval >= beta) return beta;
			}
		Moves.Generate ( InBoard, 0 );
		}

	if (BestMove < 100 && depth > 2) {Eval = ABSearch ( InBoard, alpha, beta, depth-2, ahead+1, BestMove, true);
									  if (Eval == TIME) return TIME;}
	Moves.ScoreMoves ( InBoard, Color, BestMove );

	BestMove = 0;
	while ( Moves.GetNextMove ( nMove ) ) {
		Board = InBoard;
		Board.DoMove ( nMove );
		g_Nodes++;
		if ( Board.IsCheck ( Color ) ) continue;
		NextBest = 0; Eval = -32000;

		if (depth > 1) {
			n64Hash = TEntry::HashBoard ( Board );
			if (Repetition  ( n64Hash, Board.nMoves - 40, Board.nMoves)) Eval = 0;
			else {AddRepBoard ( n64Hash, Board.nMoves);
				  TTable [ ((unsigned long)n64Hash) % HASH_SIZE ].Read ( (unsigned long)(n64Hash>>32), alpha, beta, NextBest, Eval, depth-1, ahead);
				}
			}
		if (Eval == -32000)
			{Eval = -ABSearch (Board, -beta, -alpha, (bInCheck)?(depth):(depth-1), ahead+1, NextBest, true );
			 if (Eval == -TIME) return TIME;}
		if (depth > 1) TTable [ ((unsigned long)n64Hash) % HASH_SIZE ].Write  ( (unsigned long)(n64Hash>>32), alpha, beta, NextBest, Eval, depth-1, ahead);

		if ( Eval > alpha) {
			if (ahead == 0) SendThinking ( nMove, depth, Eval, g_Nodes, false);
			BestMove =  nMove;
			alpha = Eval;
			if (alpha >= beta) return beta;
			}
		if (BestMove == 0) BestMove = 1;
		}

	if ( !Moves.m_bCaps && BestMove == 0) {return (bInCheck) ? -20000+ahead : 0;}
	return alpha;
}

void ComputerMove( SBoard &Board, int &Move, int &Eval )
{
	g_Nodes = 0;
	g_CheckNodes = g_Nodes+10000;
	g_start = clock();
	int SearchEval, SearchMove;
	for (int nDepth = 2; nDepth < 16; nDepth++)	{
		SearchEval = ABSearch (Board, -30000, 20001-nDepth, nDepth, 0, SearchMove, false);
		if (SearchEval != TIME) Eval = SearchEval;
		if (SearchMove > 100) {	Move = SearchMove;
								SendThinking ( Move ,nDepth, Eval, g_Nodes, true);}
		if ((clock()-g_start) > (g_MaxTime/2)) break;
		if (abs(Eval)>= (20001-nDepth)) break;
		}
}

void LogOutput ( char *pre, char *s )
{
FILE *fp = fopen ("outlog.txt", "at");
if (fp) {
	fprintf (fp, "%s%s\n", pre, s );
	fclose (fp);
	}
}

void LogBoard ( SBoard &Board )
{
const char cPieces[16] = { 'p', 'n', 'b', 'r', 'q', 'k', 0, 0, 'P', 'N', 'B', 'R', 'Q', 'K',0, 0};
char s[1024];
int j = 0;
strcpy (s, "");
for (int i = 20; i < 100; i++) {
	 if ( (i%10) == 0 || (i%10)==9) continue;
	 if (Board.m_Sqs[i] == 0) j+=sprintf (s+j, " . ");
	 if ((Board.m_Sqs[i]&BLACK)) j+=sprintf (s+j, " %c ", cPieces[(Board.m_Sqs[i]&7)]);
	 if ((Board.m_Sqs[i]&WHITE)) j+=sprintf (s+j, " %c ", cPieces[(Board.m_Sqs[i]&7)+8]);
	 if ( (i%10) == 8) j+=sprintf (s+j, "\n");
	}
j+=sprintf (s+j, "%d, %d : PCSq: %d\n", Board.WMat, Board.BMat, Board.Evaluate());
LogOutput ("", s);
}

void ReplayGame ( SBoard &Board, int nMoves ) {
	Board.Init();
	for( int i = 0; i < nMoves; i++) Board.DoMove ( GameMoves [i] );
}
void GetMoveString ( char *preFix, char *sMove, int move) {
	int Src = SRC(move), Dst = DST(move);
	sprintf (sMove, "%s%c%c%c%c\n", preFix, File(Src)+'a'-1, 9-Rank(Src)+'0', File(Dst)+'a'-1, 9-Rank(Dst)+'0' );
}
void SendMove ( int move) {
	char sMove[128];
	GetMoveString ( "move ",sMove, move );
	printf ( "%s", sMove );
	fflush (stdout);
	LogOutput ("sent> ", sMove);
}
void SendThinking ( int move, int Depth, int Eval, int64_t Nodes, int bFinished )
{
	char sMove[128];
	GetMoveString ( (char *)((bFinished)?"":"*"), sMove, move );
	printf("%d %d %d %I64d %s\n", Depth, Eval, (clock()-g_start)/10, Nodes, sMove);
	fflush (stdout);
}

int GetMove ( char *s, SBoard &Board )
{
	if (s[0] < 'a' || s[0] > 'h' ||	s[1] < '0' || s[1] > '9' ||	s[2] < 'a' || s[2] > 'h' ||	s[3] < '0' || s[3] > '9') return -1;
	LogOutput ("GotMove> ", s);
	int Src = SQ(s[0]-'a',7-(s[1]-'1')), Dst = SQ(s[2]-'a', 7-(s[3]-'1') ), Upgrade = 3;
	if (s[4]=='n' || s[4]=='N') Upgrade = 0;
	if (s[4]=='b' || s[4]=='B') Upgrade = 1;
	if (s[4]=='r' || s[4]=='R') Upgrade = 2;
	return Src + (Dst<<7) + (Upgrade<<14);
}

void WinboardLoop ( )
{
SBoard Board;
char CompColor = BLACK;
char line[1024], cmd[1024];
int move = 1, Eval = 0, time, inc = 0;

FILE *fp = fopen ("outlog.txt", "wt");
if (fp) fclose (fp);
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);
fflush(NULL);
signal(SIGINT, SIG_IGN);

while ( 1==1 ) {
	if (!fgets(line, 1000, stdin)) return;
	sscanf (line, "%s", cmd);
	LogOutput ("> ", line);
	if (strcmp (cmd, "protover") == 0) {
      printf("feature myname=\"SamChess\"\n");
      printf("feature variants=\"normal\"\n");
      printf("feature sigint=0\n");
      printf("feature sigterm=0\n");
      printf("feature analyze=0\n");
      printf("feature colors=1\n");
      printf("feature done=1\n");
      continue;
      }
	move = GetMove ( line, Board );
	if (move > 1) {
		GameMoves [ Board.nMoves ] = move;
		Board.DoMove (move);
		AddRepBoard ( TEntry::HashBoard ( Board ), Board.nMoves);
		LogBoard ( Board );
		}

	if (strcmp (cmd, "go") == 0) CompColor = Board.stm;
	if (strcmp (cmd, "force") == 0) CompColor = -1;
	if (CompColor == Board.stm)	{
		move = 0;
		if (Board.nMoves == 0) {srand ( clock() );
			if (rand()%100 > 40) move = (85)+(65<<7); else move = (84)+(64<<7);
			}
		else ComputerMove ( Board, move, Eval );
		FILE *fp = fopen ("outlog.txt", "at");
		if (fp) {int nps = ((clock()-g_start)==0) ? 0: (int)((g_Nodes*1000)/(clock()-g_start));
				fprintf (fp, "Eval: %d  Nodes %I64d  Time %d  NPS %d\n", Eval, g_Nodes, clock()-g_start, nps );
				fclose (fp);
				}
		if (move) {
			SendMove ( move);
			GameMoves [ Board.nMoves ] = move;
			Board.DoMove ( move );
			AddRepBoard ( TEntry::HashBoard ( Board ), Board.nMoves);
			LogBoard ( Board );
			}
		}
	if (strcmp (cmd, "time")  == 0) {
		sscanf(line, "time %d", &time);
		g_MaxTime = (CLOCKS_PER_SEC/100) * (time/30) + (inc * CLOCKS_PER_SEC);
		}
	if (strcmp (cmd, "level") == 0) {sscanf(line, "level %d %d %d", &time, &time, &inc); }
	if (strcmp (cmd, "new")   == 0) {Board.Init(); CompColor = BLACK;}
	if (strcmp (cmd, "quit")  == 0) return;
	if (strcmp (cmd, "white") == 0) CompColor = WHITE;
	if (strcmp (cmd, "black") == 0) CompColor = BLACK;
	if (strcmp (cmd, "undo")  == 0)  ReplayGame ( Board, Board.nMoves - 1);
	if (strcmp (cmd, "remove") == 0) ReplayGame ( Board, Board.nMoves - 2);
	}
}

int main ( ) {
	TEntry::Create_HashFunction ( );
	WinboardLoop ( );
return 1;
}
Jim.
JBNielsen
Posts: 267
Joined: Thu Jul 07, 2011 10:31 pm
Location: Denmark

Re: What is maximum rating per programmed line...

Post by JBNielsen »

lucasart wrote: Here's what I get with my engine DiscoCheck

Code: Select all

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
C                               17            636            582           3146
C/C++ Header                    17            120            304            439
-------------------------------------------------------------------------------
SUM:                            34            756            886           3585
-------------------------------------------------------------------------------
About 2600 elo on CEGT 40/20. Last version tested was 3.6.2 (2575 elo), and version 3.7.1 is about 30 elo better in self testing.
It is 100% portable and not obfuscated (MicroMax style). I'll try to clean up my code a little and see if I can reduce it a little, while making it more readable at the same time (since comments don't count).

The size of the Linux x86-64 executable is 82.2 KB, which is a lot larger than NanoSzachy :oops:
Would be very interesting!
JBNielsen
Posts: 267
Joined: Thu Jul 07, 2011 10:31 pm
Location: Denmark

Re: What is maximum rating per programmed line...

Post by JBNielsen »

hgm wrote:It depends how you define 'readability'.

I of course would say it is micro-Max. The slight inconvenience of having 1-character variable names is IMO more than compensated by the fact that every line is commented to say what it does. You catch on quickly enough as to what the variables mean, especially since there can be only 52.

And if the in-source comments are not enough, there is about 30 web pages of description on how it works...
You have a very good point here!