assert

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

flok

assert

Post 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
elcabesa
Posts: 855
Joined: Sun May 23, 2010 1:32 pm

Re: assert

Post 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
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: assert

Post 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.
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: assert

Post 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 ]
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: assert

Post 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.
flok

Re: assert

Post 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.
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: assert

Post 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.

mar
Posts: 2554
Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: assert

Post 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
elcabesa
Posts: 855
Joined: Sun May 23, 2010 1:32 pm

Re: assert

Post 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
Harald
Posts: 317
Joined: Thu Mar 09, 2006 1:07 am

Re: assert

Post 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;