Page 1 of 2

assert

Posted: Mon Nov 13, 2017 10:09 am
by flok
Hi,

I'm looking to improve my program.
For that I'm adding asserts all over the place to check input variables, outgoing values, and so on.
I've also added an assert that if we're in a null move, then beta == alpha + 1.
My question now is: what other things are there to check for?


regards

Re: assert

Posted: Mon Nov 13, 2017 10:47 am
by elcabesa
Your program starts to be complicated and it's difficult to understand if inside the flow there are some strange bug.
Adding assert will help you finding them.
If You expect some assertion to be true in some path of code, assert them and let the program check them at runtime.
For example alpha < beta for every node. Alpha +1= beta for scout nodes.
Depth < 0 in q search. Those are generic assert.
For each function you can add assert that you think should be true

Re: assert

Posted: Mon Nov 13, 2017 10:49 am
by lucasart
I use assert as follows.

1/ Check (systematically) that input variables of functions are within expected range.
This goes a long way already. Start with the most frequently used functions. If you don't who they are, ask the profiler (sort results by descending number of calls). Such functions are going to be things like file_of(int s): it's used almost everywhere, directly or indirectly, so if ever an invalid square value pops up, it will endup in file_of() at some point and trigger the assert.

2/ Document the implicit assumption that the code makes
Whenever there's an implicit assumption, put an assert. For example in an else block, where reaching the else implies that the move is en-passant (let's say), assert that it's en-passant. The assert is both a bug trap, *and* documentation.
In the search, alpha/beta/PVnode relationships etc.

But assert is not enough. It's not because your variables are in range that you don't have bugs. If you're serious about debugging, you should also use Clang sanitizers, or Valgrind at least. It's truly amazing the number of invisible (or very rarely visible and not reproducible) bugs that chess engines have. Even strong engines. Undefined behaviors and subtle bugs are pervasive in C, and even more so in C++. If you're coding in a more robust language like D or C#, you shouldn't have this kind of problem.

Re: assert

Posted: Mon Nov 13, 2017 11:26 am
by Henk
What I remember is that adding complete pre- post conditions and invariants makes the program more complex than it's original. And the more complicated the more errors.

Best is to write pure functions but that is not possible.

[throwing exceptions is similar to using goto statements ]

Re: assert

Posted: Mon Nov 13, 2017 11:44 am
by Henk
lucasart wrote:I use assert as follows.

1/ Check (systematically) that input variables of functions are within expected range.
This goes a long way already. Start with the most frequently used functions. If you don't who they are, ask the profiler (sort results by descending number of calls). Such functions are going to be things like file_of(int s): it's used almost everywhere, directly or indirectly, so if ever an invalid square value pops up, it will endup in file_of() at some point and trigger the assert.

2/ Document the implicit assumption that the code makes
Whenever there's an implicit assumption, put an assert. For example in an else block, where reaching the else implies that the move is en-passant (let's say), assert that it's en-passant. The assert is both a bug trap, *and* documentation.
In the search, alpha/beta/PVnode relationships etc.

But assert is not enough. It's not because your variables are in range that you don't have bugs. If you're serious about debugging, you should also use Clang sanitizers, or Valgrind at least. It's truly amazing the number of invisible (or very rarely visible and not reproducible) bugs that chess engines have. Even strong engines. Undefined behaviors and subtle bugs are pervasive in C, and even more so in C++. If you're coding in a more robust language like D or C#, you shouldn't have this kind of problem.
Better use F# or even better Haskell if it would not be impractical.

Re: assert

Posted: Mon Nov 13, 2017 11:50 am
by flok
Thank you all guys but I'm more looking for things like "in situation x, check if alpha > score and score < tt val" etc.
Like: in null move, check if alpha = beta - 1 and so on.

Re: assert

Posted: Mon Nov 13, 2017 11:59 am
by Henk
flok wrote:Thank you all guys but I'm more looking for things like "in situation x, check if alpha > score and score < tt val" etc.
Like: in null move, check if alpha = beta - 1 and so on.
If you use fail hard you know what to check. But fail hard is slower.


Re: assert

Posted: Mon Nov 13, 2017 12:05 pm
by mar
lucasart wrote:Undefined behaviors and subtle bugs are pervasive in C, and even more so in C++. If you're coding in a more robust language like D or C#, you shouldn't have this kind of problem.
nonsense.

if you index local arrays out of bounds, that's your problem.
C# is slower compared to C/C++/D, I don't see how D is more robust than C/C++ except that it zero-initializes by default (not talking about language features).

(if you think so, maybe you should switch to D/C#)

in fact, C++ is of course more "robust" than C, you can use std::array which checks bounds in debug mode and of course more.

Valgrind/DrMemory won't catch all problems, but they're pretty good.

now back to OP: asserts are nice but they also slow your code in debug mode, so too many of them probably hurts

Re: assert

Posted: Mon Nov 13, 2017 12:24 pm
by elcabesa
You can find a lot of assert in position.h and position.cpp of Vajolet source code. It's probably not perfect but can give you some idea

Re: assert

Posted: Mon Nov 13, 2017 1:53 pm
by Harald
If you want to test only parts of your code then you can do things like this:

In an header like basics.h

Code: Select all

// Which tests should be performed
#ifdef _DEBUG
#define ASSERT_BASICS&#40;c,t&#41;              &#123; if ( !&#40;c&#41; )  throw &#40;t&#41;; &#125;
#define ASSERT_PRINCIPAL_VARIATION&#40;c,t&#41; &#123; if ( !&#40;c&#41; )  throw &#40;t&#41;; &#125;
#define ASSERT_MOVE&#40;c,t&#41;                &#123; if ( !&#40;c&#41; )  throw &#40;t&#41;; &#125;
...
#else
#define ASSERT_BASICS&#40;c,t&#41;
#define ASSERT_PRINCIPAL_VARIATION&#40;c,t&#41;
#define ASSERT_MOVE&#40;c,t&#41;
...
#endif
And in a file like move.cpp

Code: Select all

void Move&#58;&#58;set&#40; ColorType color, SquareType from, SquareType to,
                PieceType piece, PieceType capture,
                PromotionType promotion
              )
&#123;
    ASSERT_MOVE&#40; color == White || color == Black, "Error&#58; bad color in Move&#58;&#58;set&#40;)" )
    ASSERT_MOVE&#40; from >= 0 && from < 64, "Error&#58; bad from in Move&#58;&#58;set&#40;)" )
    ASSERT_MOVE&#40; to   >= 0 && to   < 64, "Error&#58; bad to in Move&#58;&#58;set&#40;)" )
    ASSERT_MOVE&#40; piece >= Pawn && piece < 8, "Error&#58; bad piece in Move&#58;&#58;set&#40;)" )
    ASSERT_MOVE&#40; capture >= NoPiece && capture != King && capture < 8, "Error&#58; bad capture in Move&#58;&#58;set&#40;)" )
    ASSERT_MOVE&#40; promotion <= PromoteToKnight, "Error&#58; bad promotion in Move&#58;&#58;set&#40;)" )
    move_ = from | &#40;to << 6&#41; | &#40;promotion << 12&#41; | &#40;piece << 14&#41; | &#40;capture << 17&#41; | &#40;color << 20&#41;;
&#125;