When I started chess programming, I started with a dramatic goal to create a 3000+ engine. I thought it was easy.
Now, I know what actually is the base of a super strong engine. Its clean, concise, and well written code. Not that I learned that after 30+ years of experience, but it sounds quite obvious to me.
When I think of clean code, two engines come to my mind. Stockfish and iCE chess. Both are clean coded, and take advantages of STL.
Before, I took pride in having lines of code. move.cpp + movegen.cpp = 2000+ lines of code sounded elite. And now, the LESSer lines of code sound more elite. Without comprising on performance, you can use templates to generate sliding and non-sliding moves within one function.
And now, as you see, I've a movegen that's only 263 lines of code (due to classical formatting used by C4Driod).
I started with Silican Chess, and then rewrote it to Chress (not Chesser), and after losing all Chress codedue to display problem with my laptop, started from scratch with Yaka, using g++ with C4Driod on my Andriod
Here's the code:
Code: Select all
// movegen.h : Yaka's Move Generator
#ifndef INC_MOVEGEN_H_
#define INC_MOVEGEN_H_
#include "includes.h"
#include "move.h"
#include "position.h"
class MoveList
{
Move mList[MaxMoves];
uint index;
public:
MoveList()
{
index = 0;
}
MoveList(const Position & pos)
{
index = 0;
genAllMoves(pos);
}
inline void addMove(const Square from, const Square to)
{
mList[index++].move = from | (to << 6);
}
template <Flag flag>
inline void addMove(const Square from, const Square to) {
mList[index++].move = (from | (to << 6) | flag);
}
inline void endList()
{
mList[index].move = 0;
}
inline int legalMoveCount()
{
return index;
}
void dispList()
{
for (int i = 0; (mList[i].move != 0) && i < MaxMoves; ++i)
{
std::cout << "#" << i << ": ";
std::cout << std::hex;
mList[i].dispMove();
std::cout << " encoded as 0x" << mList[i].move;
std::cout << std::endl;
std::cout << std::dec;
}
}
void dispListInSAN(const Position & pos)
{
for (int i = 0; (mList[i].move != 0) && i < MaxMoves; ++i)
{
std::cout << "#" << i << " : ";
// mList[i].dispSAN(pos);
std::cout << " Encoded as " << mList[i].move;
std::cout << std::endl;
}
}
inline move_t getMove(int index)
{
return mList[index].move;
}
inline Move & operator[] (const int index)
{
return mList[index];
}
FORCE_INLINE void genAllMoves(const Position & pos);
template < Square Delta >
FORCE_INLINE void genPromotions(const Bitboard promPawns,
const Bitboard target);
template < PieceType pt, Color GenFor >
FORCE_INLINE void genMoves(const Position & pos,
const Bitboard Target);
template < Color GenFor >
FORCE_INLINE void genKingMoves(const Position & pos,
const Bitboard Target);
template < Color GenFor >
FORCE_INLINE void genPawnMoves(const Position & pos,
const Bitboard Target);
};
template < Color GenFor >
FORCE_INLINE
void MoveList::genPawnMoves(const Position & pos, const Bitboard Target)
{
const Bitboard FreeSquares = ~pos.PieceBB[ALL_PIECES];
const Bitboard pawnsOnRank7 =
pos.PieceBB[(GenFor == WHITE) ? WPAWN : BPAWN]
& ((GenFor == WHITE) ? Rank7Mask : Rank2Mask);
const Bitboard pawnsNotOnRank7 =
pos.PieceBB[(GenFor == WHITE) ? WPAWN : BPAWN]
& ((GenFor == WHITE) ? ~Rank7Mask : ~Rank2Mask);
const Square Right = (GenFor == WHITE) ? DELTA_NE : DELTA_SE;
const Square Left = (GenFor == WHITE) ? DELTA_NW : DELTA_SW;
const Square Up = (GenFor == WHITE) ? DELTA_N : DELTA_S;
const Bitboard enemyPieces = (GenFor == WHITE) ?
pos.ColorBB[BLACK] : pos.ColorBB[WHITE];
const Bitboard Rank3 = (GenFor == WHITE) ? Rank3Mask : Rank6Mask;
Bitboard b1, b2;
Square to;
/*--- Generate Promotions ---*/
genPromotions < Right > (pawnsOnRank7, enemyPieces);
genPromotions < Left > (pawnsOnRank7, enemyPieces);
genPromotions < Up > (pawnsOnRank7, FreeSquares);
/*--- Single and Double Pawn Pushes ---*/
b1 = shift_bb < Up > (pawnsNotOnRank7) & FreeSquares;
b2 = shift_bb < Up > (b1 & Rank3) & FreeSquares;
while (b1) // Single Pawn Pushes
{
to = pop_lsb(&b1);
addMove(to - Up, to);
}
while (b2) // Double Pawn Pushes
{
to = pop_lsb(&b2);
addMove((to - Up - Up), to);
}
/*--- Standard and En-Passant Captures ---*/
b1 = shift_bb < Right > (pawnsNotOnRank7) & enemyPieces;
b2 = shift_bb < Left > (pawnsNotOnRank7) & enemyPieces;
while (b1)
{
to = pop_lsb(&b1);
addMove((to - Right), to);
}
while (b2)
{
to = pop_lsb(&b2);
addMove((to - Left), to);
}
// En-Passant Captures
if ((pos.epSquare() != NO_SQ)
&& (GenFor == WHITE ? (pos.epSquare() > H5) : (pos.epSquare() < A4)))
{
b1 = pawnsNotOnRank7 & AttacksFrom[(GenFor == WHITE) ? BPAWN :
WPAWN][pos.epSquare()];
while (b1) addMove<FLAG_ENPASSANT>(pop_lsb(&b1), pos.epSquare());
}
}
template < Square Delta >
FORCE_INLINE void MoveList::genPromotions(const Bitboard promPawns,
const Bitboard target)
{
Bitboard b;
b = (shift_bb < Delta > (promPawns)) & target;
Square to;
while (b)
{
to = pop_lsb(&b);
addMove<FLAG_QUEEN_PROM>(to - Delta, to);
addMove<FLAG_KNIGHT_PROM>(to - Delta, to);
}
}
template < PieceType pt, Color GenFor >
FORCE_INLINE void MoveList::genMoves(const Position & pos,
const Bitboard Target)
{
Square from;
Bitboard pieceBB, b;
pieceBB = pos.PieceBB[(GenFor == WHITE) ? pt : (pt + (BPAWN - WPAWN))];
while (pieceBB)
{
from = pop_lsb(&pieceBB);
b = pos.attacks_by < pt > (from) & Target;
while (b)
addMove(from, pop_lsb(&b));
}
}
template < Color GenFor >
FORCE_INLINE void MoveList::genKingMoves(const Position & pos,
const Bitboard Target)
{
const Square kingSquare =
lsb(pos.PieceBB[(GenFor == WHITE) ? WKING : BKING]);
const CastlingRight cr = pos.castleStatus < GenFor > ();
const Bitboard KingSideMask = (GenFor == WHITE) ? 0x60 : (0x60ULL << 56);
const Bitboard QueenSideMask = (GenFor == WHITE) ? 0xE : (0xEULL << 56);
Bitboard b = pos.attacks_by < KING > (kingSquare) & Target;
while (b)
addMove(kingSquare, pop_lsb(&b));
if (!(pos.PieceBB[ALL_PIECES] & KingSideMask))
{
if ((cr == OO) || (cr == BOTH_SIDES))
{
if (!(pos.attacks_to < (GenFor == WHITE) ? BLACK : WHITE >
((GenFor == WHITE) ? G1 : G8)))
{
addMove<FLAG_CASTLING>(kingSquare, (GenFor == WHITE) ? G1 : G8);
}
}
}
if (!(pos.PieceBB[ALL_PIECES] & QueenSideMask))
{
if ((cr == OOO) || (cr == BOTH_SIDES))
{
if (!(pos.attacks_to < (GenFor == WHITE) ? BLACK : WHITE >
((GenFor == WHITE) ? C1 : C8)))
{
addMove<FLAG_CASTLING>(kingSquare, (GenFor == WHITE) ? C1 : C8);
}
}
}
}
FORCE_INLINE void MoveList::genAllMoves(const Position & pos)
{
const Bitboard Target = ~pos.ColorBB[pos.sideToMove];
if (pos.sideToMove == WHITE)
{
genPawnMoves < WHITE > (pos, Target);
genMoves < KNIGHT, WHITE > (pos, Target);
genMoves < BISHOP, WHITE > (pos, Target);
genMoves < ROOK, WHITE > (pos, Target);
genMoves < QUEEN, WHITE > (pos, Target);
genKingMoves < WHITE > (pos, Target);
}
else
{
genPawnMoves < BLACK > (pos, Target);
genMoves < KNIGHT, BLACK > (pos, Target);
genMoves < BISHOP, BLACK > (pos, Target);
genMoves < ROOK, BLACK > (pos, Target);
genMoves < QUEEN, BLACK > (pos, Target);
genKingMoves < BLACK > (pos, Target);
}
endList();
}
#endif
Code: Select all
Yaka 0.2.1 by Syed Fahad
Enter a fen String
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1
Enter depth.
5
+---+---+---+---+---+---+---+---+
| r | n | b | q | k | b | n | r |
+---+---+---+---+---+---+---+---+
| p | p | p | p | p | p | p | p |
+---+---+---+---+---+---+---+---+
| | | | | | | | |
+---+---+---+---+---+---+---+---+
| | | | | | | | |
+---+---+---+---+---+---+---+---+
| | | | | | | | |
+---+---+---+---+---+---+---+---+
| | | | | | | | |
+---+---+---+---+---+---+---+---+
| P | P | P | P | P | P | P | P |
+---+---+---+---+---+---+---+---+
| R | N | B | Q | K | B | N | R |
+---+---+---+---+---+---+---+---+
Side To Move: Black
En-Passant Square: (none)
#0: a7a6 encoded as 0xa30
#1: b7b6 encoded as 0xa71
#2: c7c6 encoded as 0xab2
#3: d7d6 encoded as 0xaf3
#4: e7e6 encoded as 0xb34
#5: f7f6 encoded as 0xb75
#6: g7g6 encoded as 0xbb6
#7: h7h6 encoded as 0xbf7
#8: a7a5 encoded as 0x830
#9: b7b5 encoded as 0x871
#10: c7c5 encoded as 0x8b2
#11: d7d5 encoded as 0x8f3
#12: e7e5 encoded as 0x934
#13: f7f5 encoded as 0x975
#14: g7g5 encoded as 0x9b6
#15: h7h5 encoded as 0x9f7
#16: b8a6 encoded as 0xa39
#17: b8c6 encoded as 0xab9
#18: g8f6 encoded as 0xb7e
#19: g8h6 encoded as 0xbfe
Took 0.963855 secs.
perft 5 = 4896998
nodes sec:5080637
It should be not more than much slower than SF on 64-bit systems. No optimization for 32-bits is done, so it's almost 1.3 times slower than SF. Besides, it doesn't use PieceLists and all that.
One more thing, I've turned 14!
Did I mention you have a compile time option to use HQ?