Discussion of chess software programming and technical issues.
Moderator: Ras
MOBMAT
Posts: 396 Joined: Sat Feb 04, 2017 11:57 pm
Location: USA
Post
by MOBMAT » Sat Feb 04, 2023 6:01 pm
I asked Chat GPT to generate C++ code that can generate moves for sliding pieces.
Here is what it came up with. What technique did it use?
Code: Select all
#include <bitset>
typedef unsigned long long U64;
const int MAX_MOVES = 28;
const int MAX_BITS = 64;
std::bitset<MAX_BITS> RookMask[MAX_BITS];
std::bitset<MAX_BITS> BishopMask[MAX_BITS];
void initMasks() {
for (int sq = 0; sq < MAX_BITS; ++sq) {
int rk = sq / 8, fl = sq % 8;
// rook masks
for (int i = rk + 1; i < 8; ++i) RookMask[sq][i * 8 + fl] = 1;
for (int i = rk - 1; i >= 0; --i) RookMask[sq][i * 8 + fl] = 1;
for (int j = fl + 1; j < 8; ++j) RookMask[sq][rk * 8 + j] = 1;
for (int j = fl - 1; j >= 0; --j) RookMask[sq][rk * 8 + j] = 1;
// bishop masks
for (int i = rk + 1, j = fl + 1; i < 8 && j < 8; ++i, ++j)
BishopMask[sq][i * 8 + j] = 1;
for (int i = rk + 1, j = fl - 1; i < 8 && j >= 0; ++i, --j)
BishopMask[sq][i * 8 + j] = 1;
for (int i = rk - 1, j = fl + 1; i >= 0 && j < 8; --i, ++j)
BishopMask[sq][i * 8 + j] = 1;
for (int i = rk - 1, j = fl - 1; i >= 0 && j >= 0; --i, --j)
BishopMask[sq][i * 8 + j] = 1;
}
}
U64 generateRookMoves(int sq, U64 occ) {
U64 attacks = RookMask[sq] & (~occ);
U64 blockers = ((attacks & (occ >> 8)) - 2 * (occ & (attacks >> 8))) & 0xFF000000000000FF;
return attacks ^ RookMask[sq + 56] * blockers;
}
U64 generateBishopMoves(int sq, U64 occ) {
U64 attacks = BishopMask[sq] & (~occ);
U64 blockers = ((attacks & (occ >> 9)) - 2 * (occ & (attacks >> 9))) & 0x7F7F7F7F7F7F7F7F;
return attacks ^ BishopMask[sq + 56] * blockers;
}
i7-6700K @ 4.00Ghz 32Gb, Win 10 Home, EGTBs on PCI SSD
Benchmark: Stockfish15.1 NNUE x64 bmi2 (nps): 1277K
syzygy
Posts: 5694 Joined: Tue Feb 28, 2012 11:56 pm
Post
by syzygy » Sat Feb 04, 2023 6:09 pm
Doesn't look like something that could work. E.g. RookMask[sq + 56] and BishopMask[sq + 56] will usually be out of bounds. And there is no reason to believe that multiplication by blockers does anything sensible.
MOBMAT
Posts: 396 Joined: Sat Feb 04, 2017 11:57 pm
Location: USA
Post
by MOBMAT » Sat Feb 04, 2023 6:14 pm
Sorry, I sent the example too soon.
I then asked Chat GPT to optimize the code for speed. The explanation given follows:
In this version, I've replaced the bitset type with unsigned long long to store the masks, which is faster as bitset uses more memory and is slower due to extra data structures and operations. I've also optimized the calculation of the final moves by using bitwise operations instead of using the bitset type.
And the new code...
Code: Select all
#include <bitset>
typedef unsigned long long U64;
const int MAX_MOVES = 28;
const int MAX_BITS = 64;
U64 RookMask[MAX_BITS];
U64 BishopMask[MAX_BITS];
void initMasks() {
for (int sq = 0; sq < MAX_BITS; ++sq) {
int rk = sq / 8, fl = sq % 8;
// rook masks
for (int i = rk + 1; i < 8; ++i) RookMask[sq] |= 1ULL << (i * 8 + fl);
for (int i = rk - 1; i >= 0; --i) RookMask[sq] |= 1ULL << (i * 8 + fl);
for (int j = fl + 1; j < 8; ++j) RookMask[sq] |= 1ULL << (rk * 8 + j);
for (int j = fl - 1; j >= 0; --j) RookMask[sq] |= 1ULL << (rk * 8 + j);
// bishop masks
for (int i = rk + 1, j = fl + 1; i < 8 && j < 8; ++i, ++j)
BishopMask[sq] |= 1ULL << (i * 8 + j);
for (int i = rk + 1, j = fl - 1; i < 8 && j >= 0; ++i, --j)
BishopMask[sq] |= 1ULL << (i * 8 + j);
for (int i = rk - 1, j = fl + 1; i >= 0 && j < 8; --i, ++j)
BishopMask[sq] |= 1ULL << (i * 8 + j);
for (int i = rk - 1, j = fl - 1; i >= 0 && j >= 0; --i, --j)
BishopMask[sq] |= 1ULL << (i * 8 + j);
}
}
U64 generateRookMoves(int sq, U64 occ) {
U64 attacks = RookMask[sq] & (~occ);
U64 blockers = ((attacks & (occ >> 8)) - 2 * (occ & (attacks >> 8))) & 0xFF000000000000FF;
return attacks ^ (RookMask[sq] & (~blockers));
}
U64 generateBishopMoves(int sq, U64 occ) {
U64 attacks = BishopMask[sq] & (~occ);
U64 blockers = ((attacks & (occ >> 9)) - 2 * (occ & (attacks >> 9))) & 0x7F7F7F7F7F7F7F7F;
return attacks ^ (BishopMask[sq] & (~blockers));
}
i7-6700K @ 4.00Ghz 32Gb, Win 10 Home, EGTBs on PCI SSD
Benchmark: Stockfish15.1 NNUE x64 bmi2 (nps): 1277K
alessandro
Posts: 52 Joined: Tue Aug 12, 2014 11:21 am
Location: Lund
Full name: Alessandro Iavicoli
Post
by alessandro » Sat Feb 04, 2023 6:20 pm
MOBMAT wrote: ↑ Sat Feb 04, 2023 6:01 pm
What technique did it use?
As this directly to ChatGPT
MOBMAT
Posts: 396 Joined: Sat Feb 04, 2017 11:57 pm
Location: USA
Post
by MOBMAT » Sat Feb 04, 2023 7:18 pm
why didn't I think of that.
ChatGPT calls what it generated Magic Bitboards.
i7-6700K @ 4.00Ghz 32Gb, Win 10 Home, EGTBs on PCI SSD
Benchmark: Stockfish15.1 NNUE x64 bmi2 (nps): 1277K
dangi12012
Posts: 1062 Joined: Tue Apr 28, 2020 10:03 pm
Full name: Daniel Infuehr
Post
by dangi12012 » Sat Feb 04, 2023 10:11 pm
Ask it to generate unit tests.
MOBMAT
Posts: 396 Joined: Sat Feb 04, 2017 11:57 pm
Location: USA
Post
by MOBMAT » Sun Feb 05, 2023 7:39 pm
Code: Select all
#include <cassert>
int main() {
initMasks();
// Test generateRookMoves
assert(generateRookMoves(0, 0) == 0x0101010101010100);
assert(generateRookMoves(7, 0) == 0x8080808080808080);
assert(generateRookMoves(56, 0) == 0x0101010101010101);
assert(generateRookMoves(63, 0) == 0x8080808080808080);
assert(generateRookMoves(28, 0) == 0x0000008080808080);
// Test generateBishopMoves
assert(generateBishopMoves(2, 0) == 0x2400000000000024);
assert(generateBishopMoves(5, 0) == 0x2800000000000028);
assert(generateBishopMoves(58, 0) == 0x2400000000000024);
assert(generateBishopMoves(61, 0) == 0x2800000000000028);
assert(generateBishopMoves(28, 0) == 0x1020400000000820);
return 0;
}
i7-6700K @ 4.00Ghz 32Gb, Win 10 Home, EGTBs on PCI SSD
Benchmark: Stockfish15.1 NNUE x64 bmi2 (nps): 1277K
dangi12012
Posts: 1062 Joined: Tue Apr 28, 2020 10:03 pm
Full name: Daniel Infuehr
Post
by dangi12012 » Mon Feb 06, 2023 4:19 pm
Yeah for simpler chess tasks it works fine (mind you you dont even have to specify chess or 8x8)
Generate C++ code that can print a bitboard
Code: Select all
#include <bitset>
#include <iostream>
void printBitboard(unsigned long long bitboard) {
std::bitset<64> b(bitboard);
for (int i = 63; i >= 0; i--) {
std::cout << b[i];
if (i % 8 == 0) {
std::cout << std::endl;
}
}
}
change the printBitboard to return std::string
Code: Select all
#include <bitset>
#include <sstream>
#include <string>
std::string printBitboard(unsigned long long bitboard) {
std::bitset<64> b(bitboard);
std::stringstream ss;
for (int i = 63; i >= 0; i--) {
ss << b[i];
if (i % 8 == 0) {
ss << std::endl;
}
}
return ss.str();
}
Mind you that the answer came faster than me going into google or github to search for it, or writing it by hand.
If you tell it to extract bits via mask to remove the bitset dependency it will oblige correctly etc..
Only matter of time until we get ChatGPT inside Visual Studio directly.
MOBMAT
Posts: 396 Joined: Sat Feb 04, 2017 11:57 pm
Location: USA
Post
by MOBMAT » Mon Feb 06, 2023 5:30 pm
Yes, it is very interesting.
I asked it to explain the difference between hard/soft alpha beta searching. It obliged. I then asked for example code for both. When I compared them, I noticed that the version for "soft" implemented it differently than I remember doing it. Perhaps I had overlooked something that made me give up on it.
But you are right, you can get code snippets very quickly IF you ask for what you want correctly.
I did find the "free" version has a limit to how many queries you can ask for in an hour.
i7-6700K @ 4.00Ghz 32Gb, Win 10 Home, EGTBs on PCI SSD
Benchmark: Stockfish15.1 NNUE x64 bmi2 (nps): 1277K