Super Fast 'Looking for magics' 1.0

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Robert Pope
Posts: 558
Joined: Sat Mar 25, 2006 8:27 pm

Re: Super Fast 'Looking for magics' 1.0

Post by Robert Pope »

I'm just starting to learn about magics, and had a general question:

Do valid magics depend on which direction you define your bitboards (e.g A1 as MSB or LSB), or do they work with any reasonable ordering because of symmetry?
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Super Fast 'Looking for magics' 1.0

Post by Sven »

Robert Pope wrote:I'm just starting to learn about magics, and had a general question:

Do valid magics depend on which direction you define your bitboards (e.g A1 as MSB or LSB), or do they work with any reasonable ordering because of symmetry?
A given set of valid magics depends on a given board orientation. I think this is related to bit shifting as well as to the fact that any occupancy bitboard involved in the "magic multiplication" depends on a board orientation, too.

The underlying principle of magic bitboards works for all reasonable board orientations but first you define your square numbering (e.g. A1=0, H8=63) and then you implement your magic bitboard generation code and your code to initialize related data structures. It is certainly possible to write such code in a generic way suitable for all possible board orientations but I never tried that and I don't know whether such code will be easy to understand. Furthermore, all the other code in the engine that accesses bitboards would need to be fully independent of the board orientation as well (which may be the easier part, though).
Robert Pope
Posts: 558
Joined: Sat Mar 25, 2006 8:27 pm

Re: Super Fast 'Looking for magics' 1.0

Post by Robert Pope »

Sven Schüle wrote:
Robert Pope wrote:I'm just starting to learn about magics, and had a general question:

Do valid magics depend on which direction you define your bitboards (e.g A1 as MSB or LSB), or do they work with any reasonable ordering because of symmetry?
A given set of valid magics depends on a given board orientation. I think this is related to bit shifting as well as to the fact that any occupancy bitboard involved in the "magic multiplication" depends on a board orientation, too.

The underlying principle of magic bitboards works for all reasonable board orientations but first you define your square numbering (e.g. A1=0, H8=63) and then you implement your magic bitboard generation code and your code to initialize related data structures. It is certainly possible to write such code in a generic way suitable for all possible board orientations but I never tried that and I don't know whether such code will be easy to understand. Furthermore, all the other code in the engine that accesses bitboards would need to be fully independent of the board orientation as well (which may be the easier part, though).
Good to know - thanks!
Robert Pope
Posts: 558
Joined: Sat Mar 25, 2006 8:27 pm

Re: Super Fast 'Looking for magics' 1.0

Post by Robert Pope »

Where do the numbers in BitTable come from?
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Super Fast 'Looking for magics' 1.0

Post by Sven »

Robert Pope wrote:Where do the numbers in BitTable come from?
What do you mean by "BitTable", to which source code do you refer?
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Super Fast 'Looking for magics' 1.0

Post by Sven »

Sven Schüle wrote:
Robert Pope wrote:Where do the numbers in BitTable come from?
What do you mean by "BitTable", to which source code do you refer?
I see, you refer to chessprogramming wiki and to Tord's magic generator code. I don't know where Tord got these numbers from. They are not directly related to magic bitboards but to general bitboard operations (pop_1st_bit).
wgarvin
Posts: 838
Joined: Thu Jul 05, 2007 5:03 pm
Location: British Columbia, Canada

Re: Super Fast 'Looking for magics' 1.0

Post by wgarvin »

I assume this is the table being asked about?

Code: Select all

const int BitTable[64] = {
  63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
  51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5, 52,
  26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38, 28,
  58, 20, 37, 17, 36, 8
};
 
int pop_1st_bit(uint64 *bb) {
  uint64 b = *bb ^ (*bb - 1);
  unsigned int fold = (unsigned) ((b & 0xffffffff) ^ (b >> 32));
  *bb &= (*bb - 1);
  return BitTable[(fold * 0x783a9b23) >> 26];
}
It looks like some sort of De Bruijn sequence for a minimal perfect hash of the 64 bitboard values with exactly 1 bit set.
Some similar examples can be found on this page, and there are many related threads linked at the bottom of that page. (thanks Gerd!)

I don't know where this specific BitTable sequence comes from, but I guess there are many possible sequences that will work and you only need one of them.

(Edit: the expression *bb ^ (*bb - 1) will return a bitboard containing all zeros in the top part and all ones in the bottom part, where "bottom part" means all bits below the lowest set bit in the original *bb. So assuming *bb was not empty to begin with, b can take only 64 different values here, which then get multiplied by something and "folded" onto the 64-entry BitTable by throwing away all but the top 6 bits of the multiplication result. This kind of "multiply and shift" is used to remap a sparse set of input values onto a dense range of table entries; this is basically the same trick as magics use.)

