#define X (MinGW)

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: #define X (MinGW)

Post by Sven »

hgm wrote:
Ras wrote:That's why people give useful names to variables and defines - then this doesn't happen. Of course, the aim to produce the shortest source text possible somewhat collides with established principles of software development, so you are in hacking territory.
I don't think that producing readable code is "against established principles of software development". When I have a 2-dimensional 18 x 18 table with initialized data, like

Code: Select all

int table[][] = {
  { X, 0, Y, 0, X, 0, N, 0, X, 0, Y, 0, X, 0, Y, 0, X, 0 },
  { 0, N, 0, Y, 0, N, 0, X, 0, N, 0, Y, 0, N, 0, X, 0, N },
  { X, 0, Y, 0, X, 0, N, 0, X, 0, Y, 0, X, 0, Y, 0, X, 0 },
  { 0, N, 0, Y, 0, N, 0, X, 0, N, 0, Y, 0, N, 0, X, 0, N },
  { X, 0, Y, 0, X, 0, N, 0, X, 0, Y, 0, X, 0, Y, 0, X, 0 },
  { 0, N, 0, Y, 0, N, 0, X, 0, N, 0, Y, 0, N, 0, X, 0, N },
  { X, 0, Y, 0, X, 0, N, 0, X, 0, Y, 0, X, 0, Y, 0, X, 0 },
  { 0, N, 0, Y, 0, N, 0, X, 0, N, 0, Y, 0, N, 0, X, 0, N },
  { X, 0, Y, 0, X, 0, N, 0, X, 0, Y, 0, X, 0, Y, 0, X, 0 },
  { 0, N, 0, Y, 0, N, 0, X, 0, N, 0, Y, 0, N, 0, X, 0, N },
  { X, 0, Y, 0, X, 0, N, 0, X, 0, Y, 0, X, 0, Y, 0, X, 0 },
  { 0, N, 0, Y, 0, N, 0, X, 0, N, 0, Y, 0, N, 0, X, 0, N },
  { X, 0, Y, 0, X, 0, N, 0, X, 0, Y, 0, X, 0, Y, 0, X, 0 },
  { 0, N, 0, Y, 0, N, 0, X, 0, N, 0, Y, 0, N, 0, X, 0, N },
  { X, 0, Y, 0, X, 0, N, 0, X, 0, Y, 0, X, 0, Y, 0, X, 0 },
  { 0, N, 0, Y, 0, N, 0, X, 0, N, 0, Y, 0, N, 0, X, 0, N },
  { X, 0, Y, 0, X, 0, N, 0, X, 0, Y, 0, X, 0, Y, 0, X, 0 },
  { 0, N, 0, Y, 0, N, 0, X, 0, N, 0, Y, 0, N, 0, X, 0, N }
}
you should try once to see how it looks when you use long, descriptive variable names...
A slightly more descriptive, less error-prone (and also "shorter") implementation would initialize the table using a (static) function, like in the following example:

Code: Select all

