Closed archishou closed 1 year ago
Ok, I was able to solve it with this (manually switching between two move lists), but I'm now getting a seg fault. Wondering if this may be an issue with the library itself or something I'm doing.
template <Color color>
Move randomMove(Position board) {
MoveList<color> moveList(board);
std::vector<Move> out;
size_t nelems = 1;
std::sample(
moveList.begin(),
moveList.end(),
std::back_inserter(out),
nelems,
std::mt19937{std::random_device{}()}
);
return out[0];
}
template <Color color>
int checkmate(Position board) {
std::cout << "here 2" << std::endl;
MoveList<color> moveList(board);
std::cout << "here 3" << std::endl;
return moveList.size() == 0;
}
int main() {
initialise_all_databases();
zobrist::initialise_zobrist_keys();
const std::string& startFen ="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" ;
Position board;
Position::set(startFen, board);
std::cout << board;
int totalMovesMade = 0;
while (!checkmate<WHITE>(board) && !checkmate<BLACK>(board)) {
std::cout << "here 0" << std::endl;
if (!checkmate<WHITE>(board)) {
std::cout << "here 4" << std::endl;
Move move = randomMove<WHITE>(board);
std::cout << "here 5" << std::endl;
board.play<WHITE>(move);
totalMovesMade += 1;
std::cout << board << totalMovesMade << std::endl;
}
std::cout << "here 1" << std::endl;
if (!checkmate<BLACK>(board)) {
std::cout << "here 6" << std::endl;
Move move = randomMove<BLACK>(board);
std::cout << "here 7" << std::endl;
board.play<BLACK>(move);
totalMovesMade += 1;
std::cout << board << totalMovesMade << std::endl;
}
std::cout << "here 10" << std::endl;
}
return 0;
}
Sure, you could do something like
// Color Us has the first move in the position
template<Color Us>
void play_random_game(Position& p) {
std::random_device dev;
std::mt19937 rng(dev());
int max_length = 100;
for(int i = 0; i < max_length; i++) {
MoveList<Us> our_list(p);
if(our_list.size()) {
std::uniform_int_distribution<std::mt19937::result_type> dist(0, our_list.size() - 1);
int idx = dist(rng);
Move move = *(our_list.begin() + idx);
p.play<Us>(move);
std::cout << move << "\n";
} else break;
MoveList<~Us> their_list(p);
if(their_list.size()) {
std::uniform_int_distribution<std::mt19937::result_type> dist(0, their_list.size() - 1);
int idx = dist(rng);
Move move = *(their_list.begin() + idx);
p.play<~Us>(move);
std::cout << move << "\n";
} else break;
}
std::cout << p;
}
The issue with playing until the move list is empty is that the program does not currently support the 50 move rule. So technically you could have king vs king ad infinitum.
Also the reason automatic side detection wasn't supported out of the box is because there are advanced move generation and eval techniques that involve playing moves "out of turn"
Hm I've run your code, and If I set the maxmimum length to anything around 300, I get a seg fault. Do you know why that might be?
Also the reason automatic side detection wasn't supported out of the box is because there are advanced move generation and eval techniques that involve playing moves "out of turn"
Also, thats pretty cool, I definitely take a look at that.
The segfault almost definitely comes from UndoInfo history[256]
in position.h
overflowing - increase that to 1000 or something and it should be fine
Ah! That makes a lot of sense. My debugger kept pointing to something in the pawn attacks and left me very confused. Thanks for helping me out!
Ok... final question, I promise. I seem to have come across a bug in the .play()
method. See this reproducible example below.
int main() {
initialise_all_databases();
zobrist::initialise_zobrist_keys();
const std::string& startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
Position p;
Position::set(startFen, p);
const std::string& Line = "c2c4 h7h5 d2d4 h8h7 c1f4 c7c5 d4c5 g7g6 g1h3 d8a5 b1c3 a5c3 d1d2 c3c1";
for (int i = 0; i < Line.size(); i += 5) {
Move nextMove(Line.substr(i, 4));
if (p.turn() == BLACK) {
std::cout << MoveList<BLACK>(p).size() << std::endl;
p.play<BLACK>(nextMove);
}
else {
std::cout << MoveList<WHITE>(p).size() << std::endl;
p.play<WHITE>(nextMove);
}
}
if (p.turn() == BLACK) {
std::cout << MoveList<BLACK>(p).size() << std::endl;
}
else {
std::cout << MoveList<WHITE>(p).size() << std::endl;
}
Position check;
Position::set(p.fen(), check);
if (check.turn() == BLACK) {
std::cout << MoveList<BLACK>(check).size() << std::endl;
}
else {
std::cout << MoveList<WHITE>(check).size() << std::endl;
}
return 0;
}
The position check
has 3 possible total moves (which is correct according to stock fish). The position p
has one more, 4. However, both have the same FEN.
Is this a mistake on how I'm using the library or have I truly come across a mistake in the code?
My use case here is parsing UCI input. I want to start from the default board position, grab the UCI moves in the order the appear, make those moves on the board, and then have my engine use this new board to find the best position. I'm looking at the issues and it seems my issue may be coming from #11 . If that is the case, I'm wondering if there is some alternative I'm not aware of.
UCI input usually looks something like: position startpos moves a2a3 g8h6 g1h3 e7e5 h1g1 d8e7 c2c3 a7a5 h3g5 e7c5 g2g3 e5e4 b2b4 c5e3 g1h1 a8a6 h1g1 b7b5 c3c4 f7f5 d2d4 e3a3 a1a2 a6a7 f2f3 c7c6 c1f4 a7a6 d1c2 a3b3 c2e4 e8d8
which is why I formated the testcase above similarly, not sure how else to go about this.
You need to make sure all capture moves have the CAPTURES
flag enabled, otherwise it leads to bugs (because of that you can't directly parse UCI, as you have found out, and as the other issue mentions)
Hey, I'm trying to write something I think should be pretty simple. Play a random move for each side until checkmate.
This is what I've got going right now, but obviously it doesn't switch sides after white plays. My questions are this
~Us
idea shown inchess-engine.cpp
file but that only works because its recursive, in this iterative approach, I can't see how to do something similar because the Color must be a constant. So I can't do something simple likeColor team = White
and then do something liketeam = ~team
at the end of the loop.side_to_play
variable and since both moveList andp.play
use a position, why not just use theside_to_play
variable inside each position? Seems redundant and a little confusing for that not to be the case.I'm extremely new to C++ (Just a day in!) So if my questions are obvious or trivial sorry in advance!