(Edit: my description of the xor expression sucks and might be wrong. I need coffee.. search for "With separated LS1B" on the BitScan page for a better description. Or this one.)
wgarvin
Posts: 838
Joined: Thu Jul 05, 2007 5:03 pm
Location: British Columbia, Canada

Re: Super Fast 'Looking for magics' 1.0

Post by wgarvin »

It looks basically the same as this folding trick by Matt Taylor, although the multiplication constant and BitTable sequence are not quite the same as what is listed on that page.

Also, in Stockfish you can find this snippet which I suppose is derived from Tord's original code:

Code: Select all

  // De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
  const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL;
  const uint32_t DeBruijn_32 = 0x783A9B23;
...
    // Matt Taylor's folding for 32 bit systems, extended to 64 bits by Kim Walisch
    b ^= (b - 1);
    return Is64Bit ? (b * DeBruijn_64) >> 58
                   : ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26;
User avatar
vittyvirus
Posts: 646
Joined: Wed Jun 18, 2014 2:30 pm
Full name: Fahad Syed

Re: Super Fast 'Looking for magics' 1.0

Post by vittyvirus »

Here's the version 1.1 of this program. It takes much, much less time than 1.1 on my poor Android (0.2 sec) and should be instantaneous on a normal PC:

Code: Select all

/* Super Fast 'Looking for Magics' by Tord Romstad * Made 'Super-Fast' by Syed 
   Fahad, and Matthew Brades * * Description: This program is an improvement
   over Tord Romstad's Feeding in Randoms to generate magics. I (Syed Fahad)
   started this working on this program when I ran Tord's program on my
   andriod and it couldn't generate the numbers. It would always fail. So, I
   began to speedup his program. Then, when I was stuck fighting a bug that
   freezed the computation somewhere when generating Bishop Magics, I showed
   my code to my close friend, Matthew Brades, who fixed the code and and then 
   further speeded up the code by Hardware Instructions. * * Results: *
   BEFORE: This program took 2 seconds for generating all magics on Core Duo
   2.8 GHz. * AFTER: This program generates all magics almost instantly w/o
   compiler optimization on my 1 GHz Micromax Canvas Doodle 3 with 512 MB of
   RAM using GCC on C4Driod. * Huge improvement, you see. * * There are a lot
   of possible speed ups still, so... * Version: 1.1 */

#include <stdio.h>
#include <stdlib.h>
#include<time.h>
#define USE_32_BIT_MULTIPLICATIONS

typedef unsigned long long uint64;

const uint64 BitMask&#91;64&#93; = &#123;
	0x1ULL, 0x2ULL, 0x4ULL, 0x8ULL, 0x10ULL, 0x20ULL, 0x40ULL, 0x80ULL,
	0x100ULL, 0x200ULL, 0x400ULL, 0x800ULL, 0x1000ULL, 0x2000ULL,
	0x4000ULL, 0x8000ULL, 0x10000ULL, 0x20000ULL, 0x40000ULL, 0x80000ULL,
	0x100000ULL, 0x200000ULL, 0x400000ULL, 0x800000ULL, 0x1000000ULL,
	0x2000000ULL, 0x4000000ULL, 0x8000000ULL, 0x10000000ULL, 0x20000000ULL,
	0x40000000ULL, 0x80000000ULL, 0x100000000ULL, 0x200000000ULL,
	0x400000000ULL, 0x800000000ULL, 0x1000000000ULL, 0x2000000000ULL,
	0x4000000000ULL, 0x8000000000ULL, 0x10000000000ULL, 0x20000000000ULL,
	0x40000000000ULL, 0x80000000000ULL, 0x100000000000ULL,
	0x200000000000ULL, 0x400000000000ULL, 0x800000000000ULL,
	0x1000000000000ULL, 0x2000000000000ULL, 0x4000000000000ULL,
	0x8000000000000ULL, 0x10000000000000ULL, 0x20000000000000ULL,
	0x40000000000000ULL, 0x80000000000000ULL, 0x100000000000000ULL,
	0x200000000000000ULL, 0x400000000000000ULL, 0x800000000000000ULL,
	0x1000000000000000ULL, 0x2000000000000000ULL, 0x4000000000000000ULL,
	0x8000000000000000ULL
&#125;;

uint64 RMask&#91;64&#93;;
uint64 BMask&#91;64&#93;;


