N.E.G. 1.0 released

Discussion of chess software programming and technical issues.

Moderator: Ras

User avatar
hgm
Posts: 28378
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: N.E.G. 1.0 released

Post by hgm »

I will still have to contemplate all that.

Anyway, I made a new version of N.E.G., which should not play illegal moves anymore. It still cannot castle or capture e.p., but when these are the only legal moves (so that it finds no move), it now resigns:

Code: Select all

#include <stdio.h>

#ifdef WIN32 
#    include <windows.h>
#else
#    include <sys/time.h>
#    include <sys/times.h>
#    include <unistd.h>
     int GetTickCount()
     {	struct timeval t;
	gettimeofday(&t, NULL);
	return t.tv_sec*1000 + t.tv_usec/1000;
     }
#endif

#define WHITE 8
#define BLACK 16
#define COLOR (WHITE|BLACK)


typedef void Func(int stm, int from, int to, void *closure);
typedef int Board[128];

int value[8] = { 0, 100, 100, 10000, 325, 350, 500, 950 };
int firstDir[] = { 0, 0, 27, 4, 18, 8, 13, 4 };
int steps[] = { -16, -15, -17, 0, 1, -1, 16, -16, 15, -15, 17, -17, 0, 1, -1, 16, -16, 0, 18, 31, 33, 14, -18, -31, -33, -14, 0, 16, 15, 17, 0 };
Board PST = {
 0, 2, 4, 6, 6, 4, 2, 0,   0,0,0,0,0,0,0,0,
 2, 8,10,12,12,10, 8, 2,   0,0,0,0,0,0,0,0,
 6,12,16,18,18,16,12, 6,   0,0,0,0,0,0,0,0,
 8,14,18,20,20,18,14, 8,   0,0,0,0,0,0,0,0,
 8,14,18,20,20,18,14, 8,   0,0,0,0,0,0,0,0,
 6,12,16,18,18,16,12, 6,   0,0,0,0,0,0,0,0,
 2, 8,10,12,12,10, 8, 2,   0,0,0,0,0,0,0,0,
 0, 2, 4, 6, 6, 4, 2, 0,   0,0,0,0,0,0,0,0
};


//              "abcdefghijklmnopqrstuvwxyz"
char pieces[] = ".5........3..4.176........";
Board board, attacks[2], lva[2];
int bestScore, bestFrom, bestTo, lastMover, lastChecker, randomize, post, iron[2], minors[2], material[2];

void
MoveGen (Board board, int stm, Func proc, void *cl)
{
  int from, to, piece, victim, type, dir, step;
  for(from=0; from<128; from = from + 9 & ~8) {
    piece = board[from];
    if(piece & stm) {
      type = piece & 7;
      if(type == 4 || type == 5) minors[!(piece & WHITE)]++, iron[!(piece & WHITE)] = from; // count minors and remember location of one
      material[!(piece & WHITE)] += value[type];
      dir = firstDir[type];
      while((step = steps[dir++])) {
        to = from;
        do {
          to += step;
          if(to & 0x88) break;
          victim = board[to];
          (*proc)(piece & COLOR, from, to, cl);
          victim += type < 5;
          if(!(to - from & 7) && type < 3 && (type == 1 ? to > 79 : to < 48)) victim--;
        } while(!victim);
      }
    }
  }
}

int InCheck (int stm)
{
  int k, xstm = COLOR - stm;
  for(k=0; k<128; k++) if( board[k] == stm + 3 ) {
    int dir=4, step;
    int f = (stm == WHITE ? -16 : 16); // forward
    int p = (stm == WHITE ? 2 : 1);
    if(!(k + f + 1 & 0x88) && board[k + f + 1] == xstm + p) return k + f + 2;
    if(!(k + f - 1 & 0x88) && board[k + f - 1] == xstm + p) return k + f;
    while((step = steps[dir])) {
	int from = k + steps[dir + 14];
	if(!(from & 0x88) && board[from] == xstm + 4) return from + 1;
	from = k + step;
	if(!(from & 0x88) && board[from] == xstm + 3) return from + 1;
	from = k;
	while(!((from += step) & 0x88)) if(board[from]) { // occupied
	    if(dir < 8 && (board[from] & COLOR + 6) == xstm + 6) return from + 1; // R or Q and orthogonal
	    if(dir > 7 && (board[from] & COLOR + 5) == xstm + 5) return from + 1; // B or Q and diagonal
	    break;
	}
	dir++;
    }
    break;
  }
  return 0;
}

