Are Bitboards More Intoxicating Than They Are Good?

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Mike Sherwin
Posts: 860
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Are Bitboards More Intoxicating Than They Are Good?

Post by Mike Sherwin »

hgm wrote: Sun Feb 28, 2021 11:25 am
Mike Sherwin wrote: Wed Feb 24, 2021 6:54 pmIn Carnivor only material counting and psqt evaluation are done and it is also incremental. And everytime a move is made in search the nodes counter is incremented. So for example searching to 13 ply deep takes 17.42 seconds and 448,943,111 moves are made. On my calculator 448943111 / 17.42 = 25,771,705 moves per second.
I still want to understand this better, because it is unusually fast. So I am curious what exactly you are doing here.

You mention you count making moves, and this might cause tricky differences, as I am used to counting move generations. So what kind of search is this, exactly? Is there a quiescence search? Is there futility pruning?

I ask that, because futility pruning dispenses with moves without actually making them. This of course makes the engine faster, which is why we do it. But if you count makemoves, it would make the nps go down. So I am a bit worried that the high nps could be a consequences of making mostly useless moves, to nodes for which you knew in advance you did not have to do anything there.
Carnivor (then after, RomiChess) was my first real attempt at programming anything more than a non graphical roulette wheel in Basic. So it is possible I made some weird mistake counting nodes. I did not even know they were called nodes. I just used a global int x, lol. If I remember correctly Car does do shallow searches (1 move + Qsearch) with an open window to score the moves if there are a few plys of depth left. Those iid moves are counted. I guess they are not tree nodes so maybe they should not be counted. All I can suggest is to take a look at the code if it would not be too painful to read such beginner_ish code. And it still compiles without too much trouble.

Code: Select all

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
#include <time.h>
#include <signal.h>

#pragma warning(disable : 4996)

#define TRUE  1
#define FALSE 0

#define WHITE 0
#define BLACK 1
#define BOTH  2
#define OFF  3

#define         BISHOP          0
#define         ROOK            1
#define         QUEEN           2
#define         KNIGHT          3
#define         KING            4
#define         PAWN            5
#define         CASks           6
#define         CASqs           7
#define         COMCAS          8

#define         VACANT          1
#define         FRIEND          0
#define         ENEMY           2
#define         ILLEGAL         3

#define         LR1             0
#define         L1              1
#define         R1              2
#define         LR2             3
#define         L2              4
#define         R2              5
#define         LRE             6
#define         LE              7
#define         RE              8
#define         LRQ             9
#define         LQ              10
#define         RQ              11

#define MOVE       0
#define MOVEq      1
#define MOVEr      2
#define MOVEb      3
#define MOVEn      4
#define PDMOVE     5
#define CAPTURE    6
#define CAPTUREq   7
#define CAPTUREr   8
#define CAPTUREb   9
#define CAPTUREn   10
#define CEP        11 
#define CASTLEwk   12
#define CASTLEwq   13
#define CASTLEbk   14
#define CASTLEbq   15
#define INSwc      16
#define INSwkc     17
#define INSwqc     18
#define INSbc      19
#define INSbkc     20
#define INSbqc     21

#define inf 10002

#define EMPTY 17 

#define delete(x) piece[piece[(x)].nxt].prv=piece[(x)].prv;piece[piece[(x)].prv].nxt=piece[(x)].nxt
#define insert(x) piece[piece[(x)].nxt].prv=(x);piece[piece[(x)].prv].nxt=(x)

#define getmove() fs=tree[li].fsq;ts=tree[li].tsq;fid=brd[fs];
#define getunmv() fs=hist[rply].fsq;ts=hist[rply].tsq;fid=brd[ts];

#define evalft() hist[rply].sco=hist[rply-1].sco-pctbl[side][piece[fid].pt][fs]+ \
                 pctbl[side][piece[fid].pt][ts]
#define evalcap() hist[rply].sco-=pctbl[side^1][piece[tid].pt][ts]                 
#define evalcas(s,kf,kt,rf,rt) hist[rply].sco=hist[rply-1].sco- \
        pctbl[s][4][kf]-pctbl[s][1][rf]+pctbl[s][4][kt]+pctbl[s][1][rt]
#define evalf() hist[rply].sco=hist[rply-1].sco-pctbl[side][piece[fid].pt][fs]
#define evalt() hist[rply].sco+=pctbl[side][piece[fid].pt][ts]

#define storemove() hist[rply].fsq=fs;hist[rply].tsq=ts;hist[rply].typ=mt
#define movepiece() brd[ts]=fid;brd[fs]=EMPTY;piece[fid].ps=ts;storemove()
#define unmvpiece(x) brd[fs]=fid;piece[fid].ps=fs;brd[ts]=x 
#define queen(x,y) piece[fid].pt=x;if(side){piece[fid].pv=-y;hist[rply].sco=hist[rply].sco+100-y;} \
                             else{piece[fid].pv=y;hist[rply].sco=hist[rply].sco-100+y;}
#define unqueen() if(side){piece[fid].pt=5;piece[fid].pv=-100;} \
                       else{piece[fid].pt=5;piece[fid].pv=100;}                             
#define remove() tid=brd[ts];car[rply]=tid;delete(tid);hist[rply].sco-=piece[tid].pv;evalcap()
#define putback() tid=car[rply];insert(tid)
#define capep() ceps=(side)?ts+8:ts-8;tid=brd[ceps];brd[ceps]=EMPTY;car[rply]=tid; \
                delete(tid);hist[rply].sco=hist[rply].sco-pctbl[side^1][piece[tid].pt][ceps]-piece[tid].pv
#define uncapep() tid=car[rply];brd[piece[tid].ps]=tid;insert(tid)

#define link(x) ti->fsq=fs;ti->tsq=ts;ti->typ=(x);ti++

void initmg(void);
void initdata(void);
void drawgraf(void);
int  wmg(void);
int  bmg(void);
int  wcg(void);
int  bcg(void);         
void compute(void);
int  move(void);
void unmove(void);
void getcmd(void);
int  wsearch(int,int,int);
int  bsearch(int,int,int);
int  wlegal(void);
int  blegal(void);
int  wquiesce(int,int);
int  bquiesce(int,int);
int  wpick(void);
int  bpick(void);
int  wqpick(void);
int  bqpick(void);
int  reps(void); 
void convert(int,int);
void board();
void sortpv(void);
void bench(unsigned);

unsigned char ibrd[]=
{ 14,10,12,15,16,11, 9,13,
   5, 4, 3, 2, 1, 8, 7, 6, 
  17,17,17,17,17,17,17,17,
  17,17,17,17,17,17,17,17, 
  17,17,17,17,17,17,17,17,
  17,17,17,17,17,17,17,17,
  22,21,20,19,18,25,24,23,
  31,27,29,32,33,28,26,30 };

typedef struct
{
  int ps;
  int pv;
  int pt;
  int prv;
  int nxt;
} pieces;

pieces piece[41];

pieces ipiece[41]=
{
  { 0,   0, 0, 0, 1},
  {12, 120, 5, 0, 2},
  {11, 120, 5, 1, 3},
  {10, 100, 5, 2, 4},
  { 9, 100, 5, 3, 5},
  { 8, 100, 5, 4, 6},
  {15, 100, 5, 5, 7},
  {14, 100, 5, 6, 8},
  {13, 100, 5, 7, 9},
  { 6, 290, 3, 8,10},
  { 1, 290, 3, 9,11},
  { 5, 340, 0,10,12},
  { 2, 340, 0,11,13},
  { 7, 500, 1,12,14},
  { 0, 500, 1,13,15},
  { 3, 950, 2,14,16},
  { 4,   0, 4,15,35},
  { 0,   0, 0, 0,18},
  {52,-120, 5,17,19},
  {51,-120, 5,18,20},
  {50,-100, 5,19,21},
  {49,-100, 5,20,22},
  {48,-100, 5,21,23},
  {55,-100, 5,22,24},
  {54,-100, 5,23,25},
  {53,-100, 5,24,26},
  {62,-290, 3,25,27},
  {57,-290, 3,26,28},
  {61,-340, 0,27,29},
  {58,-340, 0,28,30},
  {63,-500, 1,29,31},
  {56,-500, 1,30,32},
  {59,-950, 2,31,33},
  {60,   0, 4,32,37},
  { 0,   0, 8, 0, 0},
  { 0,   0, 6,16,36},
  { 0,   0, 7,35,39},
  { 0,   0, 6,33,38},
  { 0,   0, 7,37,40},
  { 0,   0, 0,36,39},
  { 0,   0, 0,38,40}};


unsigned char wpwn[]=
{  0,  0,  0,  0,  0,  0,  0,  0,
  R2,LR2,LR2,LR2,LR2,LR2,LR2, L2,
  R1,LR1,LR1,LR1,LR1,LR1,LR1, L1,
  R1,LR1,LR1,LR1,LR1,LR1,LR1, L1,
  RE,LRE,LRE,LRE,LRE,LRE,LRE, LE,
  R1,LR1,LR1,LR1,LR1,LR1,LR1, L1,
  RQ,LRQ,LRQ,LRQ,LRQ,LRQ,LRQ, LQ,
   0,  0,  0,  0,  0,  0,  0,  0 };

unsigned char bpwn[]=
{  0,  0,  0,  0,  0,  0,  0,  0,
  LQ,LRQ,LRQ,LRQ,LRQ,LRQ,LRQ, RQ,
  L1,LR1,LR1,LR1,LR1,LR1,LR1, R1,
  LE,LRE,LRE,LRE,LRE,LRE,LRE, RE,
  L1,LR1,LR1,LR1,LR1,LR1,LR1, R1,
  L1,LR1,LR1,LR1,LR1,LR1,LR1, R1,
  L2,LR2,LR2,LR2,LR2,LR2,LR2, R2,
   0,  0,  0,  0,  0,  0,  0,  0 };
                