int table[18][18];
static void initTable()
{
  for &#40;int rank = 0; rank < 18; rank++) &#123;
    for &#40;int file = 0; file < 18; file++) &#123;
      if (&#40;rank + file&#41; % 2&#41; == 0&#41; &#123;
        table&#91;rank&#93;&#91;file&#93; = 0;
      &#125; else
      if (&#40;rank % 2&#41; == 0&#41; &#123;
        table&#91;rank&#93;&#91;file&#93; =
          (&#40;file % 4&#41; == 0&#41; ? X &#58;
          &#40;file == 6&#41; ? N &#58; Y;
      &#125; else &#123;
        table&#91;rank&#93;&#91;file&#93; =
          ((&#40;file - 1&#41; % 4&#41; == 0&#41; ? N &#58;
          ((&#40;file - 1&#41; % 8&#41; == 2&#41; ? Y &#58; X;
      &#125;
    &#125;
  &#125;
&#125;
And from there it would be an acceptable change to choose better names for X, Y, N ...
User avatar
hgm
Posts: 27808
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: #define X (MinGW)

Post by hgm »

I don't consider that an improvement at all. From the table you see immediately what you have, for the code you first have to run it 'in your head'.

This was a completely hypothetical example anyway, and the regularity you recogized in it was mainly because I was lazy and used copy-paste a lot to generate it. The actual table I got the error in had no regularity: it was the move-generator table that listed for each piece type what its maximum rage was in every direction, with some special codes indicating whether it could jump in that direction, etc. No way to generate it automatically.
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: #define X (MinGW)

Post by Sven »

hgm wrote:I don't consider that an improvement at all. From the table you see immediately what you have, for the code you first have to run it 'in your head'.

This was a completely hypothetical example anyway, and the regularity you recogized in it was mainly because I was lazy and used copy-paste a lot to generate it. The actual table I got the error in had no regularity: it was the move-generator table that listed for each piece type what its maximum rage was in every direction, with some special codes indicating whether it could jump in that direction, etc. No way to generate it automatically.
I understand, therefore I called my code an "example" (I expected that the real table might be different). Apart from that, any table that can be initialized "manually" by an initializer can also be initialized "automatically" by a function. There should be a way to describe in words how the table has to be set up, and then you can also write a function for it. It may be a matter of taste to use the one or the other method. But for considerably large tables like this one l would *always* prefer a function over a static initializer for one simple reason: errors (typos, like in your "+" instead of "=" example some posts earlier) are very hard to detect, e.g. if you swap X and Y somewhere. And if you only know there is an error but you don't know where then good luck in finding it ... Also, what about a 36x36 table, or a 144x144 table, would you still use a static initializer? And what if the dimensions are not fixed but given as a dynamic parameter?

Also I would always try to do everything that is needed to avoid using a lot of X, Y, Z, N, ... variable names, and that would already justify to use the "init function" approach. I simply don't want my programs to look like a BASIC program.
User avatar
hgm
Posts: 27808
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: #define X (MinGW)

Post by hgm »

Well, sometimes you just have no choice. A routine with 144 assignement statements table[xxx] = yyy; isn't any less error prone than initialized data. They did not invent initialzers so they should not be used...

It is also quite difficult to convert the words "Bishops move diagonally, Rooks, orthogonally, Queens can both" to an algorithm for initializing a table.
syzygy
Posts: 5566
Joined: Tue Feb 28, 2012 11:56 pm

Re: #define X (MinGW)

Post by syzygy »

hgm wrote:Well, sometimes you just have no choice.
I see absolutely nothing wrong with using X, Y, Z, N like that for filling a table. But one idea that could avoid some problems caused by X, Y, Z, N unexpectedly being used in other parts of the program (or even in system headers...) is to #define them right before the table initialisation and to #undef them right after.
User avatar
hgm
Posts: 27808
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: #define X (MinGW)

Post by hgm »

Indeed, that would be very good practice. I never run into trouble myself with this, because I used the convention that all variables start with lower case. But for #included system headers you cannot rely on that. They would likely not declare common names as globals, but they could use them as formal parameters, like happenend here.

In this case the X is used in code, however, admittedly in a bit of a hacky way: the table in question indicates the ranges of various piece types in various directions, but since that makes any value larger than the board dimensions equivalent, I use different large values to encode additional capabilities. E.g. X=36 is used for normal sliders, while Y=37 indicates that the slide can jump over pieces when making a capture.

When used in the code the space limitation that applies to the table does not apply, however. So it would be cleaner to do it like this:

Code: Select all

#define UNLIMITED 36

#define X UNLIMITED

int ranges&#91;NPIECES&#93;&#91;16&#93; = &#123; /* 4 orthogonal, 4 diagonal, 8 hippogonal */
  &#123; 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 &#125;, // King
  &#123; X, X, X, X, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // Rook
  &#123; 0, 0, 0, 0, X, X, X, X, 0, 0, 0, 0, 0, 0, 0, 0 &#125;, // Bishop
  &#123; 0, 0, 0, 0, X, X, X, X, 1, 1, 1, 1, 1, 1, 1, 1 &#125;, // Archbishop
  &#123; X, X, X, X, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 &#125;, // Dragon King
  &#123; X, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 &#125;, // Vertical Soldier
  ...
&#125;;

#undef X

//
  if&#40;ranges&#91;piece&#93;&#91;direction&#93; > UNLIMITED&#41; // special move
  ...
Ras
Posts: 2488
Joined: Tue Aug 30, 2016 8:19 pm
Full name: Rasmus Althoff

Re: #define X (MinGW)

Post by Ras »

It might also be possible to do the automation not in a initialiser function that is called at runtime, but to shift that over to build time and auto-generate the C sources. That's especially useful when the tables become considerably more complex.

In the CT800, the biggest initialised table I have is the opening book which is a uint8_t blob of 95 kB or so, roughly 40% of the firmware. The actual input is a line based text file with about 12,000 lines. After legality checking, my opening book compiler generates the position based format out of that, plus some cache indices for faster access and some derived #defines.

In the end, it's a 600 kB C file, fully auto-generated at build time, and the generation is part of the build script. That C file then gets #included in the book.c file where the book handling is done.