void
Count (int stm, int from, int to, void *cl)
{
  int s = (stm == BLACK);
  if(!(to - from & 7) && (board[from] & 7) < 3) return; // ignore Pawn non-captures
  attacks[s][to]++;
  if(lva[s][to] > value[board[from] & 7]) lva[s][to] = value[board[from] & 7];
}

void
Mark (int stm, int from, int to, void *cl)
{
  int s = (stm == BLACK);
  if(from != iron[s]) return; // only consider moves of iron piece
  attacks[s][to] += 5;        // and count its attacks as many
}

void
Score (int stm, int from, int to, void *cl)
{
  int score = PST[to] - PST[from];
  int piece = board[from];
  int victim = board[to];
  int myVal = value[piece & 7];
  int hisVal = value[victim & 7];
  int push = (piece & 7) < 3 && to - from & 7;// Pawn non-capture
  int s = (stm == BLACK);
  int check;
  if((piece & 7) < 3 && !(to - from & 7) != !victim) return; // weed out illegal pawn modes
  if((piece & 7) == 3) score -= score;        // keep King out of center
  else if(myVal > 400) score = 0;             // no centralization for R, Q
  if((piece & ~7) == (victim & ~7)) return;   // self capture
  board[from] = 0; board[to] = piece;
  if(from != lastChecker && InCheck(COLOR - stm)) score += 50; // bonus for checking with new piece
  check = InCheck(stm);                       // in check after move?
  board[to] = victim; board[from] = piece;
  if(check) return;                           // illegal
  score += ((rand()>>8 & 31) - 16)*randomize; // randomize
  if(from == lastMover) score -= 10;          // discourage moving same piece twice
  if(hisVal && hisVal < 400) score += PST[to];// centralization bonus of victim
  score += hisVal;                            // captured piece
  if(iron[!s] == to) score -= 500;            // discourage capturing last minor, to reduce stalemate probability
  if(attacks[!s][to]) {                       // to-square was attacked
    if(attacks[s][to] - 1 + push < attacks[!s][to] ) score -= myVal; else // not sufficiently protected
    if(myVal > lva[!s][to]) score += lva[!s][to] - myVal; // or protected, but more valuable
  }
  if((piece & 7) != 3 && attacks[!s][from]) { // from-square was attacked (and not King)
    if(attacks[s][from] < attacks[!s][from] ) score += myVal; else // not sufficiently protected
    if(myVal > lva[!s][from]) score -= lva[!s][from] - myVal; // or protected, but more valuable
  }
  if((piece & 7) == 1 && to < 48) score += 50;
  if((piece & 7) == 2 && to > 79) score += 50;

  if(score > bestScore) bestScore = score, bestFrom = from, bestTo = to; // remember best move
  if(post) printf("2 %d 0 1 %c%d%c%d\n", score, (from&7) + 'a', 8 - (from >> 4), (to&7) + 'a', 8 - (to >> 4));
}

int
Setup (char *fen)
{
  char c;
  int i;
  for(i=0; i<128; i++) board[i] = 0;
  i = 0;
  while((c = *fen++)) {
    if(c == 'p') board[i++] = BLACK + 2; else
    if(c >= '0' && c <= '9') i += c - '0'; else
    if(c >= 'a' && c <= 'z') board[i++] = BLACK + pieces[c - 'a'] - '0'; else
    if(c >= 'A' && c <= 'Z') board[i++] = WHITE + pieces[c - 'A'] - '0'; else
    if(c == '/') i = (i | 15) + 1; else break;
  }
  for(i=0; i<128; i = i + 9 & ~8) printf(i&7 ? " %2d" : "\n# %2d", board[i]); printf("\n");
  return (*fen == 'w' ? WHITE : BLACK);
}

