Okay, so what in the world does that code do?
Well, one thing it should do is convince programmers to be careful whenever they use min, max or abs macros!
Many compilers including Microsoft's, supply min(a,b) and max(a,b) as macros, something like this (from WinDef.h):
Code: Select all
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
It seems the authors of Ippolit were also using a macro for abs. And their min/max macros are slightly different from Microsoft's. Anyway, here's the form that was used in Ippolit:
Code: Select all
#define max(a,b) (((a) >= (b)) ? (a) : (b))
#define min(a,b) (((a) <= (b)) ? (a) : (b))
#define abs(a) (((a) >= 0) ? (a) : -(a))
I started with the abs() calls, searching for >= 0 and extracting expressions and converting them to abs. Then max and min. Anyway, after a while I had something that looked like this:
Code: Select all
//inline int RankOf(x) { return (x >> 3); }
//inline int FileOf(x) { return (x & 7); }
if ((tower_dynamics->flag & 128))
{
if (score > 0)
{
int bkRank = (position_fixed.black_king >> 3);
int bkFile = (position_fixed.black_king & 7);
if (position_fixed.bb[eWhiteBright])
score -= 20 * min(max(abs(FileOf(A8) - bkFile), abs(RankOf(A8) - bkRank)), max(abs(FileOf(H1) - bkFile), abs(RankOf(H1) - bkRank)))
+ 10 * min(min(abs(FileOf(A8) - bkFile), abs(RankOf(A8) - bkRank)), min(abs(FileOf(H1) - bkFile), abs(RankOf(H1) - bkRank)));
else
score -= 20 * min(max(abs(FileOf(A1) - bkFile), abs(RankOf(A1) - bkRank)), max(abs(FileOf(H8) - bkFile), abs(RankOf(H8) - bkRank)))
+ 10 * min(min(abs(FileOf(A1) - bkFile), abs(RankOf(A1) - bkRank)), min(abs(FileOf(H8) - bkFile), abs(RankOf(H8) - bkRank)));
}
else
{
int wkRank = (position_fixed.white_king >> 3);
int wkFile = (position_fixed.white_king & 7);
if (position_fixed.bb[eBlackBright])
score += 20 * min(max(abs(FileOf(A8) - wkFile), abs(RankOf(A8) - wkRank)), max(abs(FileOf(H1) - wkFile), abs(RankOf(H1) - wkRank)))
+ 10 * min(min(abs(FileOf(A8) - wkFile), abs(RankOf(A8) - wkRank)), min(abs(FileOf(H1) - wkFile), abs(RankOf(H1) - wkRank)));
else
score += 20 * min(max(abs(FileOf(A1) - wkFile), abs(RankOf(A1) - wkRank)), max(abs(FileOf(H8) - wkFile), abs(RankOf(H8) - wkRank)))
+ 10 * min(min(abs(FileOf(A1) - wkFile), abs(RankOf(A1) - wkRank)), min(abs(FileOf(H8) - wkFile), abs(RankOf(H8) - wkRank)));
}
}
That's still a little unwieldly, so I pulled out the abs() expressions to produce this version:
Code: Select all
//inline int RankOf(x) { return (x >> 3); }
//inline int FileOf(x) { return (x & 7); }
if ((tower_dynamics->flag & 128))
{
if (score > 0)
{
int bkRank = (position_fixed.black_king >> 3);
int bkFile = (position_fixed.black_king & 7);
int bkRanksFrom1 = abs(RANK_1 - bkRank);
int bkRanksFrom8 = abs(RANK_8 - bkRank);
int bkFilesFromA = abs(FILE_A - bkFile);
int bkFilesFromH = abs(FILE_H - bkFile);
if (position_fixed.bb[eWhiteBright])
score -= 20 * min(max(bkFilesFromA, bkRanksFrom8), max(bkFilesFromH, bkRanksFrom1))
+ 10 * min(min(bkFilesFromA, bkRanksFrom8), min(bkFilesFromH, bkRanksFrom1));
else
score -= 20 * min(max(bkFilesFromA, bkRanksFrom1), max(bkFilesFromH, bkRanksFrom8))
+ 10 * min(min(bkFilesFromA, bkRanksFrom1), min(bkFilesFromH, bkRanksFrom8));
}
else
{
int wkRank = (position_fixed.white_king >> 3);
int wkFile = (position_fixed.white_king & 7);
int wkRanksFrom1 = abs(RANK_1 - wkRank);
int wkRanksFrom8 = abs(RANK_8 - wkRank);
int wkFilesFromA = abs(FILE_A - wkFile);
int wkFilesFromH = abs(FILE_H - wkFile);
if (position_fixed.bb[eBlackBright])
score += 20 * min(max(wkFilesFromA, wkRanksFrom8), max(wkFilesFromH, wkRanksFrom1))
+ 10 * min(min(wkFilesFromA, wkRanksFrom8), min(wkFilesFromH, wkRanksFrom1));
else
score += 20 * min(max(wkFilesFromA, wkRanksFrom1), max(wkFilesFromH, wkRanksFrom8))
+ 10 * min(min(wkFilesFromA, wkRanksFrom1), min(wkFilesFromH, wkRanksFrom8));
}
}
Now its much easier to see what's going on.
The min/max stuff is being used to calculate some approximate distances from certain corners of the board. I have no idea why this isn't just a pre-computed table, but whatever.
Note that min, max and abs can all be implemented in a branchless fashion on most platforms. I think Microsoft compilers always do this for abs(), but I'm not sure about min/max. Anyways, if you use your own macro you make it a lot harder for the compiler, which now has to try and do common-subexpression elimination on your stuff and turn it back into something sensible.
As we've seen here, nesting 2 or 3 levels of these macros quickly gets out of hand. Rather than assume your compiler can work miracles with recklessly-expanded macro code, you might prefer to use a non-macro version. Maybe something like this:
Code: Select all
// These could be optimized further to be branchless (though good compilers will do it themselves anyhow)
template<class T> __forceinline T min(const T& a, const T& b) { return a <= b? a : b; }
template<class T> __forceinline T max(const T& a, const T& b) { return a >= b? a : b; }
template<class T> __forceinline T abs(const T& a) { return a >= 0? a : -a; }