unsigned char wtrgt[]=
{ 0,
  0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,
  1,
  2,2,2,2,2,2,2,2,
  2,2,2,2,2,2,2,3 };

unsigned char btrgt[]=
{ 0,
  2,2,2,2,2,2,2,2,
  2,2,2,2,2,2,2,3,
  1,
  0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0 };
unsigned char bns[1808],bnd[1808],rns[3704],rnd[3704],qns[3872],qnd[3872];
unsigned char nns[890],kns[694],wpns[360],bpns[360];
unsigned int bol[64],rol[64],qol[64],nol[64],kol[64],pol[64];
  
unsigned char brd[64];
int  away;

typedef     struct      { unsigned char fsq;
                          unsigned char tsq;
                          unsigned char typ;
                          unsigned char cep; } amove;
                          
typedef     union       { amove;
                          int umove; } pmove;

typedef     union       { int sco;
                          time_t time; } value;
                          
typedef     struct      { pmove;
                          value; } node;
                          
node                    tree[2000];

node                    hist[200];

node                    pvr[200][200];
                                                                               

signed char pctbl[2][7][64]=
{{{ 6,  0,  0,  0,  0,  0,  0,  6,
   10, 30, 12, 12, 12, 12, 30, 10,
    0, 20, 30, 12, 12, 30, 20,  0,
   16, 12, 32, 32, 32, 32, 12, 16,
    0, 30, 24, 32, 32, 24, 30,  0,
    0, 12, 30, 24, 24, 30, 12,  0,
    0, 18, 20, 20, 20, 20, 18,  0,
    6,  0,  0,  0,  0,  0,  0,  6 },
  
 {  6, 10, 12, 14, 14, 12, 10,  6,
    8, 10, 10, 16, 16, 10, 10,  8,
    6,  8, 10, 12, 12, 10,  8,  6,
    4,  6,  8, 10, 10,  8,  6,  4,
    4,  6,  8, 10, 10,  8,  6,  4,
    6,  8, 10, 12, 12, 10,  8,  6,
   20, 20, 20, 20, 20, 20, 20, 20,
   18, 18, 18, 18, 18, 18, 18, 18 },
   
 { 14, 14, 14, 14, 14, 14, 14, 14,
   14, 16, 16, 16, 16, 16, 16, 14,
   14, 16, 18, 18, 18, 18, 16, 14,
   14, 16, 18, 18, 18, 18, 16, 14,
   16, 18, 20, 20, 20, 20, 18, 16,
   16, 18, 20, 20, 20, 20, 18, 16,
   18, 20, 20, 20, 20, 20, 20, 18,
   18, 18, 18, 18, 18, 18, 18, 18 },
  
 { 0,-10, 12, 12, 12, 12,-10,  0,
   6, 12, 24, 30, 26, 24, 12,  6,
  12, 24, 30, 36, 36, 38, 24, 12,
  12, 24, 36, 40, 40, 36, 24, 12,
  12, 24, 36, 42, 42, 36, 24, 12,
  12, 24, 36, 40, 40, 36, 24, 12,
   6, 12, 24, 36, 36, 24, 12,  6,
   0,  6, 12, 12, 12, 12,  6,  0  },
   
 { 30, 60, 20,-40,  0, 20, 60, 30,
  -30,-30,-30,-30,-30,-30,-30,-30,
  -20,-20,-20,-20,-20,-20,-20,-20,
  -10,  0, 20, 20, 20, 20,  0,-10,
  -10,  0, 20, 40, 40, 20,  0,-10,
  -10,  0, 20, 20, 20, 20,  0,-10,
  -10,  0,  0,  0,  0,  0,  0,-10,
  -10,-10,-10,-10,-10,-10,-10,-10 },
  
 {  0,  0,  0,  0,  0,  0,  0,  0,
    4,  4,-24,-30,-30,  8,  8,  8,
   12,  8,  0,-10,-10,  0,  4,  6,
    8, 14, 12, 20, 20,  8,  6,  4,
   10, 16, 16, 30, 30, 12, 10,  8,
   20, 40, 40, 60, 60, 40, 40, 20,
   40, 60, 60, 80, 80, 60, 60, 40,
    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,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,0,0,0,0,0,0,0,0  }},
     
{{  -6,  0,  0,  0,  0,  0,  0, -6,
     0,-18,-20,-20,-20,-20,-18,  0,
     0,-12,-30,-24,-24,-30,-12,  0,
     0,-30,-24,-32,-32,-24,-30,  0,
   -16,-12,-32,-32,-32,-32,-12,-16,
     0,-20,-30,-12,-12,-30,-20,  0,
   -10,-30,-12,-12,-12,-12,-30,-10,
    -6,  0,  0,  0,  0,  0,  0, -6 },
  
 {-22,-22,-22,-22,-22,-22,-22,-22,
  -24,-24,-24,-24,-24,-24,-24,-24,
   -6, -8,-10,-12,-12,-10, -8, -6,
   -4, -6, -8,-10,-10, -8, -6, -4,
   -4, -6, -8,-10,-10, -8, -6, -4,
   -6, -8,-10,-12,-12,-10, -8, -6,
   -8,-10,-10,-22,-22,-10,-10, -8,
   -8,-10,-12,-20,-20,-12,-10, -8 },

 {-18,-18,-18,-18,-18,-18,-18,-18,
  -18,-20,-20,-20,-20,-20,-20,-18,
  -16,-18,-20,-20,-20,-20,-18,-16,
  -16,-18,-20,-20,-20,-20,-18,-16,
  -14,-16,-18,-18,-18,-18,-16,-14,
  -14,-16,-18,-18,-18,-18,-16,-14,
  -14,-16,-16,-16,-16,-16,-16,-14,
  -14,-14,-14,-14,-14,-14,-14,-14 },
  
 { 0, -6,-12,-12,-12,-12, -6,  0,
  -6,-12,-24,-36,-36,-24,-12, -6,
 -12,-24,-36,-40,-40,-36,-24,-12,
 -12,-24,-36,-42,-42,-36,-24,-12,
 -12,-24,-36,-40,-40,-36,-24,-12,
 -12,-24,-30,-36,-36,-38,-24,-12,
  -6,-12,-24,-30,-26,-24,-12, -6,
   0, 10,-12,-12,-12,-12, 10,  0 },

 { 10, 10, 10, 10, 10, 10, 10, 10,
   10,  0,  0,  0,  0,  0,  0, 10,
   10,  0,-20,-20,-20,-20,  0, 10,
   10,  0,-20,-40,-40,-20,  0, 10,
   10,  0,-20,-20,-20, -0,  0, 10,
   20, 20, 20, 20, 20, 20, 20, 20,
   30, 30, 30, 30, 30, 30, 30, 30,
  -20,-60,-20, 60,  0,-20,-60,-20 },

 {  0,  0,  0,  0,  0,  0,  0,  0,
  -40,-60,-60,-80,-80,-60,-60,-40,
  -20,-40,-40,-60,-60,-40,-40,-20,
  -10,-16,-12,-30,-30,-12, -8, -6,
   -8,-14,-16,-20,-20, -8, -6, -4,
  -12, -8,  0, 10, 10,  0, -4, -6,
   -4, -4, 24, 30, 30, -8, -8, -4,
    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,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,0,0,0,0,0,0,0,0 }}};
  
unsigned int xboard=FALSE,levm,levt,levi,cap,pro,bas,repis,pvl[200],fpv;
signed int   tmp,high=0;
unsigned int car[200],first[200],lir[200],fifty[200],m,wi=FALSE,bi=FALSE,md,maxd,z;
unsigned long x; 
unsigned int repeat,side,computer,ply,rply,fs,ts,id,fid,tid,mt,li,ptype;
unsigned int wktmp,bktmp,wkrtmp,wqrtmp,bkrtmp,bqrtmp,ceps,wcastmp,bcastmp,i,j;
unsigned int wntmp,bntmp,wcas34,bcas34,nulcas;
unsigned char cmd[20],data[256],c;
time_t st,et,at;

     
int __cdecl main(void)
{ 
  initmg();
  initdata();
  drawgraf();
//  srand(clock());
    
  maxd=12800;
  bas=64;
  pro=64;
  away=128;
  at=120000;
  repeat=TRUE;
  side=WHITE;
  computer=OFF;

  while(repeat)
  {
    if(!xboard)board();
    getcmd();
    if(computer==side || computer==BOTH) compute();
  }
  return(0);
}