// #define u1 (&#40;uint64&#41; (&#40;unsigned short&#41; &#40;rand&#40;))))
// #define u2 (&#40;uint64&#41; (&#40;unsigned short&#41; &#40;rand&#40;))))
// #define u3 (&#40;uint64&#41; (&#40;unsigned short&#41; &#40;rand&#40;))))
// #define u4 (&#40;uint64&#41; (&#40;unsigned short&#41; &#40;rand&#40;))))

// #define random_uint64&#40;) &#40;u1 | &#40;u2 << 16&#41; | &#40;u3 << 32&#41; | &#40;u4 << 48&#41;)
uint64 a, b, c, d;

#define ROTATE_L&#40;x, k&#41; (&#40;x << k&#41; | &#40;x >> &#40;64 - k&#41;))

uint64 random_uint64&#40;)
&#123;
	uint64 e = a - ROTATE_L&#40;b, 7&#41;;
	a = b ^ ROTATE_L&#40;c, 13&#41;;
	b = c + ROTATE_L&#40;d, 37&#41;;
	c = d + e;
	return d = e + a;
&#125;

/* 
   uint64 random_uint64_fewbits&#40;) &#123; return random_uint64&#40;) & random_uint64&#40;) &
   random_uint64&#40;); &#125; */
#define random_uint64_fewbits&#40;) &#40;random_uint64&#40;) & random_uint64&#40;) & random_uint64&#40;))
int count_1s&#40;uint64 b&#41;
&#123;
	// Original
	// int r;
	// for &#40;r = 0; b; r++, b &= b - 1&#41;;
	// Non-GCC users
	/* unsigned w = &#40;unsigned&#41;b >> 32, v = &#40;unsigned&#41;b; v -= &#40;v >> 1&#41; &
	   0x55555555; // 0-2 in 2 bits w -= &#40;w >> 1&#41; & 0x55555555; v = (&#40;v >> 2&#41;
	   & 0x33333333&#41; + &#40;v & 0x33333333&#41;; // 0-4 in 4 bits w = (&#40;w >> 2&#41; &
	   0x33333333&#41; + &#40;w & 0x33333333&#41;; v = (&#40;v >> 4&#41; + v + &#40;w >> 4&#41; + w&#41; &
	   0x0F0F0F0F; return &#40;int&#41;(&#40;v * 0x01010101&#41; >> 24&#41;; */
	return __builtin_popcountll&#40;b&#41;;
&#125;

const int BitTable&#91;64&#93; =
	&#123; 63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
	51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5, 52,
	26, 60, 6, 23,
	44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38, 28, 58, 20, 37, 17,
	36, 8
&#125;;

int pop_1st_bit&#40;uint64 * bb&#41;
&#123;
	/* 
	   uint64 b = *bb ^ (*bb - 1&#41;; unsigned int fold = &#40;unsigned&#41;(&#40;b &
	   0xffffffff&#41; ^ &#40;b >> 32&#41;); *bb &= (*bb - 1&#41;; return BitTable&#91;&#40;fold *
	   0x783a9b23&#41; >> 26&#93;; */
	int lsb = __builtin_ctz&#40;*bb&#41;;
	*bb &= *bb - 1;
	return lsb;

&#125;

uint64 index_to_uint64&#40;int index, int bits, uint64 m&#41;
&#123;
	int i, j, k;
	uint64 result = 0ULL;
	for &#40;i = 0; i < bits; i++)
	&#123;
		j = pop_1st_bit&#40;&m&#41;;
		if (&#40;index & &#40;BitMask&#91;i&#93;)) != 0&#41;
			result |= &#40;BitMask&#91;j&#93;);
	&#125;
	return result;
&#125;

uint64 rmask&#40;int sq&#41;
&#123;
	/* 
	   uint64 result = 0ULL; int rk = sq / 8, fl = sq % 8, r, f; for &#40;r = rk + 
	   1; r <= 6; r++) result |= &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;); for &#40;r = rk - 1; r
	   >= 1; r--) result |= &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;); for &#40;f = fl + 1; f <= 6;
	   f++) result |= &#40;BitMask&#91;&#40;f + rk * 8&#41;&#93;); for &#40;f = fl - 1; f >= 1; f--)
	   result |= &#40;BitMask&#91;&#40;f + rk * 8&#41;&#93;); return result; */
	return RMask&#91;sq&#93;;
&#125;

uint64 bmask&#40;int sq&#41;
&#123;
	/* 
	   uint64 result = 0ULL; int rk = sq / 8, fl = sq % 8, r, f; for &#40;r = rk + 
	   1, f = fl + 1; r <= 6 && f <= 6; r++, f++) result |= &#40;BitMask&#91;&#40;f + r *
	   8&#41;&#93;); for &#40;r = rk + 1, f = fl - 1; r <= 6 && f >= 1; r++, f--) result
	   |= &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;); for &#40;r = rk - 1, f = fl + 1; r >= 1 && f <=
	   6; r--, f++) result |= &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;); for &#40;r = rk - 1, f = fl
	   - 1; r >= 1 && f >= 1; r--, f--) result |= &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;);
	   return result; */
	return BMask&#91;sq&#93;;
&#125;

uint64 rmask_gen&#40;int sq&#41;
&#123;
	uint64 result = 0ULL;
	int rk = sq / 8, fl = sq % 8, r, f;
	for &#40;r = rk + 1; r <= 6; r++)
		result |= &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;);
	for &#40;r = rk - 1; r >= 1; r--)
		result |= &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;);
	for &#40;f = fl + 1; f <= 6; f++)
		result |= &#40;BitMask&#91;&#40;f + rk * 8&#41;&#93;);
	for &#40;f = fl - 1; f >= 1; f--)
		result |= &#40;BitMask&#91;&#40;f + rk * 8&#41;&#93;);
	return result;
