Stockfish Nullmove uci option #178

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

AlvaroBegue
Posts: 931
Joined: Tue Mar 09, 2010 3:46 pm
Location: New York
Full name: Álvaro Begué (RuyDos)

Re: Stockfish Nullmove uci option #178

Post by AlvaroBegue »

hgm wrote:The pedantic compiler warning about this can be turned off.
Oh, I just found how to do it in g++: -Wno-parentheses

Thanks!
syzygy
Posts: 5576
Joined: Tue Feb 28, 2012 11:56 pm

Re: Stockfish Nullmove uci option #178

Post by syzygy »

hgm wrote:
syzygy wrote:Ok, so you find

Code: Select all

if &#40;a < b ||  c > 5 && d == 2&#41;
more readable than

Code: Select all

if &#40;a < b || &#40;c > 5 && d ==2&#41;)
Very well. I certainly don't.
A more realistic example could be

Code: Select all

if&#40;4*x + 1 < 2*y || f&#40;3*x - 1&#41; > 5 && d&#91;i - 1&#93; == 2&#41;)
versus

Code: Select all

if&#40;((&#40;4*x&#41; + 1&#41; < &#40;2*y&#41;) || (&#40;f&#40;&#40;3*x&#41; - 1&#41; > 5&#41; && &#40;d&#91;i - 1&#93; == 2&#41;)))
Good layout is enormously more helpful to code readability than parentheses.
Maybe you noticed I was specifically talking about && versus ||.

Looking at it from a Boolean algebra point of view, in which "A || B && C" would typically be written as "A + BC", there is indeed less need for parentheses. But in programming mode parentheses work for me in this case and I am reasonable sure that most programmers do not automatically recognise which one of || and && has higher precedence.
phenri
Posts: 284
Joined: Tue Aug 13, 2013 9:44 am

Re: Stockfish Nullmove uci option #178

Post by phenri »

AlvaroBegue wrote:
hgm wrote:The pedantic compiler warning about this can be turned off.
Oh, I just found how to do it in g++: -Wno-parentheses

Thanks!
I am glad you have found practical information to exploit in this thread.
Stockfish, a superb precision engineering as well as in F1, but dangerous for the driver because unfortunately it has no rear-view mirror and no brakes.
User avatar
hgm
Posts: 27894
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Stockfish Nullmove uci option #178

Post by hgm »

syzygy wrote:Maybe you noticed I was specifically talking about && versus ||.
Sure. But expresions with && and || can occur in multiple levels too (e.g. A || B && (C || D && E)), and it often happens that you really need parenthesis in the operands of those (e.g. because they are function calls, or things like "(x & MASK) == 0"), and adding unneeded parentheses at the level of && and || would really obfuscate things.

Parentheses are good for computers, but not so good for humans. If I need a visual reminder of which sub-expressions form a tightly-coupled unit, it works much better for me to use more spacing between the operators of lower priority. Like "x+1 == y" or "x>>4 & 0xFF", and throw in linefeeds for even weaker couplings.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Stockfish Nullmove uci option #178

Post by bob »

hgm wrote:OTOH, long, informative names make the line much longer, forcing it to be split over several lines, and pushing closing parentheses out of view.

The keyword in my remark was excessive. In Ronald's example there was only a single pair of parentheses, and that of course is always trivial, because it is obvious which closing parenthesis belongs to which opening parenthesis if there is only one. But it becomes tricky when they are nested, and several of them follow each other, so that you now really have to count them to realize which belongs with which. Consecutive parentheses are best avoided, and attempts to do so lead to bugs when one is ignorant on operator priorities.

The pedantic compiler warning about this can be turned off.
My take on this argument.

#1, && has higher precedence than ||. I would not resort to parens until I had a complex expression with multiple &&'s and ||'s, which could become difficult to read. In general, I consider parens as something that makes code harder to read in most cases, but which can make it clearer in others. Ever heard the serenity prayer alcoholics and such are taught over and over? Paraphrased:

"God, grant me the serenity to avoid using parentheses when they are not needed, the courage to use them when they are needed, and the wisdom to know the difference."

Fits for me.

#2. I don't particularly like parentheses. I've had to (been forced to) work with Lisp several times in the past. I consider that a "write-only programming language." It is very difficult to read something where adding an "extra" level of parens is not benign, but actually affects the final results of the expression.
Henk
Posts: 7222
Joined: Mon May 27, 2013 10:31 am

Re: Stockfish Nullmove uci option #178

Post by Henk »