void getcmd()
{
  char retry;
  int tmp;

  tmp=(side)?blegal():wlegal();
  if(!m)
  {
    if(!xboard)printf("\nCheckmate!\n");
    else if(side)
           printf("0-1 {Black mates}\n");
         else
           printf("1-0 {White mates}\n");
  }
  retry=TRUE;
  while(retry)
  {
    retry=FALSE;
    if(!xboard){printf((side)?"Black":"White");printf("%d> ",rply/2);}
    if(!fgets(data,256,stdin)){repeat=FALSE;continue;}
    if(data[0]=='\n')continue;
    sscanf(data,"%s",cmd);
    strlwr(cmd);

    if(!strcmp(cmd,"xb"))
    {
      xboard=xboard^1;
      continue;
    }

    if(!strcmp(cmd,"xboard"))
    {
      xboard=TRUE;
      signal(SIGINT,SIG_IGN);
      printf("\n");
      fflush(stdout);
      continue;
    }  
    
    if(!strcmp(cmd,"white"))
    {
      side=WHITE;
      computer=BLACK;
      continue;
    }
      
    if(!strcmp(cmd,"black"))
    {
      side=BLACK;
      computer=WHITE;
      continue;
    }
      
    if(!strcmp(cmd,"both"))
    {
      computer=BOTH;
      continue;
    }
      
    if(!strcmp(cmd,"off") || !strcmp(cmd,"force"))
    {
      computer=OFF;
      continue;
    }
      
    if(!strcmp(cmd,"go"))
    {
      computer=side;
      continue;
    }
      
    if(!strcmp(cmd,"u") || !strcmp(cmd,"undo"))
    {
      if(rply<3)continue;
      computer=OFF;
      unmove();
      ply=0;
      high=0;
      continue;
    }

    if(!strcmp(cmd,"r") || !strcmp(cmd,"remove"))
    {
      if(rply<3)continue;
      unmove();
      unmove();
      ply=0;
      high=0;
      continue;
    }
      
    if(!strcmp(cmd,"new"))
    {
      for(i=rply;i>2;i--)unmove();
      initdata();
      computer=BLACK;
      side=WHITE;
      high=0;
      continue;
    }
      
    if(!strcmp(cmd,"quit"))
    {
      repeat=FALSE;
      continue;
    }

    if(!strcmp(cmd,"sa"))
    {
      sscanf(data,"sa %d",&away);
      away=away<<6;
      continue;
    }

    if(!strcmp(cmd,"sb"))
    {
      sscanf(data,"sb %d",&bas);
      continue;
    }

    if(!strcmp(cmd,"sp"))
    {
      sscanf(data,"sp %d",&pro);
      continue;
    }

    if(!strcmp(cmd,"sd"))
    {  
      sscanf(data,"sd %d",&maxd);
      maxd=maxd*64;
      at=10000000;
      continue;
    }

    if(!strcmp(cmd,"st"))
    {  
      sscanf(data,"st %lu",&at);
      at=at*(CLOCKS_PER_SEC/2);
      maxd=12800;
      continue;
    }
    
    if(!strcmp(cmd,"time"))
    {
      sscanf(data,"time %d",&at);
      at=at*4/levm;
      maxd=12800;
      continue;
    }

    if(!strcmp(cmd,"level"))
    {
      sscanf(data,"level %d %d %d",&levm,&levt,&levi);
      continue;
    }
    
    if(!strcmp(cmd,"bench"))
    {
      sscanf(data,"bench %d",&i);
      z=0;
      st=clock();
      bench(i);
      et=clock();
      printf("nodes=%d  time=%d\n",z,(int)(et-st));
      printf("nodes/sec = %d\n",(int)((float)((float)z/(float)((et-st)/(float)1000))));
      continue;
    }
       
     
    retry=TRUE;
    if(!m)continue;
    fs=cmd[0] - 'a' + 8 * (cmd[1] - '1');
    ts=cmd[2] - 'a' + 8 * (cmd[3] - '1');
    for(li=first[0];li<first[1];li++)
      if(tree[li].fsq==fs && tree[li].tsq==ts)
      { retry=FALSE;
        if(tree[li].typ==MOVEq)  
        { switch(cmd[4])
          { case 'b':tree[li].typ=MOVEb;break;
            case 'n':tree[li].typ=MOVEn;break;
            case 'q':break;
            case 'r':tree[li].typ=MOVEr;break;
            default:
                  printf("You must enter q,r,b or n when promoting\n");              
                  retry=TRUE;
          }
        }
        else
        if(tree[li].typ==CAPTUREq)  
        { switch(cmd[4])
          { case 'b':tree[li].typ=CAPTUREb;break;
            case 'n':tree[li].typ=CAPTUREn;break;
            case 'q':break;
            case 'r':tree[li].typ=CAPTUREr;break;
            default:
              printf("You must enter q,r,b or n when promoting\n");              
              retry=TRUE;
          }
        }
             
        break;  
      }
      if(retry==FALSE){lir[rply]=li;move();ply=0;}
      else {printf("Error (Unknown command): %s\n",cmd);fflush(stdout);}
      break;
  }
  return;
}

void bench(unsigned depth)
{
  if(depth<1)return;
  if(side)
  {
    if(!bmg())return;
    while(move())
    {
      z++;
      bench(depth-1);
      unmove();
    }
  }
  else
  {
    if(!wmg())return;
    while(move())
    {
      z++;
      bench(depth-1);
      unmove();
    }
  }
}

void compute(void)
{
  int best,alpha,beta;
  float sec;
  unsigned ptr,k;

  if(!xboard)printf("Ok I'm thinking.\n");
  (side)?blegal():wlegal();
  if(!m)
  {
    computer=OFF;
    if(side)
      printf("0-1 {Black mates}\n");
    else
      printf("1-0 {White mates}\n");
    return;
  }
  x=0;
  st=clock();
  memset(pvr,0,sizeof(pvr));
  alpha=-inf;beta=inf;
  for(md=bas,i=1;md<=maxd;md+=pro,i++)
  {
    ply=0;fpv=TRUE;
    (side)?bsearch(alpha,beta,md):wsearch(alpha,beta,md);
    if(!xboard)printf("\n");
    best=(side)?inf:-inf;
    for(li=first[0];li<first[1];li++)
    {
      if(side)
      {
        if(tree[li].sco<best){best=tree[li].sco;ptr=li;}
      }
      else if(tree[li].sco>best){best=tree[li].sco;ptr=li;}
    }
    fs=tree[ptr].fsq;
    ts=tree[ptr].tsq;
    convert(fs,ts);
    et=clock();
    if(best>9999)sprintf(cmd,"White MATES!");
    if(best<-9999)sprintf(cmd,"Black MATES!");
    if(!xboard)
    {
      printf("%3d   %9d  %6d ",i,x,best);
      for(k=0;k<pvl[0];k++)
      {
        fs=pvr[0][k].fsq;
        ts=pvr[0][k].tsq;
        convert(fs,ts);
        printf(" %s",cmd);
      }
      printf("\n");
    }
    else
    {
      printf("%d %d %d %d",i,best,(et-st)/CLOCKS_PER_SEC,x);
      for(k=0;k<pvl[0];k++)
      {
        fs=pvr[0][k].fsq;
        ts=pvr[0][k].tsq;
        convert(fs,ts);
        printf(" %s",cmd);
      }
      printf("\n");
      fflush(stdout);
    }  
    if(et-st > at || ((best>9000 || best<-9000) && abs(best)>high))break;
  }
  et=clock();et=(et-st);sec=(float)et/CLOCKS_PER_SEC;if(sec<.001)sec=(float).001;
  if(side)while(bpick() && move())unmove();else while(wpick() && move())unmove();
  best=(side)?inf:-inf;
  for(li=first[0];li<first[1];li++)
  {
    if(side)
    {
      if(tree[li].sco<best){best=tree[li].sco;lir[rply]=li;}
    }
    else if(tree[li].sco>best){best=tree[li].sco;lir[rply]=li;}  
  }
  if(abs(best)>high)high=best;
  move();
  convert(fs,ts);
  if(best>9999 || best<-9999)
  {
    printf("move %s\n",cmd);
    printf("\ncheckmate\n");
    fflush(stdout);
    computer=OFF;
    return;
  }
  ply=0;
  if(!xboard)
  {
    printf("\n\nMy move is %s score=%d Nodes=%lu Sec.=%.2f n/s=%lu\n\n"
      ,cmd,tree[li].sco,x,(float)sec,(long)((float)x/sec));
  }
  else
  {
    printf("move %s\n",cmd);
    fflush(stdout);
  }
}

int wsearch(int alpha,int beta,int depth)
{
  unsigned int sli;
  int s,ka,n;

  pvl[ply]=ply;
  
  if(depth<1)// return wquiesce(alpha,beta);
  {
    s=wquiesce(alpha,beta);
    if(s>=alpha && s<=beta && ply<i)depth=1;
    else return s;
  }

  if(ply)
  {
//    if(reps())return 0;
    if(!wmg())return inf-ply;
  }
  if(depth>=64)
  {
    nulcas=piece[33].nxt;piece[33].nxt=40;
    side=1^side;ply++;rply++;
    s=bquiesce(alpha,beta);
    side=1^side;ply--;rply--;
    piece[33].nxt=nulcas;
    if(s+50>=beta) depth-=(128+(ply<<2));
    else
    {
      ka=FALSE;
      if(s<-9000){ka=TRUE;depth+=(128-(ply<<3));}
      else if(s<alpha)depth+=(64-(ply<<3));
    }
  }
  
  if(ply)
  {
    while(move())
    {
      tree[li].sco=hist[rply-1].sco;
      unmove();
    }
    
    if(depth>384)
    {
      while(wpick() && move())
      {
        sli=li;
        tree[sli].sco=bquiesce(-inf,beta);
        unmove();
      }
    }
  }        
  
  n=0;
  while(wpick() && move())
  {
    if(ply==1){if(!xboard)printf(".");}
    sli=li;
    tree[sli].sco=bsearch(alpha,beta,depth-64);
    unmove();
    if(tree[sli].sco>=beta)return tree[sli].sco;
    if(tree[sli].sco>alpha)
    {
      alpha=tree[sli].sco;
      pvr[ply][ply].umove=tree[sli].umove;
      for(j=ply+1;j<pvl[ply+1];j++)pvr[ply][j]=pvr[ply+1][j];
      pvl[ply]=pvl[ply+1];
    }
    if(tree[sli].sco>(signed)(-10001+ply))n++;
  }
  if(!n && !ka)return 0;
//  if(fifty[rply-1]==100)return 0;
  return alpha;
}    