int
main ()
{
  int stm = WHITE, engineSide = 0;
  char line[256], command[20];
  srand(GetTickCount());
  while(1) {
    int i, c;
    if(stm == engineSide) {
	char *promo = "";
	for(i=0; i<128; i++) lva[0][i] = lva[1][i] = 30000, attacks[0][i] = attacks[1][i] = 0;
	minors[0] = minors[1] = material[0] = material[1] = 0;
	MoveGen(board, COLOR, &Count, NULL);
	bestScore = -30000;
	if(material[stm == BLACK] - material[stm == WHITE] < 1000 || minors[stm == WHITE] != 1) iron[stm == WHITE] = -1;
	else MoveGen(board, COLOR-stm, &Mark, NULL);
	MoveGen(board, stm, &Score, NULL);
        if(bestScore == -30000) printf("resign\n"), engineSide = 0; else {
	    board[bestTo] = board[bestFrom]; board[bestFrom] = 0; stm ^= COLOR;
	    if((board[bestTo] & 7) < 3 && (stm == BLACK ? bestTo < 16 : bestTo > 111)) board[bestTo] |= 7, promo = "q"; // always promote to Q
	    lastMover = bestTo;
	    lastChecker = InCheck(stm) ? bestTo : -1 ;
	    printf("move %c%d%c%d%s\n", (bestFrom&7) + 'a', 8 - (bestFrom >> 4), (bestTo&7) + 'a', 8 - (bestTo >> 4), promo);
        }
    }
    fflush(stdout); i = 0;
    while((line[i++] = c = getchar()) != '\n') if(c == EOF) printf("# EOF\n"), exit(1); line[i] = '\0';
    if(*line == '\n') continue;
    sscanf(line, "%s", command);
    printf("# command: %s\n", command);
    if(!strcmp(command, "usermove")) {
	int from, to; char c, d, promo, ep;
	sscanf(line, "usermove %c%d%c%d%c", &c, &from, &d, &to, &promo);
	from = (8 - from)*16 + c - 'a'; to = (8 - to)*16 + d - 'a';
	if((board[from] & 7) == 3 && to - from == 2) board[from + 1] = board[to + 1], board[to + 1] = 0; // K-side castling
	if((board[from] & 7) == 3 && from - to == 2) board[from - 1] = board[to - 2], board[to - 2] = 0; // Q-side castling
	ep = ((board[from] & 7) < 3 && !board[to]); // recognize e.p. capture
	board[to] = board[from]; if(ep) board[from & 0x70 | to & 7] = 0; board[from] = 0;
	if(promo == 'q') board[to] =  board[to] | 7;      // promote to Q
	if(promo == 'r') board[to] = (board[to] | 7) - 1; // under-promote
	if(promo == 'b') board[to] = (board[to] | 7) - 2; // under-promote
	if(promo == 'n') board[to] = (board[to] | 7) - 3; // under-promote
	stm ^= COLOR;	
    }
    else if(!strcmp(command, "protover")) printf("feature myname=\"N.E.G. 1.3b\" setboard=1 usermove=1 analyze=0 colors=0 sigint=0 sigterm=0 done=1\n");
    else if(!strcmp(command, "new"))      stm = Setup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), randomize = 0, engineSide = BLACK;
    else if(!strcmp(command, "go"))       engineSide = stm;
    else if(!strcmp(command, "result"))   engineSide = 0;
    else if(!strcmp(command, "force"))    engineSide = 0;
    else if(!strcmp(command, "setboard")) stm = Setup(line+9);
    else if(!strcmp(command, "random"))   randomize = !randomize;
    else if(!strcmp(command, "post"))     post = 1;
    else if(!strcmp(command, "nopost"))   post = 0;
    else if(!strcmp(command, "quit"))     break;
  }
  return 0;
}
It relies on the GUI to declare real checkmates and stalemates.
unserializable
Posts: 65
Joined: Sat Oct 24, 2020 6:39 pm
Full name: Taimo Peelo

Re: N.E.G. 1.0 released

Post by unserializable »