&#125;

uint64 bmask_gen&#40;int sq&#41;
&#123;
	uint64 result = 0ULL;
	int rk = sq / 8, fl = sq % 8, r, f;
	for &#40;r = rk + 1, f = fl + 1; r <= 6 && f <= 6; r++, f++)
		result |= &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;);
	for &#40;r = rk + 1, f = fl - 1; r <= 6 && f >= 1; r++, f--)
		result |= &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;);
	for &#40;r = rk - 1, f = fl + 1; r >= 1 && f <= 6; r--, f++)
		result |= &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;);
	for &#40;r = rk - 1, f = fl - 1; r >= 1 && f >= 1; r--, f--)
		result |= &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;);
	return result;
&#125;

uint64 ratt&#40;int sq, uint64 block&#41;
&#123;
	uint64 result = 0ULL;
	int rk = sq / 8, fl = sq % 8, r, f;
	for &#40;r = rk + 1; r <= 7; r++)
	&#123;
		result |= &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;);
		if (&#40;block & &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;)) != 0&#41;
			break;
	&#125;
	for &#40;r = rk - 1; r >= 0; r--)
	&#123;
		result |= &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;);
		if (&#40;block & &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;)) != 0&#41;
			break;
	&#125;
	for &#40;f = fl + 1; f <= 7; f++)
	&#123;
		result |= &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;);
		if (&#40;block & &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;)) != 0&#41;
			break;
	&#125;
	for &#40;f = fl - 1; f >= 0; f--)
	&#123;
		result |= &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;);
		if (&#40;block & &#40;BitMask&#91;&#40;fl + r * 8&#41;&#93;)) != 0&#41;
			break;
	&#125;
	return result;
&#125;

uint64 batt&#40;int sq, uint64 block&#41;
&#123;
	uint64 result = 0ULL;
	int rk = sq / 8, fl = sq % 8, r, f;
	for &#40;r = rk + 1, f = fl + 1; r <= 7 && f <= 7; r++, f++)
	&#123;
		result |= &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;);
		if (&#40;block & &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;)) != 0&#41;
			break;
	&#125;
	for &#40;r = rk + 1, f = fl - 1; r <= 7 && f >= 0; r++, f--)
	&#123;
		result |= &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;);
		if (&#40;block & &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;)) != 0&#41;
			break;
	&#125;
	for &#40;r = rk - 1, f = fl + 1; r >= 0 && f <= 7; r--, f++)
	&#123;
		result |= &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;);
		if (&#40;block & &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;)) != 0&#41;
			break;
	&#125;
	for &#40;r = rk - 1, f = fl - 1; r >= 0 && f >= 0; r--, f--)
	&#123;
		result |= &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;);
		if (&#40;block & &#40;BitMask&#91;&#40;f + r * 8&#41;&#93;)) != 0&#41;
			break;
	&#125;
	return result;
&#125;

int transform&#40;uint64 b, uint64 magic, int bits&#41;
&#123;
#if defined&#40;USE_32_BIT_MULTIPLICATIONS&#41;
	return &#40;unsigned&#41;(&#40;int&#41;b * &#40;int&#41;magic ^ &#40;int&#41;&#40;b >> 32&#41; *
					  &#40;int&#41;&#40;magic >> 32&#41;) >> &#40;32 - bits&#41;;
#else
	return &#40;int&#41;(&#40;b * magic&#41; >> &#40;64 - bits&#41;);
#endif
&#125;

uint64 find_magic&#40;int sq, int m, int bishop&#41;
&#123;
	uint64 mask, b&#91;4096&#93;, a&#91;4096&#93;, used&#91;4096&#93;, magic;
	int i, j, k, n, mbits, fail;
	int c = 0;
	mask = bishop ? bmask&#40;sq&#41; &#58; rmask&#40;sq&#41;;
	n = count_1s&#40;mask&#41;;
	for &#40;i = 0; i < &#40;BitMask&#91;n&#93;); i++)
	&#123;
		b&#91;i&#93; = index_to_uint64&#40;i, n, mask&#41;;
		a&#91;i&#93; = bishop ? batt&#40;sq, b&#91;i&#93;) &#58; ratt&#40;sq, b&#91;i&#93;);
	&#125;

	for &#40;k = 0; k < 100000000; ++k&#41;
	&#123;
		magic = random_uint64_fewbits&#40;);

		if &#40;count_1s&#40;&#40;mask * magic&#41; & 0xFF00000000000000ULL&#41; < 6&#41;
			continue;

		for &#40;i = 0; i < 4096; i++)
			used&#91;i&#93; = 0ULL;
		for &#40;i = 0, fail = 0; &#40;fail == 0&#41; && i < &#40;BitMask&#91;n&#93;); i++)
		&#123;
			j = transform&#40;b&#91;i&#93;, magic, m&#41;;
			if &#40;used&#91;j&#93; == 0ULL&#41;
				used&#91;j&#93; = a&#91;i&#93;;
			else if &#40;used&#91;j&#93; != a&#91;i&#93;)
				fail = 1;
		&#125;
		if &#40;fail == 0&#41;
			return magic;
	&#125;
	printf&#40;"***Failed***\n");
	return 0ULL;
&#125;

const int RBits&#91;64&#93; =
	&#123; 12, 11, 11, 11, 11, 11, 11, 12, 11, 10, 10, 10, 10, 10, 10, 11, 11, 10,
	10, 10, 10, 10, 10, 11, 11, 10, 10, 10, 10, 10, 10, 11, 11, 10, 10, 10, 10,
	10, 10,
	11, 11, 10, 10, 10, 10, 10, 10, 11, 11, 10, 10, 10, 10, 10, 10, 11, 12, 11,
	11, 11,
	11, 11, 11, 12
&#125;;
const int BBits&#91;64&#93; =
	&#123; 6, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 5, 5,
	5, 5, 7, 9, 9, 7, 5, 5, 5, 5, 7, 9, 9, 7, 5, 5, 5, 5, 7, 7, 7, 7, 5, 5, 5,
	5, 5, 5,
	5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 6
&#125;;


int main&#40;)
&#123;
	a = 0xF1EA5EED, b = c = d = 0xD4E12C77;
	int square;
	uint64 magic;
	uint64 RookMagics&#91;64&#93;;
	uint64 BishMagics&#91;64&#93;;

	for &#40;square = 0; square < 64; ++square&#41;
	&#123;
		RMask&#91;square&#93; = rmask_gen&#40;square&#41;;
		BMask&#91;square&#93; = bmask_gen&#40;square&#41;;
	&#125;

	double startTime, endTime;

	startTime = clock&#40;);
	 for &#40;square = 0; square < 64; square++)
	&#123;
		RookMagics&#91;square&#93; = find_magic&#40;square, RBits&#91;square&#93;, 0&#41;;
		BishMagics&#91;square&#93; = find_magic&#40;square, BBits&#91;square&#93;, 1&#41;;
	&#125;
	endTime = clock&#40;);

	printf&#40;"const uint64 RMagic&#91;64&#93; = &#123;\n");
	for &#40;square = 0; square < 64; square++)
		printf&#40;" 0x%llxULL,\n", RookMagics&#91;square&#93;);
	printf&#40;"&#125;;\n\n");

	printf&#40;"const uint64 BMagic&#91;64&#93; = &#123;\n");
	for &#40;square = 0; square < 64; square++)
		printf&#40;" 0x%llxULL,\n", BishMagics&#91;square&#93;);
	printf&#40;"&#125;;\n\n");

	printf&#40;"Took %f seconds", &#40;endTime - startTime&#41; / &#40;double&#41;CLOCKS_PER_SEC&#41;;
	// fflush&#40;stdout&#41;;
	return 0;
&#125;
Did I tell you it records the time taken too?