int bsearch(int alpha,int beta,int depth)
{
  unsigned int sli;
  int s,ka,n;

  pvl[ply]=ply;
  
  if(depth<1)// return bquiesce(alpha,beta);
  {
    s=bquiesce(alpha,beta);
    if(s>=alpha && s<=beta && ply<i)depth=1;
    else return s;
  }
     
  if(ply)
  {
//    if(reps())return 0;
    if(!bmg())return -inf+ply;
  }

  if(depth>=64)
  {
    nulcas=piece[16].nxt;piece[16].nxt=39;
    side=1^side;ply++;rply++;
    s=wquiesce(alpha,beta);
    side=1^side;ply--;rply--;
    piece[16].nxt=nulcas;
    if(s-50<=alpha) depth-=(128+(ply<<2));
    else
    {
      ka=FALSE;
      if(s>9000){ka=TRUE;depth+=(128-(ply<<3));}
      else if(s>beta)depth+=(64-(ply<<3));
    }
  }
  if(ply)
  {
    while(move())
    {
      tree[li].sco=hist[rply-1].sco;
      unmove();
    }
    
    if(depth>384)
    { 
      while(bpick() && move())
      {
        sli=li;
        tree[sli].sco=wquiesce(alpha,inf);
        unmove();
      }
    }
  }        

  n=0;
  while(bpick() && move())
  {
    if(ply==1){if(!xboard)printf(".");}
    sli=li;
    tree[sli].sco=wsearch(alpha,beta,depth-64);
    unmove();
    if(tree[sli].sco<=alpha)return tree[sli].sco;
    if(tree[sli].sco<beta)
    {
      beta=tree[sli].sco;
      pvr[ply][ply].umove=tree[sli].umove;
      for(j=ply+1;j<pvl[ply+1];j++)pvr[ply][j]=pvr[ply+1][j];
      pvl[ply]=pvl[ply+1];
    }
    if(tree[sli].sco<(signed)(10001-ply))n++;
  }
  if(!n && !ka)return 0;
//  if(fifty[rply-1]==100)return 0;
  return beta;
}    
      
int wlegal()
{ 
  unsigned int sli;
  int alpha=-inf;
        
  if(!wmg())return FALSE;
  while(move())
  {
    tree[li].sco=hist[rply-1].sco;
    unmove();
  }
  m=0;
  while(wpick() && move())
  { 
    m++;
    sli=li;
    tree[sli].sco=bquiesce(-inf,inf);
    unmove();
    if(tree[sli].sco==-inf+1)
    {
      m--;
      first[ply+1]--;
      li=first[ply+1];
      if(li==sli)continue;
      tree[sli].fsq=tree[li].fsq;tree[sli].tsq=tree[li].tsq;tree[sli].typ=tree[li].typ;
      lir[rply]--;continue;
    }
    if(tree[sli].sco>alpha)alpha=tree[sli].sco;  
  }
  return alpha;
}


int blegal()
{ 
  unsigned int sli;
  int beta=inf;
        
  if(!bmg())return FALSE;
  while(move())
  {
    tree[li].sco=hist[rply-1].sco;
    unmove();
  }
  m=0;
  while(bpick() && move())
  { 
    m++;
    sli=li;
    tree[sli].sco=wquiesce(-inf,inf);
    unmove();
    if(tree[sli].sco==inf-1)
    {
      m--;
      first[ply+1]--;
      li=first[ply+1];
      if(li==sli)continue;
      tree[sli].fsq=tree[li].fsq;tree[sli].tsq=tree[li].tsq;tree[sli].typ=tree[li].typ;
      lir[rply]--;continue;
    }
    if(tree[sli].sco<beta)beta=tree[sli].sco;  
  }
  return beta;
}

int wquiesce(int alpha,int beta)
{
  signed int s;

  s=hist[rply-1].sco;
  if(s>=beta)return s;
  if(s>alpha)alpha=s;
  if(!wcg())return inf-ply;
  if(first[ply]==first[ply+1])return alpha;

  while(move())
  {
    tree[li].sco=hist[rply-1].sco;
    unmove();
  }
 
  while(wpick() && move())
  {
    if(hist[rply-1].sco<alpha){unmove();return hist[rply-1].sco;} 
    s=bquiesce(alpha,beta);
    unmove();
    if(s>=beta)return s;
    if(s>alpha)alpha=s;
  }
  return alpha;
}
  
int bquiesce(int alpha,int beta)
{
  int s;
  
  s=hist[rply-1].sco;
  if(s<=alpha)return s;
  if(s<beta)beta=s;
  if(!bcg())return -inf+ply;
  if(first[ply]==first[ply+1])return beta;

  while(move())
  {
    tree[li].sco=hist[rply-1].sco;
    unmove();
  }

  while(bpick() && move())
  {
    if(hist[rply-1].sco>beta){unmove();return hist[rply-1].sco;}
    s=wquiesce(alpha,beta);
    unmove();
    if(s<=alpha)return s;
    if(s<beta)beta=s;
  }
  return beta;
}

int reps()
{
  unsigned i;
  int b[64];
  int c=0;
//  int r=0;

  if(fifty[rply-1]<=3)return 0;

  memset(b,0,sizeof(b));

  for(i=rply-1;i>=rply-fifty[rply-1]-1;--i)
  {
    if(++b[hist[i].fsq]==0)--c;
    else ++c;
    if(--b[hist[i].tsq]==0)--c;
    else ++c;
    if(c==0)return 1;
  }
  return 0;
}

void sortpv()
{
  unsigned i;
  
  fpv=FALSE;
  for(i=first[ply];i<first[ply+1];i++)
    if(tree[i].umove==pvr[0][ply].umove)
    {
      fpv=TRUE;
      tree[i].sco=(side)?-100000:100000;
      return;
    }
}

int wpick()
{
  register signed int best=-20000;
  register unsigned int ptr,sli;
  register unsigned tmp;

  li=lir[rply];
  if(li==first[ply+1]-1)return TRUE;
  if(li>=first[ply+1]){lir[rply]=first[ply];return FALSE;}
  for(sli=li;sli<first[ply+1];sli++)
   if(tree[sli].sco>best){best=tree[sli].sco;ptr=sli;}
  if(li!=ptr)
  { tmp=tree[ptr].umove;tree[ptr].umove=tree[li].umove;tree[li].umove=tmp;
    tree[ptr].sco=tree[li].sco;tree[li].sco=best;
  }
  return TRUE;
}    
      
int bpick()
{
  register signed int best=20000;
  register unsigned int ptr,sli;
  register unsigned tmp;

  li=lir[rply];
  if(li==first[ply+1]-1)return TRUE;
  if(li>=first[ply+1]){lir[rply]=first[ply];return FALSE;}
  for(sli=li;sli<first[ply+1];sli++)
   if(tree[sli].sco<best){best=tree[sli].sco;ptr=sli;}
  if(li!=ptr)
  { tmp=tree[ptr].umove;tree[ptr].umove=tree[li].umove;tree[li].umove=tmp;
    tree[ptr].sco=tree[li].sco;tree[li].sco=best;
  }
  return TRUE;
}    

/*int wqpick()
{
  signed int best=-20000,s;
  unsigned int ptr,sli;

  if(lir[rply]+1==first[ply+1])return TRUE;
  li=lir[rply];
  if(li>=first[ply+1])return FALSE;
  for(sli=li;sli<first[ply+1];sli++)
  {
    switch(mtl[sli])
    { 
      case CAPTURE:
        s=-piece[fid].pv[brd[fsl[sli]]]-piece[fid].pv[brd[tsl[sli]]];
        if(s>best){best=s;ptr=sli;}
        break;
      case CAPTUREq:
        s=850-piece[fid].pv[brd[tsl[sli]]];
        if(s>best){best=s;ptr=sli;}
        break;
      case MOVEq:
        if(850>best){best=850;ptr=sli;}
        break;
      case CEP:
        if(0>best){best=0;ptr=sli;}
        break;
    }
  } 
  if(li!=ptr)
  { fs=tree[ptr].fsq;tree[ptr].fsq=tree[li].fsq;tree[li].fsq=fs;
    ts=tree[ptr].tsq;tree[ptr].tsq=tree[li].tsq;tree[li].tsq=ts;
    mt=tree[ptr].typ;tree[ptr].typ=tree[li].typ;tree[li].typ=mt;
  }
  return TRUE;
}*/

/*int bqpick()
{
  signed int best=20000,s;
  unsigned int ptr,sli;

  if(lir[rply]+1==first[ply+1])return TRUE;
  li=lir[rply];
  if(li>=first[ply+1])return FALSE;
  for(sli=li;sli<first[ply+1];sli++)
  {
    switch(mtl[sli])
    { 
      case CAPTURE:
        s=-piece[fid].pv[brd[fsl[sli]]]-piece[fid].pv[brd[tsl[sli]]];
        if(s<best){best=s;ptr=sli;}
        break;
      case CAPTUREq:
        s=-850-piece[fid].pv[brd[tsl[sli]]];
        if(s<best){best=s;ptr=sli;}
        break;
      case MOVEq:
        if(-850<best){best=-850;ptr=sli;}
        break;
      case CEP:
        if(0<best){best=0;ptr=sli;}
        break;
    }
  } 
  if(li!=ptr)
  { fs=tree[ptr].fsq;tree[ptr].fsq=tree[li].fsq;tree[li].fsq=fs;
    ts=tree[ptr].tsq;tree[ptr].tsq=tree[li].tsq;tree[li].tsq=ts;
    mt=tree[ptr].typ;tree[ptr].typ=tree[li].typ;tree[li].typ=mt;
  }
  return TRUE;
}*/
Mike Sherwin
Posts: 860
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Are Bitboards More Intoxicating Than They Are Good?