hgm wrote: Sat Nov 14, 2020 1:25 pm Anyway, I made a new version of N.E.G., which should not play illegal moves anymore. It still cannot castle or capture e.p., but when these are the only legal moves (so that it finds no move), it now resigns:
Thanks, great stuff, it now should be flawless engine :), brief testing gameplay testing from couple positions with e.p. being only legal move resulted in resignment indeed. Situation where castling is only legal move would never happen (rook moves and king moves would also be available). Hope you update N.E.G. at https://home.hccnet.nl/h.g.muller/dwnldpage.html too, that is most certainly the place where most of the people make acquintance with formidably fast N.E.G. :)
Monchester 1.0, chess engine playing at scholastic level: https://github.com/unserializable/monchester ("Daddy, it is gonna take your horsie!")
Tickle Monchester at: https://lichess.org/@/monchester
User avatar
Roland Chastain
Posts: 680
Joined: Sat Jun 08, 2013 10:07 am
Location: France
Full name: Roland Chastain

Re: N.E.G. 1.0 released

Post by Roland Chastain »

@H.G.Muller

Thank you for the new version of NEG. (May we call it NEG 1.4?)

I take the occasion to share a logo that I made for NEG. (I don't remember to have seen a logo for this engine.)

logo.zip
Qui trop embrasse mal étreint.
Guenther
Posts: 4718
Joined: Wed Oct 01, 2008 6:33 am
Location: Regensburg, Germany
Full name: Guenther Simon

Re: N.E.G. 1.0 released

Post by Guenther »

Roland Chastain wrote: Sat Nov 14, 2020 3:23 pm @H.G.Muller

Thank you for the new version of NEG. (May we call it NEG 1.4?)

I take the occasion to share a logo that I made for NEG. (I don't remember to have seen a logo for this engine.)

logo.zip
It's already different to 1.3, this is sufficient for me. I will make a windows compile.

Code: Select all

else if(!strcmp(command, "protover")) printf("feature myname=\"N.E.G. 1.3b\"
Edit:
Does this look correct?

Code: Select all

xboard
# command: xboard
protover 2
# command: protover
feature myname="N.E.G. 1.3b" setboard=1 usermove=1 analyze=0 colors=0 sigint=0 sigterm=0 done=1
new
# command: new

# 22 20 21 23 19 21 20 22
# 18 18 18 18 18 18 18 18
#  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
#  9  9  9  9  9  9  9  9
# 14 12 13 15 11 13 12 14
post
# command: post
go
# command: go
2 4 0 1 a2a3
2 6 0 1 a2a4
2 4 0 1 b2b3
2 6 0 1 b2b4
2 6 0 1 c2c3
2 8 0 1 c2c4
2 6 0 1 d2d3
2 8 0 1 d2d4
2 6 0 1 e2e3
2 8 0 1 e2e4
2 6 0 1 f2f3
2 8 0 1 f2f4
2 4 0 1 g2g3
2 6 0 1 g2g4
2 4 0 1 h2h3
2 6 0 1 h2h4
2 14 0 1 b1c3
2 4 0 1 b1a3
2 4 0 1 g1h3
2 14 0 1 g1f3
move b1c3
go
# command: go
2 4 0 1 b8a6
2 14 0 1 b8c6
2 14 0 1 g8f6
2 4 0 1 g8h6
2 4 0 1 a7a6
2 6 0 1 a7a5
2 4 0 1 b7b6
2 -94 0 1 b7b5
2 6 0 1 c7c6
2 8 0 1 c7c5
2 6 0 1 d7d6
2 -92 0 1 d7d5
2 6 0 1 e7e6
2 8 0 1 e7e5
2 6 0 1 f7f6
2 8 0 1 f7f5
2 4 0 1 g7g6
2 6 0 1 g7g5
2 4 0 1 h7h6
2 6 0 1 h7h5
move b8c6
https://rwbc-chess.de

[Trolls n'existent pas...]
User avatar
hgm
Posts: 28378
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: N.E.G. 1.0 released

Post by hgm »

That looks correct. Because N.E.G. doesn't search ahead there is no iterative deepening, and it uses Thinking Output in a sort of multi-PV way for printing the entire list of root moves and the score it assigned to those.

I called it 1.3b because it basically plays exactly the same as 1.3, except that it resigns where it would otherwise forfeit. That is purely a cosmetic thing.
User avatar
Roland Chastain
Posts: 680
Joined: Sat Jun 08, 2013 10:07 am
Location: France
Full name: Roland Chastain

Re: N.E.G. 1.0 released

Post by Roland Chastain »

hgm wrote: Sun Dec 29, 2019 11:45 am The Ram 2.0 source is below. It should be linked with the MersenneTwister random generator.
If ever someone is interested: Ram 2.0 ready to build (with a Makefile).
Qui trop embrasse mal étreint.
Dann Corbit
Posts: 12790
Joined: Wed Mar 08, 2006 8:57 pm
Location: Redmond, WA USA

Re: N.E.G. 1.0 released

Post by Dann Corbit »

Roger Brown wrote: Sat Dec 27, 2014 4:09 pm
hgm wrote:Just for fun I made a small improvement to my non-searching Chess engine N.E.G.: it had a nasty tendency to draw games where it had reduced the opponent to a bare King, because when there is nothing left to capture it prefers checks, and chasing the bare King with a Queen (which often was the only active piece) of course leads to nothing.

So in this new version it only gets the bonus for checking with a new piece. This usually is enough to get a bare King checkmated.

I also made it WB v2, supporting setboard, and Linux-compatible. This is the source code:

Code: Select all

SNIP
Hello H.G.,

I went to your site but the the N.E.G. version available is 0.3d.exe.

Where is version 1.0 available?

Later.
I did not see a response (maybe I missed it). Anyway, here is the modified code (I reformatted it a bit and added a header) and a 64 bit binary:


It uses the UCI protocol, by the way.
Taking ideas is not a vice, it is a virtue. We have another word for this. It is called learning.
But sharing ideas is an even greater virtue. We have another word for this. It is called teaching.
User avatar
Roland Chastain
Posts: 680
Joined: Sat Jun 08, 2013 10:07 am
Location: France
Full name: Roland Chastain

Re: N.E.G. 1.0 released

Post by Roland Chastain »

Dann Corbit wrote: Wed Jul 03, 2024 2:23 pm (I reformatted it a bit and added a header)
Thank you for sharing. For Linux (with GCC), I added the two following headers.

Code: Select all

#include <unistd.h>
#include <stdlib.h> // <---
#include <string.h> // <---
Dann Corbit wrote: Wed Jul 03, 2024 2:23 pm It uses the UCI protocol, by the way.
I think you meant WinBoard protocol?
Qui trop embrasse mal étreint.
Dann Corbit
Posts: 12790
Joined: Wed Mar 08, 2006 8:57 pm
Location: Redmond, WA USA

Re: N.E.G. 1.0 released

Post by Dann Corbit »

Yes, of course, winboard/xboard
Taking ideas is not a vice, it is a virtue. We have another word for this. It is called learning.
But sharing ideas is an even greater virtue. We have another word for this. It is called teaching.
ChessLogic
Posts: 1
Joined: Sun Jul 14, 2024 8:15 pm
Full name: Johann Liebenfels

Re: N.E.G. 1.0 released

Post by ChessLogic »

Hello,
hgm wrote: Fri Dec 26, 2014 3:25 pm

Code: Select all

Board PST = &#123;
 0, 2, 4, 6, 6, 4, 2, 0,   0,0,0,0,0,0,0,0,
 2, 8,10,12,12,10, 8, 2,   0,0,0,0,0,0,0,0,
 6,12,16,18,18,16,12, 6,   0,0,0,0,0,0,0,0,
 8,14,18,20,20,18,14, 8,   0,0,0,0,0,0,0,0,
 8,14,18,20,20,18,14, 8,   0,0,0,0,0,0,0,0,
 6,12,16,18,18,16,12, 6,   0,0,0,0,0,0,0,0,
 2, 8,10,12,12,10, 8, 2,   0,0,0,0,0,0,0,0,
 0, 2, 4, 6, 6, 4, 2, 0,   0,0,0,0,0,0,0,0
&#125;;
sorry for asking, but what is this for a board representation?
Is there any information on the net?

Thank you. Greetings
ChessLogic