bob wrote:
hgm wrote:OTOH, long, informative names make the line much longer, forcing it to be split over several lines, and pushing closing parentheses out of view.

The keyword in my remark was excessive. In Ronald's example there was only a single pair of parentheses, and that of course is always trivial, because it is obvious which closing parenthesis belongs to which opening parenthesis if there is only one. But it becomes tricky when they are nested, and several of them follow each other, so that you now really have to count them to realize which belongs with which. Consecutive parentheses are best avoided, and attempts to do so lead to bugs when one is ignorant on operator priorities.

The pedantic compiler warning about this can be turned off.
My take on this argument.

#1, && has higher precedence than ||. I would not resort to parens until I had a complex expression with multiple &&'s and ||'s, which could become difficult to read. In general, I consider parens as something that makes code harder to read in most cases, but which can make it clearer in others. Ever heard the serenity prayer alcoholics and such are taught over and over? Paraphrased:

"God, grant me the serenity to avoid using parentheses when they are not needed, the courage to use them when they are needed, and the wisdom to know the difference."

Fits for me.

#2. I don't particularly like parentheses. I've had to (been forced to) work with Lisp several times in the past. I consider that a "write-only programming language." It is very difficult to read something where adding an "extra" level of parens is not benign, but actually affects the final results of the expression.
(In 1988 I programmed in a dialect of LISP for three years. Problem was that it didn't have good type checking for you only have lists, atoms and dotted pairs. Therefor you get a lot of run time errors instead of compile time errors. For instance if you put the arguments in the wrong order. In the mean time they were fixing the garbage collector bugs for they created a LISP dialect on top of C. Nowadays I still use lists instead of arrays.)
syzygy
Posts: 5576
Joined: Tue Feb 28, 2012 11:56 pm

Re: Stockfish Nullmove uci option #178

Post by syzygy »

bob wrote:I would not resort to parens until I had a complex expression with multiple &&'s and ||'s, which could become difficult to read. In general, I consider parens as something that makes code harder to read in most cases, but which can make it clearer in others.
A quick look at crafty gives the strong impression that in practice you do use (redundant) parentheses whenever you mix && and || in one conditional expression.

Code: Select all

search.c&#58;
    if &#40;smp_idle && moves_searched > 1 &&
        tree->nodes_searched - start_nodes > smp_split_nodes && &#40;ply > 1 ||
            &#40;smp_split_at_root && NextRootMoveParallel&#40;)))) &#123;

  if &#40;tree->nodes_searched > noise_level || &#40;tree->parent &&
          tree->parent->nodes_searched > noise_level&#41;) &#123;

utility.c &#40;this one occurs 3 times&#41;&#58;
    if ((&#40;tmove&#91;0&#93; >= 'a' && tmove&#91;0&#93; <= 'z') || &#40;tmove&#91;0&#93; >= 'A' &&
                tmove&#91;0&#93; <= 'Z')) || !strcmp&#40;tmove, "0-0")
        || !strcmp&#40;tmove, "0-0-0")) &#123;

setboard.c&#58;
  if ((&#40;Castle&#40;0, white&#41; & 2&#41; && &#40;PcOnSq&#40;A1&#41; != rook&#41;)
      || (&#40;Castle&#40;0, white&#41; & 1&#41; && &#40;PcOnSq&#40;H1&#41; != rook&#41;)
      || (&#40;Castle&#40;0, black&#41; & 2&#41; && &#40;PcOnSq&#40;A8&#41; != -rook&#41;)
      || (&#40;Castle&#40;0, black&#41; & 1&#41; && &#40;PcOnSq&#40;H8&#41; != -rook&#41;)) &#123;

  if (&#40;twtm && EnPassant&#40;0&#41; && &#40;PcOnSq&#40;EnPassant&#40;0&#41; + 8&#41; != -pawn&#41;
          && &#40;PcOnSq&#40;EnPassant&#40;0&#41; - 7&#41; != pawn&#41;
          && &#40;PcOnSq&#40;EnPassant&#40;0&#41; - 9&#41; != pawn&#41;) || &#40;Flip&#40;twtm&#41;
          && EnPassant&#40;0&#41;
          && &#40;PcOnSq&#40;EnPassant&#40;0&#41; - 8&#41; != pawn&#41;
          && &#40;PcOnSq&#40;EnPassant&#40;0&#41; + 7&#41; != -pawn&#41;
          && &#40;PcOnSq&#40;EnPassant&#40;0&#41; + 9&#41; != -pawn&#41;)) &#123;

evaluate.c&#58;
  tree->dangerous&#91;white&#93; = &#40;Queens&#40;white&#41; && TotalPieces&#40;white, occupied&#41; > 9&#41;
      || &#40;TotalPieces&#40;white, rook&#41; > 1 && TotalPieces&#40;white, occupied&#41; > 15&#41;;
  tree->dangerous&#91;black&#93; = &#40;Queens&#40;black&#41; && TotalPieces&#40;black, occupied&#41; > 9&#41;
      || &#40;TotalPieces&#40;black, rook&#41; > 1 && TotalPieces&#40;black, occupied&#41; > 15&#41;;

      if (&#40;TotalPieces&#40;white, occupied&#41; == 0 &&
              tree->pawn_score.passed&#91;black&#93;)
          || &#40;TotalPieces&#40;black, occupied&#41; == 0 &&
              tree->pawn_score.passed&#91;white&#93;))

        if &#40;TotalPieces&#40;white, occupied&#41; == 3 &&
            TotalPieces&#40;black, occupied&#41; == 3 &&
            (&#40;TotalPieces&#40;white, pawn&#41; < 4 && TotalPieces&#40;black, pawn&#41; < 4&#41;
                || Abs&#40;TotalPieces&#40;white, pawn&#41; - TotalPieces&#40;black,
                        pawn&#41;) < 2&#41;)

    if &#40;Rank&#40;square&#41; == (&#40;side&#41; ? RANK6 &#58; RANK3&#41;
        && SetMask&#40;square + direction&#91;side&#93;) & Pawns&#40;enemy&#41;
        && (&#40;File&#40;square&#41; < FILEH &&
                SetMask&#40;square + 9 - 16 * side&#41; & Pawns&#40;side&#41;
                && !&#40;mask_hidden_right&#91;side&#93;&#91;File&#40;square&#41;&#93; & Pawns&#40;enemy&#41;))
            || &#40;File&#40;square&#41; > FILEA &&
                SetMask&#40;square + 7 - 16 * side&#41; & Pawns&#40;side&#41;
                && !&#40;mask_hidden_left&#91;side&#93;&#91;File&#40;square&#41;&#93; & Pawns&#40;enemy&#41;)))) &#123;

  if &#40;TotalPieces&#40;side, pawn&#41; == 1 && TotalPieces&#40;enemy, pawn&#41; == 0 &&
      (&#40;TotalPieces&#40;side, occupied&#41; == 5 && TotalPieces&#40;enemy, occupied&#41; == 5&#41;
          || (&#40;TotalPieces&#40;side, occupied&#41; == 9 &&
                  TotalPieces&#40;enemy, occupied&#41; == 9&#41;))) &#123;

book.c&#58;
          if &#40;bs_percent&#91;i&#93; < bs_percent&#91;i + 1&#93;
              || &#40;bs_percent&#91;i&#93; == bs_percent&#91;i + 1&#93;
                  && bs_value&#91;i&#93; < bs_value&#91;i + 1&#93;)) &#123;

        if (&#40;book_status&#91;i&#93; & book_accept_mask &&
                !&#40;book_status&#91;i&#93; & book_reject_mask&#41;)
            || (!&#40;book_status&#91;i&#93; & book_reject_mask&#41; && &#40;bs_percent&#91;i&#93;
                    || book_status&#91;i&#93; & 0x18 || &#40;wtm && book_status&#91;i&#93; & 0x80&#41;
                    || (!wtm && book_status&#91;i&#93; & 0x20&#41;)))

        if (&#40;book_status&#91;i&#93; & book_accept_mask &&
                !&#40;book_status&#91;i&#93; & book_reject_mask&#41;)
            || (!&#40;book_status&#91;i&#93; & book_reject_mask&#41; && (&#40;wtm &&
                        book_status&#91;i&#93; & 0x80&#41; || (!wtm &&
                        book_status&#91;i&#93; & 0x20&#41;)))

      if (!puzzling && (!book_random || &#40;mode == tournament_mode &&
                  np < book_search_trigger&#41;)) &#123;

drawn.c&#58;
  if (&#40;TotalPieces&#40;white, occupied&#41; == 6 && !Bishops&#40;white&#41; && Material > 0&#41;
      || &#40;TotalPieces&#40;black, occupied&#41; == 6 && !Bishops&#40;black&#41;
          && Material < 0&#41;)
For some of these probably even HGM will agree parentheses improve readability, but most expressions are quite simple.

I did not find any example in crafty of "A || B && C" without parentheses. It would surprise me if they exist.

Another example of redundant parentheses in movgen.c:

Code: Select all

    if (!Attacks&#40;tree, to, enemy&#41;
        && &#40;directions&#91;from&#93;&#91;to&#93; != check_direction1&#41;
        && &#40;directions&#91;from&#93;&#91;to&#93; != check_direction2&#41;)
I have no problem at all with these parentheses. None of them make your code any less readable for me.
Michel
Posts: 2273
Joined: Mon Sep 29, 2008 1:50 am

Re: Stockfish Nullmove uci option #178

Post by Michel »

Here is a little C test. What is the order of precedence for ^,&,| ?
Should one still refrain from using parentheses when using these operators?
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Stockfish Nullmove uci option #178

Post by bob »

syzygy wrote:
bob wrote:I would not resort to parens until I had a complex expression with multiple &&'s and ||'s, which could become difficult to read. In general, I consider parens as something that makes code harder to read in most cases, but which can make it clearer in others.
A quick look at crafty gives the strong impression that in practice you do use (redundant) parentheses whenever you mix && and || in one conditional expression.

Code: Select all

search.c&#58;
    if &#40;smp_idle && moves_searched > 1 &&
        tree->nodes_searched - start_nodes > smp_split_nodes && &#40;ply > 1 ||
            &#40;smp_split_at_root && NextRootMoveParallel&#40;)))) &#123;

  if &#40;tree->nodes_searched > noise_level || &#40;tree->parent &&
          tree->parent->nodes_searched > noise_level&#41;) &#123;

utility.c &#40;this one occurs 3 times&#41;&#58;
    if ((&#40;tmove&#91;0&#93; >= 'a' && tmove&#91;0&#93; <= 'z') || &#40;tmove&#91;0&#93; >= 'A' &&
                tmove&#91;0&#93; <= 'Z')) || !strcmp&#40;tmove, "0-0")
        || !strcmp&#40;tmove, "0-0-0")) &#123;

setboard.c&#58;
  if ((&#40;Castle&#40;0, white&#41; & 2&#41; && &#40;PcOnSq&#40;A1&#41; != rook&#41;)
      || (&#40;Castle&#40;0, white&#41; & 1&#41; && &#40;PcOnSq&#40;H1&#41; != rook&#41;)
      || (&#40;Castle&#40;0, black&#41; & 2&#41; && &#40;PcOnSq&#40;A8&#41; != -rook&#41;)
      || (&#40;Castle&#40;0, black&#41; & 1&#41; && &#40;PcOnSq&#40;H8&#41; != -rook&#41;)) &#123;

  if (&#40;twtm && EnPassant&#40;0&#41; && &#40;PcOnSq&#40;EnPassant&#40;0&#41; + 8&#41; != -pawn&#41;
          && &#40;PcOnSq&#40;EnPassant&#40;0&#41; - 7&#41; != pawn&#41;
          && &#40;PcOnSq&#40;EnPassant&#40;0&#41; - 9&#41; != pawn&#41;) || &#40;Flip&#40;twtm&#41;
          && EnPassant&#40;0&#41;
          && &#40;PcOnSq&#40;EnPassant&#40;0&#41; - 8&#41; != pawn&#41;
          && &#40;PcOnSq&#40;EnPassant&#40;0&#41; + 7&#41; != -pawn&#41;
          && &#40;PcOnSq&#40;EnPassant&#40;0&#41; + 9&#41; != -pawn&#41;)) &#123;

evaluate.c&#58;
  tree->dangerous&#91;white&#93; = &#40;Queens&#40;white&#41; && TotalPieces&#40;white, occupied&#41; > 9&#41;
      || &#40;TotalPieces&#40;white, rook&#41; > 1 && TotalPieces&#40;white, occupied&#41; > 15&#41;;
  tree->dangerous&#91;black&#93; = &#40;Queens&#40;black&#41; && TotalPieces&#40;black, occupied&#41; > 9&#41;
      || &#40;TotalPieces&#40;black, rook&#41; > 1 && TotalPieces&#40;black, occupied&#41; > 15&#41;;

      if (&#40;TotalPieces&#40;white, occupied&#41; == 0 &&
              tree->pawn_score.passed&#91;black&#93;)
          || &#40;TotalPieces&#40;black, occupied&#41; == 0 &&
              tree->pawn_score.passed&#91;white&#93;))

        if &#40;TotalPieces&#40;white, occupied&#41; == 3 &&
            TotalPieces&#40;black, occupied&#41; == 3 &&
            (&#40;TotalPieces&#40;white, pawn&#41; < 4 && TotalPieces&#40;black, pawn&#41; < 4&#41;
                || Abs&#40;TotalPieces&#40;white, pawn&#41; - TotalPieces&#40;black,
                        pawn&#41;) < 2&#41;)

    if &#40;Rank&#40;square&#41; == (&#40;side&#41; ? RANK6 &#58; RANK3&#41;
        && SetMask&#40;square + direction&#91;side&#93;) & Pawns&#40;enemy&#41;
        && (&#40;File&#40;square&#41; < FILEH &&
                SetMask&#40;square + 9 - 16 * side&#41; & Pawns&#40;side&#41;
                && !&#40;mask_hidden_right&#91;side&#93;&#91;File&#40;square&#41;&#93; & Pawns&#40;enemy&#41;))
            || &#40;File&#40;square&#41; > FILEA &&
                SetMask&#40;square + 7 - 16 * side&#41; & Pawns&#40;side&#41;
                && !&#40;mask_hidden_left&#91;side&#93;&#91;File&#40;square&#41;&#93; & Pawns&#40;enemy&#41;)))) &#123;

  if &#40;TotalPieces&#40;side, pawn&#41; == 1 && TotalPieces&#40;enemy, pawn&#41; == 0 &&
      (&#40;TotalPieces&#40;side, occupied&#41; == 5 && TotalPieces&#40;enemy, occupied&#41; == 5&#41;
          || (&#40;TotalPieces&#40;side, occupied&#41; == 9 &&
                  TotalPieces&#40;enemy, occupied&#41; == 9&#41;))) &#123;

book.c&#58;
          if &#40;bs_percent&#91;i&#93; < bs_percent&#91;i + 1&#93;
              || &#40;bs_percent&#91;i&#93; == bs_percent&#91;i + 1&#93;
                  && bs_value&#91;i&#93; < bs_value&#91;i + 1&#93;)) &#123;

        if (&#40;book_status&#91;i&#93; & book_accept_mask &&
                !&#40;book_status&#91;i&#93; & book_reject_mask&#41;)
            || (!&#40;book_status&#91;i&#93; & book_reject_mask&#41; && &#40;bs_percent&#91;i&#93;
                    || book_status&#91;i&#93; & 0x18 || &#40;wtm && book_status&#91;i&#93; & 0x80&#41;
                    || (!wtm && book_status&#91;i&#93; & 0x20&#41;)))

        if (&#40;book_status&#91;i&#93; & book_accept_mask &&
                !&#40;book_status&#91;i&#93; & book_reject_mask&#41;)
            || (!&#40;book_status&#91;i&#93; & book_reject_mask&#41; && (&#40;wtm &&
                        book_status&#91;i&#93; & 0x80&#41; || (!wtm &&
                        book_status&#91;i&#93; & 0x20&#41;)))

      if (!puzzling && (!book_random || &#40;mode == tournament_mode &&
                  np < book_search_trigger&#41;)) &#123;

drawn.c&#58;
  if (&#40;TotalPieces&#40;white, occupied&#41; == 6 && !Bishops&#40;white&#41; && Material > 0&#41;
      || &#40;TotalPieces&#40;black, occupied&#41; == 6 && !Bishops&#40;black&#41;
          && Material < 0&#41;)
For some of these probably even HGM will agree parentheses improve readability, but most expressions are quite simple.

I did not find any example in crafty of "A || B && C" without parentheses. It would surprise me if they exist.

Another example of redundant parentheses in movgen.c:

Code: Select all

    if (!Attacks&#40;tree, to, enemy&#41;
        && &#40;directions&#91;from&#93;&#91;to&#93; != check_direction1&#41;
        && &#40;directions&#91;from&#93;&#91;to&#93; != check_direction2&#41;)
I have no problem at all with these parentheses. None of them make your code any less readable for me.
I only do them to suppress warnings. Not because I like the style. I've tried for years to keep warnings out. I've pretty well succeeded, although it seems new warnings come with each new compiler release.

However, I generally don't fix that stuff until after it has been tested and tuned, to appease the compiler.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Stockfish Nullmove uci option #178

Post by bob »

Michel wrote:Here is a little C test. What is the order of precedence for ^,&,| ?
Should one still refrain from using parentheses when using these operators?
I think if you mix a lot, parens are probably ok. If you JUST mix && and ||, you should be able to write that without parens in most (but not all) cases. But along that line, what about +, -, *, /?. I ALSO don't have any problems remembering the precedence for those. Ditto if you add <,>, etc in with the arithmetic operators...