Post by Mike Sherwin »

Code: Select all

int wmg()
{
  char *sbns,*sbnd;
  node *ti;
  int fs,ts;
  
  ti=tree+first[ply];
  for(id=piece[0].nxt;id<39;id=piece[id].nxt)
  {
    fs=piece[id].ps;
    switch(piece[id].pt)
    {
      case BISHOP:
             sbns=bns+bol[fs];
             sbnd=bnd+bol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(wtrgt[brd[ts]])
               {
                 case VACANT:
                        link(MOVE);
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }
             continue;
             
      case ROOK:
             sbns=rns+rol[fs];
             sbnd=rnd+rol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(wtrgt[brd[ts]])
               {
                 case VACANT:
                        link(MOVE);
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case QUEEN:
             sbns=qns+qol[fs];
             sbnd=qnd+qol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(wtrgt[brd[ts]])
               {
                 case VACANT:
                        link(MOVE);
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case KNIGHT:
             sbns=nns+nol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(wtrgt[brd[ts]])
               {
                 case VACANT:
                        link(MOVE);
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbns+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbns+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case KING:
             sbns=kns+kol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(wtrgt[brd[ts]])
               {
                 case VACANT:
                        link(MOVE);
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbns+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbns+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case PAWN:
             switch(wpwn[fs])
             {
               case LR1:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs+8]==EMPTY)
                               {link(MOVE);}
                      continue;
               case L1:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs+8]==EMPTY)
                               {link(MOVE);}
                      continue;
               case R1:
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs+8]==EMPTY)
                               {link(MOVE);}
                      continue;
               case LR2:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs+8]==EMPTY)
                      {
                               link(MOVE);
                               if(brd[ts=fs+16]==EMPTY)
                               {link(PDMOVE);}
                      }         
                      continue;
               case L2:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs+8]==EMPTY)
                      {
                               link(MOVE);
                               if(brd[ts=fs+16]==EMPTY)
                               {link(PDMOVE);}
                      }         
                      continue;
               case R2:
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs+8]==EMPTY)
                      {
                               link(MOVE);
                               if(brd[ts=fs+16]==EMPTY)
                               {link(PDMOVE);}
                      }         
                      continue;
               case LRE:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               break;
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               break;
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs+8]==EMPTY)
                               {link(MOVE);}
                      continue;
               case LE:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               break;
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs+8]==EMPTY)
                               {link(MOVE);}
                      continue;
               case RE:
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               break;
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs+8]==EMPTY)
                              {link(MOVE);}
                      continue;
               case LRQ:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs+8]==EMPTY)
                               {link(MOVEq);}
                      continue;
               case LQ:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs+8]==EMPTY)
                               {link(MOVEq);}
                      continue;
               case RQ:
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs+8]==EMPTY)
                              {link(MOVEq);}
                      continue;         
             }
             continue;
      case CASks:                       
             if(brd[4]==16)
             { if(brd[7]==13)
               { if(brd[5]==EMPTY && brd[6]==EMPTY)
                 { ti->typ=CASTLEwk;ti->fsq=4;ti->tsq=6;ti++;}}
               else
               { wkrtmp=hist[rply-1].typ;hist[rply-1].typ=INSwkc;delete(35);}}    
             else
             { wktmp=hist[rply-1].typ;hist[rply-1].typ=INSwc;wntmp=piece[16].nxt;piece[16].nxt=39;id=39;}  
             continue;
      case CASqs:
             if(brd[4]==16)
             { if(brd[0]==14)
               { if(brd[3]==EMPTY && brd[2]==EMPTY && brd[1]==EMPTY)
                 { ti->typ=CASTLEwq;ti->fsq=4;ti->tsq=2;ti++;}}
               else
               { wqrtmp=hist[rply-1].typ;hist[rply-1].typ=INSwqc;delete(36);}}    
             else
             { wktmp=hist[rply-1].typ;hist[rply-1].typ=INSwc;wntmp=piece[16].nxt;piece[16].nxt=39;id=39;}  
             continue;
      default:
             switch(hist[rply-1].typ)
             {
               case CASTLEbk:brd[60]=EMPTY;brd[61]=30;piece[33].ps=62;piece[30].ps=61;piece[16].nxt=wcas34;
                           id=16;bcastmp=piece[33].nxt;piece[33].nxt=40;
                           continue;
               case CASTLEbq:brd[60]=EMPTY;brd[59]=31;piece[33].ps=58;piece[31].ps=59;piece[16].nxt=wcas34;
                           id=16;bcastmp=piece[33].nxt;piece[33].nxt=40;
                           continue;
             }      
             continue;
     }
  }
  first[ply+1]=ti-tree;
  lir[rply]=first[ply];
  return TRUE;
}

int bmg()
{
  char *sbns,*sbnd;
  node *ti;
  int fs,ts;
  
  ti=tree+first[ply];
  for(id=piece[17].nxt;id<40;id=piece[id].nxt)
  {
    fs=piece[id].ps;
    switch(piece[id].pt)
    {
      case BISHOP:
             sbns=bns+bol[fs];
             sbnd=bnd+bol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(btrgt[brd[ts]])
               {
                 case VACANT:
                        link(MOVE);
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case ROOK:
             sbns=rns+rol[fs];
             sbnd=rnd+rol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(btrgt[brd[ts]])
               {
                 case VACANT:
                        link(MOVE);
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case QUEEN:
             sbns=qns+qol[fs];
             sbnd=qnd+qol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(btrgt[brd[ts]])
               {
                 case VACANT:
                        link(MOVE);
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case KNIGHT:
             sbns=nns+nol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(btrgt[brd[ts]])
               {
                 case VACANT:
                        link(MOVE);
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbns+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbns+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case KING:
             sbns=kns+kol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(btrgt[brd[ts]])
               {
                 case VACANT:
                        link(MOVE);
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbns+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbns+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case PAWN:
             switch(bpwn[fs])
             {
               case LR1:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVE);}
                      continue;
               case L1:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVE);}
                      continue;
               case R1:
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVE);}
                      continue;
               case LR2:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs-8]==EMPTY)
                      {
                               link(MOVE);
                               if(brd[ts=fs-16]==EMPTY)
                               {link(PDMOVE);}
                      }         
                      continue;
               case L2:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs-8]==EMPTY)
                      {
                               link(MOVE);
                               if(brd[ts=fs-16]==EMPTY)
                               {link(PDMOVE);}
                      }         
                      continue;
               case R2:
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs-8]==EMPTY)
                      {
                               link(MOVE);
                               if(brd[ts=fs-16]==EMPTY)
                               {link(PDMOVE);}
                      }         
                      continue;
               case LRE:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               break;
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               break;
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVE);}
                      continue;
               case LE:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               break;
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVE);}
                      continue;
               case RE:
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               break;
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVE);}
                      continue;
               case LRQ:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVEq);}
                      continue;
               case LQ:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVEq);}
                      continue;
               default:
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVEq);}
                      continue;         
             }
             continue;
      case CASks:
             if(brd[60]==33)
             { if(brd[63]==30)
               { if(brd[61]==EMPTY && brd[62]==EMPTY)
                 { ti->typ=CASTLEbk;ti->fsq=60;ti->tsq=62;ti++;}}
               else
               { bkrtmp=hist[rply-1].typ;hist[rply-1].typ=INSbkc;delete(37);}}    
             else
             { bktmp=hist[rply-1].typ;hist[rply-1].typ=INSbc;bntmp=piece[33].nxt;piece[33].nxt=40;id=40;}  
             continue;
      case CASqs:
             if(brd[60]==33)
             { if(brd[56]==31)
               { if(brd[59]==EMPTY && brd[58]==EMPTY && brd[57]==EMPTY)
                 { ti->typ=CASTLEbq;ti->fsq=60;ti->tsq=58;ti++;}}
               else
               { bqrtmp=hist[rply-1].typ;hist[rply-1].typ=INSbqc;delete(38);}}    
             else
             { bktmp=hist[rply-1].typ;hist[rply-1].typ=INSbc;bntmp=piece[33].nxt;piece[33].nxt=40;id=40;}  
             continue;
      default:
             switch(hist[rply-1].typ)
             { case CASTLEwk:brd[4]=EMPTY;brd[5]=13;piece[16].ps=6;piece[13].ps=5;piece[33].nxt=bcas34;
                           id=33;wcastmp=piece[16].nxt;piece[16].nxt=39;
                           continue;
               case CASTLEwq:brd[4]=EMPTY;brd[3]=14;piece[16].ps=2;piece[14].ps=3;piece[33].nxt=bcas34;
                           id=33;wcastmp=piece[16].nxt;piece[16].nxt=39;
                           continue;
             }      
             continue;
    }
  }
  first[ply+1]=ti-tree;
  lir[rply]=first[ply];
  return TRUE;
}

  
Mike Sherwin
Posts: 860
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Are Bitboards More Intoxicating Than They Are Good?

Post by Mike Sherwin »

Code: Select all

