Starting a new project - Rommie

Discussion of chess software programming and technical issues.

Moderator: Ras

User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: Starting a new project - Rommie

Post by mvanthoor »

Mike Sherwin wrote: Fri Mar 04, 2022 6:39 pm

Code: Select all

// Final Thread pointer setup example.

// declarations:
s32 maxThreads;
Thread** thread;
Thread* t;

// initialization:
  // must figure out how to get the number of available threads
  // and then reduce to the number of threads the user request
  maxThreads = 32;

  thread = new Thread*[maxThreads];

  for (s32 i = 0; i < maxThreads; i++) {
	thread[i] = new Thread;
  }

  t = thread[0];
You made an array of Thread-pointers using new, and got a pointer to that array. Then you initialize the threads in the next for-loop.

So now you have this:

Code: Select all

Thread** thread --> [Thread* 0][Thread* 1][Thread* 2]
                        |           |          |
                        V           V          V
                      Thread      Thread    Thread
Make sure that before you exit your program, you do another for-loop over that array, and delete all the thread objects. Then delete the array itself. If you don't, you'll have a massive memory leak.

I might be completely obvious here, but IMHO, you didn't choose the easiest option. Personally I would have just made an array of Thread objects:

Code: Select all

Thread threads[32];
If I remember correctly, the Thread objects are initialized with the default constructor; you don't even need the new operator here, because you're not declaring this array dynamically. If the [32] must be a constant (I don't remember...) you'd have to use new:

Code: Select all

int x = 32;
Thread *threads = new Thread[x];
So now you'd have this:

Code: Select all

Thread *threads --> [Thread 0][Thread 1][Thread 2]...
When you're done with it, you can just delete it:

Code: Select all

delete[] threads
(My syntax may be a bit off because I haven't used C and C++ in a _very_ long time. And I have to say, after writing Rust for over 2.5 years now, I _really_ prefer that language, because I don't have to remember which function returns pointers or even pointers to pointers or how many indirections I need... the compiler does all of that automatically.)
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Starting a new project - Rommie

Post by Mike Sherwin »

mvanthoor wrote: Sat Mar 05, 2022 2:35 pm
Mike Sherwin wrote: Fri Mar 04, 2022 6:39 pm

Code: Select all

// Final Thread pointer setup example.

// declarations:
s32 maxThreads;
Thread** thread;
Thread* t;

// initialization:
  // must figure out how to get the number of available threads
  // and then reduce to the number of threads the user request
  maxThreads = 32;

  thread = new Thread*[maxThreads];

  for (s32 i = 0; i < maxThreads; i++) {
	thread[i] = new Thread;
  }

  t = thread[0];
You made an array of Thread-pointers using new, and got a pointer to that array. Then you initialize the threads in the next for-loop.

So now you have this:

Code: Select all

Thread** thread --> [Thread* 0][Thread* 1][Thread* 2]
                        |           |          |
                        V           V          V
                      Thread      Thread    Thread
Make sure that before you exit your program, you do another for-loop over that array, and delete all the thread objects. Then delete the array itself. If you don't, you'll have a massive memory leak.

I might be completely obvious here, but IMHO, you didn't choose the easiest option. Personally I would have just made an array of Thread objects:

Code: Select all

Thread threads[32];
If I remember correctly, the Thread objects are initialized with the default constructor; you don't even need the new operator here, because you're not declaring this array dynamically. If the [32] must be a constant (I don't remember...) you'd have to use new:

Code: Select all

int x = 32;
Thread *threads = new Thread[x];
So now you'd have this:

Code: Select all

Thread *threads --> [Thread 0][Thread 1][Thread 2]...
When you're done with it, you can just delete it:

Code: Select all

delete[] threads
(My syntax may be a bit off because I haven't used C and C++ in a _very_ long time. And I have to say, after writing Rust for over 2.5 years now, I _really_ prefer that language, because I don't have to remember which function returns pointers or even pointers to pointers or how many indirections I need... the compiler does all of that automatically.)
Yes, thank you for verifying that my design will do what I wanted it to do. I know to delete all the Thread objects before exiting. Like I explained to Mergi each Thread can be huge. Each Tread will be a complete chess engine on its own with its own hash table of varying size maybe up to a gigabyte or more. If someone has a 64 core Threadripper and they want to use all 128 threads they better have like 256 gigs of ram. Say for example they only have 128 gigs then the engine won't run at all the single block allocated way. But if only one Thread is allocated at a time a failure to allocate can be detected and reported to the user that they only have enough memory for 96 threads.
tcusr
Posts: 325
Joined: Tue Aug 31, 2021 10:32 pm
Full name: tcusr

Re: Starting a new project - Rommie

Post by tcusr »

it's pointless to free memory right before exiting, the OS will do that for you.
the only moments you have to call delete[] is when resizing and allocating again with new.
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Starting a new project - Rommie

Post by Mike Sherwin »

tcusr wrote: Sat Mar 05, 2022 5:29 pm it's pointless to free memory right before exiting, the OS will do that for you.
the only moments you have to call delete[] is when resizing and allocating again with new.
Okay thanks. I won't be doing any resizing and reallocating.
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Starting a new project - Rommie

Post by Mike Sherwin »

The GenPieceBB function is finished. I read over it a few times and it looks to be correct. But of course it can't be run in the debugger yet.

Code: Select all

s32 GenPiecesBB(Thread* t) {
  u64 fSquares, at, enemy, empty, notMe, occ;
  u08 fs, sq;
  s32 ft;

  fSquares = pieceSquareBits[stm];
  enemy = pieceSquareBits[1 - stm];
  empty = ~(fSquares | enemy);
  notMe = ~fSquares;
  occ = fSquares | enemy;
  atkbb[ply] = 0;

  do {
	fs = std::countr_zero(fSquares);
	fSquares ^= 1ull << fs;
	ft = board[fs];
	switch (ft) {
	case EMPTY:
	  // can't get here
	  break;
	case WP2:
	  sq = std::countr_zero(wPawnMoves[fs] & occ);
	  genbb[ply][fs] = wPawnMoves[fs] & below[sq];
	  at = wPawnCapts[fs];
	  genbb[ply][fs] |= at & enemy;
	  atkbb[ply] |= at;
	  break;
	case WP3:
	case WP4:
	  genbb[ply][fs] = wPawnMoves[fs] & empty;
	  at = wPawnCapts[fs];
	  genbb[ply][fs] |= at & enemy;
	  atkbb[ply] |= at;
	  break;
	case WP5:
	  genbb[ply][fs] = wPawnMoves[fs] & empty;
	  at = wPawnCapts[fs];
	  genbb[ply][fs] |= at & (enemy | epbit[ply]);
	  atkbb[ply] |= at;
	  break;
	case WP6:
	case WP7:
	  genbb[ply][fs] = wPawnMoves[fs] & empty;
	  at = wPawnCapts[fs];
	  genbb[ply][fs] |= at & enemy;
	  atkbb[ply] |= at;
	  break;
	case WN:
	  genbb[ply][fs] = knightMoves[fs] & notMe;
	  atkbb[ply] |= knightMoves[fs];
	  break;
	case WB:
	  occ |= 0x8000000000000001;
	  genbb[ply][fs]
		= ray[std::countr_zero(ray[fs].rwsNW & occ)].rayNW
		| ray[std::countr_zero(ray[fs].rwsNE & occ)].rayNE
		| ray[63 - std::countl_zero(ray[fs].rwsSE & occ)].raySE
		| ray[63 - std::countl_zero(ray[fs].rwsSW & occ)].raySW
		^ bob[fs]; // bishop on board [fs]
	  atkbb[ply] |= genbb[ply][fs];
	  genbb[ply][fs] &= notMe;
	  break;
	case WRC:
	case WR:
	  occ |= 0x8000000000000001;
	  genbb[ply][fs]
		= ray[std::countr_zero(ray[fs].rwsNN & occ)].rayNN
		| ray[std::countr_zero(ray[fs].rwsEE & occ)].rayEE
		| ray[63 - std::countl_zero(ray[fs].rwsSS & occ)].raySS
		| ray[63 - std::countl_zero(ray[fs].rwsWW & occ)].rayWW
		^ rob[fs]; // rook on board [fs]
	  atkbb[ply] |= genbb[ply][fs];
	  genbb[ply][fs] &= notMe;
	  break;
	case WQ:
	  occ |= 0x8000000000000001;
	  genbb[ply][fs]
		= ray[std::countr_zero(ray[fs].rwsNW & occ)].rayNW
		| ray[std::countr_zero(ray[fs].rwsNN & occ)].rayNN
		| ray[std::countr_zero(ray[fs].rwsNE & occ)].rayNE
		| ray[std::countr_zero(ray[fs].rwsEE & occ)].rayEE
		| ray[63 - std::countl_zero(ray[fs].rwsSE & occ)].raySE
		| ray[63 - std::countl_zero(ray[fs].rwsSS & occ)].raySS
		| ray[63 - std::countl_zero(ray[fs].rwsSW & occ)].raySW
		| ray[63 - std::countl_zero(ray[fs].rwsWW & occ)].rayWW
		^ qob[fs]; // quen on board [fs]
	  atkbb[ply] |= genbb[ply][fs];
	  genbb[ply][fs] &= notMe;
	  break;
	case WKC:
	case WK:
	  genbb[ply][fs] = kingMoves[fs] & notMe;
	  atkbb[ply] |= kingMoves[fs];
	  break;
	case WCS:
	case WCL:
	  // can't get here
	  break;
	case BP7:
	  sq = std::countr_zero(bPawnMoves[fs] & occ);
	  genbb[ply][fs] = bPawnMoves[fs] & above[sq];
	  at = bPawnCapts[fs];
	  genbb[ply][fs] |= at & enemy;
	  atkbb[ply] |= at;
	  break;
	case BP6:
	case BP5:
	  genbb[ply][fs] = bPawnMoves[fs] & empty;
	  at = bPawnCapts[fs];
	  genbb[ply][fs] |= at & enemy;
	  atkbb[ply] |= at;
	  break;
	case BP4:
	  genbb[ply][fs] = bPawnMoves[fs] & empty;
	  at = bPawnCapts[fs];
	  genbb[ply][fs] |= at & (enemy | epbit[ply]);
	  atkbb[ply] |= at;
	  break;
	case BP3:
	case BP2:
	  genbb[ply][fs] = bPawnMoves[fs] & empty;
	  at = bPawnCapts[fs];
	  genbb[ply][fs] |= at & enemy;
	  atkbb[ply] |= at;
	  break;
	case BN:
	  genbb[ply][fs] = knightMoves[fs] & notMe;
	  atkbb[ply] |= knightMoves[fs];
	  break;
	case BB:
	  occ |= 0x8000000000000001;
	  genbb[ply][fs]
		= ray[std::countr_zero(ray[fs].rwsNW & occ)].rayNW
		| ray[std::countr_zero(ray[fs].rwsNE & occ)].rayNE
		| ray[63 - std::countl_zero(ray[fs].rwsSE & occ)].raySE
		| ray[63 - std::countl_zero(ray[fs].rwsSW & occ)].raySW
		^ bob[fs];
	  atkbb[ply] |= genbb[ply][fs];
	  genbb[ply][fs] &= notMe;
	  break;
	case BRC:
	case BR:
	  occ |= 0x8000000000000001;
	  genbb[ply][fs]
		= ray[std::countr_zero(ray[fs].rwsNN & occ)].rayNN
		| ray[std::countr_zero(ray[fs].rwsEE & occ)].rayEE
		| ray[63 - std::countl_zero(ray[fs].rwsSS & occ)].raySS
		| ray[63 - std::countl_zero(ray[fs].rwsWW & occ)].rayWW
		^ rob[fs];
	  atkbb[ply] |= genbb[ply][fs];
	  genbb[ply][fs] &= notMe;
	  break;
	case BQ:
	  occ |= 0x8000000000000001;
	  genbb[ply][fs] = ray[std::countr_zero(ray[fs].rwsNW & occ)].rayNW
		| ray[std::countr_zero(ray[fs].rwsNN & occ)].rayNN
		| ray[std::countr_zero(ray[fs].rwsNE & occ)].rayNE
		| ray[std::countr_zero(ray[fs].rwsEE & occ)].rayEE
		| ray[63 - std::countl_zero(ray[fs].rwsSE & occ)].raySE
		| ray[63 - std::countl_zero(ray[fs].rwsSS & occ)].raySS
		| ray[63 - std::countl_zero(ray[fs].rwsSW & occ)].raySW
		| ray[63 - std::countl_zero(ray[fs].rwsWW & occ)].rayWW
		^ qob[fs];
	  atkbb[ply] |= genbb[ply][fs];
	  genbb[ply][fs] &= notMe;
	  break;
	case BKC:
	case BK:
	  genbb[ply][fs] = kingMoves[fs] & notMe;
	  atkbb[ply] |= kingMoves[fs];
	  break;
	}
  } while (fSquares);
  if (atkbb[ply] & kings[1 - stm]) return false;
  return true;
}
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Starting a new project - Rommie

Post by Mike Sherwin »

The MakeMove() and TakeBack() functions are finished. Well to the point that a Speedt() function can be written. The Speedt() function will only care about the raw speed of the three key functions GenPieceBits(), MakeMove() and TakeBack(). The Speedt() function does not care about material or legality checking for the last ply of a pseudo legal move generator. Therefore MakeMove() and TakeBack() do not have some incrementally updated items that will be added later. Mainly though the purpose of Speedt() besides measuring speed is just so I can start debugging what I have so far. I have scanned the code several times so I'm hoping that debugging won't take too long. When debugging is finished I'll upload the exe and source code to MediaFire.
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Starting a new project - Rommie

Post by Mike Sherwin »

I've been debugging for 9 hours. I think it is time to watch some TV.

The good news is that I got kings, knights and bishops debugged. Did not allow king captures.

Code: Select all

      if (1ull << move.s.ts == kings[1 - stm]) continue;
      numberOfMoves++;
Edit: Every generated move made and unmade even at the leafs and no tricks like hashing or whatever else there is.

108,580,668 n/s
[fen]4kb2/8/8/8/8/8/8/2B1K3 w - - 0 1 [/fen]


70,316,000 n/s
[fen]4k1n1/8/8/8/8/8/8/4K1N1 w - - 0 1 [/fen]


90,588,900 n/s
[fen]4kbn1/8/8/8/8/8/8/2B1K1N1 w - - 0 1 [/fen]


None on edge.
104,855,460 n/s
[fen]8/3k4/3b4/3n4/4N3/4B3/4K3/8 w - - 0 1 [/fen]
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Starting a new project - Rommie

Post by Mike Sherwin »

Managed to get the rook working before going to bed. Found the bug almost immediately after watching some TV. :D

111,384,578 n/s
[fen]8/8/4k3/2r5/6R1/4K3/8/8 w - - 0 1 [/fen]
My move generator is using Classical Bob-Mike. When I get it all debugged I'll try plugging in a couple more just to see the difference.
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Starting a new project - Rommie

Post by Mike Sherwin »

I can't tell how many times I went over the queen code and could not see the bug. After a few hours today I was staring at the screen contemplating if I should just spend the rest of my life playing video games. I didn't even realize that I was staring right at the bug. As I came out of my stupor of feeling defeated I noticed that there was no break; statement at the end of case: WQ:. Bug found and killed. Although I'm still contemplating just playing video games for the rest of my life. :lol:

109,209,822 n/s
[fen]8/1q6/4k3/8/8/4K3/2Q5/8 w - - 0 1[/fen]
And now last but not least, the pawns! Those :twisted: little pawns!
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Starting a new project - Rommie

Post by Mike Sherwin »

Just incase one might think that I am not counting correctly.

Code: Select all

  if (!strcmp(tok, "speedt")) {
    numberOfMoves = 0;
    begin = clock();
    Speedt(t, 5);
    end = clock();
    std::cout << (s32)(numberOfMoves / ((double)(end - begin) / CLOCKS_PER_SEC)) << "\n";
    return;
  }

Code: Select all

void Speedt(Thread* t, s32 depth) {
  uMove move;
  u64 fSquares, tSquares;
  GenMovesBB(t);
  fSquares = pieceSquareBits[stm];
  do {
    move.s.fs = std::countr_zero(fSquares);
    fSquares ^= 1ull << move.s.fs;
    tSquares = genbb[ply][move.s.fs];
    while (tSquares) {
      move.s.ts = std::countr_zero(tSquares);
      tSquares ^= 1ull << move.s.ts;
      if (1ull << move.s.ts == kings[1 - stm]) continue;
      numberOfMoves++;
      MakeMove(t, &move);
      if (depth > 1) Speedt(t, depth - 1);
      TakeBack(t, &move);
    }
  } while (fSquares);
}