├── .gitignore ├── libs └── unity │ ├── unity.o │ ├── libunity.a │ ├── makefile │ └── unity.c ├── src ├── utils.h ├── fen.h ├── search.h ├── evaluation.h ├── san.h ├── bitboards.h ├── moveorderer.h ├── magics.h ├── bitboards.c ├── zobrist.h ├── tt.h ├── board.h ├── tt.c ├── movegen.h ├── profiling.c ├── utils.c ├── Makefile ├── zobrist.c ├── typedefs.h ├── moveorderer.c ├── san.c ├── search.c ├── uci.c ├── magics.c ├── evaluation.c ├── fen.c ├── board.c ├── perft.c ├── test.c └── movegen.c └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | sophia.out 2 | debugperft.py 3 | sophia 4 | perft 5 | 6 | -------------------------------------------------------------------------------- /libs/unity/unity.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartekspitza/sophia/HEAD/libs/unity/unity.o -------------------------------------------------------------------------------- /libs/unity/libunity.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartekspitza/sophia/HEAD/libs/unity/libunity.a -------------------------------------------------------------------------------- /libs/unity/makefile: -------------------------------------------------------------------------------- 1 | default: unity 2 | 3 | unity: unity.c 4 | gcc -c unity.c 5 | ar rcs libunity.a unity.o 6 | 7 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include "typedefs.h" 5 | 6 | void printMoves(Move moves[], int length); 7 | void printBitboard(Bitboard bb); 8 | void printBits(Bitboard bb); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/fen.h: -------------------------------------------------------------------------------- 1 | #ifndef FEN_H 2 | #define FEN_H 3 | 4 | #define START_FEN "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" 5 | 6 | #include "typedefs.h" 7 | 8 | void setFen(Board* board, char* fen); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/search.h: -------------------------------------------------------------------------------- 1 | #ifndef SEARCH_H 2 | #define SEARCH_H 3 | 4 | #include "typedefs.h" 5 | 6 | extern int SEARCH_NODES_SEARCHED; 7 | extern Move SEARCH_BEST_MOVE; 8 | 9 | int search(Board board, int depth); 10 | 11 | #endif -------------------------------------------------------------------------------- /src/evaluation.h: -------------------------------------------------------------------------------- 1 | #ifndef EVALUATION_H 2 | #define EVALUATION_H 3 | 4 | #include "typedefs.h" 5 | 6 | extern int MAX_EVAL; 7 | extern int MIN_EVAL; 8 | 9 | void initEvaluation(void); 10 | int evaluate(Board board, int result); 11 | #endif -------------------------------------------------------------------------------- /src/san.h: -------------------------------------------------------------------------------- 1 | #ifndef SAN_H 2 | #define SAN_H 3 | 4 | #include "typedefs.h" 5 | 6 | void moveToSan(Move move, char san[]); 7 | void sanToMove(Board board, Move* move, char* san); 8 | void pushSan(Board* board, char* san); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/bitboards.h: -------------------------------------------------------------------------------- 1 | #ifndef BITBOARDS_H 2 | #define BITBOARDS_H 3 | 4 | #include "typedefs.h" 5 | 6 | extern Bitboard SQUARE_BITBOARDS[64]; 7 | extern Bitboard RANK_1; 8 | extern Bitboard RANK_7; 9 | 10 | void initBitboards(void); 11 | 12 | #endif -------------------------------------------------------------------------------- /src/moveorderer.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVEORDERER_H 2 | #define MOVEORDERER_H 3 | 4 | #include "typedefs.h" 5 | #include "tt.h" 6 | 7 | void score_moves(Board board, TTEntry entry, Move moves[], int cmoves); 8 | int select_move(Move moves[], int cmoves); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | Sophia is a chess engine written in C. She supports the standard UCI protocol and can be connected to a UCI compatible GUI such as Cutechess, Arena etc. 4 | 5 | ## UCI Options 6 | Currently doesn't support any UCI options and searches to a fixed depth. 7 | -------------------------------------------------------------------------------- /src/magics.h: -------------------------------------------------------------------------------- 1 | #ifndef MAGICS_H 2 | #define MAGICS_H 3 | 4 | #include "typedefs.h" 5 | 6 | /** 7 | * Magics for the Magic Bitbaord sliding-pieces move generation technique 8 | **/ 9 | 10 | extern Bitboard ROOK_MAGICS[64]; 11 | extern Bitboard BISHOP_MAGICS[64]; 12 | 13 | #endif -------------------------------------------------------------------------------- /src/bitboards.c: -------------------------------------------------------------------------------- 1 | #include "bitboards.h" 2 | 3 | Bitboard SQUARE_BITBOARDS[64]; 4 | Bitboard RANK_1 = 0xFF00; 5 | Bitboard RANK_7 = 0x00FF000000000000; 6 | 7 | void initBitboards(void) { 8 | for (int i = 0; i < 64; i++) { 9 | Bitboard bb = 1ULL << i; 10 | SQUARE_BITBOARDS[i] = bb; 11 | } 12 | } -------------------------------------------------------------------------------- /src/zobrist.h: -------------------------------------------------------------------------------- 1 | #ifndef ZOBRIST_H 2 | #define ZOBRIST_H 3 | 4 | #include "typedefs.h" 5 | 6 | extern Bitboard PIECES[12][64]; 7 | extern Bitboard EN_PASSANT[64]; 8 | extern Bitboard CASTLING[16]; 9 | extern Bitboard WHITE_TO_MOVE; 10 | 11 | void initZobrist(void); 12 | Bitboard hash(Board board); 13 | 14 | #endif -------------------------------------------------------------------------------- /src/tt.h: -------------------------------------------------------------------------------- 1 | #ifndef TT_H 2 | #define TT_H 3 | 4 | #include "typedefs.h" 5 | 6 | typedef struct { 7 | Bitboard zobrist; 8 | int eval; 9 | int nodeType; 10 | int depth; 11 | Move move; 12 | } TTEntry; 13 | 14 | enum NODE_TYPE {EXACT, LOWER, UPPER}; 15 | 16 | extern TTEntry TT_TABLE[]; 17 | extern const int TT_SIZE; 18 | 19 | TTEntry getTTEntry(Bitboard zobrist); 20 | void addTTEntry(Board board, int eval, Move move, int depth, int beta, int alpha); 21 | 22 | #endif -------------------------------------------------------------------------------- /src/board.h: -------------------------------------------------------------------------------- 1 | #ifndef BOARD_H 2 | #define BOARD_H 3 | 4 | #include "typedefs.h" 5 | #include "bitboards.h" 6 | 7 | #define getBit(bitboard, square) (bitboard & SQUARE_BITBOARDS[square]) 8 | #define popBit(bitboard, square) (bitboard & ~SQUARE_BITBOARDS[square]) 9 | #define setBit(bitboard, square) bitboard | SQUARE_BITBOARDS[square] 10 | #define toggleBit(bitboard, square) (bitboard ^ SQUARE_BITBOARDS[square]) 11 | 12 | int result(Board board, Move pseudoLegal[], int length); 13 | void computeOccupancyMasks(Board* board); 14 | void printBoard(Board board); 15 | void pushMove(Board* board, Move move); 16 | 17 | #endif -------------------------------------------------------------------------------- /src/tt.c: -------------------------------------------------------------------------------- 1 | #include "tt.h" 2 | 3 | const int TT_SIZE = 100000; 4 | TTEntry TT_TABLE[100000]; 5 | 6 | TTEntry getTTEntry(Bitboard zobrist) { 7 | return TT_TABLE[zobrist % TT_SIZE]; 8 | } 9 | 10 | void addTTEntry(Board board, int eval, Move move, int depth, int beta, int alpha) { 11 | TTEntry entry; 12 | 13 | if (eval <= alpha) 14 | entry.nodeType = UPPER; 15 | else if (eval >= beta) 16 | entry.nodeType = LOWER; 17 | else 18 | entry.nodeType = EXACT; 19 | 20 | entry.eval = eval; 21 | entry.move = move; 22 | entry.depth = depth; 23 | entry.zobrist = board.hash; 24 | 25 | TT_TABLE[entry.zobrist % TT_SIZE] = entry; 26 | } -------------------------------------------------------------------------------- /src/movegen.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVEGEN_H 2 | #define MOVEGEN_H 3 | 4 | #include 5 | #include "typedefs.h" 6 | #include "magics.h" 7 | 8 | extern Bitboard PAWN_START_WHITE; 9 | extern Bitboard PAWN_START_BLACK; 10 | extern Bitboard PAWN_W_ATTACKS_EAST[64]; 11 | extern Bitboard PAWN_W_ATTACKS_WEST[64]; 12 | extern Bitboard PAWN_B_ATTACKS_EAST[64]; 13 | extern Bitboard PAWN_B_ATTACKS_WEST[64]; 14 | extern Bitboard KNIGHT_MOVEMENT[64]; 15 | extern Bitboard BISHOP_MOVEMENT[64]; 16 | extern Bitboard ROOK_MOVEMENT[64]; 17 | extern Bitboard KING_MOVEMENT[64]; 18 | 19 | void initMoveGeneration(void); 20 | int legalMoves(Board* board, Move moves[]); 21 | int bitScanForward(Bitboard* bb); 22 | bool isSquareAttacked(Board board, int square); 23 | Bitboard getRookAttacks(int square, Bitboard occupancy); 24 | Bitboard getBishopAttacks(int square, Bitboard occupancy); 25 | 26 | #endif -------------------------------------------------------------------------------- /src/profiling.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "board.h" 6 | #include "fen.h" 7 | #include "search.h" 8 | #include "movegen.h" 9 | #include "evaluation.h" 10 | #include "zobrist.h" 11 | #include "san.h" 12 | #include "bitboards.h" 13 | #include "tt.h" 14 | 15 | 16 | int main(void) { 17 | initBitboards(); 18 | initEvaluation(); 19 | initMoveGeneration(); 20 | initZobrist(); 21 | 22 | Board board; 23 | setFen(&board, START_FEN); 24 | 25 | 26 | int depth = 6; 27 | Move bestMove; 28 | 29 | clock_t start = clock(); 30 | for (int i = 0; i < 3;i++) { 31 | int eval = search(board, depth, &bestMove); 32 | memset(TT_TABLE, 0, sizeof(TTEntry) * TT_SIZE); 33 | } 34 | double elapsed = (double) (clock() - start) / CLOCKS_PER_SEC; 35 | 36 | 37 | printf("Elapsed: %.2fs\n", elapsed); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "utils.h" 4 | #include "san.h" 5 | 6 | void printMoves(Move moves[], int length) { 7 | printf("Moves: %d\n", length); 8 | for(int i = 0;i analysis.txt 10 | rm profiling 11 | rm gmon.out 12 | 13 | test: test.c 14 | @clang test.c -o test bitboards.c evaluation.c san.c zobrist.c board.c fen.c utils.c movegen.c magics.c -L../libs/unity -lunity 15 | @./test 16 | @rm test 17 | 18 | # Fully optimized perft target using LTO and PGO. 19 | perft: perft.c 20 | @echo "Building instrumented binary for profile generation..." 21 | clang -O2 -flto -fprofile-generate perft.c bitboards.c san.c zobrist.c board.c fen.c utils.c movegen.c magics.c -o perft-gen 22 | @echo "Running instrumented binary with depth flag..." 23 | LLVM_PROFILE_FILE=default.profraw ./perft-gen -d 5 24 | @echo "Merging profile data..." 25 | llvm-profdata merge -output=default.profdata default.profraw 26 | @echo "Building fully optimized perft binary..." 27 | clang -O2 -flto -fprofile-use perft.c bitboards.c san.c zobrist.c board.c fen.c utils.c movegen.c magics.c -o perft 28 | @echo "Cleaning up temporary files..." 29 | rm perft-gen default.profraw 30 | -------------------------------------------------------------------------------- /src/zobrist.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "zobrist.h" 3 | 4 | Bitboard randomBitboard(); 5 | 6 | Bitboard PIECES[12][64]; 7 | Bitboard EN_PASSANT[64]; 8 | Bitboard CASTLING[16]; 9 | Bitboard WHITE_TO_MOVE; 10 | 11 | Bitboard hash(Board board) { 12 | Bitboard hash = 0LL; 13 | 14 | // Castling rights 15 | hash ^= CASTLING[board.castling]; 16 | 17 | // Pieces 18 | for (int i = 0; i < 12; i++) { 19 | Bitboard bb = *(&(board.pawn_W)+i); 20 | 21 | while (bb) { 22 | int square = __builtin_ctzll(bb); 23 | hash ^= PIECES[i][square]; 24 | bb &= bb -1; 25 | } 26 | } 27 | 28 | // En passant 29 | if (board.epSquare != -1) { 30 | hash ^= EN_PASSANT[board.epSquare]; 31 | } 32 | 33 | // Side to move 34 | if (board.turn) { 35 | hash ^= WHITE_TO_MOVE; 36 | } 37 | 38 | return hash; 39 | } 40 | 41 | void initZobrist() { 42 | for (int i = 0; i < 12; i++) for (int j = 0; j < 64;j++) PIECES[i][j] = randomBitboard(); 43 | for (int i = 0; i < 64; i++) EN_PASSANT[i] = randomBitboard(); 44 | for (int i = 0; i < 16; i++) CASTLING[i] = randomBitboard(); 45 | WHITE_TO_MOVE = randomBitboard(); 46 | } 47 | 48 | Bitboard randomBitboard(void) { 49 | Bitboard r = 0; 50 | for (int i=0; i < 64; i++) { 51 | Bitboard tmp = (Bitboard) rand() % 2; 52 | r |= tmp << i; 53 | } 54 | return r; 55 | } 56 | -------------------------------------------------------------------------------- /src/typedefs.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPEDEFS_H 2 | #define TYPEDEFS_H 3 | 4 | #define BLACK 0 5 | #define WHITE 1 6 | 7 | #include 8 | #include 9 | 10 | typedef uint64_t Bitboard; 11 | typedef struct { 12 | Bitboard pawn_W; 13 | Bitboard knight_W; 14 | Bitboard bishop_W; 15 | Bitboard rook_W; 16 | Bitboard queen_W; 17 | Bitboard king_W; 18 | Bitboard pawn_B; 19 | Bitboard knight_B; 20 | Bitboard bishop_B; 21 | Bitboard rook_B; 22 | Bitboard queen_B; 23 | Bitboard king_B; 24 | int turn; 25 | int castling; 26 | int epSquare; 27 | int halfmoves; 28 | int fullmoves; 29 | int whiteKingSq; 30 | int blackKingSq; 31 | Bitboard occupancy; 32 | Bitboard occupancyWhite; 33 | Bitboard occupancyBlack; 34 | Bitboard hash; 35 | Bitboard attacks; // The attack mask of the other side, i.e. when it's white's turn, this is the black attack mask 36 | } Board; 37 | 38 | typedef struct { 39 | int fromSquare; 40 | int toSquare; 41 | int promotion; 42 | int castle; 43 | int validation; 44 | int pieceType; 45 | int score; 46 | bool exhausted; 47 | } Move; 48 | 49 | enum PIECES { 50 | PAWN_W, KNIGHT_W, BISHOP_W, ROOK_W, QUEEN_W, KING_W, 51 | PAWN_B, KNIGHT_B, BISHOP_B, ROOK_B, QUEEN_B, KING_B 52 | }; 53 | 54 | enum SQUARES { 55 | H1, G1, F1, E1, D1, C1, B1, A1, 56 | H2, G2, F2, E2, D2, C2, B2, A2, 57 | H3, G3, F3, E3, D3, C3, B3, A3, 58 | H4, G4, F4, E4, D4, C4, B4, A4, 59 | H5, G5, F5, E5, D5, C5, B5, A5, 60 | H6, G6, F6, E6, D6, C6, B6, A6, 61 | H7, G7, F7, E7, D7, C7, B7, A7, 62 | H8, G8, F8, E8, D8, C8, B8, A8 63 | }; 64 | 65 | enum MOVE_VALIDAITON {NOT_VALIDATED, LEGAL, ILLEGAL}; 66 | 67 | enum GAME_RESULT {UN_DETERMINED, WHITE_WIN, BLACK_WIN, DRAW}; 68 | 69 | enum CASTLING {K=1, Q=2, k=4, q=8}; 70 | 71 | enum FILES {H, G, F, E, D, C, B, A}; 72 | 73 | extern char SQUARE_NAMES[64][3]; 74 | 75 | #endif -------------------------------------------------------------------------------- /src/moveorderer.c: -------------------------------------------------------------------------------- 1 | #include "moveorderer.h" 2 | #include "bitboards.h" 3 | 4 | const int VICTIMS[] = { 5 | 100 * 16, 6 | 320 * 16, 7 | 330 * 16, 8 | 500 * 16, 9 | 900 * 16, 10 | }; 11 | const int ATTACKERS[] = { 12 | 100, 13 | 200, 14 | 300, 15 | 400, 16 | 500, 17 | 600, 18 | 100, 200, 300, 400, 500, 600, // Same as first 6 for easier indexing 19 | }; 20 | 21 | const int MAX_MOVE_SCORE = 100000; 22 | const int MVV_LVA_PAWN_EXCHANGE = (100 * 16) - 100; 23 | 24 | 25 | bool moves_are_equal(Move a, Move b); 26 | 27 | void score_moves(Board board, TTEntry entry, Move moves[], int cmoves) { 28 | Move pvMove; 29 | if (board.hash == entry.zobrist && entry.nodeType < UPPER) { 30 | pvMove = entry.move; 31 | } 32 | 33 | for (int i = 0; i < cmoves; i++) { 34 | Move* move = &moves[i]; 35 | 36 | // Pv Move 37 | if (moves_are_equal(pvMove, *move)) { 38 | move->score = MAX_MOVE_SCORE; 39 | 40 | // MVV_LVA 41 | } else { 42 | Bitboard enemyOcc = board.turn ? board.occupancyBlack : board.occupancyWhite; 43 | bool isCapture = enemyOcc & SQUARE_BITBOARDS[move->toSquare]; 44 | bool isEnPassant = board.epSquare == move->toSquare; 45 | 46 | if (isEnPassant) { 47 | moves[i].score = MVV_LVA_PAWN_EXCHANGE; 48 | } else if (isCapture) { 49 | // Find captured piece type 50 | int capturedPiece; 51 | Bitboard toSquare = SQUARE_BITBOARDS[moves[i].toSquare]; 52 | Bitboard* bb = board.turn ? &board.pawn_B : &board.pawn_W; 53 | for (int i = 0; i < 5; i++) { 54 | if (toSquare & *bb) { 55 | capturedPiece = i; 56 | break; 57 | } 58 | bb++; 59 | } 60 | 61 | move->score = VICTIMS[capturedPiece] - ATTACKERS[move->pieceType]; 62 | } 63 | } 64 | } 65 | } 66 | 67 | int select_move(Move moves[], int cmoves) { 68 | int bestScore = 0; 69 | int indx = -1; 70 | 71 | for (int i = 0; i < cmoves; i++) { 72 | if (!moves[i].exhausted && moves[i].score >= bestScore) { 73 | bestScore = moves[i].score; 74 | indx = i; 75 | } 76 | } 77 | 78 | if (indx != -1) { 79 | moves[indx].exhausted = true; 80 | } 81 | 82 | return indx; 83 | } 84 | 85 | bool moves_are_equal(Move a, Move b) { 86 | return a.fromSquare == b.fromSquare && a.toSquare == b.toSquare && a.promotion == b.promotion; 87 | } 88 | -------------------------------------------------------------------------------- /src/san.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "san.h" 4 | #include "board.h" 5 | 6 | void sanToMove(Board board, Move* move, char* san); 7 | void moveToSan(Move move, char san[]); 8 | 9 | 10 | void pushSan(Board* board, char* san) { 11 | Move move; 12 | sanToMove(*board, &move, san); 13 | pushMove(board, move); 14 | } 15 | 16 | void sanToMove(Board board, Move* move, char* san) { 17 | int fromFile = 'h' - san[0]; 18 | int fromRank = atoi(&san[1]) - 1; 19 | int toFile = 'h' - san[2]; 20 | int toRank = atoi(&san[3]) - 1; 21 | move->fromSquare = 8 * (fromRank) + fromFile; 22 | move->toSquare = 8 * (toRank) + toFile; 23 | move->castle = 0; 24 | move->promotion = -1; 25 | 26 | if (move->fromSquare == E1 && move->toSquare == G1 && (board.castling & K)) { 27 | move->castle = K; 28 | } 29 | else if (move->fromSquare == E1 && move->toSquare == C1 && (board.castling & Q)) { 30 | move->castle = Q; 31 | } 32 | else if (move->fromSquare == E8 && move->toSquare == G8 && (board.castling & k)) { 33 | move->castle = k; 34 | } 35 | else if (move->fromSquare == E8 && move->toSquare == C8 && (board.castling & q)) { 36 | move->castle = q; 37 | } 38 | 39 | if (san[4] == 'q') { 40 | move->promotion = board.turn ? QUEEN_W : QUEEN_B; 41 | } 42 | else if (san[4] == 'r') { 43 | move->promotion = board.turn ? ROOK_W : ROOK_B; 44 | } 45 | else if (san[4] == 'n') { 46 | move->promotion = board.turn ? KNIGHT_W : KNIGHT_B; 47 | } 48 | else if (san[4] == 'b') { 49 | move->promotion = board.turn ? BISHOP_W : BISHOP_B; 50 | } 51 | 52 | 53 | // Calculate what piece moved 54 | for (int i = 0; i < 12; i++) { 55 | Bitboard bb = *(&board.pawn_W+i); 56 | if (getBit(bb, move->fromSquare)) { 57 | move->pieceType = i; 58 | break; 59 | } 60 | } 61 | } 62 | 63 | void moveToSan(Move move, char san[]) { 64 | if (move.castle) { 65 | if (move.castle == K) { san = strcpy(san, "e1g1"); } 66 | else if (move.castle == k) { san = strcpy(san, "e8g8"); } 67 | else if (move.castle == Q) { san = strcpy(san, "e1c1"); } 68 | else if (move.castle == q) { san = strcpy(san, "e8c8"); } 69 | 70 | } else { 71 | san[0] = SQUARE_NAMES[move.fromSquare][0]; 72 | san[1] = SQUARE_NAMES[move.fromSquare][1]; 73 | san[2] = SQUARE_NAMES[move.toSquare][0]; 74 | san[3] = SQUARE_NAMES[move.toSquare][1]; 75 | 76 | switch (move.promotion) { 77 | case QUEEN_W: 78 | case QUEEN_B: 79 | san[4] = 'q'; 80 | break; 81 | case ROOK_W: 82 | case ROOK_B: 83 | san[4] = 'r'; 84 | break; 85 | case BISHOP_W: 86 | case BISHOP_B: 87 | san[4] = 'b'; 88 | break; 89 | case KNIGHT_W: 90 | case KNIGHT_B: 91 | san[4] = 'n'; 92 | break; 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /src/search.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "search.h" 4 | #include "evaluation.h" 5 | #include "board.h" 6 | #include "movegen.h" 7 | #include "tt.h" 8 | #include "zobrist.h" 9 | #include "moveorderer.h" 10 | 11 | #define min(a, b) (a < b) ? a : b; 12 | #define max(a, b) (a > b) ? a : b; 13 | 14 | int alphabeta(Board board, int depth, int alpha, int beta); 15 | 16 | // Search variables 17 | int SEARCH_NODES_SEARCHED = 0; 18 | Move SEARCH_BEST_MOVE; 19 | int _DEPTH; 20 | 21 | 22 | int search(Board board, int depth) { 23 | // Reset previous search results 24 | memset(&SEARCH_BEST_MOVE, 0, sizeof(Move)); 25 | SEARCH_NODES_SEARCHED = 0; 26 | _DEPTH = depth; 27 | 28 | int eval = alphabeta(board, depth, MIN_EVAL, MAX_EVAL); 29 | return eval; 30 | } 31 | 32 | int alphabeta(Board board, int depth, int alpha, int beta) { 33 | SEARCH_NODES_SEARCHED++; 34 | int origAlpha = alpha; 35 | 36 | // TT table lookup 37 | TTEntry entry = getTTEntry(board.hash); 38 | if (board.hash == entry.zobrist && entry.depth >= depth) { 39 | 40 | if (entry.nodeType == EXACT) { 41 | 42 | if (depth == _DEPTH) 43 | SEARCH_BEST_MOVE = entry.move; 44 | 45 | return entry.eval; 46 | } else if (entry.nodeType == LOWER) { 47 | alpha = max(alpha, entry.eval); 48 | } else if (entry.nodeType == UPPER) { 49 | beta = min(beta, entry.eval); 50 | } 51 | 52 | if (alpha >= beta) { 53 | if (depth == _DEPTH) 54 | SEARCH_BEST_MOVE = entry.move; 55 | 56 | return entry.eval; 57 | } 58 | } 59 | 60 | Move moves[256]; 61 | int cmoves = legalMoves(&board, moves); 62 | 63 | int res = result(board, moves, cmoves); 64 | if (res) { 65 | int eval = evaluate(board, res); 66 | 67 | if (res != DRAW) 68 | eval += (1 * _DEPTH - depth) * (board.turn ? 1 : -1); 69 | 70 | return eval * (board.turn ? 1 : -1); 71 | } else if (depth == 0) { 72 | return evaluate(board, res) * (board.turn ? 1 : -1); 73 | } 74 | 75 | int nextMove; 76 | int eval = MIN_EVAL; 77 | Move best_move; 78 | score_moves(board, entry, moves, cmoves); 79 | 80 | while (nextMove = select_move(moves, cmoves), nextMove != -1) { 81 | if (moves[nextMove].validation == LEGAL) { 82 | Board child = board; 83 | pushMove(&child, moves[nextMove]); 84 | int childEval = -alphabeta(child, depth-1, -beta, -alpha); 85 | 86 | if (childEval > eval) { 87 | eval = childEval; 88 | best_move = moves[nextMove]; 89 | 90 | if (depth == _DEPTH) 91 | SEARCH_BEST_MOVE = best_move; 92 | } 93 | 94 | alpha = max(alpha, childEval); 95 | 96 | if (alpha >= beta) 97 | break; 98 | } 99 | } 100 | 101 | addTTEntry(board, eval, best_move, depth, beta, origAlpha); 102 | 103 | return eval; 104 | } 105 | -------------------------------------------------------------------------------- /src/uci.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "board.h" 6 | #include "fen.h" 7 | #include "search.h" 8 | #include "movegen.h" 9 | #include "evaluation.h" 10 | #include "zobrist.h" 11 | #include "san.h" 12 | #include "bitboards.h" 13 | 14 | void parsePosition(char *command, Board* board); 15 | void getBestMove(Board board); 16 | void printEngineInfo(void); 17 | 18 | char* AUTHOR = "Bartek Spitza"; 19 | char* ENGINE_NAME = "Sophia"; 20 | 21 | int main(void) { 22 | // Init engine 23 | initBitboards(); 24 | initZobrist(); 25 | initMoveGeneration(); 26 | initEvaluation(); 27 | // Init engine 28 | 29 | setbuf(stdin, NULL); 30 | setbuf(stdout, NULL); 31 | 32 | int bufferSize = 2000; 33 | char input[bufferSize]; 34 | 35 | Board board = {}; 36 | while(1) { 37 | 38 | memset(input, 0, sizeof(input)); 39 | fflush(stdout); 40 | 41 | if (!fgets(input, bufferSize, stdin)) 42 | continue; 43 | 44 | if (input[0] == '\n') 45 | continue; 46 | 47 | if (strncmp(input, "isready", 7) == 0) { 48 | printf("readyok\n"); 49 | continue; 50 | } 51 | 52 | else if (strncmp(input, "position", 8) == 0) 53 | parsePosition(input, &board); 54 | 55 | else if (strncmp(input, "ucinewgame", 10) == 0) 56 | parsePosition("position startpos", &board); 57 | 58 | else if (strncmp(input, "go", 2) == 0) 59 | getBestMove(board); 60 | 61 | else if (strncmp(input, "quit", 4) == 0) 62 | break; 63 | 64 | else if (strncmp(input, "uci", 3) == 0) 65 | printEngineInfo(); 66 | } 67 | 68 | return 0; 69 | } 70 | 71 | void parsePosition(char *command, Board* board) { 72 | // shift pointer to the right where next token begins 73 | 74 | command += 9; 75 | char *current_char = command; 76 | 77 | setFen(board, START_FEN); 78 | 79 | current_char = strstr(command, "fen"); 80 | if (current_char != NULL) { 81 | current_char += 4; 82 | setFen(board, current_char); 83 | } 84 | 85 | char* san = strstr(command, "moves"); 86 | 87 | if (san != NULL) { 88 | san += 6; 89 | 90 | while(*san) { 91 | pushSan(board, san); 92 | 93 | // Move pointer to end of current move 94 | while (*san && *san != ' ') san++; 95 | 96 | // Move pointer to beginning of next move 97 | san++; 98 | } 99 | } 100 | } 101 | 102 | void getBestMove(Board board) { 103 | int depth = 7; 104 | int nodesSearched = 0; 105 | 106 | clock_t start = clock(); 107 | int eval = search(board, depth); 108 | clock_t end = clock(); 109 | double time_spent = (double) (end - start) / CLOCKS_PER_SEC * 1000; 110 | 111 | printf("info depth %d time %.0f nodes %d score cp %d\n", depth, time_spent, SEARCH_NODES_SEARCHED, eval); 112 | 113 | char san[6]; 114 | memset(san, 0, sizeof(san)); 115 | moveToSan(SEARCH_BEST_MOVE, san); 116 | printf("bestmove %s\n", san); 117 | } 118 | 119 | void printEngineInfo(void) { 120 | printf("id name %s\n", ENGINE_NAME); 121 | printf("id author %s\n", AUTHOR); 122 | printf("uciok\n"); 123 | } 124 | -------------------------------------------------------------------------------- /src/magics.c: -------------------------------------------------------------------------------- 1 | #include "magics.h" 2 | 3 | Bitboard ROOK_MAGICS[64] = { 4 | 0xa8002c000108020ULL, 5 | 0x6c00049b0002001ULL, 6 | 0x100200010090040ULL, 7 | 0x2480041000800801ULL, 8 | 0x280028004000800ULL, 9 | 0x900410008040022ULL, 10 | 0x280020001001080ULL, 11 | 0x2880002041000080ULL, 12 | 0xa000800080400034ULL, 13 | 0x4808020004000ULL, 14 | 0x2290802004801000ULL, 15 | 0x411000d00100020ULL, 16 | 0x402800800040080ULL, 17 | 0xb000401004208ULL, 18 | 0x2409000100040200ULL, 19 | 0x1002100004082ULL, 20 | 0x22878001e24000ULL, 21 | 0x1090810021004010ULL, 22 | 0x801030040200012ULL, 23 | 0x500808008001000ULL, 24 | 0xa08018014000880ULL, 25 | 0x8000808004000200ULL, 26 | 0x201008080010200ULL, 27 | 0x801020000441091ULL, 28 | 0x800080204005ULL, 29 | 0x1040200040100048ULL, 30 | 0x120200402082ULL, 31 | 0xd14880480100080ULL, 32 | 0x12040280080080ULL, 33 | 0x100040080020080ULL, 34 | 0x9020010080800200ULL, 35 | 0x813241200148449ULL, 36 | 0x491604001800080ULL, 37 | 0x100401000402001ULL, 38 | 0x4820010021001040ULL, 39 | 0x400402202000812ULL, 40 | 0x209009005000802ULL, 41 | 0x810800601800400ULL, 42 | 0x4301083214000150ULL, 43 | 0x204026458e001401ULL, 44 | 0x40204000808000ULL, 45 | 0x8001008040010020ULL, 46 | 0x8410820820420010ULL, 47 | 0x1003001000090020ULL, 48 | 0x804040008008080ULL, 49 | 0x12000810020004ULL, 50 | 0x1000100200040208ULL, 51 | 0x430000a044020001ULL, 52 | 0x280009023410300ULL, 53 | 0xe0100040002240ULL, 54 | 0x200100401700ULL, 55 | 0x2244100408008080ULL, 56 | 0x8000400801980ULL, 57 | 0x2000810040200ULL, 58 | 0x8010100228810400ULL, 59 | 0x2000009044210200ULL, 60 | 0x4080008040102101ULL, 61 | 0x40002080411d01ULL, 62 | 0x2005524060000901ULL, 63 | 0x502001008400422ULL, 64 | 0x489a000810200402ULL, 65 | 0x1004400080a13ULL, 66 | 0x4000011008020084ULL, 67 | 0x26002114058042ULL, 68 | }; 69 | 70 | // bishop magic number 71 | Bitboard BISHOP_MAGICS[64] = { 72 | 0x89a1121896040240ULL, 73 | 0x2004844802002010ULL, 74 | 0x2068080051921000ULL, 75 | 0x62880a0220200808ULL, 76 | 0x4042004000000ULL, 77 | 0x100822020200011ULL, 78 | 0xc00444222012000aULL, 79 | 0x28808801216001ULL, 80 | 0x400492088408100ULL, 81 | 0x201c401040c0084ULL, 82 | 0x840800910a0010ULL, 83 | 0x82080240060ULL, 84 | 0x2000840504006000ULL, 85 | 0x30010c4108405004ULL, 86 | 0x1008005410080802ULL, 87 | 0x8144042209100900ULL, 88 | 0x208081020014400ULL, 89 | 0x4800201208ca00ULL, 90 | 0xf18140408012008ULL, 91 | 0x1004002802102001ULL, 92 | 0x841000820080811ULL, 93 | 0x40200200a42008ULL, 94 | 0x800054042000ULL, 95 | 0x88010400410c9000ULL, 96 | 0x520040470104290ULL, 97 | 0x1004040051500081ULL, 98 | 0x2002081833080021ULL, 99 | 0x400c00c010142ULL, 100 | 0x941408200c002000ULL, 101 | 0x658810000806011ULL, 102 | 0x188071040440a00ULL, 103 | 0x4800404002011c00ULL, 104 | 0x104442040404200ULL, 105 | 0x511080202091021ULL, 106 | 0x4022401120400ULL, 107 | 0x80c0040400080120ULL, 108 | 0x8040010040820802ULL, 109 | 0x480810700020090ULL, 110 | 0x102008e00040242ULL, 111 | 0x809005202050100ULL, 112 | 0x8002024220104080ULL, 113 | 0x431008804142000ULL, 114 | 0x19001802081400ULL, 115 | 0x200014208040080ULL, 116 | 0x3308082008200100ULL, 117 | 0x41010500040c020ULL, 118 | 0x4012020c04210308ULL, 119 | 0x208220a202004080ULL, 120 | 0x111040120082000ULL, 121 | 0x6803040141280a00ULL, 122 | 0x2101004202410000ULL, 123 | 0x8200000041108022ULL, 124 | 0x21082088000ULL, 125 | 0x2410204010040ULL, 126 | 0x40100400809000ULL, 127 | 0x822088220820214ULL, 128 | 0x40808090012004ULL, 129 | 0x910224040218c9ULL, 130 | 0x402814422015008ULL, 131 | 0x90014004842410ULL, 132 | 0x1000042304105ULL, 133 | 0x10008830412a00ULL, 134 | 0x2520081090008908ULL, 135 | 0x40102000a0a60140ULL, 136 | }; -------------------------------------------------------------------------------- /src/evaluation.c: -------------------------------------------------------------------------------- 1 | #include "evaluation.h" 2 | #include "board.h" 3 | 4 | int PIECE_VALUES[] = { 5 | 100, 6 | 320, 7 | 330, 8 | 500, 9 | 900, 10 | 2000, 11 | -100, 12 | -320, 13 | -330, 14 | -500, 15 | -900, 16 | -2000 17 | }; 18 | 19 | 20 | int PAWN_W_PST[] = { 21 | 0, 0, 0, 0, 0, 0, 0, 0, 22 | 5, 10, 10,-20,-20, 10, 10, 5, 23 | 5, -5,-10, 0, 0,-10, -5, 5, 24 | 0, 0, 0, 20, 20, 0, 0, 0, 25 | 5, 5, 10, 25, 25, 10, 5, 5, 26 | 10, 10, 20, 30, 30, 20, 10, 10, 27 | 50, 50, 50, 50, 50, 50, 50, 50, 28 | 0, 0, 0, 0, 0, 0, 0, 0 29 | }; 30 | int KNIGHT_W_PST[] = { 31 | -50,-40,-30,-30,-30,-30,-40,-50, 32 | -40,-20, 0, 5, 5, 0,-20,-40, 33 | -30, 5, 10, 15, 15, 10, 5,-30, 34 | -30, 0, 15, 20, 20, 15, 0,-30, 35 | -30, 5, 15, 20, 20, 15, 5,-30, 36 | -30, 0, 10, 15, 15, 10, 0,-30, 37 | -40,-20, 0, 0, 0, 0,-20,-40, 38 | -50,-40,-30,-30,-30,-30,-40,-50 39 | }; 40 | 41 | int BISHOP_W_PST[] = { 42 | -20,-10,-10,-10,-10,-10,-10,-20, 43 | -10, 5, 0, 0, 0, 0, 5,-10, 44 | -10, 10, 10, 10, 10, 10, 10,-10, 45 | -10, 0, 10, 10, 10, 10, 0,-10, 46 | -10, 5, 5, 10, 10, 5, 5,-10, 47 | -10, 0, 5, 10, 10, 5, 0,-10, 48 | -10, 0, 0, 0, 0, 0, 0,-10, 49 | -20,-10,-10,-10,-10,-10,-10,-20 50 | }; 51 | int ROOK_W_PST[] = { 52 | 0, 0, 5, 10, 10, 5, 0, 0, 53 | -5, 0, 0, 0, 0, 0, 0, -5, 54 | -5, 0, 0, 0, 0, 0, 0, -5, 55 | -5, 0, 0, 0, 0, 0, 0, -5, 56 | -5, 0, 0, 0, 0, 0, 0, -5, 57 | -5, 0, 0, 0, 0, 0, 0, -5, 58 | 5, 10, 10, 10, 10, 10, 10, 5, 59 | 0, 0, 0, 0, 0, 0, 0, 0, 60 | }; 61 | int QUEEN_W_PST[] = { 62 | -20,-10,-10, -5, -5,-10,-10,-20 63 | -10, 0, 5, 0, 0, 0, 0,-10, 64 | -10, 5, 5, 5, 5, 5, 0,-10, 65 | 0, 0, 5, 5, 5, 5, 0, -5, 66 | -5, 0, 5, 5, 5, 5, 0, -5, 67 | -10, 0, 5, 5, 5, 5, 0,-10, 68 | -10, 0, 0, 0, 0, 0, 0,-10, 69 | -20,-10,-10, -5, -5,-10,-10,-20, 70 | }; 71 | int KING_W_PST[] = { 72 | 20, 30, 10, 0, 0, 10, 30, 20, 73 | 20, 20, 0, 0, 0, 0, 20, 20, 74 | -10, -20, -20, -20, -20, -20, -20, -10, 75 | -20, -30, -30, -40, -40, -30, -30, -20, 76 | -30, -40, -40, -50, -50, -40, -40, -30, 77 | -30, -40, -40, -50, -50, -40, -40, -30, 78 | -30, -40, -40, -50, -50, -40, -40, -30, 79 | -30, -40, -40, -50, -50, -40, -40, -30, 80 | }; 81 | 82 | int PAWN_B_PST[64]; 83 | int KNIGHT_B_PST[64]; 84 | int BISHOP_B_PST[64]; 85 | int ROOK_B_PST[64]; 86 | int QUEEN_B_PST[64]; 87 | int KING_B_PST[64]; 88 | 89 | int MIN_EVAL = -100000; 90 | int MAX_EVAL = 100000; 91 | 92 | void initEvaluation(void) { 93 | for (int i = 0; i < 64; i++) { 94 | PAWN_B_PST[i] = PAWN_W_PST[63-i]; 95 | KNIGHT_B_PST[i] = KNIGHT_W_PST[63-i]; 96 | BISHOP_B_PST[i] = BISHOP_W_PST[63-i]; 97 | ROOK_B_PST[i] = ROOK_W_PST[63-i]; 98 | QUEEN_B_PST[i] = QUEEN_W_PST[63-i]; 99 | KING_B_PST[i] = KING_W_PST[63-i]; 100 | } 101 | } 102 | 103 | int evaluate(Board board, int result) { 104 | if (result == DRAW) return 0; 105 | else if (result == WHITE_WIN) return MAX_EVAL; 106 | else if (result == BLACK_WIN) return MIN_EVAL; 107 | 108 | int eval = 0; 109 | 110 | while (board.pawn_W) { 111 | int sq = __builtin_ctzll(board.pawn_W); 112 | eval += PIECE_VALUES[PAWN_W]; 113 | eval += PAWN_W_PST[sq]; 114 | board.pawn_W &= board.pawn_W -1 ; 115 | } 116 | while (board.knight_W) { 117 | int sq = __builtin_ctzll(board.knight_W); 118 | eval += PIECE_VALUES[KNIGHT_W]; 119 | eval += KNIGHT_W_PST[sq]; 120 | board.knight_W &= board.knight_W -1 ; 121 | } 122 | while (board.bishop_W) { 123 | int sq = __builtin_ctzll(board.bishop_W); 124 | eval += PIECE_VALUES[BISHOP_W]; 125 | eval += BISHOP_W_PST[sq]; 126 | board.bishop_W &= board.bishop_W -1 ; 127 | } 128 | while (board.rook_W) { 129 | int sq = __builtin_ctzll(board.rook_W); 130 | eval += PIECE_VALUES[ROOK_W]; 131 | eval += ROOK_W_PST[sq]; 132 | board.rook_W &= board.rook_W -1 ; 133 | } 134 | while (board.queen_W) { 135 | int sq = __builtin_ctzll(board.queen_W); 136 | eval += PIECE_VALUES[QUEEN_W]; 137 | eval += QUEEN_W_PST[sq]; 138 | board.queen_W &= board.queen_W -1 ; 139 | } 140 | while (board.pawn_B) { 141 | int sq = __builtin_ctzll(board.pawn_B); 142 | eval += PIECE_VALUES[PAWN_B]; 143 | eval -= PAWN_B_PST[sq]; 144 | board.pawn_B &= board.pawn_B -1 ; 145 | } 146 | while (board.knight_B) { 147 | int sq = __builtin_ctzll(board.knight_B); 148 | eval += PIECE_VALUES[KNIGHT_B]; 149 | eval -= KNIGHT_B_PST[sq]; 150 | board.knight_B &= board.knight_B -1 ; 151 | } 152 | while (board.bishop_B) { 153 | int sq = __builtin_ctzll(board.bishop_B); 154 | eval += PIECE_VALUES[BISHOP_B]; 155 | eval -= BISHOP_B_PST[sq]; 156 | board.bishop_B &= board.bishop_B -1 ; 157 | } 158 | while (board.rook_B) { 159 | int sq = __builtin_ctzll(board.rook_B); 160 | eval += PIECE_VALUES[ROOK_B]; 161 | eval -= ROOK_B_PST[sq]; 162 | board.rook_B &= board.rook_B -1 ; 163 | } 164 | while (board.queen_B) { 165 | int sq = __builtin_ctzll(board.queen_B); 166 | eval += PIECE_VALUES[QUEEN_B]; 167 | eval -= QUEEN_B_PST[sq]; 168 | board.queen_B &= board.queen_B -1 ; 169 | } 170 | 171 | // King square bonuses 172 | eval += KING_W_PST[board.whiteKingSq]; 173 | eval -= KING_B_PST[board.blackKingSq]; 174 | 175 | return eval; 176 | } -------------------------------------------------------------------------------- /src/fen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "fen.h" 3 | #include "board.h" 4 | #include "zobrist.h" 5 | #include "utils.h" 6 | #include "movegen.h" 7 | 8 | Bitboard* pieceBitboard(Board* board, int pieceType) { 9 | return (Bitboard*) board + pieceType; 10 | } 11 | 12 | void reset(Board* board) { 13 | Bitboard* bb = (Bitboard*) (board); 14 | for (int i = 0; i < 12; i++) { 15 | *bb &= 0; 16 | bb++; 17 | } 18 | board->turn = WHITE; 19 | board->castling = 0; 20 | board->epSquare = -1; 21 | } 22 | 23 | /** 24 | * Whether the given square is attacked by the opponent 25 | */ 26 | Bitboard computeAttacks(Board board) { 27 | Bitboard attackMask = 0; 28 | 29 | Bitboard pawn = board.turn ? board.pawn_B : board.pawn_W; 30 | Bitboard king = board.turn ? board.king_B : board.king_W; 31 | Bitboard knight = board.turn ? board.knight_B : board.knight_W; 32 | Bitboard bishop = board.turn ? board.bishop_B : board.bishop_W; 33 | Bitboard rook = board.turn ? board.rook_B : board.rook_W; 34 | Bitboard queen = board.turn ? board.queen_B : board.queen_W; 35 | 36 | while (queen) { 37 | int sq = __builtin_ctzl(queen); 38 | Bitboard attacks = getBishopAttacks(sq, board.occupancy); 39 | attacks |= getRookAttacks(sq, board.occupancy); 40 | attackMask |= attacks; 41 | queen &= queen - 1; 42 | } 43 | while (bishop) { 44 | int sq = __builtin_ctzl(bishop); 45 | Bitboard attacks = getBishopAttacks(sq, board.occupancy); 46 | attackMask |= attacks; 47 | bishop &= bishop - 1; 48 | } 49 | while (rook) { 50 | int sq = __builtin_ctzl(rook); 51 | Bitboard attacks = getRookAttacks(sq, board.occupancy); 52 | attackMask |= attacks; 53 | rook &= rook - 1; 54 | } 55 | while (knight) { 56 | int sq = __builtin_ctzl(knight); 57 | attackMask |= KNIGHT_MOVEMENT[sq]; 58 | knight &= knight - 1; 59 | } 60 | while (pawn) { 61 | int sq = __builtin_ctzl(pawn); 62 | 63 | if (board.turn) { 64 | attackMask |= PAWN_B_ATTACKS_EAST[sq]; 65 | attackMask |= PAWN_B_ATTACKS_WEST[sq]; 66 | } else { 67 | attackMask |= PAWN_W_ATTACKS_EAST[sq]; 68 | attackMask |= PAWN_W_ATTACKS_WEST[sq]; 69 | } 70 | pawn &= pawn - 1; 71 | } 72 | while (king) { 73 | int sq = __builtin_ctzll(king); 74 | attackMask |= KING_MOVEMENT[sq]; 75 | king &= king - 1; 76 | } 77 | 78 | return attackMask; 79 | } 80 | 81 | void setFen(Board* board, char* fen) { 82 | reset(board); 83 | 84 | int rank = 7; 85 | int file = 0; 86 | int piece = -1; 87 | int turn; 88 | int spacesEncountered = 0; 89 | char enPassantFile; 90 | char enPassantRank; 91 | while (*fen) { 92 | piece = -1; 93 | 94 | switch (*fen) { 95 | case 'b': 96 | piece = BISHOP_B; 97 | turn = BLACK; 98 | enPassantFile = *fen; 99 | break; 100 | case 'w': 101 | turn = WHITE; 102 | break; 103 | case 'p': piece = PAWN_B; break; 104 | case 'n': piece = KNIGHT_B; break; 105 | case 'r': piece = ROOK_B; break; 106 | case 'q': piece = QUEEN_B; break; 107 | case 'k': piece = KING_B; break; 108 | case 'P': piece = PAWN_W; break; 109 | case 'N': piece = KNIGHT_W; break; 110 | case 'B': piece = BISHOP_W; break; 111 | case 'R': piece = ROOK_W; break; 112 | case 'Q': piece = QUEEN_W; break; 113 | case 'K': piece = KING_W; break; 114 | 115 | case 'a': 116 | case 'c': 117 | case 'd': 118 | case 'e': 119 | case 'f': 120 | case 'g': 121 | case 'h': 122 | enPassantFile = *fen; 123 | break; 124 | 125 | case '1': 126 | case '2': 127 | case '3': 128 | case '4': 129 | case '5': 130 | case '6': 131 | case '7': 132 | case '8': 133 | enPassantRank = *fen; 134 | file += atoi(fen); 135 | break; 136 | 137 | case '/': 138 | rank--; 139 | file = 0; 140 | fen++; 141 | continue; 142 | case ' ': 143 | spacesEncountered++; 144 | enPassantFile = '0'; 145 | enPassantRank = '0'; 146 | fen++; 147 | continue; 148 | case '-': 149 | fen++; 150 | continue; 151 | } 152 | 153 | // Pieces 154 | if (spacesEncountered == 0) { 155 | if (piece != -1 && rank >= 0) { 156 | int square = ((rank+1) * 8) - file - 1; 157 | file++; 158 | 159 | Bitboard* bb = pieceBitboard(board, piece); 160 | *bb |= 1LL << square; 161 | } 162 | 163 | // Turn 164 | } else if (spacesEncountered == 1) { 165 | board->turn = turn; 166 | 167 | // Castling rights 168 | } else if (spacesEncountered == 2) { 169 | if (piece == KING_W) board->castling |= 1; 170 | else if (piece == QUEEN_W) board->castling |= 1 << 1; 171 | else if (piece == KING_B) board->castling |= 1 << 2; 172 | else if (piece == QUEEN_B) board->castling |= 1 << 3; 173 | 174 | // En passant square 175 | } else if (spacesEncountered == 3) { 176 | if (enPassantRank != '0' && enPassantFile != '0') { 177 | int file = 'h' - enPassantFile; 178 | int rank = enPassantRank - '1'; 179 | board->epSquare = 8 * rank + file; 180 | } 181 | } 182 | 183 | // TODO: Half- and fullmove clock 184 | 185 | fen++; 186 | } 187 | 188 | // Initialize king squares 189 | for (int i = 0; i < 64; i++) { 190 | if (getBit(board->king_W, i)) { 191 | board->whiteKingSq = i; 192 | } 193 | if (getBit(board->king_B, i)) { 194 | board->blackKingSq = i; 195 | } 196 | } 197 | 198 | // Initialize occupancy masks 199 | computeOccupancyMasks(board); 200 | 201 | // Set hash 202 | board->hash = hash(*board); 203 | 204 | // Initialize attack mask through an usused movegeneration 205 | board->attacks = computeAttacks(*board); 206 | } -------------------------------------------------------------------------------- /src/board.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "board.h" 6 | #include "movegen.h" 7 | #include "zobrist.h" 8 | #include "utils.h" 9 | 10 | #define TWO_RANKS 16 11 | 12 | char SQUARE_NAMES[64][3] = { 13 | "h1", "g1", "f1", "e1", "d1", "c1", "b1", "a1", 14 | "h2", "g2", "f2", "e2", "d2", "c2", "b2", "a2", 15 | "h3", "g3", "f3", "e3", "d3", "c3", "b3", "a3", 16 | "h4", "g4", "f4", "e4", "d4", "c4", "b4", "a4", 17 | "h5", "g5", "f5", "e5", "d5", "c5", "b5", "a5", 18 | "h6", "g6", "f6", "e6", "d6", "c6", "b6", "a6", 19 | "h7", "g7", "f7", "e7", "d7", "c7", "b7", "a7", 20 | "h8", "g8", "f8", "e8", "d8", "c8", "b8", "a8" 21 | }; 22 | 23 | char CASTLING_RIGHTS[4][2] = { "K", "Q", "k", "q" }; 24 | 25 | int ALL_CASTLE_W = 0b0011; 26 | int ALL_CASTLE_B = 0b1100; 27 | 28 | bool isInsufficientMaterial(Board board) { 29 | if (board.pawn_W || board.pawn_B || board.rook_W || board.rook_B || board.queen_W || board.queen_B) { 30 | return false; 31 | } 32 | 33 | int knights = 0; 34 | int bishops = 0; 35 | Bitboard knightsBB = board.knight_W | board.knight_B; 36 | Bitboard bishopsBB = board.bishop_W | board.bishop_B; 37 | while (knightsBB) { 38 | knightsBB &= knightsBB - 1; 39 | knights++; 40 | } 41 | while (bishopsBB) { 42 | bishopsBB &= bishopsBB - 1; 43 | bishops++; 44 | } 45 | 46 | if (!bishops && knights) return true; // KN or KNN vs K 47 | if (!knights && bishops == 1) return true; // KB vs K 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * The game result 54 | */ 55 | int result(Board board, Move legal[], int length) { 56 | if (isInsufficientMaterial(board)) { 57 | return DRAW; 58 | } 59 | 60 | bool noLegalMoves = length == 0; 61 | 62 | if (noLegalMoves) { 63 | Bitboard kingInCheck = SQUARE_BITBOARDS[board.turn ? board.whiteKingSq : board.blackKingSq] && board.attacks; 64 | if (kingInCheck) { 65 | return board.turn ? BLACK_WIN : WHITE_WIN; 66 | } 67 | 68 | return DRAW; 69 | } 70 | 71 | return UN_DETERMINED; 72 | } 73 | 74 | void computeOccupancyMasks(Board* board) { 75 | board->occupancyWhite = board->pawn_W | board->knight_W | board->bishop_W | board->rook_W | board->queen_W | board->king_W; 76 | board->occupancyBlack = board->pawn_B | board->knight_B | board->bishop_B | board->rook_B | board->queen_B | board->king_B; 77 | board->occupancy = board->occupancyBlack | board->occupancyWhite; 78 | } 79 | 80 | void makeEnPassantMove(Board* board, Move move) { 81 | int capturedSquare = board->epSquare + (board->turn ? -8 : 8); 82 | 83 | board->hash ^= EN_PASSANT[board->epSquare]; 84 | board->hash ^= PIECES[move.pieceType][move.fromSquare]; 85 | board->hash ^= PIECES[move.pieceType][move.toSquare]; 86 | board->hash ^= PIECES[board->turn ? PAWN_B : PAWN_W][capturedSquare]; 87 | board->hash ^= WHITE_TO_MOVE; 88 | 89 | Bitboard* friendlyPawns = board->turn ? &(board->pawn_W) : &(board->pawn_B); 90 | Bitboard* opponentPawns = board->turn ? &(board->pawn_B) : &(board->pawn_W); 91 | 92 | // Capture the pawn 93 | *opponentPawns = toggleBit(*opponentPawns, capturedSquare); 94 | 95 | // Move the pawn that takes 96 | *friendlyPawns = toggleBit(*friendlyPawns, move.fromSquare); 97 | *friendlyPawns = setBit(*friendlyPawns, board->epSquare); 98 | 99 | board->epSquare = -1; 100 | board->turn = board->turn ? 0 : 1; 101 | computeOccupancyMasks(board); 102 | } 103 | 104 | void makeCastleMove(Board* board, Move move) { 105 | if (move.castle == K) { 106 | board->hash ^= PIECES[KING_W][E1]; 107 | board->hash ^= PIECES[KING_W][G1]; 108 | board->hash ^= PIECES[ROOK_W][H1]; 109 | board->hash ^= PIECES[ROOK_W][F1]; 110 | board->king_W = board->king_W >> 2; 111 | board->rook_W = toggleBit(board->rook_W, H1); 112 | board->rook_W = setBit(board->rook_W, F1); 113 | board->whiteKingSq = G1; 114 | } else if (move.castle == Q) { 115 | board->hash ^= PIECES[KING_W][E1]; 116 | board->hash ^= PIECES[KING_W][C1]; 117 | board->hash ^= PIECES[ROOK_W][A1]; 118 | board->hash ^= PIECES[ROOK_W][D1]; 119 | board->king_W = board->king_W << 2; 120 | board->rook_W = toggleBit(board->rook_W, A1); 121 | board->rook_W = setBit(board->rook_W, D1); 122 | board->whiteKingSq = C1; 123 | } else if (move.castle == k) { 124 | board->hash ^= PIECES[KING_B][E8]; 125 | board->hash ^= PIECES[KING_B][G8]; 126 | board->hash ^= PIECES[ROOK_B][H8]; 127 | board->hash ^= PIECES[ROOK_B][F8]; 128 | board->king_B = board->king_B >> 2; 129 | board->rook_B = toggleBit(board->rook_B, H8); 130 | board->rook_B = setBit(board->rook_B, F8); 131 | board->blackKingSq = G8; 132 | } else if (move.castle == q) { 133 | board->hash ^= PIECES[KING_B][E8]; 134 | board->hash ^= PIECES[KING_B][C8]; 135 | board->hash ^= PIECES[ROOK_B][A8]; 136 | board->hash ^= PIECES[ROOK_B][D8]; 137 | board->king_B = board->king_B << 2; 138 | board->rook_B = toggleBit(board->rook_B, A8); 139 | board->rook_B = setBit(board->rook_B, D8); 140 | board->blackKingSq = C8; 141 | } 142 | 143 | if (board->epSquare != -1) { 144 | board->hash ^= EN_PASSANT[board->epSquare]; 145 | } 146 | 147 | board->hash ^= WHITE_TO_MOVE; 148 | board->hash ^= CASTLING[board->castling]; 149 | 150 | // Update castling rights 151 | board->castling &= board->turn ? ALL_CASTLE_B : ALL_CASTLE_W; 152 | board->hash ^= CASTLING[board->castling]; 153 | 154 | computeOccupancyMasks(board); 155 | board->epSquare = -1; 156 | board->turn = board->turn ? 0 : 1; 157 | } 158 | 159 | void pushMove(Board* board, Move move) { 160 | bool isEnPassantMove = move.toSquare==board->epSquare && (move.pieceType == PAWN_W || move.pieceType == PAWN_B); 161 | if (isEnPassantMove) { 162 | makeEnPassantMove(board, move); 163 | return; 164 | } 165 | 166 | if (move.castle) { 167 | makeCastleMove(board, move); 168 | return; 169 | } 170 | 171 | // Update hash 172 | board->hash ^= PIECES[move.pieceType][move.fromSquare]; 173 | board->hash ^= WHITE_TO_MOVE; 174 | board->hash ^= CASTLING[board->castling]; // XOR out old castling rights 175 | if (board->epSquare != -1) { // XOR out potential ep square 176 | board->hash ^= EN_PASSANT[board->epSquare]; 177 | } 178 | 179 | // Set potential ep-square 180 | board->epSquare = -1; 181 | bool starterPawnMoved = (move.pieceType == PAWN_W && (move.fromSquare > A1 && move.fromSquare < H3)) || 182 | (move.pieceType == PAWN_B && (move.fromSquare > A6 && move.fromSquare < H8)); 183 | if (starterPawnMoved) { 184 | int distanceCovered = abs(move.fromSquare - move.toSquare); 185 | if (distanceCovered == TWO_RANKS) { 186 | board->epSquare = board->turn ? move.fromSquare + 8 : move.fromSquare - 8; 187 | } 188 | } 189 | 190 | // Update hash with the new ep square 191 | if (board->epSquare != -1) { 192 | board->hash ^= EN_PASSANT[board->epSquare]; 193 | } 194 | 195 | // Make move 196 | Bitboard friendlyRooks = board->turn ? board->rook_W : board->rook_B; 197 | Bitboard* pieceThatMoved = (&board->pawn_W + move.pieceType); 198 | 199 | // Update castling rights 200 | if (move.pieceType == KING_W || move.pieceType == KING_B) { 201 | board->castling &= board->turn ? ALL_CASTLE_B : ALL_CASTLE_W; 202 | } else if (*pieceThatMoved == friendlyRooks) { 203 | if (board->turn && move.fromSquare == A1) board->castling &= 0b1101; 204 | if (board->turn && move.fromSquare == H1) board->castling &= 0b1110; 205 | if (!board->turn && move.fromSquare == A8) board->castling &= 0b0111; 206 | if (!board->turn && move.fromSquare == H8) board->castling &= 0b1011; 207 | } 208 | 209 | // "Lift up the piece" 210 | *pieceThatMoved = toggleBit(*pieceThatMoved, move.fromSquare); 211 | 212 | // If not promotion set the piece square directly 213 | if (move.promotion <= 0) { 214 | *pieceThatMoved = setBit(*pieceThatMoved, move.toSquare); 215 | 216 | if (*pieceThatMoved == board->king_W) { 217 | board->whiteKingSq = move.toSquare; 218 | } else if (*pieceThatMoved == board->king_B) { 219 | board->blackKingSq = move.toSquare; 220 | } 221 | 222 | board->hash ^= PIECES[move.pieceType][move.toSquare]; 223 | } else { 224 | Bitboard* targetMask = &(board->pawn_W) + move.promotion; 225 | *targetMask = setBit(*targetMask, move.toSquare); 226 | board->hash ^= PIECES[move.promotion][move.toSquare]; 227 | } 228 | 229 | Bitboard* bb = board->turn ? &(board->pawn_B) : &(board->pawn_W); 230 | Bitboard opponentRooks = board->turn ? board->rook_B : board->rook_W; 231 | for (int i = 0; i < 5; i++) { 232 | 233 | if (getBit(*bb, move.toSquare)) { 234 | 235 | // Update hash 236 | board->hash ^= PIECES[(board->turn ? 6 : 0) + i][move.toSquare]; 237 | 238 | // Update castling rights if rooks are captured 239 | if (*bb == opponentRooks) { 240 | if (board->turn && move.toSquare == H8) board->castling &= 0b1011; 241 | if (board->turn && move.toSquare == A8) board->castling &= 0b0111; 242 | if (!board->turn && move.toSquare == A1) board->castling &= 0b1101; 243 | if (!board->turn && move.toSquare == H1) board->castling &= 0b1110; 244 | } 245 | 246 | // Remove captured piece 247 | *bb = toggleBit(*bb, move.toSquare); 248 | } 249 | ++bb; 250 | } 251 | 252 | // XOR in new castling rights 253 | board->hash ^= CASTLING[board->castling]; 254 | 255 | // Toggle turn 256 | board->turn = board->turn ? 0 : 1; 257 | computeOccupancyMasks(board); 258 | } 259 | 260 | void printBoard(Board board) { 261 | char pieceSymbols[] = "PNBRQKpnbrqk"; 262 | printf("\n"); 263 | for (int y = 0; y < 8; y++) { 264 | for (int x = 0; x < 8; x++) { 265 | 266 | int loc = 63 - ((y*8) + x); 267 | bool printed = false; 268 | 269 | for (int i = 0; i < 12; i++) { 270 | Bitboard* bb = ((Bitboard*) &board) + i; 271 | 272 | if (getBit(*bb, loc)) { 273 | printf("%c", pieceSymbols[i]); 274 | printed = true; 275 | break; 276 | } 277 | } 278 | 279 | if (! printed) putchar('.'); 280 | printf(" "); 281 | } 282 | printf("\n"); 283 | } 284 | 285 | printf("Turn: %s\n", board.turn ? "White" : "Black"); 286 | printf("Castling: "); 287 | for (int i = 0; i < 4; i++) { 288 | if ((board.castling & (1 << i)) >> i) { 289 | putchar(CASTLING_RIGHTS[i][0]); 290 | } 291 | } 292 | printf("\nEp square: %s\n", board.epSquare == -1 ? "None" : &SQUARE_NAMES[board.epSquare][0]); 293 | printf("\n"); 294 | } 295 | -------------------------------------------------------------------------------- /src/perft.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "board.h" 8 | #include "fen.h" 9 | #include "movegen.h" 10 | #include "san.h" 11 | #include "bitboards.h" 12 | 13 | typedef unsigned long long u64; 14 | u64 perft(Board board, int depth, bool divide); 15 | void parseArgs(int argc, char* argv[], int* depth, bool* extraOutput, int* cFens, bool* divide); 16 | 17 | int NUM_FENS = 126; 18 | char* FENS[250]; 19 | u64 RESULTS[5][126]; 20 | 21 | int main(int argc, char* argv[]) { 22 | initBitboards(); 23 | initMoveGeneration(); 24 | setlocale(LC_NUMERIC, ""); // For prettier number formatting 25 | 26 | int depth = 0; 27 | int cFens = NUM_FENS; 28 | bool showProgress = false; 29 | bool divide = false; 30 | parseArgs(argc, argv, &depth, &showProgress, &cFens, ÷); 31 | 32 | int correct = 0; 33 | u64 totalNodes = 0; 34 | clock_t start = clock(); 35 | for (int i = 0; i < cFens;i++) { 36 | char* fen = FENS[i]; 37 | 38 | Board board = {}; 39 | setFen(&board, fen); 40 | 41 | u64 nodes = perft(board, depth, divide); 42 | totalNodes += nodes; 43 | u64 expected = RESULTS[depth-1][i]; 44 | bool matches = nodes == expected; 45 | correct += matches ? 1 : 0; 46 | 47 | if (showProgress && matches) { 48 | printf("SUCCESS %d: %s\n", i, fen); 49 | } else if (!matches) { 50 | printf("FAIL %d: %s\n", i, fen); 51 | } 52 | } 53 | 54 | double timeSpent = (double) (clock() - start) / CLOCKS_PER_SEC; 55 | int nps = totalNodes / timeSpent; 56 | 57 | printf("\n"); 58 | printf("Depth: %d\n", depth); 59 | printf("Correct: %d of %d\n", correct, cFens); 60 | printf("Time elapsed: %.2fs\n", timeSpent); 61 | printf("Nps: %'d\n", nps); 62 | printf("Total nodes: %'llu\n", totalNodes); 63 | return 0; 64 | } 65 | 66 | u64 perft(Board board, int depth, bool divide) { 67 | u64 nodes = 0; 68 | Move moves[256]; 69 | int numMoves = legalMoves(&board, moves); 70 | 71 | if (depth == 1) { 72 | int legal = 0; 73 | 74 | for (int i = 0; i < numMoves; i++) { 75 | if (moves[i].validation == LEGAL) legal++; 76 | } 77 | 78 | return legal; 79 | } 80 | 81 | for (int i = 0; i < numMoves; i++) { 82 | if (moves[i].validation == LEGAL) { 83 | // Copy-make 84 | Board cpy = board; 85 | pushMove(&cpy, moves[i]); 86 | 87 | u64 count = perft(cpy, depth-1, false); 88 | if (divide) { 89 | char san[6]; 90 | memset(&san, 0, sizeof(san)); 91 | moveToSan(moves[i], san); 92 | printf("%s: %llu\n", san, count); 93 | } 94 | nodes += count; 95 | } 96 | } 97 | 98 | return nodes; 99 | } 100 | 101 | void parseArgs(int argc, char* argv[], int* depth, bool* extraOutput, int* cFens, bool* divide) { 102 | if (argc < 3) { 103 | printf("Depth must be provided using the -d flag\n"); 104 | exit(0); 105 | } 106 | 107 | for (int i = 1; i < argc; i++) { 108 | char* flag = argv[i]; 109 | 110 | if (strcmp("-d", flag) == 0) { 111 | *depth = atoi(argv[i+1]); 112 | } 113 | 114 | if (strcmp("-fens", flag) == 0) { 115 | *cFens = atoi(argv[i+1]); 116 | } 117 | 118 | if (strcmp("--show-progress", flag) == 0) { 119 | *extraOutput = true; 120 | } 121 | 122 | if (strcmp("--divide", flag) == 0) { 123 | *divide = true; 124 | } 125 | } 126 | 127 | if (*depth == 0) { 128 | printf("Depth should be within 1-5\n"); 129 | exit(0); 130 | } 131 | } 132 | 133 | char* FENS[250] = { 134 | "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 135 | "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 136 | "4k3/8/8/8/8/8/8/4K2R w K - 0 1", 137 | "4k3/8/8/8/8/8/8/R3K3 w Q - 0 1", 138 | "4k2r/8/8/8/8/8/8/4K3 w k - 0 1", 139 | "r3k3/8/8/8/8/8/8/4K3 w q - 0 1", 140 | "4k3/8/8/8/8/8/8/R3K2R w KQ - 0 1", 141 | "r3k2r/8/8/8/8/8/8/4K3 w kq - 0 1", 142 | "8/8/8/8/8/8/6k1/4K2R w K - 0 1", 143 | "8/8/8/8/8/8/1k6/R3K3 w Q - 0 1", 144 | "4k2r/6K1/8/8/8/8/8/8 w k - 0 1", 145 | "r3k3/1K6/8/8/8/8/8/8 w q - 0 1", 146 | "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1", 147 | "r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1", 148 | "r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1", 149 | "r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1", 150 | "1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 151 | "2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 152 | "r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1", 153 | "4k3/8/8/8/8/8/8/4K2R b K - 0 1", 154 | "4k3/8/8/8/8/8/8/R3K3 b Q - 0 1", 155 | "4k2r/8/8/8/8/8/8/4K3 b k - 0 1", 156 | "r3k3/8/8/8/8/8/8/4K3 b q - 0 1", 157 | "4k3/8/8/8/8/8/8/R3K2R b KQ - 0 1", 158 | "r3k2r/8/8/8/8/8/8/4K3 b kq - 0 1", 159 | "8/8/8/8/8/8/6k1/4K2R b K - 0 1", 160 | "8/8/8/8/8/8/1k6/R3K3 b Q - 0 1", 161 | "4k2r/6K1/8/8/8/8/8/8 b k - 0 1", 162 | "r3k3/1K6/8/8/8/8/8/8 b q - 0 1", 163 | "r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1", 164 | "r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1", 165 | "r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1", 166 | "r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1", 167 | "1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 168 | "2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 169 | "r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1", 170 | "8/1n4N1/2k5/8/8/5K2/1N4n1/8 w - - 0 1", 171 | "8/1k6/8/5N2/8/4n3/8/2K5 w - - 0 1", 172 | "8/8/4k3/3Nn3/3nN3/4K3/8/8 w - - 0 1", 173 | "K7/8/2n5/1n6/8/8/8/k6N w - - 0 1", 174 | "k7/8/2N5/1N6/8/8/8/K6n w - - 0 1", 175 | "8/1n4N1/2k5/8/8/5K2/1N4n1/8 b - - 0 1", 176 | "8/1k6/8/5N2/8/4n3/8/2K5 b - - 0 1", 177 | "8/8/3K4/3Nn3/3nN3/4k3/8/8 b - - 0 1", 178 | "K7/8/2n5/1n6/8/8/8/k6N b - - 0 1", 179 | "k7/8/2N5/1N6/8/8/8/K6n b - - 0 1", 180 | "B6b/8/8/8/2K5/4k3/8/b6B w - - 0 1", 181 | "8/8/1B6/7b/7k/8/2B1b3/7K w - - 0 1", 182 | "k7/B7/1B6/1B6/8/8/8/K6b w - - 0 1", 183 | "K7/b7/1b6/1b6/8/8/8/k6B w - - 0 1", 184 | "B6b/8/8/8/2K5/5k2/8/b6B b - - 0 1", 185 | "8/8/1B6/7b/7k/8/2B1b3/7K b - - 0 1", 186 | "k7/B7/1B6/1B6/8/8/8/K6b b - - 0 1", 187 | "K7/b7/1b6/1b6/8/8/8/k6B b - - 0 1", 188 | "7k/RR6/8/8/8/8/rr6/7K w - - 0 1", 189 | "R6r/8/8/2K5/5k2/8/8/r6R w - - 0 1", 190 | "7k/RR6/8/8/8/8/rr6/7K b - - 0 1", 191 | "R6r/8/8/2K5/5k2/8/8/r6R b - - 0 1", 192 | "6kq/8/8/8/8/8/8/7K w - - 0 1", 193 | "6KQ/8/8/8/8/8/8/7k b - - 0 1", 194 | "K7/8/8/3Q4/4q3/8/8/7k w - - 0 1", 195 | "6qk/8/8/8/8/8/8/7K b - - 0 1", 196 | "6KQ/8/8/8/8/8/8/7k b - - 0 1", 197 | "K7/8/8/3Q4/4q3/8/8/7k b - - 0 1", 198 | "8/8/8/8/8/K7/P7/k7 w - - 0 1", 199 | "8/8/8/8/8/7K/7P/7k w - - 0 1", 200 | "K7/p7/k7/8/8/8/8/8 w - - 0 1", 201 | "7K/7p/7k/8/8/8/8/8 w - - 0 1", 202 | "8/2k1p3/3pP3/3P2K1/8/8/8/8 w - - 0 1", 203 | "8/8/8/8/8/K7/P7/k7 b - - 0 1", 204 | "8/8/8/8/8/7K/7P/7k b - - 0 1", 205 | "K7/p7/k7/8/8/8/8/8 b - - 0 1", 206 | "7K/7p/7k/8/8/8/8/8 b - - 0 1", 207 | "8/2k1p3/3pP3/3P2K1/8/8/8/8 b - - 0 1", 208 | "8/8/8/8/8/4k3/4P3/4K3 w - - 0 1", 209 | "4k3/4p3/4K3/8/8/8/8/8 b - - 0 1", 210 | "8/8/7k/7p/7P/7K/8/8 w - - 0 1", 211 | "8/8/k7/p7/P7/K7/8/8 w - - 0 1", 212 | "8/8/3k4/3p4/3P4/3K4/8/8 w - - 0 1", 213 | "8/3k4/3p4/8/3P4/3K4/8/8 w - - 0 1", 214 | "8/8/3k4/3p4/8/3P4/3K4/8 w - - 0 1", 215 | "k7/8/3p4/8/3P4/8/8/7K w - - 0 1", 216 | "8/8/7k/7p/7P/7K/8/8 b - - 0 1", 217 | "8/8/k7/p7/P7/K7/8/8 b - - 0 1", 218 | "8/8/3k4/3p4/3P4/3K4/8/8 b - - 0 1", 219 | "8/3k4/3p4/8/3P4/3K4/8/8 b - - 0 1", 220 | "8/8/3k4/3p4/8/3P4/3K4/8 b - - 0 1", 221 | "k7/8/3p4/8/3P4/8/8/7K b - - 0 1", 222 | "7k/3p4/8/8/3P4/8/8/K7 w - - 0 1", 223 | "7k/8/8/3p4/8/8/3P4/K7 w - - 0 1", 224 | "k7/8/8/7p/6P1/8/8/K7 w - - 0 1", 225 | "k7/8/7p/8/8/6P1/8/K7 w - - 0 1", 226 | "k7/8/8/6p1/7P/8/8/K7 w - - 0 1", 227 | "k7/8/6p1/8/8/7P/8/K7 w - - 0 1", 228 | "k7/8/8/3p4/4p3/8/8/7K w - - 0 1", 229 | "k7/8/3p4/8/8/4P3/8/7K w - - 0 1", 230 | "7k/3p4/8/8/3P4/8/8/K7 b - - 0 1", 231 | "7k/8/8/3p4/8/8/3P4/K7 b - - 0 1", 232 | "k7/8/8/7p/6P1/8/8/K7 b - - 0 1", 233 | "k7/8/7p/8/8/6P1/8/K7 b - - 0 1", 234 | "k7/8/8/6p1/7P/8/8/K7 b - - 0 1", 235 | "k7/8/6p1/8/8/7P/8/K7 b - - 0 1", 236 | "k7/8/8/3p4/4p3/8/8/7K b - - 0 1", 237 | "k7/8/3p4/8/8/4P3/8/7K b - - 0 1", 238 | "7k/8/8/p7/1P6/8/8/7K w - - 0 1", 239 | "7k/8/p7/8/8/1P6/8/7K w - - 0 1", 240 | "7k/8/8/1p6/P7/8/8/7K w - - 0 1", 241 | "7k/8/1p6/8/8/P7/8/7K w - - 0 1", 242 | "k7/7p/8/8/8/8/6P1/K7 w - - 0 1", 243 | "k7/6p1/8/8/8/8/7P/K7 w - - 0 1", 244 | "3k4/3pp3/8/8/8/8/3PP3/3K4 w - - 0 1", 245 | "7k/8/8/p7/1P6/8/8/7K b - - 0 1", 246 | "7k/8/p7/8/8/1P6/8/7K b - - 0 1", 247 | "7k/8/8/1p6/P7/8/8/7K b - - 0 1", 248 | "7k/8/1p6/8/8/P7/8/7K b - - 0 1", 249 | "k7/7p/8/8/8/8/6P1/K7 b - - 0 1", 250 | "k7/6p1/8/8/8/8/7P/K7 b - - 0 1", 251 | "3k4/3pp3/8/8/8/8/3PP3/3K4 b - - 0 1", 252 | "8/Pk6/8/8/8/8/6Kp/8 w - - 0 1", 253 | "n1n5/1Pk5/8/8/8/8/5Kp1/5N1N w - - 0 1", 254 | "8/PPPk4/8/8/8/8/4Kppp/8 w - - 0 1", 255 | "n1n5/PPPk4/8/8/8/8/4Kppp/5N1N w - - 0 1", 256 | "8/Pk6/8/8/8/8/6Kp/8 b - - 0 1", 257 | "n1n5/1Pk5/8/8/8/8/5Kp1/5N1N b - - 0 1", 258 | "8/PPPk4/8/8/8/8/4Kppp/8 b - - 0 1", 259 | "n1n5/PPPk4/8/8/8/8/4Kppp/5N1N b - - 0 1", 260 | }; 261 | 262 | u64 RESULTS[5][126] = { 263 | // Depth 1 results 264 | { 20,48,15,16,5,5,26,5,12,15,3,4,26,25,25,25,26,25,25,5,5,15,16,5,26,3,4,12,15,26,26,25,25, 265 | 25,25,25,14,11,19,3,17,15,16,4,17,3,17,21,21,7,6,17,7,21,19,36,19,36,2,2,6,22,2,6,3,3,1, 266 | 1,7,1,1,3,3,5,2,2,3,3,5,8,8,4,3,3,5,8,8,4,4,5,5,4,5,4,3,4,5,4,5,4,5,4,5,4,5,4,5,4,5,5,7, 267 | 5,4,5,4,5,5,7,11,24,18,24,11,24,18,24, 268 | }, 269 | // Depth 2 results 270 | { 400,2039,66,71,75,80,112,130,38,65,32,49,568,567,548,547,583,560,560,75,80,66,71,130,112, 271 | 32,49,38,65,568,583,560,560,567,548,547,195,156,289,51,54,193,180,68,54,51,278,316,144,143, 272 | 106,309,143,144,275,1027,275,1027,36,36,35,43,36,35,7,7,3,3,35,3,3,7,7,35,8,8,9,9,25,61,61,15, 273 | 9,9,25,61,61,15,19,19,22,16,22,16,15,16,19,19,22,16,22,16,15,16,22,16,22,16,25,25,49,22,16,22, 274 | 16,25,25,49,97,421,270,496,97,421,270,496, 275 | }, 276 | // Depth 3 results 277 | { 8902,97862,1197,1287,459,493,3189,782,564,1018,134,243,13744,14095,13502,13579,14252,13592,13607,459, 278 | 493,1197,1287,782,3189,134,243,564,1018,13744,14252,13592,13607,14095,13502,13579,2760,1636,4442,345,835, 279 | 2816,2290,1118,835,345,4607,5744,3242,1416,1829,5133,1416,3242,5300,29215,5300,29227,143,143,495,1015,143, 280 | 495,43,43,12,12,210,12,12,43,43,182,44,44,57,57,180,483,411,90,57,57,180,411,483,89,117,116,139,101,139, 281 | 101,84,101,117,117,139,101,139,101,102,101,139,101,139,101,161,161,378,139,101,139,101,161,161,378,887,7421,4699,9483,887,7421,4699,9483, 282 | }, 283 | // Depth 4 results 284 | { 197281,4085603,7059,7626,8290,8897,17945,22180,2219,4573,2073,3991,314346,328965,312835,316214,334705,317324, 285 | 320792,8290,8897,7059,7626,22180,17945,2073,3991,2219,4573,314346,334705,317324,320792,328965,312835,316214, 286 | 38675,20534,73584,5301,5910,40039,24640,16199,5910,5301,76778,93338,32955,31787,31151,93603,31787,32955,104342, 287 | 771461,104342,771368,3637,3637,8349,4167,3637,8349,199,199,80,80,1091,80,80,199,199,1091,282,282,360,360,1294, 288 | 3213,3213,534,360,360,1294,3213,3213,537,720,716,877,637,877,637,573,637,720,712,877,637,877,637,569,637,877,637, 289 | 877,637,1035,1035,2902,877,637,877,637,1035,1035,2902,8048,124608,79355,182838,8048,124608,79355,182838, 290 | }, 291 | // Depth 5 results 292 | { 4865609,193690690,133987,145232,47635,52710,532933,118882,37735,80619,10485,20780,7594526,8153719,7736373,7878456, 293 | 8198901,7710115,7848606,47635,52710,133987,145232,118882,532933,10485,20780,37735,80619,7594526,8198901,7710115,7848606,8153719, 294 | 7736373,7878456,570726,223507,1198299,38348,92250,582642,288141,281190,92250,38348,1320507,1713368,787524,310862,530585,1591064, 295 | 310862,787524,2161211,20506480,2161211,20521342,14893,14893,166741,105749,14893,166741,1347,1347,342,342,7028,342,342,1347,1347, 296 | 5408,1814,1814,1969,1969,8296,23599,21637,3450,1969,1969,8296,21637,23599,3309,4661,4786,6112,4354,6112,4354,3013,4271,5014,4658, 297 | 6112,4354,6112,4354,4337,4271,6112,4354,6112,4354,7574,7574,24122,6112,4354,6112,4354,7574,7574,24122,90606,2193768,1533145,3605103,90606,2193768,1533145,3605103, 298 | } 299 | }; 300 | 301 | 302 | -------------------------------------------------------------------------------- /src/test.c: -------------------------------------------------------------------------------- 1 | #include "../libs/unity/unity.h" 2 | #include "evaluation.h" 3 | #include "board.h" 4 | #include "fen.h" 5 | #include "movegen.h" 6 | #include "utils.h" 7 | #include "san.h" 8 | #include "bitboards.h" 9 | #include "zobrist.h" 10 | 11 | void setUp(void); 12 | void tearDown(void); 13 | 14 | /*-------Tests--------*/ 15 | 16 | void parseFen_turn(void); 17 | void parseFen_pieceMasks(void); 18 | void parseFen_epSquare(void); 19 | void parseFen_kingSquares(void); 20 | void parseFen_castlingRights(void); 21 | void parseFen_hash(void); 22 | 23 | void moveToSan_normal(void); 24 | void moveToSan_castle(void); 25 | void moveToSan_promotion(void); 26 | 27 | void sanToMove_normal(void); 28 | void sanToMove_castle(void); 29 | void sanToMove_promotion(void); 30 | void sanToMove_pieceType(void); 31 | 32 | void pushMove_hash_noCapture(void); 33 | void pushMove_hash_capture(void); 34 | void pushMove_hash_moveThatSetsEpSquare(void); 35 | void pushMove_hash_moveThatResetsEpSquare(void); 36 | void pushMove_hash_enPassantMove(void); 37 | void pushMove_hash_castlingMoveWhiteKing(void); 38 | void pushMove_hash_castlingMoveWhiteQueen(void); 39 | void pushMove_hash_castlingMoveBlackKing(void); 40 | void pushMove_hash_castlingMoveBlackQueen(void); 41 | void pushMove_hash_kingMoveThatRemovesCastlingRights(void); 42 | void pushMove_hash_rookMoveThatRemovesCastlingRights(void); 43 | void pushMove_hash_castlingMoveWhenEpSquareIsSet(void); 44 | void pushMove_hash_promotionMove(void); 45 | void pushMove_hash_fullSearchCheck(void); 46 | 47 | void gameResult_blackCheckmate(void); 48 | void gameResult_whiteCheckmate(void); 49 | void gameResult_blackStalemate(void); 50 | void gameResult_whiteStalemate(void); 51 | void gameResult_insufficientMaterial_KN(void); 52 | void gameResult_insufficientMaterial_KNN(void); 53 | void gameResult_insufficientMaterial_KB(void); 54 | void gameResult_undetermined(void); 55 | 56 | /*-------Tests--------*/ 57 | 58 | int main(void) { 59 | initBitboards(); 60 | initMoveGeneration(); 61 | initZobrist(); 62 | 63 | UNITY_BEGIN(); 64 | RUN_TEST(parseFen_turn); 65 | RUN_TEST(parseFen_pieceMasks); 66 | RUN_TEST(parseFen_epSquare); 67 | RUN_TEST(parseFen_kingSquares); 68 | RUN_TEST(parseFen_castlingRights); 69 | RUN_TEST(parseFen_hash); 70 | 71 | RUN_TEST(moveToSan_normal); 72 | RUN_TEST(moveToSan_castle); 73 | RUN_TEST(moveToSan_promotion); 74 | 75 | RUN_TEST(sanToMove_normal); 76 | RUN_TEST(sanToMove_castle); 77 | RUN_TEST(sanToMove_promotion); 78 | RUN_TEST(sanToMove_pieceType); 79 | 80 | RUN_TEST(pushMove_hash_noCapture); 81 | RUN_TEST(pushMove_hash_capture); 82 | RUN_TEST(pushMove_hash_moveThatSetsEpSquare); 83 | RUN_TEST(pushMove_hash_moveThatResetsEpSquare); 84 | RUN_TEST(pushMove_hash_enPassantMove); 85 | RUN_TEST(pushMove_hash_castlingMoveWhiteKing); 86 | RUN_TEST(pushMove_hash_castlingMoveWhiteQueen); 87 | RUN_TEST(pushMove_hash_castlingMoveBlackKing); 88 | RUN_TEST(pushMove_hash_castlingMoveBlackQueen); 89 | RUN_TEST(pushMove_hash_kingMoveThatRemovesCastlingRights); 90 | RUN_TEST(pushMove_hash_rookMoveThatRemovesCastlingRights); 91 | RUN_TEST(pushMove_hash_castlingMoveWhenEpSquareIsSet); 92 | RUN_TEST(pushMove_hash_promotionMove); 93 | RUN_TEST(pushMove_hash_fullSearchCheck); 94 | 95 | RUN_TEST(gameResult_blackCheckmate); 96 | RUN_TEST(gameResult_blackStalemate); 97 | RUN_TEST(gameResult_whiteCheckmate); 98 | RUN_TEST(gameResult_whiteStalemate); 99 | RUN_TEST(gameResult_insufficientMaterial_KN); 100 | RUN_TEST(gameResult_insufficientMaterial_KNN); 101 | RUN_TEST(gameResult_insufficientMaterial_KB); 102 | RUN_TEST(gameResult_undetermined); 103 | return UNITY_END(); 104 | } 105 | 106 | 107 | void zobristSearch(Board board, int depth) { 108 | Bitboard correctHash = hash(board); 109 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 110 | if (depth == 0) return; 111 | 112 | Move moves[256]; 113 | int numMoves = legalMoves(&board, moves); 114 | 115 | for (int i = 0; i < numMoves; i++) { 116 | if (moves[i].validation == LEGAL) { 117 | Board cpy = board; 118 | pushMove(&cpy, moves[i]); 119 | zobristSearch(cpy, depth-1); 120 | 121 | } 122 | } 123 | } 124 | void pushMove_hash_fullSearchCheck(void) { 125 | Board board; 126 | 127 | setFen(&board, START_FEN); 128 | zobristSearch(board, 4); 129 | 130 | setFen(&board, "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1"); 131 | zobristSearch(board, 4); 132 | 133 | setFen(&board, "rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ"); 134 | zobristSearch(board, 4); 135 | 136 | setFen(&board, "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 1"); 137 | zobristSearch(board, 4); 138 | } 139 | 140 | void pushMove_hash_promotionMove(void) { 141 | Board board; 142 | setFen(&board, "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 b kq - 0 1"); 143 | pushSan(&board, "b2a1q"); 144 | 145 | Bitboard correctHash = hash(board); 146 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 147 | } 148 | 149 | void pushMove_hash_castlingMoveWhenEpSquareIsSet(void) { 150 | Board board; 151 | setFen(&board, "r3k2r/8/8/8/3P4/8/8/4K3 b kq d3 0 1"); 152 | pushSan(&board, "e8g8"); 153 | 154 | Bitboard correctHash = hash(board); 155 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 156 | } 157 | 158 | void pushMove_hash_kingMoveThatRemovesCastlingRights(void) { 159 | Board board; 160 | setFen(&board, "4k3/8/8/8/8/8/8/R3K2R w KQ - 0 1"); 161 | pushSan(&board, "h1h2"); 162 | 163 | Bitboard correctHash = hash(board); 164 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 165 | } 166 | 167 | void pushMove_hash_rookMoveThatRemovesCastlingRights(void) { 168 | Board board; 169 | setFen(&board, "4k3/8/8/8/8/8/8/R3K2R w KQ - 0 1"); 170 | pushSan(&board, "a1a2"); 171 | 172 | Bitboard correctHash = hash(board); 173 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 174 | } 175 | 176 | void pushMove_hash_castlingMoveWhiteQueen(void) { 177 | Board board; 178 | setFen(&board, "4k3/8/8/8/8/8/8/R3K2R w KQ - 0 1"); 179 | pushSan(&board, "e1c1"); 180 | 181 | Bitboard correctHash = hash(board); 182 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 183 | } 184 | void pushMove_hash_castlingMoveBlackKing(void) { 185 | Board board; 186 | setFen(&board, "r3k2r/8/8/8/8/8/8/4K3 b kq - 0 1"); 187 | pushSan(&board, "e8g8"); 188 | 189 | Bitboard correctHash = hash(board); 190 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 191 | } 192 | 193 | void pushMove_hash_castlingMoveBlackQueen(void) { 194 | Board board; 195 | setFen(&board, "r3k2r/8/8/8/8/8/8/4K3 b kq - 0 1"); 196 | pushSan(&board, "e8c8"); 197 | 198 | Bitboard correctHash = hash(board); 199 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 200 | } 201 | 202 | void pushMove_hash_castlingMoveWhiteKing(void) { 203 | Board board; 204 | setFen(&board, "4k3/8/8/8/8/8/8/R3K2R w KQ - 0 1"); 205 | pushSan(&board, "e1g1"); 206 | 207 | Bitboard correctHash = hash(board); 208 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 209 | } 210 | 211 | void pushMove_hash_enPassantMove(void) { 212 | Board board; 213 | setFen(&board, START_FEN); 214 | pushSan(&board, "e2e4"); 215 | pushSan(&board, "a7a6"); 216 | pushSan(&board, "e4e5"); 217 | pushSan(&board, "d7d5"); 218 | pushSan(&board, "e5d6"); 219 | 220 | Bitboard correctHash = hash(board); 221 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 222 | } 223 | 224 | void pushMove_hash_moveThatResetsEpSquare(void) { 225 | Board board; 226 | setFen(&board, START_FEN); 227 | pushSan(&board, "e2e4"); 228 | pushSan(&board, "e7e6"); 229 | 230 | Bitboard correctHash = hash(board); 231 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 232 | } 233 | 234 | void pushMove_hash_moveThatSetsEpSquare(void) { 235 | Board board; 236 | setFen(&board, START_FEN); 237 | pushSan(&board, "e2e4"); 238 | 239 | Bitboard correctHash = hash(board); 240 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 241 | } 242 | void pushMove_hash_noCapture(void) { 243 | Board board; 244 | setFen(&board, START_FEN); 245 | pushSan(&board, "e2e3"); 246 | Bitboard correctHash = hash(board); 247 | 248 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 249 | } 250 | 251 | void pushMove_hash_capture(void) { 252 | Board board; 253 | setFen(&board, START_FEN); 254 | pushSan(&board, "e2e7"); 255 | Bitboard correctHash = hash(board); 256 | TEST_ASSERT_EQUAL_UINT64(correctHash, board.hash); 257 | } 258 | 259 | void gameResult_undetermined(void) { 260 | Board board; 261 | char* fen = "8/4bb1k/8/8/8/4K3/8/8 w - - 0 1"; 262 | setFen(&board, fen); 263 | 264 | Move moves[250]; 265 | int length = legalMoves(&board, moves); 266 | int gameResult = result(board, moves, length); 267 | TEST_ASSERT_EQUAL_INT16(UN_DETERMINED, gameResult); 268 | } 269 | 270 | void gameResult_insufficientMaterial_KB(void) { 271 | Board board; 272 | char* fen = "8/5b1k/8/8/8/8/K7/8 w - - 0 1"; 273 | setFen(&board, fen); 274 | 275 | Move moves[250]; 276 | int length = legalMoves(&board, moves); 277 | int gameResult = result(board, moves, length); 278 | TEST_ASSERT_EQUAL_INT16(DRAW, gameResult); 279 | } 280 | void gameResult_insufficientMaterial_KNN(void) { 281 | Board board; 282 | char* fen = "8/4nn1k/8/8/8/8/K7/8 w - - 0 1"; 283 | setFen(&board, fen); 284 | 285 | Move moves[250]; 286 | int length = legalMoves(&board, moves); 287 | int gameResult = result(board, moves, length); 288 | TEST_ASSERT_EQUAL_INT16(DRAW, gameResult); 289 | } 290 | 291 | void gameResult_insufficientMaterial_KN(void) { 292 | Board board; 293 | char* fen = "8/5n1k/8/8/8/8/K7/8 w - - 0 1"; 294 | setFen(&board, fen); 295 | 296 | Move moves[250]; 297 | int length = legalMoves(&board, moves); 298 | int gameResult = result(board, moves, length); 299 | TEST_ASSERT_EQUAL_INT16(DRAW, gameResult); 300 | } 301 | 302 | void gameResult_whiteStalemate(void) { 303 | Board board; 304 | char* fen = "1r6/7k/8/8/8/2q5/K7/8 w - - 0 1"; 305 | setFen(&board, fen); 306 | 307 | Move moves[250]; 308 | int length = legalMoves(&board, moves); 309 | int gameResult = result(board, moves, length); 310 | TEST_ASSERT_EQUAL_INT16(DRAW, gameResult); 311 | } 312 | 313 | void gameResult_blackStalemate(void) { 314 | Board board; 315 | char* fen = "8/7k/5Q2/8/8/8/8/K5R1 b - - 0 1"; 316 | setFen(&board, fen); 317 | 318 | Move moves[250]; 319 | int length = legalMoves(&board, moves); 320 | int gameResult = result(board, moves, length); 321 | TEST_ASSERT_EQUAL_INT16(DRAW, gameResult); 322 | } 323 | 324 | void gameResult_blackCheckmate(void) { 325 | Board board; 326 | char* fen = "1r2k3/8/8/8/8/8/1q6/K7 w - - 0 1"; 327 | setFen(&board, fen); 328 | 329 | Move moves[250]; 330 | int length = legalMoves(&board, moves); 331 | int gameResult = result(board, moves, length); 332 | TEST_ASSERT_EQUAL_INT16(BLACK_WIN, gameResult); 333 | } 334 | 335 | void gameResult_whiteCheckmate(void) { 336 | Board board; 337 | char* fen = "4k3/4Q3/8/8/8/8/8/K3R3 b - - 0 1"; 338 | setFen(&board, fen); 339 | 340 | Move moves[250]; 341 | int length = legalMoves(&board, moves); 342 | int gameResult = result(board, moves, length); 343 | TEST_ASSERT_EQUAL_INT16(WHITE_WIN, gameResult); 344 | } 345 | 346 | void sanToMove_pieceType(void) { 347 | Board board; 348 | setFen(&board, "rnbqk3/6p1/8/8/8/8/6P1/RNBQK3 w Qq - 0 1"); 349 | Move move; 350 | 351 | // White moves 352 | sanToMove(board, &move, "g2g3"); 353 | TEST_ASSERT_EQUAL_INT16(PAWN_W, move.pieceType); 354 | sanToMove(board, &move, "e1e2"); 355 | TEST_ASSERT_EQUAL_INT16(KING_W, move.pieceType); 356 | sanToMove(board, &move, "d1d2"); 357 | TEST_ASSERT_EQUAL_INT16(QUEEN_W, move.pieceType); 358 | sanToMove(board, &move, "c1d2"); 359 | TEST_ASSERT_EQUAL_INT16(BISHOP_W, move.pieceType); 360 | sanToMove(board, &move, "b1c3"); 361 | TEST_ASSERT_EQUAL_INT16(KNIGHT_W, move.pieceType); 362 | sanToMove(board, &move, "a1a2"); 363 | TEST_ASSERT_EQUAL_INT16(ROOK_W, move.pieceType); 364 | 365 | // Black moves 366 | board.turn = BLACK; 367 | sanToMove(board, &move, "g7g7"); 368 | TEST_ASSERT_EQUAL_INT16(PAWN_B, move.pieceType); 369 | sanToMove(board, &move, "e8e7"); 370 | TEST_ASSERT_EQUAL_INT16(KING_B, move.pieceType); 371 | sanToMove(board, &move, "d8d7"); 372 | TEST_ASSERT_EQUAL_INT16(QUEEN_B, move.pieceType); 373 | sanToMove(board, &move, "c8d7"); 374 | TEST_ASSERT_EQUAL_INT16(BISHOP_B, move.pieceType); 375 | sanToMove(board, &move, "b8c6"); 376 | TEST_ASSERT_EQUAL_INT16(KNIGHT_B, move.pieceType); 377 | sanToMove(board, &move, "a8a7"); 378 | TEST_ASSERT_EQUAL_INT16(ROOK_B, move.pieceType); 379 | } 380 | 381 | void sanToMove_castle(void) { 382 | Board board; 383 | board.castling = K | Q | k | q; 384 | Move move; 385 | 386 | sanToMove(board, &move, "e1g1"); 387 | TEST_ASSERT_EQUAL_INT16(K, move.castle); 388 | sanToMove(board, &move, "e1c1"); 389 | TEST_ASSERT_EQUAL_INT16(Q, move.castle); 390 | sanToMove(board, &move, "e8g8"); 391 | TEST_ASSERT_EQUAL_INT16(k, move.castle); 392 | sanToMove(board, &move, "e8c8"); 393 | TEST_ASSERT_EQUAL_INT16(q, move.castle); 394 | } 395 | 396 | void sanToMove_promotion(void) { 397 | Board board; 398 | board.turn = WHITE; 399 | Move move; 400 | sanToMove(board, &move, "e7e8q"); 401 | 402 | TEST_ASSERT_EQUAL_INT16(E7, move.fromSquare); 403 | TEST_ASSERT_EQUAL_INT16(E8, move.toSquare); 404 | TEST_ASSERT_EQUAL_INT16(QUEEN_W, move.promotion); 405 | } 406 | 407 | void sanToMove_normal(void) { 408 | Board board; 409 | Move move; 410 | sanToMove(board, &move, "e2e3"); 411 | 412 | TEST_ASSERT_EQUAL_INT16(E2, move.fromSquare); 413 | TEST_ASSERT_EQUAL_INT16(E3, move.toSquare); 414 | } 415 | 416 | void moveToSan_normal(void) { 417 | Move move = {.fromSquare=E2, .toSquare=E3}; 418 | char san[6]; 419 | moveToSan(move, san); 420 | TEST_ASSERT_EQUAL_STRING("e2e3", san); 421 | } 422 | 423 | void moveToSan_castle(void) { 424 | Move move = {.fromSquare=0, .toSquare=0, .castle=K}; 425 | char san[6]; 426 | moveToSan(move, san); 427 | TEST_ASSERT_EQUAL_STRING("e1g1", san); 428 | } 429 | 430 | void moveToSan_promotion(void) { 431 | Move move = {.fromSquare=E7, .toSquare=E8, .promotion=QUEEN_W}; 432 | char san[6]; 433 | moveToSan(move, san); 434 | TEST_ASSERT_EQUAL_STRING("e7e8q", san); 435 | } 436 | 437 | void parseFen_castlingRights(void) { 438 | Board board; 439 | char* fen = "r3k3/8/8/8/8/8/8/4K2R b Kq d3 0 1"; 440 | setFen(&board, fen); 441 | 442 | TEST_ASSERT_EQUAL_INT16(K | q, board.castling); 443 | } 444 | 445 | void parseFen_kingSquares(void) { 446 | Board board; 447 | char* fen = "8/8/4k3/8/8/3K4/8/8 b - d3 0 1"; 448 | setFen(&board, fen); 449 | 450 | TEST_ASSERT_EQUAL_INT16(D3, board.whiteKingSq); 451 | TEST_ASSERT_EQUAL_INT16(E6, board.blackKingSq); 452 | } 453 | 454 | void parseFen_turn(void) { 455 | Board board; 456 | char* fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1"; 457 | setFen(&board, fen); 458 | 459 | TEST_ASSERT_TRUE(board.turn == BLACK); 460 | } 461 | 462 | void parseFen_hash(void) { 463 | Board board; 464 | setFen(&board, START_FEN); 465 | Bitboard zob = hash(board); 466 | 467 | TEST_ASSERT_EQUAL_UINT64(zob, board.hash); 468 | } 469 | 470 | void parseFen_pieceMasks(void) { 471 | Board board; 472 | char* fen = "r3k2r/Pp1p1ppp/1b3nbN/nP6/BBpPP3/q4N2/Pp4PP/R2Q1RK1 b kq d3 0 1"; 473 | setFen(&board, fen); 474 | 475 | Bitboard correctPieceMasks[] = { 476 | 36029072299557632, 1099511889920, 3221225472, 132, 16, 2, 477 | 24488323510714368, 4947802324992, 72567767433216, 9295429630892703744U, 8388608, 576460752303423488 478 | }; 479 | 480 | // Compare piece masks 481 | for (int i = 0;i < 12;i ++) { 482 | Bitboard pieceMask = *(&(board.pawn_W)+i); 483 | Bitboard expected = correctPieceMasks[i]; 484 | TEST_ASSERT_EQUAL_UINT64(expected, pieceMask); 485 | } 486 | } 487 | 488 | void parseFen_epSquare(void) { 489 | Board board; 490 | char* fen = "r3k2r/Pp1p1ppp/1b3nbN/nP6/BBpPP3/q4N2/Pp4PP/R2Q1RK1 b kq d3 0 1"; 491 | setFen(&board, fen); 492 | 493 | TEST_ASSERT_EQUAL_INT16(D3, board.epSquare); 494 | } 495 | 496 | 497 | 498 | // Unity functions 499 | void setUp(void) {} 500 | void tearDown(void) {} 501 | -------------------------------------------------------------------------------- /src/movegen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "movegen.h" 6 | #include "board.h" 7 | #include "utils.h" 8 | 9 | #define getMove(from, to, promo, castling, pType) {.fromSquare=from, .toSquare=to, .promotion=promo, .castle=castling, .pieceType=pType} 10 | #define addMove moves[length] = move; length++; 11 | 12 | Bitboard PAWN_START_WHITE = 0xFF00; 13 | Bitboard PAWN_START_BLACK = 0x00FF000000000000; 14 | Bitboard WHITE_CASTLE_K_PATH = 0b110; 15 | Bitboard WHITE_CASTLE_Q_PATH = 0b1110000; 16 | Bitboard BLACK_CASTLE_K_PATH = 0b11LL << G8; 17 | Bitboard BLACK_CASTLE_Q_PATH = 0b111LL << D8; 18 | int WHITE_PROMOTIONS[4] = {QUEEN_W, BISHOP_W, KNIGHT_W, ROOK_W}; 19 | int BLACK_PROMOTIONS[4] = {QUEEN_B, BISHOP_B, KNIGHT_B, ROOK_B}; 20 | int NO_PROMOTION = -1; 21 | int NOT_CASTLE = 0; 22 | 23 | Bitboard PAWN_W_ATTACKS_EAST[64]; 24 | Bitboard PAWN_W_ATTACKS_WEST[64]; 25 | Bitboard PAWN_B_ATTACKS_EAST[64]; 26 | Bitboard PAWN_B_ATTACKS_WEST[64]; 27 | Bitboard KNIGHT_MOVEMENT[64]; 28 | Bitboard BISHOP_MOVEMENT[64]; 29 | Bitboard ROOK_MOVEMENT[64]; 30 | Bitboard KING_MOVEMENT[64]; 31 | Bitboard BISHOP_ATTACKS[64][512]; 32 | Bitboard ROOK_ATTACKS[64][4096]; 33 | 34 | int ROOK_RELEVANT_BITS[64] = { 35 | 12, 11, 11, 11, 11, 11, 11, 12, 36 | 11, 10, 10, 10, 10, 10, 10, 11, 37 | 11, 10, 10, 10, 10, 10, 10, 11, 38 | 11, 10, 10, 10, 10, 10, 10, 11, 39 | 11, 10, 10, 10, 10, 10, 10, 11, 40 | 11, 10, 10, 10, 10, 10, 10, 11, 41 | 11, 10, 10, 10, 10, 10, 10, 11, 42 | 12, 11, 11, 11, 11, 11, 11, 12 43 | }; 44 | int BISHOP_RELEVANT_BITS[64] = { 45 | 6, 5, 5, 5, 5, 5, 5, 6, 46 | 5, 5, 5, 5, 5, 5, 5, 5, 47 | 5, 5, 7, 7, 7, 7, 5, 5, 48 | 5, 5, 7, 9, 9, 7, 5, 5, 49 | 5, 5, 7, 9, 9, 7, 5, 5, 50 | 5, 5, 7, 7, 7, 7, 5, 5, 51 | 5, 5, 5, 5, 5, 5, 5, 5, 52 | 6, 5, 5, 5, 5, 5, 5, 6 53 | }; 54 | 55 | 56 | /*--------------------------------------------- 57 | 58 | Initializes piece movement tables 59 | 60 | ---------------------------------------------*/ 61 | 62 | void initKingMovementTable(void) { 63 | for (int sq = 0; sq < 64; sq++) { 64 | Bitboard moves = 0; 65 | int file = sq%8; 66 | 67 | if (sq <= A7) moves |= 1LL << (sq+8); // UP 68 | if (sq >= H2) moves |= 1LL << (sq-8); // DOWN 69 | if (file < A) moves |= 1LL << (sq+1); // LEFT 70 | if (file > H) moves |= 1LL << (sq-1); // RIGHT 71 | if (file < A && sq <= A7) moves |= 1LL << (sq+9); // UP LEFT 72 | if (file > H && sq <= A7) moves |= 1LL << (sq+7); // UP RIGHT 73 | if (file < A && sq >= H2) moves |= 1LL << (sq-7); // DOWN LEFT 74 | if (file > H && sq >= H2) moves |= 1LL << (sq-9); // DOWN RIGHT 75 | 76 | KING_MOVEMENT[sq] = moves; 77 | } 78 | } 79 | 80 | void initKnightMovementTable(void) { 81 | for (int sq = 0; sq < 64; sq++) { 82 | Bitboard bb = 0LL; 83 | int file = sq%8; 84 | 85 | if (file != A && sq <= A6) { bb |= 1LL << sq+17;} // UP LEFT 86 | if (file != H && sq <= A6) { bb |= 1LL << sq+15;} // UP RIGHT 87 | if (file != A && sq >= A2) { bb |= 1LL << sq-15;} // DOWN LEFT 88 | if (file != H && sq > H3) { bb |= 1LL << sq-17;} // DOWN RIGHT 89 | if (file < B && sq <= A7) { bb |= 1LL << sq+10;} // LEFT UP 90 | if (file < B && sq >= H2) { bb |= 1LL << sq-6;} // LEFT DOWN 91 | if (file > G && sq >= H2) { bb |= 1LL << sq-10;} // RIGHT DOWN 92 | if (file > G && sq <= A7) { bb |= 1LL << sq+6;} // RIGHT UP 93 | 94 | KNIGHT_MOVEMENT[sq] = bb; 95 | } 96 | } 97 | 98 | void initPawnAttackTables(void) { 99 | for (int sq = 0; sq < 64;sq++) { 100 | int file = sq%8; 101 | 102 | Bitboard attack = 0; 103 | if (file != A && sq <= H8) attack |= 1LL << (sq+9); // White west 104 | PAWN_W_ATTACKS_WEST[sq] = attack; 105 | 106 | attack = 0; 107 | if (file != H && sq <= H8) attack |= 1LL << (sq+7); // White east 108 | PAWN_W_ATTACKS_EAST[sq] = attack; 109 | 110 | attack = 0; 111 | if (file != H && sq >= H2) attack |= 1LL << (sq-9); // Black west 112 | PAWN_B_ATTACKS_WEST[sq] = attack; 113 | 114 | attack = 0; 115 | if (file != A && sq >= H2) attack |= 1LL << (sq-7); // Black east 116 | PAWN_B_ATTACKS_EAST[sq] = attack; 117 | } 118 | } 119 | 120 | /*--------------------------------------------- 121 | 122 | Magic bitboard methods 123 | 124 | ---------------------------------------------*/ 125 | 126 | Bitboard occupancyMask(int index, int bits, Bitboard attackMask) { 127 | Bitboard occupancy = 0ULL; 128 | 129 | for (int i = 0; i < bits; i++) { 130 | int square = __builtin_ctzll(attackMask); 131 | attackMask = toggleBit(attackMask, square); 132 | 133 | if (index & (1 << i)) { 134 | occupancy |= (1ULL << square); 135 | } 136 | } 137 | 138 | return occupancy; 139 | } 140 | 141 | Bitboard bishopAttacksOnTheFly(int square, Bitboard block) { 142 | Bitboard attacks = 0ULL; 143 | 144 | // init files & ranks 145 | int f, r; 146 | // init target files & ranks 147 | int tr = square / 8; 148 | int tf = square % 8; 149 | 150 | for (r = tr + 1, f = tf + 1; r <= 7 && f <= 7; r++, f++) { 151 | attacks |= (1ULL << (r * 8 + f)); 152 | if (block & (1ULL << (r * 8 + f))) break; 153 | } 154 | 155 | for (r = tr + 1, f = tf - 1; r <= 7 && f >= 0; r++, f--) { 156 | attacks |= (1ULL << (r * 8 + f)); 157 | if (block & (1ULL << (r * 8 + f))) break; 158 | } 159 | 160 | for (r = tr - 1, f = tf + 1; r >= 0 && f <= 7; r--, f++) { 161 | attacks |= (1ULL << (r * 8 + f)); 162 | if (block & (1ULL << (r * 8 + f))) break; 163 | } 164 | 165 | for (r = tr - 1, f = tf - 1; r >= 0 && f >= 0; r--, f--) { 166 | attacks |= (1ULL << (r * 8 + f)); 167 | if (block & (1ULL << (r * 8 + f))) break; 168 | } 169 | 170 | return attacks; 171 | } 172 | 173 | Bitboard rookAttacksOnTheFly(int square, Bitboard block) { 174 | Bitboard attacks = 0ULL; 175 | 176 | // init files & ranks 177 | int f, r; 178 | // init target files & ranks 179 | int tr = square / 8; 180 | int tf = square % 8; 181 | 182 | for (r = tr + 1; r <= 7; r++) { 183 | attacks |= (1ULL << (r * 8 + tf)); 184 | if (block & (1ULL << (r * 8 + tf))) break; 185 | } 186 | 187 | for (r = tr - 1; r >= 0; r--) { 188 | attacks |= (1ULL << (r * 8 + tf)); 189 | if (block & (1ULL << (r * 8 + tf))) break; 190 | } 191 | 192 | for (f = tf + 1; f <= 7; f++) { 193 | attacks |= (1ULL << (tr * 8 + f)); 194 | if (block & (1ULL << (tr * 8 + f))) break; 195 | } 196 | 197 | for (f = tf - 1; f >= 0; f--) { 198 | attacks |= (1ULL << (tr * 8 + f)); 199 | if (block & (1ULL << (tr * 8 + f))) break; 200 | } 201 | 202 | return attacks; 203 | } 204 | 205 | Bitboard bishopMovement(int square) { 206 | // File and rank 207 | int f, r; 208 | 209 | // Target file and rank 210 | int tf = square % 8; 211 | int tr = square / 8; 212 | 213 | Bitboard movement = 0ULL; 214 | for (r = tr + 1, f = tf + 1; r <= 6 && f <= 6; r++, f++) movement |= (1ULL << (r * 8 + f)); 215 | for (r = tr + 1, f = tf - 1; r <= 6 && f >= 1; r++, f--) movement |= (1ULL << (r * 8 + f)); 216 | for (r = tr - 1, f = tf + 1; r >= 1 && f <= 6; r--, f++) movement |= (1ULL << (r * 8 + f)); 217 | for (r = tr - 1, f = tf - 1; r >= 1 && f >= 1; r--, f--) movement |= (1ULL << (r * 8 + f)); 218 | 219 | return movement; 220 | } 221 | 222 | Bitboard rookMovement(int square) { 223 | // File and rank 224 | int f, r; 225 | 226 | // Target file and rank 227 | int tf = square % 8; 228 | int tr = square / 8; 229 | 230 | Bitboard movement = 0ULL; 231 | for (r = tr + 1; r <= 6; r++) movement |= (1ULL << (r * 8 + tf)); 232 | for (r = tr - 1; r >= 1; r--) movement |= (1ULL << (r * 8 + tf)); 233 | for (f = tf + 1; f <= 6; f++) movement |= (1ULL << (tr * 8 + f)); 234 | for (f = tf - 1; f >= 1; f--) movement |= (1ULL << (tr * 8 + f)); 235 | 236 | return movement; 237 | } 238 | 239 | void initBishopRookAttackTables() { 240 | for (int square = 0; square < 64; square++) { 241 | 242 | // Fill movement masks 243 | BISHOP_MOVEMENT[square] = bishopMovement(square); 244 | ROOK_MOVEMENT[square] = rookMovement(square); 245 | 246 | Bitboard bishopMask = BISHOP_MOVEMENT[square]; 247 | Bitboard rookMask = ROOK_MOVEMENT[square]; 248 | int bishopRelevantBits = BISHOP_RELEVANT_BITS[square]; 249 | int rookRelevantBits = ROOK_RELEVANT_BITS[square]; 250 | int bishopOccupancyVariations = 1 << bishopRelevantBits; 251 | int rookOccupancyVariations = 1 << rookRelevantBits; 252 | 253 | for (int i = 0; i < bishopOccupancyVariations; i++) { 254 | Bitboard occupancy = occupancyMask(i, bishopRelevantBits, bishopMask); 255 | Bitboard magic_index = occupancy * BISHOP_MAGICS[square] >> 64 - bishopRelevantBits; 256 | BISHOP_ATTACKS[square][magic_index] = bishopAttacksOnTheFly(square, occupancy); 257 | } 258 | 259 | for (int i = 0; i < rookOccupancyVariations; i++) { 260 | Bitboard occupancy = occupancyMask(i, rookRelevantBits, rookMask); 261 | Bitboard magic_index = occupancy * ROOK_MAGICS[square] >> 64 - rookRelevantBits; 262 | ROOK_ATTACKS[square][magic_index] = rookAttacksOnTheFly(square, occupancy); 263 | } 264 | } 265 | } 266 | 267 | Bitboard getBishopAttacks(int square, Bitboard occupancy) { 268 | occupancy &= BISHOP_MOVEMENT[square]; 269 | occupancy *= BISHOP_MAGICS[square]; 270 | occupancy >>= 64 - BISHOP_RELEVANT_BITS[square]; 271 | return BISHOP_ATTACKS[square][occupancy]; 272 | } 273 | 274 | Bitboard getRookAttacks(int square, Bitboard occupancy) { 275 | occupancy &= ROOK_MOVEMENT[square]; 276 | occupancy *= ROOK_MAGICS[square]; 277 | occupancy >>= 64 - ROOK_RELEVANT_BITS[square]; 278 | return ROOK_ATTACKS[square][occupancy]; 279 | } 280 | 281 | Bitboard getKingMask(Board board) { 282 | int kingSquare = board.turn ? board.whiteKingSq : board.blackKingSq; 283 | Bitboard opponentOccupancy = board.turn ? board.occupancyWhite : board.occupancyBlack; 284 | return KING_MOVEMENT[kingSquare] & ~opponentOccupancy; 285 | } 286 | 287 | void validateMove(Board board, Move* move) { 288 | if (move->castle) { 289 | // Does king travel over attacked squares? 290 | int sq; 291 | if (move->castle == K) sq = F1; 292 | if (move->castle == Q) sq = D1; 293 | if (move->castle == k) sq = F8; 294 | if (move->castle == q) sq = D8; 295 | bool attackedTravel = isSquareAttacked(board, sq); 296 | if (attackedTravel) { 297 | move->validation = ILLEGAL; 298 | return; 299 | } 300 | 301 | // Is king in check? 302 | bool isInCheck = isSquareAttacked(board, board.turn ? board.whiteKingSq : board.blackKingSq); 303 | if (isInCheck) { 304 | move->validation = ILLEGAL; 305 | return; 306 | } 307 | } 308 | 309 | Board cpy = board; 310 | pushMove(&cpy, *move); 311 | 312 | int kingSquare = cpy.turn ? cpy.blackKingSq : cpy.whiteKingSq; 313 | cpy.turn = cpy.turn ? 0 : 1; 314 | bool isInCheckAfterMove = isSquareAttacked(cpy, kingSquare); 315 | 316 | move->validation = isInCheckAfterMove ? ILLEGAL : LEGAL; 317 | } 318 | 319 | /** 320 | * Computes pawn moves, single and double pushes 321 | */ 322 | void pawnSingleAndDblPushes(Board board, Bitboard* single, Bitboard* dbl) { 323 | *single = board.turn ? board.pawn_W << 8 : board.pawn_B >> 8; 324 | *single = *single & ~board.occupancy; 325 | 326 | *dbl = board.turn ? (board.pawn_W & PAWN_START_WHITE) << 16 : (board.pawn_B & PAWN_START_BLACK) >> 16; 327 | *dbl = *dbl & ~board.occupancy; 328 | 329 | // Double pushes can't jump over pieces 330 | *dbl = board.turn ? *dbl >> 8 : *dbl << 8; 331 | *dbl &= *single; 332 | *dbl = board.turn ? *dbl << 8 : *dbl >> 8; 333 | } 334 | 335 | void addPawnAdvanceWithPossiblePromos(Board board, bool isPromoting, int turn, int from, int to, Move moves[], int* indx) { 336 | if (isPromoting) { 337 | for (int i = 0; i < 4; i++) { 338 | Move move = getMove(from, to, turn ? WHITE_PROMOTIONS[i] : BLACK_PROMOTIONS[i], NOT_CASTLE, board.turn ? PAWN_W : PAWN_B); 339 | validateMove(board, &move); 340 | if (move.validation == ILLEGAL) continue; 341 | 342 | moves[*indx] = move; 343 | *indx += 1; 344 | } 345 | return; 346 | } 347 | 348 | Move move = getMove(from, to, NO_PROMOTION, NOT_CASTLE, board.turn ? PAWN_W : PAWN_B); 349 | validateMove(board, &move); 350 | if (move.validation == ILLEGAL) return; 351 | moves[*indx] = move; 352 | *indx += 1; 353 | } 354 | 355 | 356 | /*--------------------------------------------- 357 | 358 | Visible methods 359 | 360 | ---------------------------------------------*/ 361 | 362 | void initMoveGeneration(void) { 363 | initKnightMovementTable(); 364 | initKingMovementTable(); 365 | initPawnAttackTables(); 366 | initBishopRookAttackTables(); 367 | } 368 | 369 | /** 370 | * Whether the given square is attacked by the opponent 371 | */ 372 | bool isSquareAttacked(Board board, int square) { 373 | Bitboard sqBb = SQUARE_BITBOARDS[square]; 374 | Bitboard pawn = board.turn ? board.pawn_B : board.pawn_W; 375 | Bitboard king = board.turn ? board.king_B : board.king_W; 376 | Bitboard knight = board.turn ? board.knight_B : board.knight_W; 377 | Bitboard bishop = board.turn ? board.bishop_B : board.bishop_W; 378 | Bitboard rook = board.turn ? board.rook_B : board.rook_W; 379 | Bitboard queen = board.turn ? board.queen_B : board.queen_W; 380 | 381 | while (queen) { 382 | int sq = __builtin_ctzl(queen); 383 | 384 | Bitboard attacks = getBishopAttacks(sq, board.occupancy); 385 | attacks |= getRookAttacks(sq, board.occupancy); 386 | if (attacks & sqBb) return true; 387 | 388 | queen &= queen - 1; 389 | } 390 | while (bishop) { 391 | int sq = __builtin_ctzl(bishop); 392 | Bitboard attacks = getBishopAttacks(sq, board.occupancy); 393 | if (attacks & sqBb) return true; 394 | bishop &= bishop - 1; 395 | } 396 | while (rook) { 397 | int sq = __builtin_ctzl(rook); 398 | Bitboard attacks = getRookAttacks(sq, board.occupancy); 399 | if (attacks & sqBb) return true; 400 | rook &= rook - 1; 401 | } 402 | while (knight) { 403 | int sq = __builtin_ctzl(knight); 404 | if (KNIGHT_MOVEMENT[sq] & sqBb) return true; 405 | knight &= knight - 1; 406 | } 407 | while (pawn) { 408 | int sq = __builtin_ctzl(pawn); 409 | 410 | if (board.turn) { 411 | if (PAWN_B_ATTACKS_EAST[sq] & sqBb) return true; 412 | if (PAWN_B_ATTACKS_WEST[sq] & sqBb) return true; 413 | } else { 414 | if (PAWN_W_ATTACKS_EAST[sq] & sqBb) return true; 415 | if (PAWN_W_ATTACKS_WEST[sq] & sqBb) return true; 416 | } 417 | pawn &= pawn - 1; 418 | } 419 | while (king) { 420 | int sq = __builtin_ctzll(king); 421 | if (KING_MOVEMENT[sq] & sqBb) return true; 422 | king &= king - 1; 423 | } 424 | 425 | return false; 426 | } 427 | 428 | /** 429 | * Generates all legal moves for the given board 430 | */ 431 | int legalMoves(Board* board, Move moves[]) { 432 | int length = 0; 433 | 434 | // Already known 435 | int kingSquare = board->turn ? board->whiteKingSq : board->blackKingSq; 436 | Bitboard friendlyOccupancy = board->turn ? board->occupancyWhite : board->occupancyBlack; 437 | Bitboard bishopBitboard = board->turn ? board->bishop_W : board->bishop_B; 438 | Bitboard rookBitboard = board->turn ? board->rook_W : board->rook_B; 439 | Bitboard queenBitboard = board->turn ? board->queen_W : board->queen_B; 440 | Bitboard knightBitboard = board->turn ? board->knight_W : board->knight_B; 441 | Bitboard pawnMask = board->turn ? board->pawn_W : board->pawn_B; 442 | 443 | Bitboard attackMask = 0; 444 | 445 | // Generate all pieces movement, starting with pawns 446 | Bitboard singlePush; 447 | Bitboard doublePush; 448 | pawnSingleAndDblPushes(*board, &singlePush, &doublePush); // Single and double pawn pushes 449 | 450 | // Captures 451 | Bitboard epSquare = board->epSquare == -1 ? 0LL : SQUARE_BITBOARDS[board->epSquare]; 452 | while (pawnMask) { 453 | int sq = __builtin_ctzll(pawnMask); 454 | bool isPromoting = board->turn ? (SQUARE_BITBOARDS[sq] & RANK_7) : (SQUARE_BITBOARDS[sq] & RANK_1); 455 | 456 | Bitboard occ = epSquare | (board->turn ? board->occupancyBlack : board->occupancyWhite); 457 | if (board->turn) { 458 | Bitboard eastAttacks = PAWN_W_ATTACKS_EAST[sq] & occ; 459 | attackMask |= PAWN_W_ATTACKS_EAST[sq]; 460 | attackMask |= PAWN_W_ATTACKS_WEST[sq]; 461 | if (eastAttacks) { 462 | int toSquare = sq + 7; 463 | addPawnAdvanceWithPossiblePromos(*board, isPromoting, board->turn, sq, toSquare, moves, &length); 464 | } 465 | Bitboard westAttacks = PAWN_W_ATTACKS_WEST[sq] & occ; 466 | if (westAttacks) { 467 | int toSquare = sq + 9; 468 | addPawnAdvanceWithPossiblePromos(*board, isPromoting, board->turn, sq, toSquare, moves, &length); 469 | } 470 | } else { 471 | Bitboard eastAttacks = PAWN_B_ATTACKS_EAST[sq] & occ; 472 | attackMask |= PAWN_B_ATTACKS_EAST[sq]; 473 | attackMask |= PAWN_B_ATTACKS_WEST[sq]; 474 | if (eastAttacks) { 475 | int toSquare = sq - 7; 476 | addPawnAdvanceWithPossiblePromos(*board, isPromoting, board->turn, sq, toSquare, moves, &length); 477 | } 478 | Bitboard westAttacks = PAWN_B_ATTACKS_WEST[sq] & occ; 479 | if (westAttacks) { 480 | int toSquare = sq - 9; 481 | addPawnAdvanceWithPossiblePromos(*board, isPromoting, board->turn, sq, toSquare, moves, &length); 482 | } 483 | } 484 | pawnMask &= pawnMask - 1; 485 | } 486 | 487 | Bitboard kingMovesMask = getKingMask(*board); 488 | attackMask |= KING_MOVEMENT[kingSquare]; 489 | 490 | while (singlePush) { 491 | int sq = __builtin_ctzll(singlePush); 492 | int fromSquare = board->turn ? sq-8 : sq+8; 493 | bool isPromoting = board->turn ? (fromSquare >= H7 && fromSquare <= A7) : (fromSquare >= H2 && fromSquare <= A2); 494 | addPawnAdvanceWithPossiblePromos(*board, isPromoting, board->turn, fromSquare, sq, moves, &length); 495 | singlePush &= singlePush - 1; 496 | } 497 | 498 | int startPieceType = board->turn ? PAWN_W : PAWN_B; 499 | while (doublePush) { 500 | int sq = __builtin_ctzll(doublePush); 501 | int fromSquare = board->turn ? sq-8*2 : sq+8*2; 502 | Move move = getMove(fromSquare, sq, NO_PROMOTION, NOT_CASTLE, startPieceType + PAWN_W); 503 | validateMove(*board, &move); 504 | if (move.validation == LEGAL) { 505 | addMove 506 | } 507 | doublePush &= doublePush - 1; 508 | } 509 | 510 | while (kingMovesMask) { 511 | int sq = __builtin_ctzll(kingMovesMask); 512 | Move move = getMove(kingSquare, sq, NO_PROMOTION, NOT_CASTLE, startPieceType + KING_W); 513 | validateMove(*board, &move); 514 | if (move.validation == LEGAL) { 515 | addMove 516 | } 517 | kingMovesMask &= kingMovesMask - 1; 518 | } 519 | while (bishopBitboard) { 520 | int sq = __builtin_ctzll(bishopBitboard); 521 | Bitboard attacks = getBishopAttacks(sq, board->occupancy); 522 | attackMask |= attacks; 523 | attacks = attacks & ~friendlyOccupancy; 524 | 525 | while (attacks) { 526 | int indx = __builtin_ctzll(attacks); 527 | Move move = getMove(sq, indx, NO_PROMOTION, NOT_CASTLE, startPieceType + BISHOP_W); 528 | validateMove(*board, &move); 529 | if (move.validation == LEGAL) { 530 | addMove 531 | } 532 | attacks &= attacks - 1; 533 | } 534 | bishopBitboard &= bishopBitboard - 1; 535 | } 536 | while (rookBitboard) { 537 | int sq = __builtin_ctzll(rookBitboard); 538 | Bitboard attacks = getRookAttacks(sq, board->occupancy); 539 | attackMask |= attacks; 540 | attacks = attacks & ~friendlyOccupancy; 541 | 542 | while (attacks) { 543 | int indx = __builtin_ctzll(attacks); 544 | Move move = getMove(sq, indx, NO_PROMOTION, NOT_CASTLE, startPieceType + ROOK_W); 545 | validateMove(*board, &move); 546 | if (move.validation == LEGAL) { 547 | addMove 548 | } 549 | attacks &= attacks - 1; 550 | } 551 | 552 | rookBitboard &= rookBitboard - 1; 553 | } 554 | while (queenBitboard) { 555 | int sq = __builtin_ctzll(queenBitboard); 556 | Bitboard rookAttacks = getRookAttacks(sq, board->occupancy); 557 | Bitboard bishopAttacks = getBishopAttacks(sq, board->occupancy); 558 | Bitboard attacks = bishopAttacks | rookAttacks; 559 | attackMask |= attacks; 560 | attacks = attacks & ~friendlyOccupancy; 561 | 562 | while (attacks) { 563 | int indx = __builtin_ctzll(attacks); 564 | Move move = getMove(sq, indx, NO_PROMOTION, NOT_CASTLE, startPieceType + QUEEN_W); 565 | validateMove(*board, &move); 566 | if (move.validation == LEGAL) { 567 | addMove 568 | } 569 | attacks &= attacks - 1; 570 | } 571 | queenBitboard &= queenBitboard - 1; 572 | 573 | } 574 | while (knightBitboard) { 575 | int sq = __builtin_ctzll(knightBitboard); 576 | attackMask |= KNIGHT_MOVEMENT[sq]; 577 | Bitboard target = KNIGHT_MOVEMENT[sq] & ~friendlyOccupancy; 578 | 579 | while (target) { 580 | int indx = __builtin_ctzll(target); 581 | Move move = getMove(sq, indx, NO_PROMOTION, NOT_CASTLE, startPieceType + KNIGHT_W); 582 | validateMove(*board, &move); 583 | if (move.validation == LEGAL) { 584 | addMove 585 | } 586 | target &= target - 1; 587 | } 588 | knightBitboard &= knightBitboard - 1; 589 | } 590 | 591 | // Castling 592 | if (board->turn) { 593 | if (board->castling & K) { 594 | bool pathClear = (board->occupancy & WHITE_CASTLE_K_PATH) == 0; 595 | if (pathClear) { 596 | Move move = getMove(E1, G1, NO_PROMOTION, K, -1); 597 | validateMove(*board, &move); 598 | if (move.validation == LEGAL) { 599 | addMove 600 | } 601 | } 602 | } 603 | if (board->castling & Q) { 604 | bool pathClear = (board->occupancy & WHITE_CASTLE_Q_PATH) == 0; 605 | if (pathClear) { 606 | Move move = getMove(E1, C1, NO_PROMOTION, Q, -1); 607 | validateMove(*board, &move); 608 | if (move.validation == LEGAL) { 609 | addMove 610 | } 611 | } 612 | } 613 | } else { 614 | if (board->castling & k) { 615 | bool pathClear = (board->occupancy & BLACK_CASTLE_K_PATH) == 0; 616 | if (pathClear) { 617 | Move move = getMove(E8, G8, NO_PROMOTION, k, -1); 618 | validateMove(*board, &move); 619 | if (move.validation == LEGAL) { 620 | addMove 621 | } 622 | } 623 | } 624 | if (board->castling & q) { 625 | bool pathClear = (board->occupancy & BLACK_CASTLE_Q_PATH) == 0; 626 | 627 | if (pathClear) { 628 | Move move = getMove(E8, C8, NO_PROMOTION, q, -1); 629 | validateMove(*board, &move); 630 | if (move.validation == LEGAL) { 631 | addMove 632 | } 633 | } 634 | } 635 | } 636 | 637 | board->attacks = attackMask; 638 | return length; 639 | } 640 | -------------------------------------------------------------------------------- /libs/unity/unity.c: -------------------------------------------------------------------------------- 1 | /* ========================================================================= 2 | Unity Project - A Test Framework for C 3 | Copyright (c) 2007-19 Mike Karlesky, Mark VanderVoord, Greg Williams 4 | [Released under MIT License. Please refer to license.txt for details] 5 | ============================================================================ */ 6 | 7 | #include "unity.h" 8 | #include 9 | 10 | #ifdef AVR 11 | #include 12 | #else 13 | #define PROGMEM 14 | #endif 15 | 16 | /* If omitted from header, declare overrideable prototypes here so they're ready for use */ 17 | #ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION 18 | void UNITY_OUTPUT_CHAR(int); 19 | #endif 20 | 21 | /* Helpful macros for us to use here in Assert functions */ 22 | #define UNITY_FAIL_AND_BAIL { Unity.CurrentTestFailed = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } 23 | #define UNITY_IGNORE_AND_BAIL { Unity.CurrentTestIgnored = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } 24 | #define RETURN_IF_FAIL_OR_IGNORE if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) TEST_ABORT() 25 | 26 | struct UNITY_STORAGE_T Unity; 27 | 28 | #ifdef UNITY_OUTPUT_COLOR 29 | const char PROGMEM UnityStrOk[] = "\033[42mOK\033[00m"; 30 | const char PROGMEM UnityStrPass[] = "\033[42mPASS\033[00m"; 31 | const char PROGMEM UnityStrFail[] = "\033[41mFAIL\033[00m"; 32 | const char PROGMEM UnityStrIgnore[] = "\033[43mIGNORE\033[00m"; 33 | #else 34 | const char PROGMEM UnityStrOk[] = "OK"; 35 | const char PROGMEM UnityStrPass[] = "PASS"; 36 | const char PROGMEM UnityStrFail[] = "FAIL"; 37 | const char PROGMEM UnityStrIgnore[] = "IGNORE"; 38 | #endif 39 | static const char PROGMEM UnityStrNull[] = "NULL"; 40 | static const char PROGMEM UnityStrSpacer[] = ". "; 41 | static const char PROGMEM UnityStrExpected[] = " Expected "; 42 | static const char PROGMEM UnityStrWas[] = " Was "; 43 | static const char PROGMEM UnityStrGt[] = " to be greater than "; 44 | static const char PROGMEM UnityStrLt[] = " to be less than "; 45 | static const char PROGMEM UnityStrOrEqual[] = "or equal to "; 46 | static const char PROGMEM UnityStrNotEqual[] = " to be not equal to "; 47 | static const char PROGMEM UnityStrElement[] = " Element "; 48 | static const char PROGMEM UnityStrByte[] = " Byte "; 49 | static const char PROGMEM UnityStrMemory[] = " Memory Mismatch."; 50 | static const char PROGMEM UnityStrDelta[] = " Values Not Within Delta "; 51 | static const char PROGMEM UnityStrPointless[] = " You Asked Me To Compare Nothing, Which Was Pointless."; 52 | static const char PROGMEM UnityStrNullPointerForExpected[] = " Expected pointer to be NULL"; 53 | static const char PROGMEM UnityStrNullPointerForActual[] = " Actual pointer was NULL"; 54 | #ifndef UNITY_EXCLUDE_FLOAT 55 | static const char PROGMEM UnityStrNot[] = "Not "; 56 | static const char PROGMEM UnityStrInf[] = "Infinity"; 57 | static const char PROGMEM UnityStrNegInf[] = "Negative Infinity"; 58 | static const char PROGMEM UnityStrNaN[] = "NaN"; 59 | static const char PROGMEM UnityStrDet[] = "Determinate"; 60 | static const char PROGMEM UnityStrInvalidFloatTrait[] = "Invalid Float Trait"; 61 | #endif 62 | const char PROGMEM UnityStrErrShorthand[] = "Unity Shorthand Support Disabled"; 63 | const char PROGMEM UnityStrErrFloat[] = "Unity Floating Point Disabled"; 64 | const char PROGMEM UnityStrErrDouble[] = "Unity Double Precision Disabled"; 65 | const char PROGMEM UnityStrErr64[] = "Unity 64-bit Support Disabled"; 66 | static const char PROGMEM UnityStrBreaker[] = "-----------------------"; 67 | static const char PROGMEM UnityStrResultsTests[] = " Tests "; 68 | static const char PROGMEM UnityStrResultsFailures[] = " Failures "; 69 | static const char PROGMEM UnityStrResultsIgnored[] = " Ignored "; 70 | #ifndef UNITY_EXCLUDE_DETAILS 71 | static const char PROGMEM UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; 72 | static const char PROGMEM UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; 73 | #endif 74 | /*----------------------------------------------- 75 | * Pretty Printers & Test Result Output Handlers 76 | *-----------------------------------------------*/ 77 | 78 | /*-----------------------------------------------*/ 79 | /* Local helper function to print characters. */ 80 | static void UnityPrintChar(const char* pch) 81 | { 82 | /* printable characters plus CR & LF are printed */ 83 | if ((*pch <= 126) && (*pch >= 32)) 84 | { 85 | UNITY_OUTPUT_CHAR(*pch); 86 | } 87 | /* write escaped carriage returns */ 88 | else if (*pch == 13) 89 | { 90 | UNITY_OUTPUT_CHAR('\\'); 91 | UNITY_OUTPUT_CHAR('r'); 92 | } 93 | /* write escaped line feeds */ 94 | else if (*pch == 10) 95 | { 96 | UNITY_OUTPUT_CHAR('\\'); 97 | UNITY_OUTPUT_CHAR('n'); 98 | } 99 | /* unprintable characters are shown as codes */ 100 | else 101 | { 102 | UNITY_OUTPUT_CHAR('\\'); 103 | UNITY_OUTPUT_CHAR('x'); 104 | UnityPrintNumberHex((UNITY_UINT)*pch, 2); 105 | } 106 | } 107 | 108 | /*-----------------------------------------------*/ 109 | /* Local helper function to print ANSI escape strings e.g. "\033[42m". */ 110 | #ifdef UNITY_OUTPUT_COLOR 111 | static UNITY_UINT UnityPrintAnsiEscapeString(const char* string) 112 | { 113 | const char* pch = string; 114 | UNITY_UINT count = 0; 115 | 116 | while (*pch && (*pch != 'm')) 117 | { 118 | UNITY_OUTPUT_CHAR(*pch); 119 | pch++; 120 | count++; 121 | } 122 | UNITY_OUTPUT_CHAR('m'); 123 | count++; 124 | 125 | return count; 126 | } 127 | #endif 128 | 129 | /*-----------------------------------------------*/ 130 | void UnityPrint(const char* string) 131 | { 132 | const char* pch = string; 133 | 134 | if (pch != NULL) 135 | { 136 | while (*pch) 137 | { 138 | #ifdef UNITY_OUTPUT_COLOR 139 | /* print ANSI escape code */ 140 | if ((*pch == 27) && (*(pch + 1) == '[')) 141 | { 142 | pch += UnityPrintAnsiEscapeString(pch); 143 | continue; 144 | } 145 | #endif 146 | UnityPrintChar(pch); 147 | pch++; 148 | } 149 | } 150 | } 151 | /*-----------------------------------------------*/ 152 | void UnityPrintLen(const char* string, const UNITY_UINT32 length) 153 | { 154 | const char* pch = string; 155 | 156 | if (pch != NULL) 157 | { 158 | while (*pch && ((UNITY_UINT32)(pch - string) < length)) 159 | { 160 | /* printable characters plus CR & LF are printed */ 161 | if ((*pch <= 126) && (*pch >= 32)) 162 | { 163 | UNITY_OUTPUT_CHAR(*pch); 164 | } 165 | /* write escaped carriage returns */ 166 | else if (*pch == 13) 167 | { 168 | UNITY_OUTPUT_CHAR('\\'); 169 | UNITY_OUTPUT_CHAR('r'); 170 | } 171 | /* write escaped line feeds */ 172 | else if (*pch == 10) 173 | { 174 | UNITY_OUTPUT_CHAR('\\'); 175 | UNITY_OUTPUT_CHAR('n'); 176 | } 177 | /* unprintable characters are shown as codes */ 178 | else 179 | { 180 | UNITY_OUTPUT_CHAR('\\'); 181 | UNITY_OUTPUT_CHAR('x'); 182 | UnityPrintNumberHex((UNITY_UINT)*pch, 2); 183 | } 184 | pch++; 185 | } 186 | } 187 | } 188 | 189 | /*-----------------------------------------------*/ 190 | void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style) 191 | { 192 | if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) 193 | { 194 | if (style == UNITY_DISPLAY_STYLE_CHAR) 195 | { 196 | /* printable characters plus CR & LF are printed */ 197 | UNITY_OUTPUT_CHAR('\''); 198 | if ((number <= 126) && (number >= 32)) 199 | { 200 | UNITY_OUTPUT_CHAR((int)number); 201 | } 202 | /* write escaped carriage returns */ 203 | else if (number == 13) 204 | { 205 | UNITY_OUTPUT_CHAR('\\'); 206 | UNITY_OUTPUT_CHAR('r'); 207 | } 208 | /* write escaped line feeds */ 209 | else if (number == 10) 210 | { 211 | UNITY_OUTPUT_CHAR('\\'); 212 | UNITY_OUTPUT_CHAR('n'); 213 | } 214 | /* unprintable characters are shown as codes */ 215 | else 216 | { 217 | UNITY_OUTPUT_CHAR('\\'); 218 | UNITY_OUTPUT_CHAR('x'); 219 | UnityPrintNumberHex((UNITY_UINT)number, 2); 220 | } 221 | UNITY_OUTPUT_CHAR('\''); 222 | } 223 | else 224 | { 225 | UnityPrintNumber(number); 226 | } 227 | } 228 | else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT) 229 | { 230 | UnityPrintNumberUnsigned((UNITY_UINT)number); 231 | } 232 | else 233 | { 234 | UNITY_OUTPUT_CHAR('0'); 235 | UNITY_OUTPUT_CHAR('x'); 236 | UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2)); 237 | } 238 | } 239 | 240 | /*-----------------------------------------------*/ 241 | void UnityPrintNumber(const UNITY_INT number_to_print) 242 | { 243 | UNITY_UINT number = (UNITY_UINT)number_to_print; 244 | 245 | if (number_to_print < 0) 246 | { 247 | /* A negative number, including MIN negative */ 248 | UNITY_OUTPUT_CHAR('-'); 249 | number = (~number) + 1; 250 | } 251 | UnityPrintNumberUnsigned(number); 252 | } 253 | 254 | /*----------------------------------------------- 255 | * basically do an itoa using as little ram as possible */ 256 | void UnityPrintNumberUnsigned(const UNITY_UINT number) 257 | { 258 | UNITY_UINT divisor = 1; 259 | 260 | /* figure out initial divisor */ 261 | while (number / divisor > 9) 262 | { 263 | divisor *= 10; 264 | } 265 | 266 | /* now mod and print, then divide divisor */ 267 | do 268 | { 269 | UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10))); 270 | divisor /= 10; 271 | } while (divisor > 0); 272 | } 273 | 274 | /*-----------------------------------------------*/ 275 | void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print) 276 | { 277 | int nibble; 278 | char nibbles = nibbles_to_print; 279 | 280 | if ((unsigned)nibbles > UNITY_MAX_NIBBLES) 281 | { 282 | nibbles = UNITY_MAX_NIBBLES; 283 | } 284 | 285 | while (nibbles > 0) 286 | { 287 | nibbles--; 288 | nibble = (int)(number >> (nibbles * 4)) & 0x0F; 289 | if (nibble <= 9) 290 | { 291 | UNITY_OUTPUT_CHAR((char)('0' + nibble)); 292 | } 293 | else 294 | { 295 | UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble)); 296 | } 297 | } 298 | } 299 | 300 | /*-----------------------------------------------*/ 301 | void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) 302 | { 303 | UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1); 304 | UNITY_INT32 i; 305 | 306 | for (i = 0; i < UNITY_INT_WIDTH; i++) 307 | { 308 | if (current_bit & mask) 309 | { 310 | if (current_bit & number) 311 | { 312 | UNITY_OUTPUT_CHAR('1'); 313 | } 314 | else 315 | { 316 | UNITY_OUTPUT_CHAR('0'); 317 | } 318 | } 319 | else 320 | { 321 | UNITY_OUTPUT_CHAR('X'); 322 | } 323 | current_bit = current_bit >> 1; 324 | } 325 | } 326 | 327 | /*-----------------------------------------------*/ 328 | #ifndef UNITY_EXCLUDE_FLOAT_PRINT 329 | /* 330 | * This function prints a floating-point value in a format similar to 331 | * printf("%.7g") on a single-precision machine or printf("%.9g") on a 332 | * double-precision machine. The 7th digit won't always be totally correct 333 | * in single-precision operation (for that level of accuracy, a more 334 | * complicated algorithm would be needed). 335 | */ 336 | void UnityPrintFloat(const UNITY_DOUBLE input_number) 337 | { 338 | #ifdef UNITY_INCLUDE_DOUBLE 339 | static const int sig_digits = 9; 340 | static const UNITY_INT32 min_scaled = 100000000; 341 | static const UNITY_INT32 max_scaled = 1000000000; 342 | #else 343 | static const int sig_digits = 7; 344 | static const UNITY_INT32 min_scaled = 1000000; 345 | static const UNITY_INT32 max_scaled = 10000000; 346 | #endif 347 | 348 | UNITY_DOUBLE number = input_number; 349 | 350 | /* print minus sign (does not handle negative zero) */ 351 | if (number < 0.0f) 352 | { 353 | UNITY_OUTPUT_CHAR('-'); 354 | number = -number; 355 | } 356 | 357 | /* handle zero, NaN, and +/- infinity */ 358 | if (number == 0.0f) 359 | { 360 | UnityPrint("0"); 361 | } 362 | else if (isnan(number)) 363 | { 364 | UnityPrint("nan"); 365 | } 366 | else if (isinf(number)) 367 | { 368 | UnityPrint("inf"); 369 | } 370 | else 371 | { 372 | UNITY_INT32 n_int = 0, n; 373 | int exponent = 0; 374 | int decimals, digits; 375 | char buf[16] = {0}; 376 | 377 | /* 378 | * Scale up or down by powers of 10. To minimize rounding error, 379 | * start with a factor/divisor of 10^10, which is the largest 380 | * power of 10 that can be represented exactly. Finally, compute 381 | * (exactly) the remaining power of 10 and perform one more 382 | * multiplication or division. 383 | */ 384 | if (number < 1.0f) 385 | { 386 | UNITY_DOUBLE factor = 1.0f; 387 | 388 | while (number < (UNITY_DOUBLE)max_scaled / 1e10f) { number *= 1e10f; exponent -= 10; } 389 | while (number * factor < (UNITY_DOUBLE)min_scaled) { factor *= 10.0f; exponent--; } 390 | 391 | number *= factor; 392 | } 393 | else if (number > (UNITY_DOUBLE)max_scaled) 394 | { 395 | UNITY_DOUBLE divisor = 1.0f; 396 | 397 | while (number > (UNITY_DOUBLE)min_scaled * 1e10f) { number /= 1e10f; exponent += 10; } 398 | while (number / divisor > (UNITY_DOUBLE)max_scaled) { divisor *= 10.0f; exponent++; } 399 | 400 | number /= divisor; 401 | } 402 | else 403 | { 404 | /* 405 | * In this range, we can split off the integer part before 406 | * doing any multiplications. This reduces rounding error by 407 | * freeing up significant bits in the fractional part. 408 | */ 409 | UNITY_DOUBLE factor = 1.0f; 410 | n_int = (UNITY_INT32)number; 411 | number -= (UNITY_DOUBLE)n_int; 412 | 413 | while (n_int < min_scaled) { n_int *= 10; factor *= 10.0f; exponent--; } 414 | 415 | number *= factor; 416 | } 417 | 418 | /* round to nearest integer */ 419 | n = ((UNITY_INT32)(number + number) + 1) / 2; 420 | 421 | #ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO 422 | /* round to even if exactly between two integers */ 423 | if ((n & 1) && (((UNITY_DOUBLE)n - number) == 0.5f)) 424 | n--; 425 | #endif 426 | 427 | n += n_int; 428 | 429 | if (n >= max_scaled) 430 | { 431 | n = min_scaled; 432 | exponent++; 433 | } 434 | 435 | /* determine where to place decimal point */ 436 | decimals = ((exponent <= 0) && (exponent >= -(sig_digits + 3))) ? (-exponent) : (sig_digits - 1); 437 | exponent += decimals; 438 | 439 | /* truncate trailing zeroes after decimal point */ 440 | while ((decimals > 0) && ((n % 10) == 0)) 441 | { 442 | n /= 10; 443 | decimals--; 444 | } 445 | 446 | /* build up buffer in reverse order */ 447 | digits = 0; 448 | while ((n != 0) || (digits < (decimals + 1))) 449 | { 450 | buf[digits++] = (char)('0' + n % 10); 451 | n /= 10; 452 | } 453 | while (digits > 0) 454 | { 455 | if (digits == decimals) { UNITY_OUTPUT_CHAR('.'); } 456 | UNITY_OUTPUT_CHAR(buf[--digits]); 457 | } 458 | 459 | /* print exponent if needed */ 460 | if (exponent != 0) 461 | { 462 | UNITY_OUTPUT_CHAR('e'); 463 | 464 | if (exponent < 0) 465 | { 466 | UNITY_OUTPUT_CHAR('-'); 467 | exponent = -exponent; 468 | } 469 | else 470 | { 471 | UNITY_OUTPUT_CHAR('+'); 472 | } 473 | 474 | digits = 0; 475 | while ((exponent != 0) || (digits < 2)) 476 | { 477 | buf[digits++] = (char)('0' + exponent % 10); 478 | exponent /= 10; 479 | } 480 | while (digits > 0) 481 | { 482 | UNITY_OUTPUT_CHAR(buf[--digits]); 483 | } 484 | } 485 | } 486 | } 487 | #endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ 488 | 489 | /*-----------------------------------------------*/ 490 | static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line) 491 | { 492 | #ifdef UNITY_OUTPUT_FOR_ECLIPSE 493 | UNITY_OUTPUT_CHAR('('); 494 | UnityPrint(file); 495 | UNITY_OUTPUT_CHAR(':'); 496 | UnityPrintNumber((UNITY_INT)line); 497 | UNITY_OUTPUT_CHAR(')'); 498 | UNITY_OUTPUT_CHAR(' '); 499 | UnityPrint(Unity.CurrentTestName); 500 | UNITY_OUTPUT_CHAR(':'); 501 | #else 502 | #ifdef UNITY_OUTPUT_FOR_IAR_WORKBENCH 503 | UnityPrint("'); 509 | UnityPrint(Unity.CurrentTestName); 510 | UnityPrint(" "); 511 | #else 512 | #ifdef UNITY_OUTPUT_FOR_QT_CREATOR 513 | UnityPrint("file://"); 514 | UnityPrint(file); 515 | UNITY_OUTPUT_CHAR(':'); 516 | UnityPrintNumber((UNITY_INT)line); 517 | UNITY_OUTPUT_CHAR(' '); 518 | UnityPrint(Unity.CurrentTestName); 519 | UNITY_OUTPUT_CHAR(':'); 520 | #else 521 | UnityPrint(file); 522 | UNITY_OUTPUT_CHAR(':'); 523 | UnityPrintNumber((UNITY_INT)line); 524 | UNITY_OUTPUT_CHAR(':'); 525 | UnityPrint(Unity.CurrentTestName); 526 | UNITY_OUTPUT_CHAR(':'); 527 | #endif 528 | #endif 529 | #endif 530 | } 531 | 532 | /*-----------------------------------------------*/ 533 | static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line) 534 | { 535 | UnityTestResultsBegin(Unity.TestFile, line); 536 | UnityPrint(UnityStrFail); 537 | UNITY_OUTPUT_CHAR(':'); 538 | } 539 | 540 | /*-----------------------------------------------*/ 541 | void UnityConcludeTest(void) 542 | { 543 | if (Unity.CurrentTestIgnored) 544 | { 545 | Unity.TestIgnores++; 546 | } 547 | else if (!Unity.CurrentTestFailed) 548 | { 549 | UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber); 550 | UnityPrint(UnityStrPass); 551 | } 552 | else 553 | { 554 | Unity.TestFailures++; 555 | } 556 | 557 | Unity.CurrentTestFailed = 0; 558 | Unity.CurrentTestIgnored = 0; 559 | UNITY_PRINT_EXEC_TIME(); 560 | UNITY_PRINT_EOL(); 561 | UNITY_FLUSH_CALL(); 562 | } 563 | 564 | /*-----------------------------------------------*/ 565 | static void UnityAddMsgIfSpecified(const char* msg) 566 | { 567 | if (msg) 568 | { 569 | UnityPrint(UnityStrSpacer); 570 | 571 | #ifdef UNITY_PRINT_TEST_CONTEXT 572 | UNITY_PRINT_TEST_CONTEXT(); 573 | #endif 574 | #ifndef UNITY_EXCLUDE_DETAILS 575 | if (Unity.CurrentDetail1) 576 | { 577 | UnityPrint(UnityStrDetail1Name); 578 | UnityPrint(Unity.CurrentDetail1); 579 | if (Unity.CurrentDetail2) 580 | { 581 | UnityPrint(UnityStrDetail2Name); 582 | UnityPrint(Unity.CurrentDetail2); 583 | } 584 | UnityPrint(UnityStrSpacer); 585 | } 586 | #endif 587 | UnityPrint(msg); 588 | } 589 | } 590 | 591 | /*-----------------------------------------------*/ 592 | static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual) 593 | { 594 | UnityPrint(UnityStrExpected); 595 | if (expected != NULL) 596 | { 597 | UNITY_OUTPUT_CHAR('\''); 598 | UnityPrint(expected); 599 | UNITY_OUTPUT_CHAR('\''); 600 | } 601 | else 602 | { 603 | UnityPrint(UnityStrNull); 604 | } 605 | UnityPrint(UnityStrWas); 606 | if (actual != NULL) 607 | { 608 | UNITY_OUTPUT_CHAR('\''); 609 | UnityPrint(actual); 610 | UNITY_OUTPUT_CHAR('\''); 611 | } 612 | else 613 | { 614 | UnityPrint(UnityStrNull); 615 | } 616 | } 617 | 618 | /*-----------------------------------------------*/ 619 | static void UnityPrintExpectedAndActualStringsLen(const char* expected, 620 | const char* actual, 621 | const UNITY_UINT32 length) 622 | { 623 | UnityPrint(UnityStrExpected); 624 | if (expected != NULL) 625 | { 626 | UNITY_OUTPUT_CHAR('\''); 627 | UnityPrintLen(expected, length); 628 | UNITY_OUTPUT_CHAR('\''); 629 | } 630 | else 631 | { 632 | UnityPrint(UnityStrNull); 633 | } 634 | UnityPrint(UnityStrWas); 635 | if (actual != NULL) 636 | { 637 | UNITY_OUTPUT_CHAR('\''); 638 | UnityPrintLen(actual, length); 639 | UNITY_OUTPUT_CHAR('\''); 640 | } 641 | else 642 | { 643 | UnityPrint(UnityStrNull); 644 | } 645 | } 646 | 647 | /*----------------------------------------------- 648 | * Assertion & Control Helpers 649 | *-----------------------------------------------*/ 650 | 651 | /*-----------------------------------------------*/ 652 | static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected, 653 | UNITY_INTERNAL_PTR actual, 654 | const UNITY_LINE_TYPE lineNumber, 655 | const char* msg) 656 | { 657 | /* Both are NULL or same pointer */ 658 | if (expected == actual) { return 0; } 659 | 660 | /* print and return true if just expected is NULL */ 661 | if (expected == NULL) 662 | { 663 | UnityTestResultsFailBegin(lineNumber); 664 | UnityPrint(UnityStrNullPointerForExpected); 665 | UnityAddMsgIfSpecified(msg); 666 | return 1; 667 | } 668 | 669 | /* print and return true if just actual is NULL */ 670 | if (actual == NULL) 671 | { 672 | UnityTestResultsFailBegin(lineNumber); 673 | UnityPrint(UnityStrNullPointerForActual); 674 | UnityAddMsgIfSpecified(msg); 675 | return 1; 676 | } 677 | 678 | return 0; /* return false if neither is NULL */ 679 | } 680 | 681 | /*----------------------------------------------- 682 | * Assertion Functions 683 | *-----------------------------------------------*/ 684 | 685 | /*-----------------------------------------------*/ 686 | void UnityAssertBits(const UNITY_INT mask, 687 | const UNITY_INT expected, 688 | const UNITY_INT actual, 689 | const char* msg, 690 | const UNITY_LINE_TYPE lineNumber) 691 | { 692 | RETURN_IF_FAIL_OR_IGNORE; 693 | 694 | if ((mask & expected) != (mask & actual)) 695 | { 696 | UnityTestResultsFailBegin(lineNumber); 697 | UnityPrint(UnityStrExpected); 698 | UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected); 699 | UnityPrint(UnityStrWas); 700 | UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual); 701 | UnityAddMsgIfSpecified(msg); 702 | UNITY_FAIL_AND_BAIL; 703 | } 704 | } 705 | 706 | /*-----------------------------------------------*/ 707 | void UnityAssertEqualNumber(const UNITY_INT expected, 708 | const UNITY_INT actual, 709 | const char* msg, 710 | const UNITY_LINE_TYPE lineNumber, 711 | const UNITY_DISPLAY_STYLE_T style) 712 | { 713 | RETURN_IF_FAIL_OR_IGNORE; 714 | 715 | if (expected != actual) 716 | { 717 | UnityTestResultsFailBegin(lineNumber); 718 | UnityPrint(UnityStrExpected); 719 | UnityPrintNumberByStyle(expected, style); 720 | UnityPrint(UnityStrWas); 721 | UnityPrintNumberByStyle(actual, style); 722 | UnityAddMsgIfSpecified(msg); 723 | UNITY_FAIL_AND_BAIL; 724 | } 725 | } 726 | 727 | /*-----------------------------------------------*/ 728 | void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, 729 | const UNITY_INT actual, 730 | const UNITY_COMPARISON_T compare, 731 | const char *msg, 732 | const UNITY_LINE_TYPE lineNumber, 733 | const UNITY_DISPLAY_STYLE_T style) 734 | { 735 | int failed = 0; 736 | RETURN_IF_FAIL_OR_IGNORE; 737 | 738 | if ((threshold == actual) && (compare & UNITY_EQUAL_TO)) { return; } 739 | if ((threshold == actual)) { failed = 1; } 740 | 741 | if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) 742 | { 743 | if ((actual > threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } 744 | if ((actual < threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } 745 | } 746 | else /* UINT or HEX */ 747 | { 748 | if (((UNITY_UINT)actual > (UNITY_UINT)threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } 749 | if (((UNITY_UINT)actual < (UNITY_UINT)threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } 750 | } 751 | 752 | if (failed) 753 | { 754 | UnityTestResultsFailBegin(lineNumber); 755 | UnityPrint(UnityStrExpected); 756 | UnityPrintNumberByStyle(actual, style); 757 | if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); } 758 | if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); } 759 | if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); } 760 | if (compare == UNITY_NOT_EQUAL) { UnityPrint(UnityStrNotEqual); } 761 | UnityPrintNumberByStyle(threshold, style); 762 | UnityAddMsgIfSpecified(msg); 763 | UNITY_FAIL_AND_BAIL; 764 | } 765 | } 766 | 767 | #define UnityPrintPointlessAndBail() \ 768 | { \ 769 | UnityTestResultsFailBegin(lineNumber); \ 770 | UnityPrint(UnityStrPointless); \ 771 | UnityAddMsgIfSpecified(msg); \ 772 | UNITY_FAIL_AND_BAIL; } 773 | 774 | /*-----------------------------------------------*/ 775 | void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, 776 | UNITY_INTERNAL_PTR actual, 777 | const UNITY_UINT32 num_elements, 778 | const char* msg, 779 | const UNITY_LINE_TYPE lineNumber, 780 | const UNITY_DISPLAY_STYLE_T style, 781 | const UNITY_FLAGS_T flags) 782 | { 783 | UNITY_UINT32 elements = num_elements; 784 | unsigned int length = style & 0xF; 785 | unsigned int increment = 0; 786 | 787 | RETURN_IF_FAIL_OR_IGNORE; 788 | 789 | if (num_elements == 0) 790 | { 791 | UnityPrintPointlessAndBail(); 792 | } 793 | 794 | if (expected == actual) 795 | { 796 | return; /* Both are NULL or same pointer */ 797 | } 798 | 799 | if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) 800 | { 801 | UNITY_FAIL_AND_BAIL; 802 | } 803 | 804 | while ((elements > 0) && (elements--)) 805 | { 806 | UNITY_INT expect_val; 807 | UNITY_INT actual_val; 808 | 809 | switch (length) 810 | { 811 | case 1: 812 | expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; 813 | actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; 814 | increment = sizeof(UNITY_INT8); 815 | break; 816 | 817 | case 2: 818 | expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; 819 | actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; 820 | increment = sizeof(UNITY_INT16); 821 | break; 822 | 823 | #ifdef UNITY_SUPPORT_64 824 | case 8: 825 | expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; 826 | actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; 827 | increment = sizeof(UNITY_INT64); 828 | break; 829 | #endif 830 | 831 | default: /* default is length 4 bytes */ 832 | case 4: 833 | expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; 834 | actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; 835 | increment = sizeof(UNITY_INT32); 836 | length = 4; 837 | break; 838 | } 839 | 840 | if (expect_val != actual_val) 841 | { 842 | if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) 843 | { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ 844 | UNITY_INT mask = 1; 845 | mask = (mask << 8 * length) - 1; 846 | expect_val &= mask; 847 | actual_val &= mask; 848 | } 849 | UnityTestResultsFailBegin(lineNumber); 850 | UnityPrint(UnityStrElement); 851 | UnityPrintNumberUnsigned(num_elements - elements - 1); 852 | UnityPrint(UnityStrExpected); 853 | UnityPrintNumberByStyle(expect_val, style); 854 | UnityPrint(UnityStrWas); 855 | UnityPrintNumberByStyle(actual_val, style); 856 | UnityAddMsgIfSpecified(msg); 857 | UNITY_FAIL_AND_BAIL; 858 | } 859 | /* Walk through array by incrementing the pointers */ 860 | if (flags == UNITY_ARRAY_TO_ARRAY) 861 | { 862 | expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment); 863 | } 864 | actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment); 865 | } 866 | } 867 | 868 | /*-----------------------------------------------*/ 869 | #ifndef UNITY_EXCLUDE_FLOAT 870 | /* Wrap this define in a function with variable types as float or double */ 871 | #define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff) \ 872 | if (isinf(expected) && isinf(actual) && (((expected) < 0) == ((actual) < 0))) return 1; \ 873 | if (UNITY_NAN_CHECK) return 1; \ 874 | (diff) = (actual) - (expected); \ 875 | if ((diff) < 0) (diff) = -(diff); \ 876 | if ((delta) < 0) (delta) = -(delta); \ 877 | return !(isnan(diff) || isinf(diff) || ((diff) > (delta))) 878 | /* This first part of this condition will catch any NaN or Infinite values */ 879 | #ifndef UNITY_NAN_NOT_EQUAL_NAN 880 | #define UNITY_NAN_CHECK isnan(expected) && isnan(actual) 881 | #else 882 | #define UNITY_NAN_CHECK 0 883 | #endif 884 | 885 | #ifndef UNITY_EXCLUDE_FLOAT_PRINT 886 | #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ 887 | { \ 888 | UnityPrint(UnityStrExpected); \ 889 | UnityPrintFloat(expected); \ 890 | UnityPrint(UnityStrWas); \ 891 | UnityPrintFloat(actual); } 892 | #else 893 | #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ 894 | UnityPrint(UnityStrDelta) 895 | #endif /* UNITY_EXCLUDE_FLOAT_PRINT */ 896 | 897 | /*-----------------------------------------------*/ 898 | static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual) 899 | { 900 | UNITY_FLOAT diff; 901 | UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); 902 | } 903 | 904 | /*-----------------------------------------------*/ 905 | void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, 906 | UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, 907 | const UNITY_UINT32 num_elements, 908 | const char* msg, 909 | const UNITY_LINE_TYPE lineNumber, 910 | const UNITY_FLAGS_T flags) 911 | { 912 | UNITY_UINT32 elements = num_elements; 913 | UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_expected = expected; 914 | UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_actual = actual; 915 | 916 | RETURN_IF_FAIL_OR_IGNORE; 917 | 918 | if (elements == 0) 919 | { 920 | UnityPrintPointlessAndBail(); 921 | } 922 | 923 | if (expected == actual) 924 | { 925 | return; /* Both are NULL or same pointer */ 926 | } 927 | 928 | if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) 929 | { 930 | UNITY_FAIL_AND_BAIL; 931 | } 932 | 933 | while (elements--) 934 | { 935 | if (!UnityFloatsWithin(*ptr_expected * UNITY_FLOAT_PRECISION, *ptr_expected, *ptr_actual)) 936 | { 937 | UnityTestResultsFailBegin(lineNumber); 938 | UnityPrint(UnityStrElement); 939 | UnityPrintNumberUnsigned(num_elements - elements - 1); 940 | UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual); 941 | UnityAddMsgIfSpecified(msg); 942 | UNITY_FAIL_AND_BAIL; 943 | } 944 | if (flags == UNITY_ARRAY_TO_ARRAY) 945 | { 946 | ptr_expected++; 947 | } 948 | ptr_actual++; 949 | } 950 | } 951 | 952 | /*-----------------------------------------------*/ 953 | void UnityAssertFloatsWithin(const UNITY_FLOAT delta, 954 | const UNITY_FLOAT expected, 955 | const UNITY_FLOAT actual, 956 | const char* msg, 957 | const UNITY_LINE_TYPE lineNumber) 958 | { 959 | RETURN_IF_FAIL_OR_IGNORE; 960 | 961 | 962 | if (!UnityFloatsWithin(delta, expected, actual)) 963 | { 964 | UnityTestResultsFailBegin(lineNumber); 965 | UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual); 966 | UnityAddMsgIfSpecified(msg); 967 | UNITY_FAIL_AND_BAIL; 968 | } 969 | } 970 | 971 | /*-----------------------------------------------*/ 972 | void UnityAssertFloatSpecial(const UNITY_FLOAT actual, 973 | const char* msg, 974 | const UNITY_LINE_TYPE lineNumber, 975 | const UNITY_FLOAT_TRAIT_T style) 976 | { 977 | const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; 978 | UNITY_INT should_be_trait = ((UNITY_INT)style & 1); 979 | UNITY_INT is_trait = !should_be_trait; 980 | UNITY_INT trait_index = (UNITY_INT)(style >> 1); 981 | 982 | RETURN_IF_FAIL_OR_IGNORE; 983 | 984 | switch (style) 985 | { 986 | case UNITY_FLOAT_IS_INF: 987 | case UNITY_FLOAT_IS_NOT_INF: 988 | is_trait = isinf(actual) && (actual > 0); 989 | break; 990 | case UNITY_FLOAT_IS_NEG_INF: 991 | case UNITY_FLOAT_IS_NOT_NEG_INF: 992 | is_trait = isinf(actual) && (actual < 0); 993 | break; 994 | 995 | case UNITY_FLOAT_IS_NAN: 996 | case UNITY_FLOAT_IS_NOT_NAN: 997 | is_trait = isnan(actual) ? 1 : 0; 998 | break; 999 | 1000 | case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ 1001 | case UNITY_FLOAT_IS_NOT_DET: 1002 | is_trait = !isinf(actual) && !isnan(actual); 1003 | break; 1004 | 1005 | case UNITY_FLOAT_INVALID_TRAIT: 1006 | default: 1007 | trait_index = 0; 1008 | trait_names[0] = UnityStrInvalidFloatTrait; 1009 | break; 1010 | } 1011 | 1012 | if (is_trait != should_be_trait) 1013 | { 1014 | UnityTestResultsFailBegin(lineNumber); 1015 | UnityPrint(UnityStrExpected); 1016 | if (!should_be_trait) 1017 | { 1018 | UnityPrint(UnityStrNot); 1019 | } 1020 | UnityPrint(trait_names[trait_index]); 1021 | UnityPrint(UnityStrWas); 1022 | #ifndef UNITY_EXCLUDE_FLOAT_PRINT 1023 | UnityPrintFloat((UNITY_DOUBLE)actual); 1024 | #else 1025 | if (should_be_trait) 1026 | { 1027 | UnityPrint(UnityStrNot); 1028 | } 1029 | UnityPrint(trait_names[trait_index]); 1030 | #endif 1031 | UnityAddMsgIfSpecified(msg); 1032 | UNITY_FAIL_AND_BAIL; 1033 | } 1034 | } 1035 | 1036 | #endif /* not UNITY_EXCLUDE_FLOAT */ 1037 | 1038 | /*-----------------------------------------------*/ 1039 | #ifndef UNITY_EXCLUDE_DOUBLE 1040 | static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual) 1041 | { 1042 | UNITY_DOUBLE diff; 1043 | UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); 1044 | } 1045 | 1046 | /*-----------------------------------------------*/ 1047 | void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, 1048 | UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, 1049 | const UNITY_UINT32 num_elements, 1050 | const char* msg, 1051 | const UNITY_LINE_TYPE lineNumber, 1052 | const UNITY_FLAGS_T flags) 1053 | { 1054 | UNITY_UINT32 elements = num_elements; 1055 | UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_expected = expected; 1056 | UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_actual = actual; 1057 | 1058 | RETURN_IF_FAIL_OR_IGNORE; 1059 | 1060 | if (elements == 0) 1061 | { 1062 | UnityPrintPointlessAndBail(); 1063 | } 1064 | 1065 | if (expected == actual) 1066 | { 1067 | return; /* Both are NULL or same pointer */ 1068 | } 1069 | 1070 | if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) 1071 | { 1072 | UNITY_FAIL_AND_BAIL; 1073 | } 1074 | 1075 | while (elements--) 1076 | { 1077 | if (!UnityDoublesWithin(*ptr_expected * UNITY_DOUBLE_PRECISION, *ptr_expected, *ptr_actual)) 1078 | { 1079 | UnityTestResultsFailBegin(lineNumber); 1080 | UnityPrint(UnityStrElement); 1081 | UnityPrintNumberUnsigned(num_elements - elements - 1); 1082 | UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual); 1083 | UnityAddMsgIfSpecified(msg); 1084 | UNITY_FAIL_AND_BAIL; 1085 | } 1086 | if (flags == UNITY_ARRAY_TO_ARRAY) 1087 | { 1088 | ptr_expected++; 1089 | } 1090 | ptr_actual++; 1091 | } 1092 | } 1093 | 1094 | /*-----------------------------------------------*/ 1095 | void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, 1096 | const UNITY_DOUBLE expected, 1097 | const UNITY_DOUBLE actual, 1098 | const char* msg, 1099 | const UNITY_LINE_TYPE lineNumber) 1100 | { 1101 | RETURN_IF_FAIL_OR_IGNORE; 1102 | 1103 | if (!UnityDoublesWithin(delta, expected, actual)) 1104 | { 1105 | UnityTestResultsFailBegin(lineNumber); 1106 | UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual); 1107 | UnityAddMsgIfSpecified(msg); 1108 | UNITY_FAIL_AND_BAIL; 1109 | } 1110 | } 1111 | 1112 | /*-----------------------------------------------*/ 1113 | void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, 1114 | const char* msg, 1115 | const UNITY_LINE_TYPE lineNumber, 1116 | const UNITY_FLOAT_TRAIT_T style) 1117 | { 1118 | const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; 1119 | UNITY_INT should_be_trait = ((UNITY_INT)style & 1); 1120 | UNITY_INT is_trait = !should_be_trait; 1121 | UNITY_INT trait_index = (UNITY_INT)(style >> 1); 1122 | 1123 | RETURN_IF_FAIL_OR_IGNORE; 1124 | 1125 | switch (style) 1126 | { 1127 | case UNITY_FLOAT_IS_INF: 1128 | case UNITY_FLOAT_IS_NOT_INF: 1129 | is_trait = isinf(actual) && (actual > 0); 1130 | break; 1131 | case UNITY_FLOAT_IS_NEG_INF: 1132 | case UNITY_FLOAT_IS_NOT_NEG_INF: 1133 | is_trait = isinf(actual) && (actual < 0); 1134 | break; 1135 | 1136 | case UNITY_FLOAT_IS_NAN: 1137 | case UNITY_FLOAT_IS_NOT_NAN: 1138 | is_trait = isnan(actual) ? 1 : 0; 1139 | break; 1140 | 1141 | case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ 1142 | case UNITY_FLOAT_IS_NOT_DET: 1143 | is_trait = !isinf(actual) && !isnan(actual); 1144 | break; 1145 | 1146 | case UNITY_FLOAT_INVALID_TRAIT: 1147 | default: 1148 | trait_index = 0; 1149 | trait_names[0] = UnityStrInvalidFloatTrait; 1150 | break; 1151 | } 1152 | 1153 | if (is_trait != should_be_trait) 1154 | { 1155 | UnityTestResultsFailBegin(lineNumber); 1156 | UnityPrint(UnityStrExpected); 1157 | if (!should_be_trait) 1158 | { 1159 | UnityPrint(UnityStrNot); 1160 | } 1161 | UnityPrint(trait_names[trait_index]); 1162 | UnityPrint(UnityStrWas); 1163 | #ifndef UNITY_EXCLUDE_FLOAT_PRINT 1164 | UnityPrintFloat(actual); 1165 | #else 1166 | if (should_be_trait) 1167 | { 1168 | UnityPrint(UnityStrNot); 1169 | } 1170 | UnityPrint(trait_names[trait_index]); 1171 | #endif 1172 | UnityAddMsgIfSpecified(msg); 1173 | UNITY_FAIL_AND_BAIL; 1174 | } 1175 | } 1176 | 1177 | #endif /* not UNITY_EXCLUDE_DOUBLE */ 1178 | 1179 | /*-----------------------------------------------*/ 1180 | void UnityAssertNumbersWithin(const UNITY_UINT delta, 1181 | const UNITY_INT expected, 1182 | const UNITY_INT actual, 1183 | const char* msg, 1184 | const UNITY_LINE_TYPE lineNumber, 1185 | const UNITY_DISPLAY_STYLE_T style) 1186 | { 1187 | RETURN_IF_FAIL_OR_IGNORE; 1188 | 1189 | if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) 1190 | { 1191 | if (actual > expected) 1192 | { 1193 | Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta); 1194 | } 1195 | else 1196 | { 1197 | Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta); 1198 | } 1199 | } 1200 | else 1201 | { 1202 | if ((UNITY_UINT)actual > (UNITY_UINT)expected) 1203 | { 1204 | Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta); 1205 | } 1206 | else 1207 | { 1208 | Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta); 1209 | } 1210 | } 1211 | 1212 | if (Unity.CurrentTestFailed) 1213 | { 1214 | UnityTestResultsFailBegin(lineNumber); 1215 | UnityPrint(UnityStrDelta); 1216 | UnityPrintNumberByStyle((UNITY_INT)delta, style); 1217 | UnityPrint(UnityStrExpected); 1218 | UnityPrintNumberByStyle(expected, style); 1219 | UnityPrint(UnityStrWas); 1220 | UnityPrintNumberByStyle(actual, style); 1221 | UnityAddMsgIfSpecified(msg); 1222 | UNITY_FAIL_AND_BAIL; 1223 | } 1224 | } 1225 | 1226 | /*-----------------------------------------------*/ 1227 | void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, 1228 | UNITY_INTERNAL_PTR expected, 1229 | UNITY_INTERNAL_PTR actual, 1230 | const UNITY_UINT32 num_elements, 1231 | const char* msg, 1232 | const UNITY_LINE_TYPE lineNumber, 1233 | const UNITY_DISPLAY_STYLE_T style, 1234 | const UNITY_FLAGS_T flags) 1235 | { 1236 | UNITY_UINT32 elements = num_elements; 1237 | unsigned int length = style & 0xF; 1238 | unsigned int increment = 0; 1239 | 1240 | RETURN_IF_FAIL_OR_IGNORE; 1241 | 1242 | if (num_elements == 0) 1243 | { 1244 | UnityPrintPointlessAndBail(); 1245 | } 1246 | 1247 | if (expected == actual) 1248 | { 1249 | return; /* Both are NULL or same pointer */ 1250 | } 1251 | 1252 | if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) 1253 | { 1254 | UNITY_FAIL_AND_BAIL; 1255 | } 1256 | 1257 | while ((elements > 0) && (elements--)) 1258 | { 1259 | UNITY_INT expect_val; 1260 | UNITY_INT actual_val; 1261 | 1262 | switch (length) 1263 | { 1264 | case 1: 1265 | expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; 1266 | actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; 1267 | increment = sizeof(UNITY_INT8); 1268 | break; 1269 | 1270 | case 2: 1271 | expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; 1272 | actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; 1273 | increment = sizeof(UNITY_INT16); 1274 | break; 1275 | 1276 | #ifdef UNITY_SUPPORT_64 1277 | case 8: 1278 | expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; 1279 | actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; 1280 | increment = sizeof(UNITY_INT64); 1281 | break; 1282 | #endif 1283 | 1284 | default: /* default is length 4 bytes */ 1285 | case 4: 1286 | expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; 1287 | actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; 1288 | increment = sizeof(UNITY_INT32); 1289 | length = 4; 1290 | break; 1291 | } 1292 | 1293 | if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) 1294 | { 1295 | if (actual_val > expect_val) 1296 | { 1297 | Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); 1298 | } 1299 | else 1300 | { 1301 | Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); 1302 | } 1303 | } 1304 | else 1305 | { 1306 | if ((UNITY_UINT)actual_val > (UNITY_UINT)expect_val) 1307 | { 1308 | Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); 1309 | } 1310 | else 1311 | { 1312 | Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); 1313 | } 1314 | } 1315 | 1316 | if (Unity.CurrentTestFailed) 1317 | { 1318 | if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) 1319 | { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ 1320 | UNITY_INT mask = 1; 1321 | mask = (mask << 8 * length) - 1; 1322 | expect_val &= mask; 1323 | actual_val &= mask; 1324 | } 1325 | UnityTestResultsFailBegin(lineNumber); 1326 | UnityPrint(UnityStrDelta); 1327 | UnityPrintNumberByStyle((UNITY_INT)delta, style); 1328 | UnityPrint(UnityStrElement); 1329 | UnityPrintNumberUnsigned(num_elements - elements - 1); 1330 | UnityPrint(UnityStrExpected); 1331 | UnityPrintNumberByStyle(expect_val, style); 1332 | UnityPrint(UnityStrWas); 1333 | UnityPrintNumberByStyle(actual_val, style); 1334 | UnityAddMsgIfSpecified(msg); 1335 | UNITY_FAIL_AND_BAIL; 1336 | } 1337 | /* Walk through array by incrementing the pointers */ 1338 | if (flags == UNITY_ARRAY_TO_ARRAY) 1339 | { 1340 | expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment); 1341 | } 1342 | actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment); 1343 | } 1344 | } 1345 | 1346 | /*-----------------------------------------------*/ 1347 | void UnityAssertEqualString(const char* expected, 1348 | const char* actual, 1349 | const char* msg, 1350 | const UNITY_LINE_TYPE lineNumber) 1351 | { 1352 | UNITY_UINT32 i; 1353 | 1354 | RETURN_IF_FAIL_OR_IGNORE; 1355 | 1356 | /* if both pointers not null compare the strings */ 1357 | if (expected && actual) 1358 | { 1359 | for (i = 0; expected[i] || actual[i]; i++) 1360 | { 1361 | if (expected[i] != actual[i]) 1362 | { 1363 | Unity.CurrentTestFailed = 1; 1364 | break; 1365 | } 1366 | } 1367 | } 1368 | else 1369 | { /* handle case of one pointers being null (if both null, test should pass) */ 1370 | if (expected != actual) 1371 | { 1372 | Unity.CurrentTestFailed = 1; 1373 | } 1374 | } 1375 | 1376 | if (Unity.CurrentTestFailed) 1377 | { 1378 | UnityTestResultsFailBegin(lineNumber); 1379 | UnityPrintExpectedAndActualStrings(expected, actual); 1380 | UnityAddMsgIfSpecified(msg); 1381 | UNITY_FAIL_AND_BAIL; 1382 | } 1383 | } 1384 | 1385 | /*-----------------------------------------------*/ 1386 | void UnityAssertEqualStringLen(const char* expected, 1387 | const char* actual, 1388 | const UNITY_UINT32 length, 1389 | const char* msg, 1390 | const UNITY_LINE_TYPE lineNumber) 1391 | { 1392 | UNITY_UINT32 i; 1393 | 1394 | RETURN_IF_FAIL_OR_IGNORE; 1395 | 1396 | /* if both pointers not null compare the strings */ 1397 | if (expected && actual) 1398 | { 1399 | for (i = 0; (i < length) && (expected[i] || actual[i]); i++) 1400 | { 1401 | if (expected[i] != actual[i]) 1402 | { 1403 | Unity.CurrentTestFailed = 1; 1404 | break; 1405 | } 1406 | } 1407 | } 1408 | else 1409 | { /* handle case of one pointers being null (if both null, test should pass) */ 1410 | if (expected != actual) 1411 | { 1412 | Unity.CurrentTestFailed = 1; 1413 | } 1414 | } 1415 | 1416 | if (Unity.CurrentTestFailed) 1417 | { 1418 | UnityTestResultsFailBegin(lineNumber); 1419 | UnityPrintExpectedAndActualStringsLen(expected, actual, length); 1420 | UnityAddMsgIfSpecified(msg); 1421 | UNITY_FAIL_AND_BAIL; 1422 | } 1423 | } 1424 | 1425 | /*-----------------------------------------------*/ 1426 | void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, 1427 | const char** actual, 1428 | const UNITY_UINT32 num_elements, 1429 | const char* msg, 1430 | const UNITY_LINE_TYPE lineNumber, 1431 | const UNITY_FLAGS_T flags) 1432 | { 1433 | UNITY_UINT32 i = 0; 1434 | UNITY_UINT32 j = 0; 1435 | const char* expd = NULL; 1436 | const char* act = NULL; 1437 | 1438 | RETURN_IF_FAIL_OR_IGNORE; 1439 | 1440 | /* if no elements, it's an error */ 1441 | if (num_elements == 0) 1442 | { 1443 | UnityPrintPointlessAndBail(); 1444 | } 1445 | 1446 | if ((const void*)expected == (const void*)actual) 1447 | { 1448 | return; /* Both are NULL or same pointer */ 1449 | } 1450 | 1451 | if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) 1452 | { 1453 | UNITY_FAIL_AND_BAIL; 1454 | } 1455 | 1456 | if (flags != UNITY_ARRAY_TO_ARRAY) 1457 | { 1458 | expd = (const char*)expected; 1459 | } 1460 | 1461 | do 1462 | { 1463 | act = actual[j]; 1464 | if (flags == UNITY_ARRAY_TO_ARRAY) 1465 | { 1466 | expd = ((const char* const*)expected)[j]; 1467 | } 1468 | 1469 | /* if both pointers not null compare the strings */ 1470 | if (expd && act) 1471 | { 1472 | for (i = 0; expd[i] || act[i]; i++) 1473 | { 1474 | if (expd[i] != act[i]) 1475 | { 1476 | Unity.CurrentTestFailed = 1; 1477 | break; 1478 | } 1479 | } 1480 | } 1481 | else 1482 | { /* handle case of one pointers being null (if both null, test should pass) */ 1483 | if (expd != act) 1484 | { 1485 | Unity.CurrentTestFailed = 1; 1486 | } 1487 | } 1488 | 1489 | if (Unity.CurrentTestFailed) 1490 | { 1491 | UnityTestResultsFailBegin(lineNumber); 1492 | if (num_elements > 1) 1493 | { 1494 | UnityPrint(UnityStrElement); 1495 | UnityPrintNumberUnsigned(j); 1496 | } 1497 | UnityPrintExpectedAndActualStrings(expd, act); 1498 | UnityAddMsgIfSpecified(msg); 1499 | UNITY_FAIL_AND_BAIL; 1500 | } 1501 | } while (++j < num_elements); 1502 | } 1503 | 1504 | /*-----------------------------------------------*/ 1505 | void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected, 1506 | UNITY_INTERNAL_PTR actual, 1507 | const UNITY_UINT32 length, 1508 | const UNITY_UINT32 num_elements, 1509 | const char* msg, 1510 | const UNITY_LINE_TYPE lineNumber, 1511 | const UNITY_FLAGS_T flags) 1512 | { 1513 | UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; 1514 | UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual; 1515 | UNITY_UINT32 elements = num_elements; 1516 | UNITY_UINT32 bytes; 1517 | 1518 | RETURN_IF_FAIL_OR_IGNORE; 1519 | 1520 | if ((elements == 0) || (length == 0)) 1521 | { 1522 | UnityPrintPointlessAndBail(); 1523 | } 1524 | 1525 | if (expected == actual) 1526 | { 1527 | return; /* Both are NULL or same pointer */ 1528 | } 1529 | 1530 | if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) 1531 | { 1532 | UNITY_FAIL_AND_BAIL; 1533 | } 1534 | 1535 | while (elements--) 1536 | { 1537 | bytes = length; 1538 | while (bytes--) 1539 | { 1540 | if (*ptr_exp != *ptr_act) 1541 | { 1542 | UnityTestResultsFailBegin(lineNumber); 1543 | UnityPrint(UnityStrMemory); 1544 | if (num_elements > 1) 1545 | { 1546 | UnityPrint(UnityStrElement); 1547 | UnityPrintNumberUnsigned(num_elements - elements - 1); 1548 | } 1549 | UnityPrint(UnityStrByte); 1550 | UnityPrintNumberUnsigned(length - bytes - 1); 1551 | UnityPrint(UnityStrExpected); 1552 | UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8); 1553 | UnityPrint(UnityStrWas); 1554 | UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8); 1555 | UnityAddMsgIfSpecified(msg); 1556 | UNITY_FAIL_AND_BAIL; 1557 | } 1558 | ptr_exp++; 1559 | ptr_act++; 1560 | } 1561 | if (flags == UNITY_ARRAY_TO_VAL) 1562 | { 1563 | ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; 1564 | } 1565 | } 1566 | } 1567 | 1568 | /*-----------------------------------------------*/ 1569 | 1570 | static union 1571 | { 1572 | UNITY_INT8 i8; 1573 | UNITY_INT16 i16; 1574 | UNITY_INT32 i32; 1575 | #ifdef UNITY_SUPPORT_64 1576 | UNITY_INT64 i64; 1577 | #endif 1578 | #ifndef UNITY_EXCLUDE_FLOAT 1579 | float f; 1580 | #endif 1581 | #ifndef UNITY_EXCLUDE_DOUBLE 1582 | double d; 1583 | #endif 1584 | } UnityQuickCompare; 1585 | 1586 | UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size) 1587 | { 1588 | switch(size) 1589 | { 1590 | case 1: 1591 | UnityQuickCompare.i8 = (UNITY_INT8)num; 1592 | return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8); 1593 | 1594 | case 2: 1595 | UnityQuickCompare.i16 = (UNITY_INT16)num; 1596 | return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16); 1597 | 1598 | #ifdef UNITY_SUPPORT_64 1599 | case 8: 1600 | UnityQuickCompare.i64 = (UNITY_INT64)num; 1601 | return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64); 1602 | #endif 1603 | 1604 | default: /* 4 bytes */ 1605 | UnityQuickCompare.i32 = (UNITY_INT32)num; 1606 | return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32); 1607 | } 1608 | } 1609 | 1610 | #ifndef UNITY_EXCLUDE_FLOAT 1611 | /*-----------------------------------------------*/ 1612 | UNITY_INTERNAL_PTR UnityFloatToPtr(const float num) 1613 | { 1614 | UnityQuickCompare.f = num; 1615 | return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f); 1616 | } 1617 | #endif 1618 | 1619 | #ifndef UNITY_EXCLUDE_DOUBLE 1620 | /*-----------------------------------------------*/ 1621 | UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num) 1622 | { 1623 | UnityQuickCompare.d = num; 1624 | return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d); 1625 | } 1626 | #endif 1627 | 1628 | /*----------------------------------------------- 1629 | * printf helper function 1630 | *-----------------------------------------------*/ 1631 | #ifdef UNITY_INCLUDE_PRINT_FORMATTED 1632 | static void UnityPrintFVA(const char* format, va_list va) 1633 | { 1634 | const char* pch = format; 1635 | if (pch != NULL) 1636 | { 1637 | while (*pch) 1638 | { 1639 | /* format identification character */ 1640 | if (*pch == '%') 1641 | { 1642 | pch++; 1643 | 1644 | if (pch != NULL) 1645 | { 1646 | switch (*pch) 1647 | { 1648 | case 'd': 1649 | case 'i': 1650 | { 1651 | const int number = va_arg(va, int); 1652 | UnityPrintNumber((UNITY_INT)number); 1653 | break; 1654 | } 1655 | #ifndef UNITY_EXCLUDE_FLOAT_PRINT 1656 | case 'f': 1657 | case 'g': 1658 | { 1659 | const double number = va_arg(va, double); 1660 | UnityPrintFloat((UNITY_DOUBLE)number); 1661 | break; 1662 | } 1663 | #endif 1664 | case 'u': 1665 | { 1666 | const unsigned int number = va_arg(va, unsigned int); 1667 | UnityPrintNumberUnsigned((UNITY_UINT)number); 1668 | break; 1669 | } 1670 | case 'b': 1671 | { 1672 | const unsigned int number = va_arg(va, unsigned int); 1673 | const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1; 1674 | UNITY_OUTPUT_CHAR('0'); 1675 | UNITY_OUTPUT_CHAR('b'); 1676 | UnityPrintMask(mask, (UNITY_UINT)number); 1677 | break; 1678 | } 1679 | case 'x': 1680 | case 'X': 1681 | case 'p': 1682 | { 1683 | const unsigned int number = va_arg(va, unsigned int); 1684 | UNITY_OUTPUT_CHAR('0'); 1685 | UNITY_OUTPUT_CHAR('x'); 1686 | UnityPrintNumberHex((UNITY_UINT)number, 8); 1687 | break; 1688 | } 1689 | case 'c': 1690 | { 1691 | const int ch = va_arg(va, int); 1692 | UnityPrintChar((const char *)&ch); 1693 | break; 1694 | } 1695 | case 's': 1696 | { 1697 | const char * string = va_arg(va, const char *); 1698 | UnityPrint(string); 1699 | break; 1700 | } 1701 | case '%': 1702 | { 1703 | UnityPrintChar(pch); 1704 | break; 1705 | } 1706 | default: 1707 | { 1708 | /* print the unknown format character */ 1709 | UNITY_OUTPUT_CHAR('%'); 1710 | UnityPrintChar(pch); 1711 | break; 1712 | } 1713 | } 1714 | } 1715 | } 1716 | #ifdef UNITY_OUTPUT_COLOR 1717 | /* print ANSI escape code */ 1718 | else if ((*pch == 27) && (*(pch + 1) == '[')) 1719 | { 1720 | pch += UnityPrintAnsiEscapeString(pch); 1721 | continue; 1722 | } 1723 | #endif 1724 | else if (*pch == '\n') 1725 | { 1726 | UNITY_PRINT_EOL(); 1727 | } 1728 | else 1729 | { 1730 | UnityPrintChar(pch); 1731 | } 1732 | 1733 | pch++; 1734 | } 1735 | } 1736 | } 1737 | 1738 | void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...) 1739 | { 1740 | UnityTestResultsBegin(Unity.TestFile, line); 1741 | UnityPrint("INFO"); 1742 | if(format != NULL) 1743 | { 1744 | UnityPrint(": "); 1745 | va_list va; 1746 | va_start(va, format); 1747 | UnityPrintFVA(format, va); 1748 | va_end(va); 1749 | } 1750 | UNITY_PRINT_EOL(); 1751 | } 1752 | #endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */ 1753 | 1754 | 1755 | /*----------------------------------------------- 1756 | * Control Functions 1757 | *-----------------------------------------------*/ 1758 | 1759 | /*-----------------------------------------------*/ 1760 | void UnityFail(const char* msg, const UNITY_LINE_TYPE line) 1761 | { 1762 | RETURN_IF_FAIL_OR_IGNORE; 1763 | 1764 | UnityTestResultsBegin(Unity.TestFile, line); 1765 | UnityPrint(UnityStrFail); 1766 | if (msg != NULL) 1767 | { 1768 | UNITY_OUTPUT_CHAR(':'); 1769 | 1770 | #ifdef UNITY_PRINT_TEST_CONTEXT 1771 | UNITY_PRINT_TEST_CONTEXT(); 1772 | #endif 1773 | #ifndef UNITY_EXCLUDE_DETAILS 1774 | if (Unity.CurrentDetail1) 1775 | { 1776 | UnityPrint(UnityStrDetail1Name); 1777 | UnityPrint(Unity.CurrentDetail1); 1778 | if (Unity.CurrentDetail2) 1779 | { 1780 | UnityPrint(UnityStrDetail2Name); 1781 | UnityPrint(Unity.CurrentDetail2); 1782 | } 1783 | UnityPrint(UnityStrSpacer); 1784 | } 1785 | #endif 1786 | if (msg[0] != ' ') 1787 | { 1788 | UNITY_OUTPUT_CHAR(' '); 1789 | } 1790 | UnityPrint(msg); 1791 | } 1792 | 1793 | UNITY_FAIL_AND_BAIL; 1794 | } 1795 | 1796 | /*-----------------------------------------------*/ 1797 | void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line) 1798 | { 1799 | RETURN_IF_FAIL_OR_IGNORE; 1800 | 1801 | UnityTestResultsBegin(Unity.TestFile, line); 1802 | UnityPrint(UnityStrIgnore); 1803 | if (msg != NULL) 1804 | { 1805 | UNITY_OUTPUT_CHAR(':'); 1806 | UNITY_OUTPUT_CHAR(' '); 1807 | UnityPrint(msg); 1808 | } 1809 | UNITY_IGNORE_AND_BAIL; 1810 | } 1811 | 1812 | /*-----------------------------------------------*/ 1813 | void UnityMessage(const char* msg, const UNITY_LINE_TYPE line) 1814 | { 1815 | UnityTestResultsBegin(Unity.TestFile, line); 1816 | UnityPrint("INFO"); 1817 | if (msg != NULL) 1818 | { 1819 | UNITY_OUTPUT_CHAR(':'); 1820 | UNITY_OUTPUT_CHAR(' '); 1821 | UnityPrint(msg); 1822 | } 1823 | UNITY_PRINT_EOL(); 1824 | } 1825 | 1826 | /*-----------------------------------------------*/ 1827 | /* If we have not defined our own test runner, then include our default test runner to make life easier */ 1828 | #ifndef UNITY_SKIP_DEFAULT_RUNNER 1829 | void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) 1830 | { 1831 | Unity.CurrentTestName = FuncName; 1832 | Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum; 1833 | Unity.NumberOfTests++; 1834 | UNITY_CLR_DETAILS(); 1835 | UNITY_EXEC_TIME_START(); 1836 | if (TEST_PROTECT()) 1837 | { 1838 | setUp(); 1839 | Func(); 1840 | } 1841 | if (TEST_PROTECT()) 1842 | { 1843 | tearDown(); 1844 | } 1845 | UNITY_EXEC_TIME_STOP(); 1846 | UnityConcludeTest(); 1847 | } 1848 | #endif 1849 | 1850 | /*-----------------------------------------------*/ 1851 | void UnitySetTestFile(const char* filename) 1852 | { 1853 | Unity.TestFile = filename; 1854 | } 1855 | 1856 | /*-----------------------------------------------*/ 1857 | void UnityBegin(const char* filename) 1858 | { 1859 | Unity.TestFile = filename; 1860 | Unity.CurrentTestName = NULL; 1861 | Unity.CurrentTestLineNumber = 0; 1862 | Unity.NumberOfTests = 0; 1863 | Unity.TestFailures = 0; 1864 | Unity.TestIgnores = 0; 1865 | Unity.CurrentTestFailed = 0; 1866 | Unity.CurrentTestIgnored = 0; 1867 | 1868 | UNITY_CLR_DETAILS(); 1869 | UNITY_OUTPUT_START(); 1870 | } 1871 | 1872 | /*-----------------------------------------------*/ 1873 | int UnityEnd(void) 1874 | { 1875 | UNITY_PRINT_EOL(); 1876 | UnityPrint(UnityStrBreaker); 1877 | UNITY_PRINT_EOL(); 1878 | UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests)); 1879 | UnityPrint(UnityStrResultsTests); 1880 | UnityPrintNumber((UNITY_INT)(Unity.TestFailures)); 1881 | UnityPrint(UnityStrResultsFailures); 1882 | UnityPrintNumber((UNITY_INT)(Unity.TestIgnores)); 1883 | UnityPrint(UnityStrResultsIgnored); 1884 | UNITY_PRINT_EOL(); 1885 | if (Unity.TestFailures == 0U) 1886 | { 1887 | UnityPrint(UnityStrOk); 1888 | } 1889 | else 1890 | { 1891 | UnityPrint(UnityStrFail); 1892 | #ifdef UNITY_DIFFERENTIATE_FINAL_FAIL 1893 | UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D'); 1894 | #endif 1895 | } 1896 | UNITY_PRINT_EOL(); 1897 | UNITY_FLUSH_CALL(); 1898 | UNITY_OUTPUT_COMPLETE(); 1899 | return (int)(Unity.TestFailures); 1900 | } 1901 | 1902 | /*----------------------------------------------- 1903 | * Command Line Argument Support 1904 | *-----------------------------------------------*/ 1905 | #ifdef UNITY_USE_COMMAND_LINE_ARGS 1906 | 1907 | char* UnityOptionIncludeNamed = NULL; 1908 | char* UnityOptionExcludeNamed = NULL; 1909 | int UnityVerbosity = 1; 1910 | 1911 | /*-----------------------------------------------*/ 1912 | int UnityParseOptions(int argc, char** argv) 1913 | { 1914 | int i; 1915 | UnityOptionIncludeNamed = NULL; 1916 | UnityOptionExcludeNamed = NULL; 1917 | 1918 | for (i = 1; i < argc; i++) 1919 | { 1920 | if (argv[i][0] == '-') 1921 | { 1922 | switch (argv[i][1]) 1923 | { 1924 | case 'l': /* list tests */ 1925 | return -1; 1926 | case 'n': /* include tests with name including this string */ 1927 | case 'f': /* an alias for -n */ 1928 | if (argv[i][2] == '=') 1929 | { 1930 | UnityOptionIncludeNamed = &argv[i][3]; 1931 | } 1932 | else if (++i < argc) 1933 | { 1934 | UnityOptionIncludeNamed = argv[i]; 1935 | } 1936 | else 1937 | { 1938 | UnityPrint("ERROR: No Test String to Include Matches For"); 1939 | UNITY_PRINT_EOL(); 1940 | return 1; 1941 | } 1942 | break; 1943 | case 'q': /* quiet */ 1944 | UnityVerbosity = 0; 1945 | break; 1946 | case 'v': /* verbose */ 1947 | UnityVerbosity = 2; 1948 | break; 1949 | case 'x': /* exclude tests with name including this string */ 1950 | if (argv[i][2] == '=') 1951 | { 1952 | UnityOptionExcludeNamed = &argv[i][3]; 1953 | } 1954 | else if (++i < argc) 1955 | { 1956 | UnityOptionExcludeNamed = argv[i]; 1957 | } 1958 | else 1959 | { 1960 | UnityPrint("ERROR: No Test String to Exclude Matches For"); 1961 | UNITY_PRINT_EOL(); 1962 | return 1; 1963 | } 1964 | break; 1965 | default: 1966 | UnityPrint("ERROR: Unknown Option "); 1967 | UNITY_OUTPUT_CHAR(argv[i][1]); 1968 | UNITY_PRINT_EOL(); 1969 | return 1; 1970 | } 1971 | } 1972 | } 1973 | 1974 | return 0; 1975 | } 1976 | 1977 | /*-----------------------------------------------*/ 1978 | int IsStringInBiggerString(const char* longstring, const char* shortstring) 1979 | { 1980 | const char* lptr = longstring; 1981 | const char* sptr = shortstring; 1982 | const char* lnext = lptr; 1983 | 1984 | if (*sptr == '*') 1985 | { 1986 | return 1; 1987 | } 1988 | 1989 | while (*lptr) 1990 | { 1991 | lnext = lptr + 1; 1992 | 1993 | /* If they current bytes match, go on to the next bytes */ 1994 | while (*lptr && *sptr && (*lptr == *sptr)) 1995 | { 1996 | lptr++; 1997 | sptr++; 1998 | 1999 | /* We're done if we match the entire string or up to a wildcard */ 2000 | if (*sptr == '*') 2001 | return 1; 2002 | if (*sptr == ',') 2003 | return 1; 2004 | if (*sptr == '"') 2005 | return 1; 2006 | if (*sptr == '\'') 2007 | return 1; 2008 | if (*sptr == ':') 2009 | return 2; 2010 | if (*sptr == 0) 2011 | return 1; 2012 | } 2013 | 2014 | /* Otherwise we start in the long pointer 1 character further and try again */ 2015 | lptr = lnext; 2016 | sptr = shortstring; 2017 | } 2018 | 2019 | return 0; 2020 | } 2021 | 2022 | /*-----------------------------------------------*/ 2023 | int UnityStringArgumentMatches(const char* str) 2024 | { 2025 | int retval; 2026 | const char* ptr1; 2027 | const char* ptr2; 2028 | const char* ptrf; 2029 | 2030 | /* Go through the options and get the substrings for matching one at a time */ 2031 | ptr1 = str; 2032 | while (ptr1[0] != 0) 2033 | { 2034 | if ((ptr1[0] == '"') || (ptr1[0] == '\'')) 2035 | { 2036 | ptr1++; 2037 | } 2038 | 2039 | /* look for the start of the next partial */ 2040 | ptr2 = ptr1; 2041 | ptrf = 0; 2042 | do 2043 | { 2044 | ptr2++; 2045 | if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')) 2046 | { 2047 | ptrf = &ptr2[1]; 2048 | } 2049 | } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')); 2050 | 2051 | while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ','))) 2052 | { 2053 | ptr2++; 2054 | } 2055 | 2056 | /* done if complete filename match */ 2057 | retval = IsStringInBiggerString(Unity.TestFile, ptr1); 2058 | if (retval == 1) 2059 | { 2060 | return retval; 2061 | } 2062 | 2063 | /* done if testname match after filename partial match */ 2064 | if ((retval == 2) && (ptrf != 0)) 2065 | { 2066 | if (IsStringInBiggerString(Unity.CurrentTestName, ptrf)) 2067 | { 2068 | return 1; 2069 | } 2070 | } 2071 | 2072 | /* done if complete testname match */ 2073 | if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1) 2074 | { 2075 | return 1; 2076 | } 2077 | 2078 | ptr1 = ptr2; 2079 | } 2080 | 2081 | /* we couldn't find a match for any substrings */ 2082 | return 0; 2083 | } 2084 | 2085 | /*-----------------------------------------------*/ 2086 | int UnityTestMatches(void) 2087 | { 2088 | /* Check if this test name matches the included test pattern */ 2089 | int retval; 2090 | if (UnityOptionIncludeNamed) 2091 | { 2092 | retval = UnityStringArgumentMatches(UnityOptionIncludeNamed); 2093 | } 2094 | else 2095 | { 2096 | retval = 1; 2097 | } 2098 | 2099 | /* Check if this test name matches the excluded test pattern */ 2100 | if (UnityOptionExcludeNamed) 2101 | { 2102 | if (UnityStringArgumentMatches(UnityOptionExcludeNamed)) 2103 | { 2104 | retval = 0; 2105 | } 2106 | } 2107 | 2108 | return retval; 2109 | } 2110 | 2111 | #endif /* UNITY_USE_COMMAND_LINE_ARGS */ 2112 | /*-----------------------------------------------*/ 2113 | --------------------------------------------------------------------------------