int wcg()
{
  char *sbns,*sbnd;
  node *ti;
  int fs,ts;
  
  ti=tree+first[ply];
  for(id=piece[0].nxt;id<=34;id=piece[id].nxt)
  {
    fs=piece[id].ps;
    switch(piece[id].pt)
    {
      case BISHOP:
             sbns=bns+bol[fs];
             sbnd=bnd+bol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(wtrgt[brd[ts]])
               {
                 case VACANT:
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case ROOK:
             sbns=rns+rol[fs];
             sbnd=rnd+rol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(wtrgt[brd[ts]])
               {
                 case VACANT:
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case QUEEN:
             sbns=qns+qol[fs];
             sbnd=qnd+qol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(wtrgt[brd[ts]])
               {
                 case VACANT:
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case KNIGHT:
             sbns=nns+nol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(wtrgt[brd[ts]])
               {
                 case VACANT:
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbns+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbns+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case KING:
             sbns=kns+kol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(wtrgt[brd[ts]])
               {
                 case VACANT:
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbns+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbns+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case PAWN:
             switch(wpwn[fs])
             {
               case LR1:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      continue;
               case L1:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      continue;
               case R1:
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      continue;
               case LR2:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      continue;                
               case L2:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      continue;
               case R2:
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                     continue;
               case LRE:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               break;
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               continue;
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      continue;
               case LE:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               continue;
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      continue;
               case RE:
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               continue;
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      continue;
               case LRQ:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs+8]==EMPTY)
                               {link(MOVEq);}
                      continue;
               case LQ:
                      switch(wtrgt[brd[ts=fs+7]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs+8]==EMPTY)
                               {link(MOVEq);}
                      continue;
               default:
                      switch(wtrgt[brd[ts=fs+9]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs+8]==EMPTY)
                               {link(MOVEq);}
                      continue;         
             }
             continue;
      default:
             switch(hist[rply-1].typ)
             {
               case CASTLEbk:brd[60]=EMPTY;brd[61]=30;piece[33].ps=62;piece[30].ps=61;piece[16].nxt=wcas34;
                           id=16;bcastmp=piece[33].nxt;piece[33].nxt=40;
                           continue;
               case CASTLEbq:brd[60]=EMPTY;brd[59]=31;piece[33].ps=58;piece[31].ps=59;piece[16].nxt=wcas34;
                           id=16;bcastmp=piece[33].nxt;piece[33].nxt=40;
                           continue;
             }      
             continue;
     }
  }
  first[ply+1]=ti-tree;
  lir[rply]=first[ply];
  return TRUE;
}

int bcg()
{
  char *sbns,*sbnd;
  node *ti;
  int fs,ts;
  
  ti=tree+first[ply];
  for(id=piece[17].nxt;id<=34;id=piece[id].nxt)
  {
    fs=piece[id].ps;
    switch(piece[id].pt)
    {
      case BISHOP:
             sbns=bns+bol[fs];
             sbnd=bnd+bol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(btrgt[brd[ts]])
               {
                 case VACANT:
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case ROOK:
             sbns=rns+rol[fs];
             sbnd=rnd+rol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(btrgt[brd[ts]])
               {
                 case VACANT:
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case QUEEN:
             sbns=qns+qol[fs];
             sbnd=qnd+qol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(btrgt[brd[ts]])
               {
                 case VACANT:
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbnd+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbnd+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case KNIGHT:
             sbns=nns+nol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(btrgt[brd[ts]])
               {
                 case VACANT:
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbns+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbns+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case KING:
             sbns=kns+kol[fs];
             ts=*(sbns+fs);
             while(ts<64)
             {
               switch(btrgt[brd[ts]])
               {
                 case VACANT:
                        ts=*(sbns+ts);
                        continue;
                 case FRIEND:             
                        ts=*(sbns+ts);
                        continue;
                 case ENEMY:
                        link(CAPTURE);
                        ts=*(sbns+ts);
                        continue;
                 default:
                        return FALSE;
               }                            
             }  
             continue;
             
      case PAWN:
             switch(bpwn[fs])
             {
               case LR1:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      continue;
               case L1:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }
                     continue;
               case R1:
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }
                     continue;
               case LR2:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      continue;
               case L2:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      continue;
               case R2:
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                     continue;
               case LRE:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               break;
                        case ENEMY:
                               link(CAPTURE);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               continue;
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      continue;
               case LE:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               continue;
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      continue;
               case RE:
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case VACANT:
                               if(ts==hist[rply].cep)
                               {link(CEP);}
                               continue;
                        case ENEMY:
                               link(CAPTURE);
                               continue;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      continue;
               case LRQ:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }                
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVEq);}
                      continue;
               case LQ:
                      switch(btrgt[brd[ts=fs-7]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVEq);}
                      continue;
               default:
                      switch(btrgt[brd[ts=fs-9]])
                      {
                        case ENEMY:
                               link(CAPTUREq);
                               break;
                        case ILLEGAL:
                               return FALSE;              
                      }
                      if(brd[ts=fs-8]==EMPTY)
                               {link(MOVEq);}
                      continue;         
             }
             continue;
      default:
             switch(hist[rply-1].typ)
             { case CASTLEwk:brd[4]=EMPTY;brd[5]=13;piece[16].ps=6;piece[13].ps=5;piece[33].nxt=bcas34;
                           id=33;wcastmp=piece[16].nxt;piece[16].nxt=39;
                           continue;
               case CASTLEwq:brd[4]=EMPTY;brd[3]=14;piece[16].ps=2;piece[14].ps=3;piece[33].nxt=bcas34;
                           id=33;wcastmp=piece[16].nxt;piece[16].nxt=39;
                           continue;
             }      
             continue;
    }
  }
  first[ply+1]=ti-tree;
  lir[rply]=first[ply];
  return TRUE;
}

int move(void)
{ 
  li=lir[rply];
  if(li>=first[ply+1]){lir[rply]=first[ply];return FALSE;}  
  mt=tree[li].typ;
  switch(mt)
  { case MOVE:getmove();movepiece();evalft();if(piece[id].pt!=5)fifty[rply]=fifty[rply-1]+1;
                                             else fifty[rply]=0;
     break;           
    case MOVEq:getmove();movepiece();evalf();queen(2,950);evalt();fifty[rply]=0;
     break;
    case MOVEr:getmove();movepiece();evalf();queen(1,500);evalt();fifty[rply]=0;
     break;
    case MOVEb:getmove();movepiece();evalf();queen(0,320);evalt();fifty[rply]=0;
     break;
    case MOVEn:getmove();movepiece();evalf();queen(3,290);evalt();fifty[rply]=0;
     break;
    case PDMOVE:getmove();movepiece();evalft();hist[rply+1].cep=(side)?fs-8:fs+8;
                fifty[rply]=0;
     break;
    case CAPTURE:getmove();evalft();remove();movepiece();fifty[rply]=0;
     break;
    case CAPTUREq:getmove();evalf();remove();movepiece();queen(2,950);evalt();fifty[rply]=0;
     break;
    case CAPTUREr:getmove();evalf();remove();movepiece();queen(1,500);evalt();fifty[rply]=0;
     break;
    case CAPTUREb:getmove();evalf();remove();movepiece();queen(0,320);evalt();fifty[rply]=0;
     break;
    case CAPTUREn:getmove();evalf();remove();movepiece();queen(3,290);evalt();fifty[rply]=0;
     break;      
    case CEP:getmove();
             evalft();
             capep();
             movepiece();
             fifty[rply]=0;
     break;
    case CASTLEwk: brd[7]=EMPTY;brd[6]=16;brd[5]=16;bcas34=piece[33].nxt;
                   piece[33].nxt=34;hist[rply].typ=CASTLEwk;evalcas(0,4,6,7,5);
                   fs=4;ts=6;fifty[rply]=0;                
     break;  
    case CASTLEwq: brd[0]=EMPTY;brd[2]=16;brd[3]=16;bcas34=piece[33].nxt;
                   piece[33].nxt=34;hist[rply].typ=CASTLEwq;evalcas(0,4,2,0,3);
                   fs=4;ts=2;fifty[rply]=0;                
     break;  
    case CASTLEbk: brd[63]=EMPTY;brd[62]=33;brd[61]=33;wcas34=piece[16].nxt;
                   piece[16].nxt=34;hist[rply].typ=CASTLEbk;evalcas(1,60,62,63,61);
                   fs=60;ts=62;fifty[rply]=0;              
     break;  
    case CASTLEbq: brd[56]=EMPTY;brd[58]=33;brd[59]=33;wcas34=piece[16].nxt;
                   piece[16].nxt=34;hist[rply].typ=CASTLEbq;evalcas(1,60,58,56,59);
                   fs=60;ts=58;fifty[rply]=0;                
     break;  
  }
  lir[rply]++;rply++;ply++;x++;side=1^side;
  return TRUE;
}

void unmove(void)
{ 
  side=1^side;  
  rply--;ply--;
  mt=hist[rply].typ;
um1:
  switch(mt)
  { case MOVE:getunmv();unmvpiece(EMPTY);
     break;  
    case MOVEq:getunmv();
    unqueen();
    unmvpiece(EMPTY);
     break; 
    case MOVEr:getunmv();unqueen();unmvpiece(EMPTY);
     break;  
    case MOVEb:getunmv();unqueen();unmvpiece(EMPTY);
     break;   
    case MOVEn:getunmv();unqueen();unmvpiece(EMPTY);
     break;  
    case PDMOVE:getunmv();unmvpiece(EMPTY);hist[rply+1].cep=0;
     break;
    case CAPTURE:getunmv();
    putback();
    unmvpiece(tid);
     break; 
    case CAPTUREq:getunmv();putback();unqueen();unmvpiece(tid);
     break; 
    case CAPTUREr:getunmv();putback();unqueen();unmvpiece(tid);
     break;   
    case CAPTUREb:getunmv();putback();unqueen();unmvpiece(tid);
     break;   
    case CAPTUREn:getunmv();putback();unqueen();unmvpiece(tid);
     break;   
    case CEP:getunmv();uncapep();unmvpiece(EMPTY);
     break;
    case CASTLEwk: brd[4]=16;brd[5]=EMPTY;brd[6]=EMPTY;brd[7]=13;piece[16].ps=4;piece[13].ps=7;
                          if(piece[33].nxt==34)piece[33].nxt=bcas34;else piece[16].nxt=wcastmp;
     break;
    case CASTLEwq: brd[4]=16;brd[3]=EMPTY;brd[2]=EMPTY;brd[0]=14;piece[16].ps=4;piece[14].ps=0;
                          if(piece[33].nxt==34)piece[33].nxt=bcas34;else piece[16].nxt=wcastmp;
     break;
    case CASTLEbk: brd[60]=33;brd[61]=EMPTY;brd[62]=EMPTY;brd[63]=30;piece[33].ps=60;piece[30].ps=63;
                          if(piece[16].nxt==34)piece[16].nxt=wcas34;else piece[33].nxt=bcastmp;
     break;
    case CASTLEbq: brd[60]=33;brd[59]=EMPTY;brd[58]=EMPTY;brd[56]=31;piece[33].ps=60;piece[31].ps=56;
                          if(piece[16].nxt==34)piece[16].nxt=wcas34;else piece[33].nxt=bcastmp;
     break;
    case INSwc:  piece[16].nxt=wntmp;mt=wktmp;
     goto um1;
    case INSwkc: insert(35);mt=wkrtmp;
     goto um1;
    case INSwqc: insert(36);mt=wqrtmp;
     goto um1;
    case INSbc:  piece[33].nxt=bntmp;mt=bktmp;
     goto um1;
    case INSbkc: insert(37);mt=bkrtmp;
     goto um1;
    case INSbqc: insert(38);mt=bqrtmp;
     goto um1;
  }  
}
        
void initmg(void)
{
  signed char lbrd [120] =
    {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     -1,0,1,2,3,4,5,6,7,-1,
     -1,8,9,10,11,12,13,14,15,-1,
     -1,16,17,18,19,20,21,22,23,-1,
     -1,24,25,26,27,28,29,30,31,-1,
     -1,32,33,34,35,36,37,38,39,-1,
     -1,40,41,42,43,44,45,46,47,-1,
     -1,48,49,50,51,52,53,54,55,-1,
     -1,56,57,58,59,60,61,62,63,-1,
     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1};

  signed char pdir [7] [8] =
    {{9,11,-9,-11,0,0,0,0},
     {1,10,-1,-10,0,0,0,0},
     {9,11,1,10,-9,-11,-1,-10},
     {8,12,19,21,-8,-12,-19,-21},
     {9,11,1,10,-9,-11,-1,-10},
     {10,9,11,0,0,0,0,0},
     {-10,-9,-11,0,0,0,0,0}};

  unsigned char maxs [7] =
    {6,6,6,0,0,0,0};

  unsigned char maxdir [7] =
    {3,3,7,7,7,2,2};

  signed char min=64,max=0;
  signed char ptyp;
  signed char plfs,lds,lts;
  signed char fs,ts,nds,pfs;
  signed char dir,ndir,ppdir,s;
  unsigned char *sbns = bol,*sbnd = bol;
  unsigned int *pop = bol;
  signed int offset=0;

  for(ptyp=0;ptyp<6;ptyp++)
    {
      switch (ptyp)
        {
        case 0:
          pop=bol;
          break;
        case 1:
          pop=rol;
          break;
        case 2:
          pop=qol;
          break;
        case 3:
          pop=nol;
          break;
        case 4:
          pop=kol;
          break;
        case 5:
          pop=pol;
          offset=8;
        }
      for(plfs=21;plfs<99;plfs++)
        if((pfs=fs=lbrd[plfs])>=0)
          {
            if((ptyp>0 && ptyp<5) || pfs%2 == 0) min=max=fs;
            for(dir=maxdir[ptyp];dir>=0;dir--)
              if((ts=lbrd[(lts=plfs+pdir[ptyp][dir])])>=0)
                {
                for(s=maxs[ptyp];s>=0;s--)
                  {
                    if(ts<min)min=ts;
                    if(ts>max)max=ts;
                    if((ts=lbrd[(lts=lts+pdir[ptyp][dir])])<0 || s==0)
                    break;
                   }
                 }
            if(ptyp!=0 && ptyp<5)
              {
                if(ptyp<3)
                {
                  *(pop+pfs)=(offset-min);
                  offset=offset+max-min+1;
                }
                else
                {
                  *(pop+pfs)=(offset-pfs);
                  if(ptyp==3)offset=offset+13;
                  else offset=offset+11;
                }
              }
            else
              {
                if(pfs%2!=0)
                {
                  *(pop+pfs)=(offset-min);
                  *(pop+pfs-1)=(offset-min);
                  offset=offset+max-min+2;
                }
              }
          }
        offset=0;
    }

  for(ptyp=0;ptyp<7;ptyp++)
    for(plfs=21;plfs<99;plfs++)
    /*  if(ptyp<5 || (ptyp>4 && plfs>30 && plfs<89))  */
        if((pfs=fs=lbrd[plfs])>=0)
        {
          switch (ptyp)
            {
              case 0:
                sbns=bns+bol[pfs];
                sbnd=bnd+bol[pfs];
                break;
              case 1:
                sbns=rns+rol[pfs];
                sbnd=rnd+rol[pfs];
                break;
              case 2:
                sbns=qns+qol[pfs];
                sbnd=qnd+qol[pfs];
                break;
              case 3:
                sbns=nns+nol[pfs];
                break;
              case 4:
                sbns=kns+kol[pfs];
                break;
              case 5:
                sbns=wpns+pol[pfs];
                break;
              case 6:
                sbns=bpns+pol[pfs];
            }
          for(dir=maxdir[ptyp];dir>=0;dir--)
            if((ts=lbrd[(lts=plfs+pdir[ptyp][dir])])>=0)
              {
                for(ndir=dir-1;ndir>=0;ndir--)
                if((nds=lbrd[(lds=plfs+pdir[ptyp][ndir])])>=0)break;
                ppdir=0;
                for(s=maxs[ptyp];s>=0;s--)
                  {
                    if(ptyp<5) *(sbns+fs)=ts;
                    if(ptyp==5)
                      {
                        if(dir==2 && ndir==1)
                        {
                          *(sbns+fs)=ts;
                          *(sbns+ts)=nds;
                          ppdir=2;
                        }
                        if((dir==2 && ndir==0) || (dir==1))/* && ppdir!=2))*/
                        {
                          *(sbns+fs)=ts;
                          *(sbns+ts)=65;if(pfs> 7 && pfs<16) *(sbns+ts)=66;
                                        if(pfs>31 && pfs<40) *(sbns+ts)=67;
                                        if(pfs>47 && pfs<56) *(sbns+ts)=68;
                          ppdir=0;
                        }
                        if(dir==1 && ppdir==2)
                        {
                          *(sbns+ts)=65;if(pfs> 7 && pfs<16) *(sbns+ts)=66;
                                        if(pfs>31 && pfs<40) *(sbns+ts)=67;
                                        if(pfs>47 && pfs<56) *(sbns+ts)=68;
                        }
                      }
                    if(ptyp==6)
                      {
                        if(dir==2 && ndir==1)
                        {
                          *(sbns+fs)=ts;
                          *(sbns+ts)=nds;
                          ppdir=2;
                        }
                        if((dir==2 && ndir==0) || (dir==1))/* && ppdir!=2))*/
                        {
                          *(sbns+fs)=ts;
                          *(sbns+ts)=65;if(pfs<56 && pfs>47) *(sbns+ts)=66;
                                        if(pfs<32 && pfs>23) *(sbns+ts)=67;
                                        if(pfs<16 && pfs> 7) *(sbns+ts)=68;
                           ppdir=0;
                        }
                        if(dir==1 && ppdir==2)
                        {
                          *(sbns+ts)=65;if(pfs<56 && pfs>47) *(sbns+ts)=66;
                                        if(pfs<32 && pfs>23) *(sbns+ts)=67;
                                        if(pfs<16 && pfs> 7) *(sbns+ts)=68;
                         }
                      }
                    if(ndir>=0 && ptyp<3) *(sbnd+ts)=nds;
                    else if(ptyp<3) *(sbnd+ts)=64;
                    fs=ts;
                    if((ts=lbrd[(lts=lts+pdir[ptyp][dir])])<0 || s==0)
                      {
                        if(ndir>=0 && ptyp<5)
                        {
                          *(sbns+fs)=nds;
                          if(ptyp<3) *(sbnd+fs)=nds;
                        }
                        else
                        if(ptyp<5)
                          {
                            *(sbns+fs)=64;
                            if(ptyp<3) *(sbnd+fs)=64;
                          }
                         break;
                      }
              }
        }
} }
    
void initdata(void)
{
  memcpy(brd,ibrd,64*sizeof(char));
  memcpy(piece,ipiece,820);
  ply=0;
  rply=2;
  first[0]=0;
  hist[0].sco=0;
  hist[1].sco=0;
  fifty[0]=0;
  fifty[1]=0; 
}

void drawgraf(void)
{
}

void convert(int fs,int ts)
{
  sprintf(cmd,"%c%d%c%d",(fs&7)+'a',(fs>>3)+1,(ts&7)+'a',(ts>>3)+1);
  
}  
   
void board()
{
  int ps,fs,i;
  char pieces[]="BRQNKPbrqnkp.";

  for(ps=63;ps>=0;ps--)
  {
    fs=7-(ps&7)+(ps>>3)*8;
    i=brd[fs];
    if(i<17)i=piece[i].pt;
    else if(i==17)i=12;
         else i=piece[i].pt+6;
    printf(" %c ",pieces[i]);
    if((ps&7)==0)printf("\n");
  }
     printf("\n");
} 
User avatar
hgm
Posts: 27788
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Are Bitboards More Intoxicating Than They Are Good?

Post by hgm »

Thank you for showing the code! It is a bit hard to fully judge it in such a short time, but I do see some things that make me worry whether counting the number of calls to move() (rather than those to wsearch(), bseqrch(), wqsearch() and bqsearch()) gives a fair representation of the speed:

E.g. in wsearch() and bsearch() there is this:

Code: Select all

  if(ply)
  {
    while(move())
    {
      tree[li].sco=hist[rply-1].sco;
      unmove();
    }
   ...    
This seems to just make every move without doing anything substantial (just a single assignment), and none of those would have been counted in a more conventional way of counting (since no ..search() routine was called). This would already double the count even if all moves would be searched. But since many nodes will be cut nodes, and might search only a single move, the effect could be even larger.

Can you also add counters that count the number of calls to wsearch/bsearch and wqsearch/bqsearch, and tell us how large those would be for the mentioned 13-ply search?
Mike Sherwin
Posts: 860
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Are Bitboards More Intoxicating Than They Are Good?

Post by Mike Sherwin »

hgm wrote: Sun Feb 28, 2021 7:56 pm Thank you for showing the code! It is a bit hard to fully judge it in such a short time, but I do see some things that make me worry whether counting the number of calls to move() (rather than those to wsearch(), bseqrch(), wqsearch() and bqsearch()) gives a fair representation of the speed:

E.g. in wsearch() and bsearch() there is this:

Code: Select all

  if(ply)
  {
    while(move())
    {
      tree[li].sco=hist[rply-1].sco;
      unmove();
    }
   ...    
This seems to just make every move without doing anything substantial (just a single assignment), and none of those would have been counted in a more conventional way of counting (since no ..search() routine was called). This would already double the count even if all moves would be searched. But since many nodes will be cut nodes, and might search only a single move, the effect could be even larger.

Can you also add counters that count the number of calls to wsearch/bsearch and wqsearch/bqsearch, and tell us how large those would be for the mentioned 13-ply search?
I removed x++ from Move() and put it in each search function after the main "while(wpick() && move()) { x++; ..." that calls the next level of the main search. So absolutely nothing else other than "real tree nodes is counted". The node rate for a 13 ply search was 7165896 nodes/sec. Now the question is do other engines only report true nodes/sec or do they report moves/sec? Are IID moves counted?
Mike Sherwin
Posts: 860
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Are Bitboards More Intoxicating Than They Are Good?

Post by Mike Sherwin »

The follow up is that I commented out the
/* while(move())
{
tree[li].sco=hist[rply-1].sco;
unmove();
}*/

/* if(ply)
{
while(move())
{
tree[li].sco=hist[rply-1].sco;
unmove();
}

if(depth>384)
{
while(bpick() && move())
{
sli=li;
tree[sli].sco=wquiesce(alpha,inf);
unmove();
}
}
} */

that "seems to just make every move without doing anything substantial". And the 13 ply search in 17.43 sec turned into a 10 ply search in 55.80 sec. I'm not picking at anything but I would call that substantial. The work done by those moves is valuable. So how should I count them? :?
User avatar
hgm
Posts: 27788
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Are Bitboards More Intoxicating Than They Are Good?

Post by hgm »

Mike Sherwin wrote: Sun Feb 28, 2021 8:30 pmI removed x++ from Move() and put it in each search function after the main "while(wpick() && move()) { x++; ..." that calls the next level of the main search. So absolutely nothing else other than "real tree nodes is counted". The node rate for a 13 ply search was 7165896 nodes/sec. Now the question is do other engines only report true nodes/sec or do they report moves/sec? Are IID moves counted?
I suppose that some engines will do it differently, but the most common method is to increment the counter at the top of Search() and QSearch().

That still offers plenty of possibilities for misleading figures, though: QS nodes that get a stand-pat cutoff take much less time than those that have to generate captures. If the stand-pat can be done just on the incremental PST eval (because the engine has nothing else, such as micro-Max, or because the PST eval is so much above beta that it can be used as 'lazy eval'), they would take very much less time. But these cases could already have been recognized in the parent (and indeed micro-Max does that), leading to pruning of the node (futility pruning). This speeds up the engine, but since it removes the fastest nodes from your search, the reported nps goes down.

7 Mnps is still very fast, though.
Mike Sherwin wrote: Sun Feb 28, 2021 8:51 pmthat "seems to just make every move without doing anything substantial". And the 13 ply search in 17.43 sec turned into a 10 ply search in 55.80 sec. I'm not picking at anything but I would call that substantial. The work done by those moves is valuable. So how should I count them? :?
Whether they are valuable is not the point. Many things are valuable in an engine: better (expensive) evaluation, hash probing (which enormously slows down nps!), move sorting... But none of that can be counted as 'nodes'.

The point is that to make any meaningful comparison between engines, they must count the same thing. If I understood it correctly, what you do here is a move-sorting trick. It speeds up the search (time-to-depth-wise), but it slows down the nps.
Mike Sherwin
Posts: 860
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Are Bitboards More Intoxicating Than They Are Good?

Post by Mike Sherwin »

hgm wrote: Sun Feb 28, 2021 8:53 pm
Mike Sherwin wrote: Sun Feb 28, 2021 8:30 pmI removed x++ from Move() and put it in each search function after the main "while(wpick() && move()) { x++; ..." that calls the next level of the main search. So absolutely nothing else other than "real tree nodes is counted". The node rate for a 13 ply search was 7165896 nodes/sec. Now the question is do other engines only report true nodes/sec or do they report moves/sec? Are IID moves counted?
I suppose that some engines will do it differently, but the most common method is to increment the counter at the top of Search() and QSearch().

That still offers plenty of possibilities for misleading figures, though: QS nodes that get a stand-pat cutoff take much less time than those that have to generate captures. If the stand-pat can be done just on the incremental PST eval (because the engine has nothing else, such as micro-Max, or because the PST eval is so much above beta that it can be used as 'lazy eval'), they would take very much less time. But these cases could already have been recognized in the parent (and indeed micro-Max does that), leading to pruning of the node (futility pruning). This speeds up the engine, but since it removes the fastest nodes from your search, the reported nps goes down.

7 Mnps is still very fast, though.
Thanks! :D
Okay, I'll make the same change in Bricabrac for an apples to apples comparison, brb.
...
Okay, back. Bricabrac searches 9,987,527 NODES / sec.

So now maybe, I change my opinion about how fast bitboards are! :shock:
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: Are Bitboards More Intoxicating Than They Are Good?

Post by mvanthoor »

hgm wrote: Sun Feb 28, 2021 8:53 pm I suppose that some engines will do it differently, but the most common method is to increment the counter at the top of Search() and QSearch().

That still offers plenty of possibilities for misleading figures, though: QS nodes that get a stand-pat cutoff take much less time than those that have to generate captures.
That is the reason why my engine counts a node when it is actualy going to search it: thus it increments just before it enters the loop that iterates over the generated moves. In Qsearch, a node that gets cut off because of stand-pat, is therefore not even counted, because it isn't searched. Same with leaf nodes: those don't cause an increment, because they're not searched. They are evaluated only.

Doing this, my engine searches 3-5 million nodes/second on my system depending on the position, with an average of about 4 M nps.

When I move the increment to the beginning of the search and qsearch function, the engine counts every call, even if it doesn't actually search the node, but only evaluates it; then the nodes/seconds report between 16 and 20 M nps, with an average of 18 M nps depending on the position.

If you count at the beginning of the search, instead of just before the move loop, you're also going to count all the nodes that are actually not searched, due to reductions or early cutoffs by search enhancements. I suspect that reductions and prunings will then skyrocket the node count.
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
User avatar
hgm
Posts: 27788
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Are Bitboards More Intoxicating Than They Are Good?

Post by hgm »

Well, as I pointed out, futility pruning would actually decrease the node count: it takes place within the move loop, not before. So it can depend. In an efficient implementation, there would be virtually no stand-pat cutoffs, because futility pruning would prevent you reach the nodes where you could get a stand-pat cutoff. (Thus also saving the make/unmake, but also missing the count.) Hash cutoffs are another possible cause of leaving a node early. In Joker I do not count those, because I do the hash probe in the parent. So that I also don't have to make/unmake the move when it would give me a hash cutoff.

In Fairy-Max I count the number of iterations in the IID loop; basically that counts the number of move generations, because in micro-Max the move loop is the move generator, and is rerun for every iteration. Because some nodes do many iterations, and others few, this gave the best correlation between node count and execution time. (And the original stand-alone version used node count for timing.) Later versions added an extra iteration for MVV/LVA determination, so that even QS now does two iterations. So effectively that exaggerates the node count by a factor 2. Of course some nodes get beta cutoffs, and they abort the move loop. Often after the first move. So the time per node can still vary a lot.

Advanced chess programs usually spend most of their time in evaluation. So even stand-pat cutoff doesn't make the node much faster than a node that searches all moves because it fails low. This is why they count even before evaluating. What you count is basically the number of move generations. Which makes sense if you have a very light eval, and most of the engine's time is spent in move generation.