├── .gitignore ├── README.md ├── books ├── book.bin ├── book.pgn.gz ├── start.pgn.gz └── startc.pgn.gz ├── docs ├── crafty.hlp └── craftydoc.html └── source ├── Makefile ├── analyze.c ├── annotate.c ├── attacks.c ├── autotune.c ├── bench.c ├── book.c ├── boolean.c ├── chess.h ├── crafty.c ├── data.c ├── data.h ├── drawn.c ├── edit.c ├── epd.c ├── epd.h ├── epddefs.h ├── epdglue.c ├── epdglue.h ├── evaluate.c ├── evaluate.h ├── evtest.c ├── hash.c ├── history.c ├── init.c ├── input.c ├── interrupt.c ├── iterate.c ├── learn.c ├── lock.h ├── main.c ├── make.c ├── movgen.c ├── next.c ├── option.c ├── output.c ├── ponder.c ├── quiesce.c ├── repeat.c ├── resign.c ├── root.c ├── search.c ├── see.c ├── setboard.c ├── speak ├── tbconfig.h ├── tbcore.c ├── tbcore.h ├── tbprobe.c ├── tbprobe.h ├── test.c ├── thread.c ├── time.c ├── unmake.c ├── utility.c ├── validate.c └── vcinline.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Crafty-Chess 2 | Source code for the chess enginee Crafty 3 | 4 | I didn't write this code but I have tried to collect all versions of the chess enginee Crafty here. 5 | Each commit is a different version and I have used tags so it should be easy to switch and compare different version. 6 | 7 | Feel free to contact me with any questions about this code and repository. 8 | -------------------------------------------------------------------------------- /books/book.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinMSPedersen/Crafty-Chess/98b5f4a19be517f7f4a5065f4b948bf6c3d5a04f/books/book.bin -------------------------------------------------------------------------------- /books/book.pgn.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinMSPedersen/Crafty-Chess/98b5f4a19be517f7f4a5065f4b948bf6c3d5a04f/books/book.pgn.gz -------------------------------------------------------------------------------- /books/start.pgn.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinMSPedersen/Crafty-Chess/98b5f4a19be517f7f4a5065f4b948bf6c3d5a04f/books/start.pgn.gz -------------------------------------------------------------------------------- /books/startc.pgn.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinMSPedersen/Crafty-Chess/98b5f4a19be517f7f4a5065f4b948bf6c3d5a04f/books/startc.pgn.gz -------------------------------------------------------------------------------- /source/Makefile: -------------------------------------------------------------------------------- 1 | # To build crafty: 2 | # 3 | # You want to set up for maximum optimization, but typically you will 4 | # need to experiment to see which options provide the fastest code. The 5 | # following option descriptions explain each option. To use one or more 6 | # of these options, to the "opt =" line that follows the explanations and 7 | # add the options you want, being careful to stay inside the single quote 8 | # marks. 9 | # 10 | # -DAFFINITY Include code to set processor/thread affinity on Unix 11 | # systems. Note this will not work on Apple OS X as it does 12 | # not support any sort of processor affinity mechanism. 13 | # WARNING: know what you are doing before using this. If 14 | # you test multiple copies of Crafty (each copy using just 15 | # one thread) you can have bogus results because if compiled 16 | # with -DAFFINITY, even a single-cpu execution will lock led 17 | # itself onto processor zero (since it only has thread #0) 18 | # which is probably NOT what you want. This is intended to 19 | # be used when you run Crafty using a complete dedicated 20 | # machine with nothing else running at the same time. 21 | # -DBOOKDIR Path to the directory containing the book binary files. 22 | # The default for all such path values is "." if you don't 23 | # specify a path with this macro definition. 24 | # -DCPUS=n Defines the maximum number of CPUS Crafty will be able 25 | # to use in a SMP system. Note that this is the max you 26 | # will be able to use. You need to use the smpmt=n command 27 | # to make crafty use more than the default 1 process. 28 | # -DDEBUG This is used for testing changes. Enabling this option 29 | # greatly slows Crafty down, but every time a move is made, 30 | # the corresponding position is checked to make sure all of 31 | # the bitboards are set correctly. 32 | # -DEPD If you want full EPD support built in. 33 | # -DLOGDIR Path to the directory where Crafty puts the log.nnn and 34 | # game.nnn files. 35 | # -DNODES This enables the sn=x command. Crafty will search until 36 | # exactly X nodes have been searched, then the search 37 | # terminates as if time ran out. 38 | # -DPOSITIONS Causes Crafty to emit FEN strings, one per book line, as 39 | # it creates a book. I use this to create positions to use 40 | # for cluster testing. 41 | # -DRCDIR Path to the directory where we look for the .craftyrc or 42 | # crafty.rc (windows) file. 43 | # -DSKILL Enables the "skill" command which allows you to arbitrarily 44 | # lower Crafty's playing skill so it does not seem so 45 | # invincible to non-GM-level players. 46 | # -DSYZYGY This enables syzygy endgame tables (both WDL and DTC) 47 | # -DTBDIR Path to the directory where the endgame tablebase files 48 | # are found. default = "./TB" 49 | # -DTEST Displays evaluation table after each move (in the logfile) 50 | # -DTRACE This enables the "trace" command so that the search tree 51 | # can be dumped while running. 52 | # -DUNIX This identifies the target O/S as being Unix-based, if this 53 | # option is omitted, windows is assumed. 54 | # 55 | # Note: If you compile on a machine with the hardware popcnt 56 | # instruction, you should use the -mpopcnt compiler option 57 | # to make the built-in intrinsic use the hardware rather than 58 | # the "folding" approach. That is the default in this 59 | # Makefile so if you do NOT have hardware popcnt, remove all 60 | # of the -mpopcnt strings in the Makefile lines below. 61 | # 62 | default: 63 | $(MAKE) -j unix-clang 64 | help: 65 | @echo "You must specify the system which you want to compile for:" 66 | @echo "" 67 | @echo "make unix-clang Unix w/clang compiler (MacOS usually)" 68 | @echo "make unix-gcc Unix w/gcc compiler" 69 | @echo "make unix-icc Unix w/icc compiler" 70 | @echo "make profile profile-guided-optimizations" 71 | @echo " (edit Makefile to make the profile" 72 | @echo " option use the right compiler)" 73 | @echo "" 74 | 75 | 76 | quick: 77 | $(MAKE) target=UNIX \ 78 | CC=clang \ 79 | opt='-DSYZYGY -DTEST -DTRACE -DCPUS=4' \ 80 | CFLAGS='-mpopcnt -Wall -Wno-array-bounds -pipe -O3' \ 81 | LDFLAGS='$(LDFLAGS) -lstdc++' \ 82 | crafty-make 83 | 84 | unix-gcc: 85 | $(MAKE) -j target=UNIX \ 86 | CC=gcc \ 87 | opt='-DSYZYGY -DTEST -DCPUS=4' \ 88 | CFLAGS='-Wall -Wno-array-bounds -pipe -O3 -fprofile-use \ 89 | -mpopcnt -fprofile-correction -pthread' \ 90 | LDFLAGS='$(LDFLAGS) -fprofile-use -pthread -lstdc++' \ 91 | crafty-make 92 | 93 | unix-gcc-profile: 94 | $(MAKE) -j target=UNIX \ 95 | CC=gcc \ 96 | opt='-DSYZYGY -DTEST -DCPUS=4' \ 97 | CFLAGS='-Wall -Wno-array-bounds -pipe -O3 -fprofile-arcs \ 98 | -mpopcnt -pthread' \ 99 | LDFLAGS='$(LDFLAGS) -fprofile-arcs -pthread -lstdc++ ' \ 100 | crafty-make 101 | 102 | unix-clang: 103 | @/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/llvm-profdata merge -output=crafty.profdata *.profraw 104 | $(MAKE) -j target=UNIX \ 105 | CC=clang \ 106 | opt='-DSYZYGY -DTEST -DCPUS=4' \ 107 | CFLAGS='-Wall -Wno-array-bounds -pipe -O3 \ 108 | -mpopcnt -fprofile-instr-use=crafty.profdata' \ 109 | LDFLAGS='$(LDFLAGS) -fprofile-use -lstdc++' \ 110 | crafty-make 111 | 112 | unix-clang-profile: 113 | $(MAKE) -j target=UNIX \ 114 | CC=clang \ 115 | opt='-DSYZYGY -DTEST -DCPUS=4' \ 116 | CFLAGS='-Wall -Wno-array-bounds -pipe -O3 \ 117 | -mpopcnt -fprofile-instr-generate' \ 118 | LDFLAGS='$(LDFLAGS) -fprofile-instr-generate -lstdc++ ' \ 119 | crafty-make 120 | 121 | unix-icc: 122 | $(MAKE) -j target=UNIX \ 123 | CC=icc \ 124 | opt='-DSYZYGY -DTEST -DCPUS=4' \ 125 | CFLAGS='-Wall -w -O2 -prof_use -prof_dir ./prof -fno-alias \ 126 | -mpopcnt -pthread' \ 127 | LDFLAGS='$(LDFLAGS) -pthread -lstdc++' \ 128 | crafty-make 129 | 130 | unix-icc-profile: 131 | $(MAKE) -j target=UNIX \ 132 | CC=icc \ 133 | opt='-DSYZYGY -DTEST -DCPUS=4' \ 134 | CFLAGS='-Wall -w -O2 -prof_gen -prof_dir ./prof -fno-alias \ 135 | -mpopcnt -pthread' \ 136 | LDFLAGS='$(LDFLAGS) -pthread -lstdc++ ' \ 137 | crafty-make 138 | 139 | profile: 140 | @rm -rf *.o 141 | @rm -rf log.* 142 | @rm -rf game.* 143 | @rm -rf prof 144 | @rm -rf *.gcda 145 | @mkdir prof 146 | @touch *.c *.h 147 | $(MAKE) -j unix-clang-profile 148 | @echo "#!/bin/csh" > runprof 149 | @echo "./crafty <>runprof 150 | @echo "bench" >>runprof 151 | @echo "mt=0" >>runprof 152 | @echo "quit" >>runprof 153 | @echo "EOF" >>runprof 154 | @chmod +x runprof 155 | @./runprof 156 | @rm runprof 157 | @touch *.c *.h 158 | $(MAKE) -j unix-clang 159 | 160 | 161 | # 162 | # one of the two following definitions for "objects" should be used. The 163 | # default is to compile everything separately. However, if you use the 164 | # definition that refers to crafty.o, that will compile using the file crafty.c 165 | # which #includes every source file into one large glob. This gives the 166 | # compiler max opportunity to inline functions as appropriate. You should try 167 | # compiling both ways to see which way produces the fastest code. 168 | # 169 | 170 | #objects = main.o iterate.o time.o search.o quiesce.o evaluate.o thread.o \ 171 | repeat.o hash.o next.o history.o movgen.o make.o unmake.o attacks.o \ 172 | see.o tbprobe.o boolean.o utility.o book.o drawn.o epd.o \ 173 | epdglue.o init.o input.o autotune.o interrupt.o option.o output.o \ 174 | ponder.o resign.o root.o learn.o setboard.o test.o validate.o \ 175 | annotate.o analyze.o evtest.o bench.o edit.o data.o 176 | 177 | objects = crafty.o 178 | 179 | # Do not change anything below this line! 180 | 181 | opts = $(opt) -D$(target) 182 | 183 | crafty-make: 184 | @$(MAKE) opt='$(opt)' CFLAGS='$(CFLAGS)' crafty 185 | 186 | crafty.o: *.c *.h 187 | 188 | crafty: $(objects) 189 | $(CC) $(LDFLAGS) -g -o crafty $(objects) -lm $(LIBS) 190 | 191 | evaluate.o: evaluate.h 192 | 193 | clean: 194 | -rm -f *.o crafty 195 | 196 | $(objects): chess.h data.h 197 | 198 | .c.o: 199 | $(CC) $(CFLAGS) $(opts) -c $*.c 200 | 201 | .s.o: 202 | $(AS) $(AFLAGS) -o $*.o $*.s 203 | -------------------------------------------------------------------------------- /source/analyze.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 01/18/09 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * Analyze() is used to handle the "analyze" command. This mode basically * 8 | * puts Crafty into a "permanent pondering" state, where it reads a move * 9 | * from the input stream, and then "ponders" for the opposite side. * 10 | * Whenever a move is entered, Crafty reads this move, updates the game * 11 | * board, and then starts "pondering" for the other side. * 12 | * * 13 | * The purpose of this mode is to force Crafty to follow along in a game, * 14 | * providing analysis continually for the side on move until a move is * 15 | * entered, advancing the game to the next position. * 16 | * * 17 | ******************************************************************************* 18 | */ 19 | void Analyze() { 20 | int i, v, move, back_number, readstat = 1; 21 | TREE *const tree = block[0]; 22 | 23 | /* 24 | ************************************************************ 25 | * * 26 | * Initialize. * 27 | * * 28 | ************************************************************ 29 | */ 30 | int save_swindle_mode = swindle_mode; 31 | 32 | swindle_mode = 0; 33 | ponder_move = 0; 34 | analyze_mode = 1; 35 | if (!xboard) 36 | display_options |= 1 + 2 + 4; 37 | printf("Analyze Mode: type \"exit\" to terminate.\n"); 38 | /* 39 | ************************************************************ 40 | * * 41 | * Now loop waiting on input, searching the current * 42 | * position continually until a move comes in. * 43 | * * 44 | ************************************************************ 45 | */ 46 | while (FOREVER) { 47 | do { 48 | last_pv.pathd = 0; 49 | last_pv.pathl = 0; 50 | input_status = 0; 51 | pondering = 1; 52 | tree->status[1] = tree->status[0]; 53 | Iterate(game_wtm, think, 0); 54 | pondering = 0; 55 | if (book_move) 56 | moves_out_of_book = 0; 57 | if (!xboard) { 58 | if (game_wtm) 59 | printf("analyze.White(%d): ", move_number); 60 | else 61 | printf("analyze.Black(%d): ", move_number); 62 | fflush(stdout); 63 | } 64 | /* 65 | ************************************************************ 66 | * * 67 | * If we get back to here, something has been typed in and * 68 | * is in the command buffer normally, unless the search * 69 | * terminated naturally due to finding a mate or reaching * 70 | * the max depth allowable. * 71 | * * 72 | ************************************************************ 73 | */ 74 | if (!input_status) 75 | do { 76 | readstat = Read(1, buffer); 77 | if (readstat < 0) 78 | break; 79 | nargs = ReadParse(buffer, args, " \t;"); 80 | Print(32, "%s\n", buffer); 81 | if (strstr(args[0], "timeleft") && !xboard) { 82 | if (game_wtm) 83 | printf("analyze.White(%d): ", move_number); 84 | else 85 | printf("analyze.Black(%d): ", move_number); 86 | fflush(stdout); 87 | } 88 | } while (strstr(args[0], "timeleft")); 89 | else 90 | nargs = ReadParse(buffer, args, " \t;"); 91 | if (readstat < 0) 92 | break; 93 | move = 0; 94 | if (!strcmp(args[0], "exit")) 95 | break; 96 | /* 97 | ************************************************************ 98 | * * 99 | * First, check for the special analyze command "back n" * 100 | * and handle it if present, otherwise try Option() to see * 101 | * if it recognizes the input as a command. * 102 | * * 103 | ************************************************************ 104 | */ 105 | if (OptionMatch("back", args[0])) { 106 | if (nargs > 1) 107 | back_number = atoi(args[1]); 108 | else 109 | back_number = 1; 110 | for (i = 0; i < back_number; i++) { 111 | game_wtm = Flip(game_wtm); 112 | if (Flip(game_wtm)) 113 | move_number--; 114 | } 115 | if (move_number == 0) { 116 | move_number = 1; 117 | game_wtm = 1; 118 | } 119 | sprintf(buffer, "reset %d", move_number); 120 | Option(tree); 121 | display = tree->position; 122 | } else if (Option(tree)) { 123 | display = tree->position; 124 | } 125 | /* 126 | ************************************************************ 127 | * * 128 | * If InputMove() can recognize this as a move, make it, * 129 | * swap sides, and return to the top of the loop to call * 130 | * search from this new position. * 131 | * * 132 | ************************************************************ 133 | */ 134 | else if ((move = InputMove(tree, 0, game_wtm, 1, 0, buffer))) { 135 | char *outmove = OutputMove(tree, 0, game_wtm, move); 136 | 137 | if (history_file) { 138 | fseek(history_file, ((move_number - 1) * 2 + 1 - game_wtm) * 10, 139 | SEEK_SET); 140 | fprintf(history_file, "%9s\n", outmove); 141 | } 142 | if (game_wtm) 143 | Print(32, "White(%d): ", move_number); 144 | else 145 | Print(32, "Black(%d): ", move_number); 146 | Print(32, "%s\n", outmove); 147 | if (speech) { 148 | char announce[64]; 149 | 150 | strcpy(announce, "./speak "); 151 | strcat(announce, outmove); 152 | strcat(announce, " &"); 153 | v = system(announce); 154 | if (v != 0) 155 | perror("Analyze() system() error: "); 156 | } 157 | MakeMoveRoot(tree, game_wtm, move); 158 | display = tree->position; 159 | last_mate_score = 0; 160 | if (log_file) 161 | DisplayChessBoard(log_file, tree->position); 162 | } 163 | /* 164 | ************************************************************ 165 | * * 166 | * If Option() didn't handle the input, then it is illegal * 167 | * and should be reported to the user. * 168 | * * 169 | ************************************************************ 170 | */ 171 | else { 172 | pondering = 0; 173 | if (Option(tree) == 0) 174 | printf("illegal move: %s\n", buffer); 175 | pondering = 1; 176 | display = tree->position; 177 | } 178 | } while (!move); 179 | if (readstat < 0 || !strcmp(args[0], "exit")) 180 | break; 181 | game_wtm = Flip(game_wtm); 182 | if (game_wtm) 183 | move_number++; 184 | } 185 | analyze_mode = 0; 186 | printf("analyze complete.\n"); 187 | pondering = 0; 188 | swindle_mode = save_swindle_mode; 189 | } 190 | -------------------------------------------------------------------------------- /source/attacks.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 01/07/14 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * Attacks() is used to determine if attacks . The algorithm * 8 | * is simple, and is based on the AttacksTo() algorithm, but, rather than * 9 | * returning a bitmap of squares attacking it returns a "1" as soon * 10 | * as it finds anything that attacks . * 11 | * * 12 | ******************************************************************************* 13 | */ 14 | int Attacks(TREE * RESTRICT tree, int side, int square) { 15 | if ((rook_attacks[square] & (Rooks(side) | Queens(side))) 16 | && (RookAttacks(square, 17 | OccupiedSquares) & (Rooks(side) | Queens(side)))) 18 | return 1; 19 | if ((bishop_attacks[square] & (Bishops(side) | Queens(side))) 20 | && (BishopAttacks(square, 21 | OccupiedSquares) & (Bishops(side) | Queens(side)))) 22 | return 1; 23 | if (KnightAttacks(square) & Knights(side)) 24 | return 1; 25 | if (PawnAttacks(Flip(side), square) & Pawns(side)) 26 | return 1; 27 | if (KingAttacks(square) & Kings(side)) 28 | return 1; 29 | return 0; 30 | } 31 | 32 | /* last modified 01/07/14 */ 33 | /* 34 | ******************************************************************************* 35 | * * 36 | * AttacksTo() is used to produce a bitboard which is a map of all squares * 37 | * that directly attack this . The non-sliding pieces are trivial * 38 | * to detect, but for sliding pieces, we use a bitboard trick. The idea is * 39 | * to compute the squares a queen would attack, if it was standing on * 40 | * and then look at the last square attacked in each direction to * 41 | * determine if it is a sliding piece that moves in the right direction. To * 42 | * finish up, we simply need to Or() all these attackers together. * 43 | * * 44 | ******************************************************************************* 45 | */ 46 | uint64_t AttacksTo(TREE * RESTRICT tree, int square) { 47 | uint64_t attacks = 48 | (PawnAttacks(white, square) & Pawns(black)) | (PawnAttacks(black, 49 | square) & Pawns(white)); 50 | uint64_t bsliders = 51 | Bishops(white) | Bishops(black) | Queens(white) | Queens(black); 52 | uint64_t rsliders = 53 | Rooks(white) | Rooks(black) | Queens(white) | Queens(black); 54 | attacks |= KnightAttacks(square) & (Knights(black) | Knights(white)); 55 | if (bishop_attacks[square] & bsliders) 56 | attacks |= BishopAttacks(square, OccupiedSquares) & bsliders; 57 | if (rook_attacks[square] & rsliders) 58 | attacks |= RookAttacks(square, OccupiedSquares) & rsliders; 59 | attacks |= KingAttacks(square) & (Kings(black) | Kings(white)); 60 | return attacks; 61 | } 62 | 63 | /* last modified 01/07/14 */ 64 | /* 65 | ******************************************************************************* 66 | * * 67 | * AttacksFrom() is used to compute the set of squares the piece on * 68 | * attacks. * 69 | * * 70 | ******************************************************************************* 71 | */ 72 | uint64_t AttacksFrom(TREE * RESTRICT tree, int side, int source) { 73 | 74 | switch (Abs(PcOnSq(source))) { 75 | case queen: 76 | return QueenAttacks(source, OccupiedSquares); 77 | case rook: 78 | return RookAttacks(source, OccupiedSquares); 79 | case bishop: 80 | return BishopAttacks(source, OccupiedSquares); 81 | case knight: 82 | return KnightAttacks(source); 83 | case pawn: 84 | return PawnAttacks(side, source); 85 | case king: 86 | return KingAttacks(source); 87 | } 88 | return 0; 89 | } 90 | 91 | /* last modified 01/07/14 */ 92 | /* 93 | ******************************************************************************* 94 | * * 95 | * Attacked() is used to determine if is attacked. It returns a * 96 | * two bit value, 01 if is attacked by , 10 if is * 97 | * attacked by and 11 if is attacked by both sides. * 98 | * * 99 | ******************************************************************************* 100 | */ 101 | uint64_t Attacked(TREE * RESTRICT tree, int side, uint64_t squares) { 102 | uint64_t bsliders, rsliders, set; 103 | int square; 104 | 105 | bsliders = Bishops(side) | Queens(side); 106 | rsliders = Rooks(side) | Queens(side); 107 | for (set = squares; set; set &= set - 1) { 108 | square = LSB(set); 109 | do { 110 | if (KingAttacks(square) & Kings(side)) 111 | break; 112 | if (KnightAttacks(square) & Knights(side)) 113 | break; 114 | if (bishop_attacks[square] & bsliders && 115 | BishopAttacks(square, OccupiedSquares) & bsliders) 116 | break; 117 | if (rook_attacks[square] & rsliders && 118 | RookAttacks(square, OccupiedSquares) & rsliders) 119 | break; 120 | Clear(square, squares); 121 | } while (0); 122 | } 123 | return squares; 124 | } 125 | -------------------------------------------------------------------------------- /source/autotune.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 08/26/15 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * AutoTune() is used to tune the parallel search parameters and optimize * 8 | * them for the current hardware and a specific time per move target. The * 9 | * syntax of the command is * 10 | * * 11 | * autotune time accuracy * 12 | * * 13 | * "time" is the target time to optimize for. Longer time limits require * 14 | * somewhat different tuning values, so this should be set to the typical * 15 | * time per move. The default is 30 seconds per move if not specified. * 16 | * * 17 | * "accuracy" is normally set to 4. Since SMP search results and times are * 18 | * non-deterministic, running tests 1 time can be inaccurate. This value is * 19 | * used to determine how many times to run each test. If you set it to two, * 20 | * the entire test will take 1/2 as long. Bigger numbers are better, but * 21 | * it is easy to make the test run for hours if you go too far. A big value * 22 | * will work best if allowed to run overnight. Crafty will display a time * 23 | * estimate after determining the optimal benchmark settings. If this time * 24 | * is excessive, a ^C will let you re-start Crafty and pick a more * 25 | * reasonable time/accuracy setting. * 26 | * * 27 | * AutoTune() will tune the primary SMP controls, namely the values set by * 28 | * the commands smpgroup, smpmin, smpsd and smppsl. It will NEVER change * 29 | * smpmt (max threads), smproot (split at root) and smpaffinity. those are * 30 | * user choices and in general the default is optimal. In general the * 31 | * values have min and max settings defined in data.c (search for autotune), * 32 | * and this code will try multiple values in the given range to find an * 33 | * optimal setting. For some of the values, it will test each value in the * 34 | * interval, but for values with a large range it will try reasonable * 35 | * (again, see data.c and the "tune" array) intervals. If desired, the * 36 | * low/high limits can be changed along with the interval between samples, * 37 | * by modifying the autotune data in data.c. * 38 | * * 39 | * Note that this command is best used before you go to eat or something as * 40 | * it will run a while. If you ramp up the accuracy setting, it will take * 41 | * multiples of accuracy times longer. Best results are likely obtained * 42 | * with a larger accuracy setting, but it needs to run overnight. * 43 | * * 44 | ******************************************************************************* 45 | */ 46 | void AutoTune(int nargs, char *args[]) { 47 | unsigned int target_time = 3000, accuracy = 4, atstart, atend; 48 | unsigned int time, current, setting[64], times[64], last_time, stageii; 49 | int benchd, i, v, p, best, bestv, samples; 50 | FILE *craftyrc = fopen(".craftyrc", "a"); 51 | 52 | /* 53 | ************************************************************ 54 | * * 55 | * Initialize. * 56 | * * 57 | ************************************************************ 58 | */ 59 | if (smp_max_threads < 2) { 60 | Print(4095, "ERROR: smpmt must be set to > 1 for tuning to work\n"); 61 | fclose(craftyrc); 62 | return; 63 | } 64 | if (nargs > 1) 65 | target_time = atoi(args[1]) * 100; 66 | if (nargs > 2) 67 | accuracy = atoi(args[2]); 68 | Print(4095, "AutoTune() time=%s accuracy=%d\n", 69 | DisplayHHMMSS(target_time), accuracy); 70 | /* 71 | ************************************************************ 72 | * * 73 | * First task is to find the benchmark setting that will * 74 | * run in the alotted time. The Bench() command runs six * 75 | * positions, so we want the command to run in no more * 76 | * than six times the autotune time limit to average the * 77 | * specified time per move. We break out of the loop when * 78 | * bench takes more than 6x this time limit and use the * 79 | * previous value which just fit inside the limit. * 80 | * * 81 | ************************************************************ 82 | */ 83 | atstart = ReadClock(); 84 | stageii = 0; 85 | for (v = 0; v < autotune_params; v++) 86 | for (current = tune[v].min; current <= tune[v].max; 87 | current += tune[v].increment) 88 | stageii++; 89 | Print(4095, "Calculating optimal benchmark setting.\n"); 90 | Print(4095, "Target time average = %s.\n", DisplayHHMMSS(6 * target_time)); 91 | Print(4095, "Estimated run time (stage I) is %s.\n", 92 | DisplayHHMMSS(accuracy * 12 * target_time)); 93 | Print(4095, "Estimated run time (stage II) is %s.\n", 94 | DisplayHHMMSS(accuracy * stageii * 4 * target_time)); 95 | Print(4095, "\nBegin stage I (calibration)\n"); 96 | last_time = 0; 97 | for (benchd = -5; benchd < 10; benchd++) { 98 | Print(4095, "bench %2d:", benchd); 99 | time = 0; 100 | for (v = 0; v < accuracy; v++) 101 | time += Bench(benchd, 1); 102 | time /= accuracy; 103 | Print(4095, " ->%s\n", DisplayHHMMSS(time)); 104 | if (time > 6 * target_time) 105 | break; 106 | last_time = time; 107 | } 108 | benchd--; 109 | Print(4095, "Optimal setting is " "bench %d" "\n", benchd); 110 | atend = ReadClock(); 111 | Print(4095, "Actual runtime for Stage I: %s\n", 112 | DisplayHHMMSS(atend - atstart)); 113 | Print(4095, "New estimated run time (stage II) is %s.\n", 114 | DisplayHHMMSS(accuracy * stageii * last_time)); 115 | Print(4095, "\nBegin stage II (SMP testing).\n"); 116 | atstart = ReadClock(); 117 | /* 118 | ************************************************************ 119 | * * 120 | * Next we simply take each option, one by one, and try * 121 | * reasonable values between the min/max values as defined * 122 | * in data.c. * 123 | * * 124 | * The process is fairly simple, but very time-consuming. * 125 | * We will start at the min value for a single paramenter, * 126 | * and run bench "accuracy" times and compute the average * 127 | * of the times. We then repeat for the next step in the * 128 | * parameter, and continue until we try the max value that * 129 | * is allowed. We choose the parameter value that used * 130 | * the least amount of time which optimizes this value for * 131 | * minimum time-to-depth. * 132 | * * 133 | ************************************************************ 134 | */ 135 | for (v = 0; v < autotune_params; v++) { 136 | Print(4095, "auto-tuning %s (%d ~ %d by %d)\n", tune[v].description, 137 | tune[v].min, tune[v].max, tune[v].increment); 138 | current = *tune[v].parameter; 139 | samples = 0; 140 | if (v == 0 && tune[v].min > smp_max_threads) { 141 | samples = 1; 142 | times[0] = 0; 143 | setting[0] = smp_max_threads; 144 | } else 145 | for (current = tune[v].min; current <= tune[v].max; 146 | current += tune[v].increment) { 147 | Print(4095, "Testing %d: ", current); 148 | *tune[v].parameter = current; 149 | time = 0; 150 | for (p = 0; p < accuracy; p++) 151 | time += Bench(benchd, 1); 152 | time /= accuracy; 153 | times[samples] = time; 154 | setting[samples++] = current; 155 | Print(4095, " ->%s\n", DisplayHHMMSS(time)); 156 | } 157 | best = 0; 158 | bestv = times[0]; 159 | for (i = 1; i < samples; i++) 160 | if (bestv > times[i]) { 161 | bestv = times[i]; 162 | best = i; 163 | } 164 | fprintf(craftyrc, "%s=%d\n", tune[v].command, setting[best]); 165 | Print(4095, "adding " "%s=%d" " to .craftyrc file.\n", tune[v].command, 166 | setting[best]); 167 | } 168 | atend = ReadClock(); 169 | Print(4095, "Runtime for StageII: %s\n", DisplayHHMMSS(atend - atstart)); 170 | fclose(craftyrc); 171 | } 172 | -------------------------------------------------------------------------------- /source/bench.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 09/29/16 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * Bench() runs a 64 position benchmark during the build process. The test * 8 | * positons are hard-coded. This is designed as a stand-alone benchmark to * 9 | * comper different machines, or as a profile-guided-optimization test that * 10 | * produces good profile data. * 11 | * * 12 | ******************************************************************************* 13 | */ 14 | int Bench(int increase, int autotune) { 15 | uint64_t nodes = 0; 16 | int old_do, old_st, old_sd, total_time_used, pos, old_mt = smp_max_threads; 17 | FILE *old_books, *old_book; 18 | TREE *const tree = block[0]; 19 | char fen[64][80] = { 20 | {"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b"}, 21 | {"2r2rk1/1bqnbpp1/1p1ppn1p/pP6/N1P1P3/P2B1N1P/1B2QPP1/R2R2K1 b"}, 22 | {"3rr1k1/pp3pp1/1qn2np1/8/3p4/PP1R1P2/2P1NQPP/R1B3K1 b"}, 23 | {"4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w"}, 24 | {"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w"}, 25 | {"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b"}, 26 | {"8/R7/2q5/8/6k1/8/1P5p/K6R w"}, 27 | {"2r3k1/1p2q1pp/2b1pr2/p1pp4/6Q1/1P1PP1R1/P1PN2PP/5RK1 w"}, 28 | {"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b"}, 29 | {"5r1k/6p/1n2Q2p/4p//7P/PP4PK/R1B1q/ w"}, 30 | {"4b3/p3kp2/6p1/3pP2p/2pP1P2/4K1P1/P3N2P/8 w"}, 31 | {"r1bqkb1r/4npp1/p1p4p/1p1pP1B1/8/1B6/PPPN1PPP/R2Q1RK1 w"}, 32 | {"7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w"}, 33 | {"2r1nrk1/p2q1ppp/bp1p4/n1pPp3/P1P1P3/2PBB1N1/4QPPP/R4RK1 w"}, 34 | {"8/3k4/8/8/8/4B3/4KB2/2B5 w"}, 35 | {"6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w"}, 36 | {"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w"}, 37 | {"r2q1rk1/1ppnbppp/p2p1nb1/3Pp3/2P1P1P1/2N2N1P/PPB1QP2/R1B2RK1 b"}, 38 | {"2kr1bnr/pbpq4/2n1pp2/3p3p/3P1P1B/2N2N1Q/PPP3PP/2KR1B1R w"}, 39 | {"8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w"}, 40 | {"3rn2k/ppb2rpp/2ppqp2/5N2/2P1P3/1P5Q/PB3PPP/3RR1K1 w"}, 41 | {"rnbqkb1r/p3pppp/1p6/2ppP3/3N4/2P5/PPP1QPPP/R1B1KB1R w"}, 42 | {"2r3k1/pppR1pp1/4p3/4P1P1/5P2/1P4K1/P1P5/8 w"}, 43 | {"/k/rnn////5RBB/K/ w"}, 44 | {"r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w"}, 45 | {"8/8/3P3k/8/1p6/8/1P6/1K3n2 b"}, 46 | {"8/2p4P/8/kr6/6R1/8/8/1K6 w"}, 47 | {"r2qnrnk/p2b2b1/1p1p2pp/2pPpp2/1PP1P3/PRNBB3/3QNPPP/5RK1 w"}, 48 | {"5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b"}, 49 | {"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w"}, 50 | {"2q1rr1k/3bbnnp/p2p1pp1/2pPp3/PpP1P1P1/1P2BNNP/2BQ1PRK/7R b"}, 51 | {"2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b"}, 52 | {"r2q1rk1/4bppp/p2p4/2pP4/3pP3/3Q4/PP1B1PPP/R3R1K1 w"}, 53 | {"6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b"}, 54 | {"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w"}, 55 | {"r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w"}, 56 | {"8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w"}, 57 | {"8/8/8/5N2/8/p7/8/2NK3k w"}, 58 | {"r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w"}, 59 | {"8/8/1P6/5pr1/8/4R3/7k/2K5 w"}, 60 | {"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b"}, 61 | {"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w"}, 62 | {"3r1k2/4npp1/1ppr3p/p6P/P2PPPP1/1NR5/5K2/2R5 w"}, 63 | {"r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b"}, 64 | {"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w"}, 65 | {"r1bqk2r/pp2bppp/2p5/3pP3/P2Q1P2/2N1B3/1PP3PP/R4RK1 b"}, 66 | {"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w"}, 67 | {"8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w"}, 68 | {"3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w"}, 69 | {"r1bq1rk1/pp2ppbp/2np2p1/2n5/P3PP2/N1P2N2/1PB3PP/R1B1QRK1 b"}, 70 | {"r3r1k1/ppqb1ppp/8/4p1NQ/8/2P5/PP3PPP/R3R1K1 b"}, 71 | {"rnbqkb1r/p3pppp/1p6/2ppP3/3N4/2P5/PPP1QPPP/R1B1KB1R w"}, 72 | {"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w"}, 73 | {"/k/3p/p2P1p/P2P1P///K/ w"}, 74 | {"rnb2r1k/pp2p2p/2pp2p1/q2P1p2/8/1Pb2NP1/PB2PPBP/R2Q1RK1 w"}, 75 | {"8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w"}, 76 | {"8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b"}, 77 | {"1nk1r1r1/pp2n1pp/4p3/q2pPp1N/b1pP1P2/B1P2R2/2P1B1PP/R2Q2K1 w"}, 78 | {"2K5/p7/7P/5pR1/8/5k2/r7/8 w"}, 79 | {"3rr3/2pq2pk/p2p1pnp/8/2QBPP2/1P6/P5PP/4RRK1 b"}, 80 | {"r1b2rk1/2q1b1pp/p2ppn2/1p6/3QP3/1BN1B3/PPP3PP/R4RK1 w"}, 81 | {"8/8/8/8/5kp1/P7/8/1K1N4 w"}, 82 | {"r4k2/pb2bp1r/1p1qp2p/3pNp2/3P1P2/2N3P1/PPP1Q2P/2KRR3 w"}, 83 | {"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w"} 84 | }; 85 | int fen_depth = 16; 86 | 87 | /* 88 | ************************************************************ 89 | * * 90 | * Initialize. * 91 | * * 92 | ************************************************************ 93 | */ 94 | total_time_used = 0; 95 | old_st = search_time_limit; 96 | old_sd = search_depth; 97 | old_do = display_options; 98 | search_time_limit = 90000; 99 | display_options = 1; 100 | old_book = book_file; 101 | book_file = 0; 102 | old_books = books_file; 103 | books_file = 0; 104 | if (!autotune) { 105 | if (increase) 106 | Print(4095, 107 | "Running serial benchmark (modifying depth by %d plies). . .\n", 108 | increase); 109 | else 110 | Print(4095, "Running serial benchmark. . .\n"); 111 | fflush(stdout); 112 | } 113 | /* 114 | ************************************************************ 115 | * * 116 | * Now we loop through the 64 positions. We use the * 117 | * ReadParse() procedure to break the FEN into tokens and * 118 | * then call SetBoard() to set up the positions. Then a * 119 | * call to Iterate() and we are done. * 120 | * * 121 | ************************************************************ 122 | */ 123 | for (pos = 0; pos < 64; pos++) { 124 | strcpy(buffer, fen[pos]); 125 | nargs = ReadParse(buffer, args, " \t;="); 126 | SetBoard(tree, nargs, args, 0); 127 | search_depth = fen_depth + increase; 128 | last_pv.pathd = 0; 129 | thinking = 1; 130 | tree->status[1] = tree->status[0]; 131 | InitializeHashTables(0); 132 | Iterate(game_wtm, think, 0); 133 | thinking = 0; 134 | nodes += tree->nodes_searched; 135 | total_time_used += (program_end_time - program_start_time); 136 | nodes_per_second = 137 | (uint64_t) tree->nodes_searched * 100 / 138 | Max((uint64_t) program_end_time - program_start_time, 1); 139 | if (pos % 7 == 0) 140 | Print(4095, "pos: "); 141 | Print(4095, "%2d(%s) ", pos + 1, DisplayKMB(nodes_per_second, 0)); 142 | if (pos % 7 == 6) 143 | Print(4095, "\n"); 144 | fflush(stdout); 145 | } 146 | /* 147 | ************************************************************ 148 | * * 149 | * Serial benchmark done. Now dump the results. * 150 | * * 151 | ************************************************************ 152 | */ 153 | if (!autotune) 154 | printf("\n"); 155 | if (!autotune) { 156 | Print(4095, "\nTotal nodes: %" PRIu64 "\n", nodes); 157 | Print(4095, "Raw nodes per second: %d\n", 158 | (int) ((double) nodes / ((double) total_time_used / (double) 100.0))); 159 | Print(4095, "Total elapsed time: %.2f\n\n", 160 | ((double) total_time_used / (double) 100.0)); 161 | } 162 | /* 163 | ************************************************************ 164 | * * 165 | * Now we repeat for two threads to provide PGO data for * 166 | * the compiler. * 167 | * * 168 | ************************************************************ 169 | */ 170 | if (smp_max_threads == 0) { 171 | smp_max_threads = 2; 172 | Print(4095, "Running SMP benchmark (%d threads)...\n", smp_max_threads); 173 | fflush(stdout); 174 | Print(4095, "pos: "); 175 | for (pos = 0; pos < 2 && old_mt == 0; pos++) { 176 | strcpy(buffer, fen[pos]); 177 | nargs = ReadParse(buffer, args, " \t;="); 178 | SetBoard(tree, nargs, args, 0); 179 | search_depth = fen_depth + increase; 180 | last_pv.pathd = 0; 181 | thinking = 1; 182 | tree->status[1] = tree->status[0]; 183 | InitializeHashTables(0); 184 | Iterate(game_wtm, think, 0); 185 | thinking = 0; 186 | nodes += tree->nodes_searched; 187 | total_time_used += (program_end_time - program_start_time); 188 | nodes_per_second = 189 | (uint64_t) tree->nodes_searched * 100 / 190 | Max((uint64_t) program_end_time - program_start_time, 1); 191 | Print(4095, "%2d(%s) ", pos + 1, DisplayKMB(nodes_per_second, 0)); 192 | } 193 | } 194 | /* 195 | ************************************************************ 196 | * * 197 | * Benchmark done. Now dump the results. * 198 | * * 199 | ************************************************************ 200 | */ 201 | if (!autotune) 202 | printf("\n"); 203 | if (!autotune && old_mt == 0) { 204 | Print(4095, "\nTotal nodes: %" PRIu64 "\n", nodes); 205 | Print(4095, "Raw nodes per second: %d\n", 206 | (int) ((double) nodes / ((double) total_time_used / (double) 100.0))); 207 | Print(4095, "Total elapsed time: %.2f\n\n", 208 | ((double) total_time_used / (double) 100.0)); 209 | } 210 | input_stream = stdin; 211 | early_exit = 99; 212 | display_options = old_do; 213 | search_time_limit = old_st; 214 | search_depth = old_sd; 215 | smp_max_threads = Max(0, old_mt); 216 | books_file = old_books; 217 | book_file = old_book; 218 | InitializeChessBoard(tree); 219 | return total_time_used; 220 | } 221 | -------------------------------------------------------------------------------- /source/boolean.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 02/16/14 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * This group of procedures provide the three basic bitboard operators, * 8 | * MSB(x) that determines the Most Significant Bit, LSB(x) that determines * 9 | * the Least Significant Bit, and PopCnt(x) which returns the number of one * 10 | * bits set in the word. * 11 | * * 12 | * We prefer to use hardware facilities (such as intel BSF/BSR) when they * 13 | * are available, otherwise we resort to C and table lookups to do this in * 14 | * the most efficient way possible. * 15 | * * 16 | ******************************************************************************* 17 | */ 18 | #if !defined(INLINEASM) 19 | int MSB(uint64_t arg1) { 20 | if (arg1 >> 48) 21 | return msb[arg1 >> 48] + 48; 22 | if (arg1 >> 32 & 65535) 23 | return msb[arg1 >> 32 & 65535] + 32; 24 | if (arg1 >> 16 & 65535) 25 | return msb[arg1 >> 16 & 65535] + 16; 26 | return msb[arg1 & 65535]; 27 | } 28 | 29 | int LSB(uint64_t arg1) { 30 | if (arg1 & 65535) 31 | return lsb[arg1 & 65535]; 32 | if (arg1 >> 16 & 65535) 33 | return lsb[arg1 >> 16 & 65535] + 16; 34 | if (arg1 >> 32 & 65535) 35 | return lsb[arg1 >> 32 & 65535] + 32; 36 | return lsb[arg1 >> 48] + 48; 37 | } 38 | 39 | int PopCnt(uint64_t arg1) { 40 | int c; 41 | 42 | for (c = 0; arg1; c++) 43 | arg1 &= arg1 - 1; 44 | return c; 45 | } 46 | #endif 47 | -------------------------------------------------------------------------------- /source/crafty.c: -------------------------------------------------------------------------------- 1 | /* last modified 01/18/09 */ 2 | /* 3 | ******************************************************************************* 4 | * * 5 | * This module is designed for the most efficient compiling, as it includes * 6 | * all of the source files into one large wad so that the compiler can see * 7 | * all function calls and inline whatever is appropriate. The includes are * 8 | * loosely ordered so that the most common functions occur first, to help * 9 | * with cache layout when the code is actually loaded. * 10 | * * 11 | ******************************************************************************* 12 | */ 13 | #include "iterate.c" 14 | #include "search.c" 15 | #include "movgen.c" 16 | #include "make.c" 17 | #include "unmake.c" 18 | #include "thread.c" 19 | #include "repeat.c" 20 | #include "next.c" 21 | #include "history.c" 22 | #include "quiesce.c" 23 | #include "evaluate.c" 24 | #include "hash.c" 25 | #include "attacks.c" 26 | #include "see.c" 27 | #include "utility.c" 28 | #include "tbprobe.c" 29 | #include "book.c" 30 | #include "autotune.c" 31 | #include "analyze.c" 32 | #include "annotate.c" 33 | #include "bench.c" 34 | #include "data.c" 35 | #include "drawn.c" 36 | #include "edit.c" 37 | #include "epd.c" 38 | #include "epdglue.c" 39 | #include "evtest.c" 40 | #include "init.c" 41 | #include "input.c" 42 | #include "interrupt.c" 43 | #include "learn.c" 44 | #include "main.c" 45 | #include "option.c" 46 | #include "output.c" 47 | #include "ponder.c" 48 | #include "resign.c" 49 | #include "root.c" 50 | #include "setboard.c" 51 | #include "test.c" 52 | #include "time.c" 53 | #include "validate.c" 54 | -------------------------------------------------------------------------------- /source/data.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | extern int scale; 3 | extern char version[8]; 4 | extern int presult; 5 | extern PLAYING_MODE mode; 6 | extern int batch_mode; 7 | extern int swindle_mode; 8 | extern int call_flag; 9 | extern int crafty_rating; 10 | extern int opponent_rating; 11 | extern int time_used; 12 | extern int time_used_opponent; 13 | extern uint64_t total_moves; 14 | extern int allow_cores; 15 | extern int allow_memory; 16 | extern int initialized; 17 | extern int early_exit; 18 | extern char *AK_list[128]; 19 | extern char *GM_list[128]; 20 | extern char *IM_list[128]; 21 | extern char *B_list[128]; 22 | extern char *SP_list[128]; 23 | extern char *SP_opening_filename[128]; 24 | extern char *SP_personality_filename[128]; 25 | extern FILE *input_stream; 26 | extern FILE *book_file; 27 | extern FILE *books_file; 28 | extern FILE *normal_bs_file; 29 | extern FILE *computer_bs_file; 30 | extern FILE *history_file; 31 | extern FILE *log_file; 32 | extern int log_id; 33 | extern int output_format; 34 | #if defined(SYZYGY) 35 | extern int EGTBlimit; 36 | extern int EGTB_use; 37 | extern int EGTB_draw; 38 | extern int EGTB_depth; 39 | extern int EGTB_setup; 40 | #endif 41 | extern int last_mate_score; 42 | extern int last_opponent_move; 43 | extern int check_depth; 44 | extern int null_depth; 45 | extern int null_divisor; 46 | extern int LMR_rdepth; 47 | extern int LMR_min; 48 | extern int LMR_max; 49 | extern double LMR_db; 50 | extern double LMR_mb; 51 | extern double LMR_s; 52 | extern uint8_t LMR[32][64]; 53 | extern int rep_index; 54 | extern int pgn_suggested_percent; 55 | extern char pgn_event[128]; 56 | extern char pgn_date[128]; 57 | extern char pgn_round[128]; 58 | extern char pgn_site[128]; 59 | extern char pgn_white[128]; 60 | extern char pgn_white_elo[128]; 61 | extern char pgn_black[128]; 62 | extern char pgn_black_elo[128]; 63 | extern char pgn_result[128]; 64 | extern char log_filename[256]; 65 | extern char history_filename[256]; 66 | extern int number_of_solutions; 67 | extern int solutions[10]; 68 | extern int solution_type; 69 | extern int abs_draw_score; 70 | extern int accept_draws; 71 | extern int offer_draws; 72 | extern int dynamic_draw_score; 73 | extern int adaptive_hash; 74 | extern size_t adaptive_hash_min; 75 | extern size_t adaptive_hash_max; 76 | extern size_t adaptive_hashp_min; 77 | extern size_t adaptive_hashp_max; 78 | extern int over; 79 | extern int xboard; 80 | extern int xboard_done; 81 | extern int pong; 82 | extern char book_path[128]; 83 | extern char log_path[128]; 84 | extern char tb_path[128]; 85 | extern char rc_path[128]; 86 | extern char cmd_buffer[4096]; 87 | extern char *args[512]; 88 | extern char buffer[4096]; 89 | extern int line_length; 90 | extern unsigned char convert_buff[8]; 91 | extern int nargs; 92 | extern int kibitz; 93 | extern int game_wtm; 94 | extern int last_search_value; 95 | extern int FP_margin[16]; 96 | extern int FP_depth; 97 | extern int LMP[16]; 98 | extern int LMP_depth; 99 | extern int LMP_base; 100 | extern double LMP_scale; 101 | extern double LMP_scale; 102 | extern int failhi_delta, faillo_delta; 103 | extern int ponder_value; 104 | extern int move_actually_played; 105 | extern int analyze_mode; 106 | extern int annotate_mode; 107 | extern int input_status; /* 0=no input; 108 | 1=predicted move read; 109 | 2=unpredicted move read; 110 | 3=something read, not executed. */ 111 | extern int resign; 112 | extern int resign_counter; 113 | extern int resign_count; 114 | extern int draw_counter; 115 | extern int draw_count; 116 | extern int draw_offer_pending; 117 | extern int draw_offered; 118 | extern char audible_alarm; 119 | extern char speech; 120 | extern char ponder_text[512]; 121 | extern char book_hint[512]; 122 | extern int post; 123 | extern int search_depth; 124 | extern uint64_t search_nodes; 125 | extern uint64_t temp_search_nodes; 126 | extern int search_move; 127 | extern int ponder; 128 | extern int ponder_move; 129 | extern int force; 130 | extern unsigned ponder_moves[256]; 131 | extern int num_ponder_moves; 132 | extern char initial_position[80]; 133 | extern int predicted; 134 | extern int trace_level; 135 | extern int book_move; 136 | extern int book_accept_mask; 137 | extern int book_reject_mask; 138 | extern int book_random; 139 | extern float book_weight_freq; 140 | extern float book_weight_eval; 141 | extern float book_weight_learn; 142 | extern int book_search_trigger; 143 | extern int book_selection_width; 144 | extern int show_book; 145 | extern int learn; 146 | extern int learning; 147 | extern int learn_value; 148 | extern int abort_search; 149 | extern int iteration; 150 | extern int root_wtm; 151 | extern int last_root_value; 152 | extern ROOT_MOVE root_moves[256]; 153 | extern int n_root_moves; 154 | extern int difficulty; 155 | extern int time_limit; 156 | extern int absolute_time_limit; 157 | extern int search_time_limit; 158 | extern int burp; 159 | extern int quit; 160 | extern unsigned opponent_start_time, opponent_end_time; 161 | extern unsigned program_start_time, program_end_time; 162 | extern unsigned start_time, end_time; 163 | extern TREE *block[MAX_BLOCKS + 1]; 164 | extern THREAD thread[CPUS]; 165 | #if (CPUS > 1) 166 | extern lock_t lock_smp, lock_io; 167 | # if defined(UNIX) 168 | extern pthread_attr_t attributes; 169 | # endif 170 | #endif 171 | extern unsigned int hardware_processors; 172 | extern unsigned int smp_max_threads; 173 | extern unsigned int smp_split_group; 174 | extern unsigned int smp_split_at_root; 175 | extern unsigned int smp_min_split_depth; 176 | extern unsigned int smp_gratuitous_depth; 177 | extern unsigned int smp_gratuitous_limit; 178 | extern int smp_nice; 179 | extern int smp_affinity; 180 | extern int smp_affinity_increment; 181 | extern int smp_numa; 182 | extern int autotune_params; 183 | extern struct autotune tune[16]; 184 | extern unsigned smp_split_nodes; 185 | extern unsigned parallel_splits; 186 | extern unsigned parallel_splits_wasted; 187 | extern unsigned parallel_aborts; 188 | extern unsigned parallel_joins; 189 | extern unsigned busy_percent; 190 | extern uint64_t game_max_blocks; 191 | extern volatile int smp_split; 192 | extern volatile int smp_threads; 193 | extern volatile int initialized_threads; 194 | extern int crafty_is_white; 195 | extern unsigned nodes_between_time_checks; 196 | extern unsigned nodes_per_second; 197 | extern int next_time_check; 198 | extern int transposition_age; 199 | extern int thinking; 200 | extern int pondering; 201 | extern int puzzling; 202 | extern int booking; 203 | extern int display_options; 204 | extern unsigned noise_level; 205 | extern int noise_block; 206 | extern int tc_moves; 207 | extern int tc_time; 208 | extern int tc_time_remaining[2]; 209 | extern int tc_moves_remaining[2]; 210 | extern int tc_secondary_moves; 211 | extern int tc_secondary_time; 212 | extern int tc_increment; 213 | extern int tc_sudden_death; 214 | extern int tc_safety_margin; 215 | extern int draw_score[2]; 216 | extern char kibitz_text[4096]; 217 | extern int kibitz_depth; 218 | extern int move_number; 219 | extern int moves_out_of_book; 220 | extern int first_nonbook_factor; 221 | extern int first_nonbook_span; 222 | #if defined(SKILL) 223 | extern int skill; 224 | #endif 225 | extern int book_learn_eval[LEARN_INTERVAL]; 226 | extern int book_learn_depth[LEARN_INTERVAL]; 227 | extern int learn_seekto[64]; 228 | extern uint64_t learn_key[64]; 229 | extern int learn_nmoves[64]; 230 | extern uint64_t book_learn_key; 231 | extern int learn_positions_count; 232 | extern int book_learn_nmoves; 233 | extern int book_learn_seekto; 234 | extern int usage_level; 235 | extern size_t hash_table_size; 236 | extern size_t hash_path_size; 237 | extern uint64_t hash_mask; 238 | extern uint64_t mask_clear_entry; 239 | extern uint64_t hash_path_mask; 240 | extern size_t pawn_hash_table_size; 241 | extern uint64_t pawn_hash_mask; 242 | extern HASH_ENTRY *hash_table; 243 | extern HPATH_ENTRY *hash_path; 244 | extern PAWN_HASH_ENTRY *pawn_hash_table; 245 | extern void *segments[MAX_BLOCKS + 32][2]; 246 | extern int nsegments; 247 | extern const int pcval[7]; 248 | extern const int p_vals[7]; 249 | extern const int MVV_LVA[7][7]; 250 | extern const int pieces[2][7]; 251 | extern const int lower_n; 252 | extern const int lower_b; 253 | extern const int mobility_score_n[4]; 254 | extern const int mobility_score_b[4]; 255 | extern const int mobility_score_r[4]; 256 | extern const int mob_curve_r[48]; 257 | extern PATH last_pv; 258 | extern int last_value; 259 | extern const char translate[13]; 260 | extern const char empty_sqs[9]; 261 | extern const int mate[64]; 262 | extern const uint64_t magic_rook[64]; 263 | extern const uint64_t magic_rook_mask[64]; 264 | extern const unsigned magic_rook_shift[64]; 265 | extern const uint64_t mobility_mask_b[4]; 266 | extern const uint64_t mobility_mask_r[4]; 267 | extern int16_t *magic_bishop_mobility_indices[64]; 268 | extern int16_t magic_bishop_mobility_table[5248]; 269 | extern uint64_t *magic_rook_indices[64]; 270 | extern uint64_t magic_rook_table[102400]; 271 | extern int16_t *magic_rook_mobility_indices[64]; 272 | extern int16_t magic_rook_mobility_table[102400]; 273 | extern const uint64_t magic_bishop[64]; 274 | extern const uint64_t magic_bishop_mask[64]; 275 | extern const unsigned magic_bishop_shift[64]; 276 | extern uint64_t *magic_bishop_indices[64]; 277 | extern uint64_t magic_bishop_table[5248]; 278 | extern int16_t *magic_bishop_mobility_indices[64]; 279 | extern int16_t magic_bishop_mobility_table[5248]; 280 | extern int8_t directions[64][64]; 281 | extern uint64_t pawn_attacks[2][64]; 282 | extern uint64_t knight_attacks[64]; 283 | extern uint64_t rook_attacks[64]; 284 | extern uint64_t bishop_attacks[64]; 285 | extern POSITION display; 286 | extern uint64_t king_attacks[64]; 287 | extern uint64_t intervening[64][64]; 288 | extern int history[1024]; 289 | extern const uint64_t randoms[2][7][64]; 290 | extern const uint64_t castle_random[2][2]; 291 | extern const uint64_t enpassant_random[65]; 292 | extern uint64_t clear_mask[65]; 293 | extern uint64_t set_mask[65]; 294 | extern uint64_t file_mask[8]; 295 | extern uint64_t rank_mask[8]; 296 | extern const uint64_t OO[2]; 297 | extern const uint64_t OOO[2]; 298 | extern const uint64_t mask_efgh, mask_fgh, mask_abc, mask_abcd; 299 | extern const uint64_t mask_advance_2_w; 300 | extern const uint64_t mask_advance_2_b; 301 | extern const uint64_t mask_left_edge; 302 | extern const uint64_t mask_right_edge; 303 | extern const uint64_t mask_not_edge; 304 | extern const uint64_t mask_rook_files; 305 | extern const uint64_t dark_squares; 306 | extern const uint64_t not_rook_pawns; 307 | extern const uint64_t not_rook_files; 308 | extern uint64_t plus1dir[65]; 309 | extern uint64_t plus7dir[65]; 310 | extern uint64_t plus8dir[65]; 311 | extern uint64_t plus9dir[65]; 312 | extern uint64_t minus1dir[65]; 313 | extern uint64_t minus7dir[65]; 314 | extern uint64_t minus8dir[65]; 315 | extern uint64_t minus9dir[65]; 316 | extern uint64_t mask_eptest[64]; 317 | #if !defined(INLINEASM) 318 | extern unsigned char msb[65536]; 319 | extern unsigned char lsb[65536]; 320 | #endif 321 | extern unsigned char msb_8bit[256]; 322 | extern unsigned char lsb_8bit[256]; 323 | extern unsigned char pop_cnt_8bit[256]; 324 | extern uint64_t mask_pawn_connected[2][64]; 325 | extern uint64_t mask_pawn_isolated[64]; 326 | extern uint64_t mask_passed[2][64]; 327 | extern uint64_t mask_pattacks[2][64]; 328 | extern uint64_t pawn_race[2][2][64]; 329 | extern BOOK_POSITION book_buffer[BOOK_CLUSTER_SIZE]; 330 | extern BOOK_POSITION book_buffer_char[BOOK_CLUSTER_SIZE]; 331 | extern const int rankflip[2][8]; 332 | extern const int sqflip[2][64]; 333 | extern const int rank1[2]; 334 | extern const int rank2[2]; 335 | extern const int rank3[2]; 336 | extern const int rank4[2]; 337 | extern const int rank5[2]; 338 | extern const int rank6[2]; 339 | extern const int rank7[2]; 340 | extern const int rank8[2]; 341 | extern const int epdir[2]; 342 | extern const int csq[2]; 343 | extern const int dsq[2]; 344 | extern const int esq[2]; 345 | extern const int fsq[2]; 346 | extern const int gsq[2]; 347 | extern const int sign[2]; 348 | extern const char square_color[64]; 349 | extern uint64_t distance_ring[64][8]; 350 | extern const int OOsqs[2][3]; 351 | extern const int OOOsqs[2][3]; 352 | extern const int OOfrom[2]; 353 | extern const int OOto[2]; 354 | extern const int OOOto[2]; 355 | extern const int epsq[2]; 356 | extern const int rook_A[2]; 357 | extern const int rook_D[2]; 358 | extern const int rook_F[2]; 359 | extern const int rook_G[2]; 360 | extern const int rook_H[2]; 361 | extern const int capleft[2]; 362 | extern const int capright[2]; 363 | extern const int pawnadv1[2]; 364 | extern const int pawnadv2[2]; 365 | extern int piece_values[2][7]; 366 | extern struct personality_term personality_packet[256]; 367 | -------------------------------------------------------------------------------- /source/drawn.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 12/21/09 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * Drawn() is used to answer the question "is this position a hopeless * 8 | * draw?" Several considerations are included in making this decision, but * 9 | * the most basic one is simply the remaining material for each side. If * 10 | * either side has pawns, it's not a draw. With no pawns, equal material is * 11 | * a draw. Otherwise, the superior side must have enough material to be * 12 | * able to force a mate. * 13 | * * 14 | * Drawn() has 3 possible return values: * 15 | * * 16 | * 0: Game is not a draw by rule or drawish in nature. * 17 | * * 18 | * 1: Game is a "technical draw" where material is even with no pawns and * 19 | * the search is also returning draw scores as the best outcome. * 20 | * * 21 | * 2: Game is an "actual draw" by FIDE rules, such as KB vs K, where mate * 22 | * can't be forced, even with worst possible play by one side. * 23 | * * 24 | ******************************************************************************* 25 | */ 26 | int Drawn(TREE * RESTRICT tree, int value) { 27 | /* 28 | ************************************************************ 29 | * * 30 | * If either side has pawns, the game is not a draw for * 31 | * lack of material. * 32 | * * 33 | ************************************************************ 34 | */ 35 | if (TotalPieces(white, pawn) || TotalPieces(black, pawn)) 36 | return 0; 37 | /* 38 | ************************************************************ 39 | * * 40 | * If the score suggests a mate has been found, this is * 41 | * not a draw. * 42 | * * 43 | ************************************************************ 44 | */ 45 | if (MateScore(value)) 46 | return 0; 47 | /* 48 | ************************************************************ 49 | * * 50 | * If neither side has pawns, and one side has some sort * 51 | * of material superiority, then determine if the winning * 52 | * side has enough material to win. * 53 | * * 54 | ************************************************************ 55 | */ 56 | if (TotalPieces(white, occupied) + TotalPieces(black, occupied) < 4) 57 | return 2; 58 | if (TotalPieces(white, occupied) < 5 && TotalPieces(black, occupied) < 5) 59 | return 1; 60 | if (TotalPieces(white, occupied) == 5 || TotalPieces(white, occupied) > 6) 61 | return 0; 62 | if (TotalPieces(black, occupied) == 5 || TotalPieces(black, occupied) > 6) 63 | return 0; 64 | if ((TotalPieces(white, occupied) == 6 && !Bishops(white) && Material > 0) 65 | || (TotalPieces(black, occupied) == 6 && !Bishops(black) 66 | && Material < 0)) 67 | return 1; 68 | /* 69 | ************************************************************ 70 | * * 71 | * The search result must indicate a draw also, otherwise * 72 | * it could be a tactical win or loss, so we will skip * 73 | * calling an equal material (no pawns) position a draw to * 74 | * make sure there is no mate that the search sees. * 75 | * * 76 | * If neither side has pawns, then one side must have some * 77 | * sort of material superiority, otherwise it is a draw. * 78 | * * 79 | ************************************************************ 80 | */ 81 | if (value != DrawScore(game_wtm)) 82 | return 0; 83 | if (TotalPieces(white, occupied) == TotalPieces(black, occupied)) 84 | return 1; 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /source/edit.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 05/08/14 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * Edit() is used to edit (alter) the current board position. It clears the * 8 | * board and then allows the operator to enter a position using the syntax * 9 | * defined by the xboard/winboard "edit" command. * 10 | * * 11 | * White sets the color of pieces added to white. This color will stay in * 12 | * effect until specifically changed. * 13 | * * 14 | * Black sets the color of pieces added to black. This color will stay in * 15 | * effect until specifically changed. * 16 | * * 17 | * # clears the chessboard completely. * 18 | * * 19 | * C changes (toggles) the color of pieces being placed on the board. * 20 | * * 21 | * End (or . for ICS/Xboard) terminates Edit(). * 22 | * * 23 | * Pieces are placed on the board by three character "commands" of the form * 24 | * [piece][square] where piece is a member of the normal set of pieces * 25 | * {P,N,B,R,Q,K} and [square] is algebraic square notation (a1-h8). Ex: Ke8 * 26 | * puts a king (of the current "color") on square e8. * 27 | * * 28 | ******************************************************************************* 29 | */ 30 | void Edit(void) { 31 | int athome = 1, i, piece, readstat, square, tfile, trank, wtm = 1, error = 32 | 0; 33 | static const char pieces[] = 34 | { 'x', 'X', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r', 35 | 'Q', 'q', 'K', 'k', '\0' 36 | }; 37 | TREE *const tree = block[0]; 38 | 39 | /* 40 | ************************************************************ 41 | * * 42 | * Process the commands to set the board[n] form of the * 43 | * chess position. * 44 | * * 45 | ************************************************************ 46 | */ 47 | while (FOREVER) { 48 | if ((input_stream == stdin) && !xboard) { 49 | if (wtm) 50 | printf("edit(white): "); 51 | else 52 | printf("edit(black): "); 53 | } 54 | fflush(stdout); 55 | readstat = Read(1, buffer); 56 | if (readstat < 0) 57 | return; 58 | nargs = ReadParse(buffer, args, " \t;"); 59 | if (xboard) 60 | Print(32, "edit.command:%s\n", args[0]); 61 | if (!strcmp(args[0], "white")) 62 | wtm = 1; 63 | else if (!strcmp(args[0], "black")) 64 | wtm = 0; 65 | if (!strcmp(args[0], "#")) 66 | for (i = 0; i < 64; i++) 67 | PcOnSq(i) = 0; 68 | else if (!strcmp(args[0], "c")) 69 | wtm = Flip(wtm); 70 | else if (!strcmp(args[0], "end") || (!strcmp(args[0], "."))) 71 | break; 72 | else if (!strcmp(args[0], "d")) 73 | DisplayChessBoard(stdout, tree->position); 74 | else if (strlen(args[0]) == 3) { 75 | if (strchr(pieces, args[0][0])) { 76 | piece = (strchr(pieces, args[0][0]) - pieces) >> 1; 77 | tfile = args[0][1] - 'a'; 78 | trank = args[0][2] - '1'; 79 | square = (trank << 3) + tfile; 80 | if ((square < 0) || (square > 63)) 81 | printf("unrecognized square %s\n", args[0]); 82 | if (wtm) 83 | PcOnSq(square) = piece; 84 | else 85 | PcOnSq(square) = -piece; 86 | } 87 | } else if (strlen(args[0]) == 2) { 88 | piece = pawn; 89 | tfile = args[0][0] - 'a'; 90 | trank = args[0][1] - '1'; 91 | square = (trank << 3) + tfile; 92 | if ((square < 0) || (square > 63)) 93 | printf("unrecognized square %s\n", args[0]); 94 | if (wtm) 95 | PcOnSq(square) = piece; 96 | else 97 | PcOnSq(square) = -piece; 98 | } else 99 | printf("unrecognized piece %s\n", args[0]); 100 | } 101 | /* 102 | ************************************************************ 103 | * * 104 | * Now, if a king is on its original square, check the * 105 | * rooks to see if they are and set the castle status * 106 | * accordingly. Note that this checks for pieces on the * 107 | * original rank, but not their original squares (ICS * 108 | * "wild" games) and doesn't set castling if true. * 109 | * * 110 | * The winboard/xboard "edit" command does not give us a * 111 | * way of setting castling status, so we have to guess. * 112 | * * 113 | ************************************************************ 114 | */ 115 | Castle(0, white) = 0; 116 | Castle(0, black) = 0; 117 | EnPassant(0) = 0; 118 | for (i = 0; i < 16; i++) 119 | if (PcOnSq(i) == 0 || PcOnSq(i + 48) == 0) 120 | athome = 0; 121 | if (!athome || (PcOnSq(A1) == rook && PcOnSq(B1) == knight && 122 | PcOnSq(C1) == bishop && PcOnSq(D1) == queen && PcOnSq(E1) == king && 123 | PcOnSq(F1) == bishop && PcOnSq(G1) == knight && PcOnSq(H1) == rook 124 | && PcOnSq(A8) == -rook && PcOnSq(B8) == -knight && 125 | PcOnSq(C8) == -bishop && PcOnSq(D8) == -queen && PcOnSq(E8) == -king 126 | && PcOnSq(F8) == -bishop && PcOnSq(G8) == -knight && 127 | PcOnSq(H8) == -rook)) { 128 | if (PcOnSq(E1) == king) { 129 | if (PcOnSq(A1) == rook) 130 | Castle(0, white) = Castle(0, white) | 2; 131 | if (PcOnSq(H1) == rook) 132 | Castle(0, white) = Castle(0, white) | 1; 133 | } 134 | if (PcOnSq(E8) == -king) { 135 | if (PcOnSq(A8) == -rook) 136 | Castle(0, black) = Castle(0, black) | 2; 137 | if (PcOnSq(H8) == -rook) 138 | Castle(0, black) = Castle(0, black) | 1; 139 | } 140 | } 141 | /* 142 | ************************************************************ 143 | * * 144 | * Basic board is now set. Now it's time to set the bit * 145 | * board representation correctly. * 146 | * * 147 | ************************************************************ 148 | */ 149 | SetChessBitBoards(tree); 150 | error += InvalidPosition(tree); 151 | if (!error) { 152 | if (log_file) 153 | DisplayChessBoard(log_file, tree->position); 154 | wtm = 1; 155 | move_number = 1; 156 | rep_index = 0; 157 | tree->rep_list[0] = HashKey; 158 | Reversible(0) = 0; 159 | moves_out_of_book = 0; 160 | } else { 161 | InitializeChessBoard(tree); 162 | Print(4095, "Illegal position, using normal initial chess position\n"); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /source/epd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* *INDENT-OFF* */ 3 | /*>>> epd.h: subprogram prototypes for epd.c */ 4 | /* Revised: 1996.06.23 */ 5 | /* 6 | Copyright (C) 1996 by Steven J. Edwards (sje@mv.mv.com) 7 | All rights reserved. This code may be freely redistibuted and used by 8 | both research and commerical applications. No warranty exists. 9 | */ 10 | /* 11 | Everything in this source file is independent of the host program. 12 | Requests for changes and additions should be communicated to the author 13 | via the e-mail address given above. 14 | */ 15 | /* 16 | This file was originally prepared on an Apple Macintosh using the 17 | Metrowerks CodeWarrior 6 ANSI C compiler. Tabs are set at every 18 | four columns. Further testing and development was performed on a 19 | generic PC running Linux 1.3.20 and using the gcc 2.7.0 compiler. 20 | */ 21 | void EPDFatal(charptrT s); 22 | void EPDSwitchFault(charptrT s); 23 | voidptrT EPDMemoryGrab(liT n); 24 | void EPDMemoryFree(voidptrT ptr); 25 | charptrT EPDStringGrab(charptrT s); 26 | void EPDStringFree(charptrT s); 27 | charptrT EPDStringAppendChar(charptrT s, char c); 28 | charptrT EPDStringAppendStr(charptrT s0, charptrT s1); 29 | liT EPDMapFromDuration(charptrT s); 30 | charptrT EPDMapToDuration(liT seconds); 31 | gamptrT EPDGameOpen(void); 32 | void EPDGameClose(gamptrT gamptr); 33 | void EPDGameAppendMove(gamptrT gamptr, mptrT mptr); 34 | void EPDTokenize(charptrT s); 35 | siT EPDTokenCount(void); 36 | charptrT EPDTokenFetch(siT n); 37 | siT EPDCICharEqual(char ch0, char ch1); 38 | pT EPDPieceFromCP(cpT cp); 39 | siT EPDCheckPiece(char ch); 40 | pT EPDEvaluatePiece(char ch); 41 | siT EPDCheckColor(char ch); 42 | cT EPDEvaluateColor(char ch); 43 | siT EPDCheckRank(char ch); 44 | rankT EPDEvaluateRank(char ch); 45 | siT EPDCheckFile(char ch); 46 | fileT EPDEvaluateFile(char ch); 47 | eovptrT EPDNewEOV(void); 48 | void EPDReleaseEOV(eovptrT eovptr); 49 | void EPDAppendEOV(eopptrT eopptr, eovptrT eovptr); 50 | eovptrT EPDCreateEOVStr(charptrT str); 51 | eovptrT EPDCreateEOVSym(charptrT sym); 52 | eovptrT EPDCreateEOVInt(liT lval); 53 | eovptrT EPDLocateEOV(eopptrT eopptr, charptrT strval); 54 | siT EPDCountEOV(eopptrT eopptr); 55 | void EPDReplaceEOVStr(eovptrT eovptr, charptrT str); 56 | eopptrT EPDNewEOP(void); 57 | void EPDReleaseEOP(eopptrT eopptr); 58 | void EPDAppendEOP(epdptrT epdptr, eopptrT eopptr); 59 | eopptrT EPDCreateEOP(charptrT opsym); 60 | eopptrT EPDCreateEOPCode(epdsoT epdso); 61 | eopptrT EPDLocateEOP(epdptrT epdptr, charptrT opsym); 62 | eopptrT EPDLocateEOPCode(epdptrT epdptr, epdsoT epdso); 63 | siT EPDCountEOP(epdptrT epdptr); 64 | void EPDDropIfLocEOP(epdptrT epdptr, charptrT opsym); 65 | void EPDDropIfLocEOPCode(epdptrT epdptr, epdsoT epdso); 66 | void EPDAddOpInt(epdptrT epdptr, epdsoT epdso, liT val); 67 | void EPDAddOpStr(epdptrT epdptr, epdsoT epdso, charptrT s); 68 | void EPDAddOpSym(epdptrT epdptr, epdsoT epdso, charptrT s); 69 | epdptrT EPDNewEPD(void); 70 | void EPDReleaseOperations(epdptrT epdptr); 71 | void EPDReleaseEPD(epdptrT epdptr); 72 | charptrT EPDFetchOpsym(epdsoT epdso); 73 | epdptrT EPDCloneEPDBase(epdptrT epdptr); 74 | eovptrT EPDCloneEOV(eovptrT eovptr); 75 | eopptrT EPDCloneEOP(eopptrT eopptr); 76 | epdptrT EPDCloneEPD(epdptrT epdptr); 77 | epdptrT EPDSet(rbptrT rbptr, cT actc, castT cast, sqT epsq); 78 | void EPDSetCurrentPosition(rbptrT rbptr, cT actc, castT cast, sqT epsq, 79 | siT hmvc, siT fmvn); 80 | epdptrT EPDGetCurrentPosition(void); 81 | cT EPDFetchACTC(void); 82 | castT EPDFetchCAST(void); 83 | sqT EPDFetchEPSQ(void); 84 | siT EPDFetchHMVC(void); 85 | siT EPDFetchFMVN(void); 86 | rbptrT EPDFetchBoard(void); 87 | cpT EPDFetchCP(sqT sq); 88 | charptrT EPDFetchBoardString(void); 89 | gtimT EPDGetGTIM(gamptrT gamptr); 90 | void EPDPutGTIM(gamptrT gamptr, gtimT gtim); 91 | charptrT EPDGenBasic(rbptrT rbptr, cT actc, castT cast, sqT epsq); 92 | charptrT EPDGenBasicCurrent(void); 93 | epdptrT EPDDecodeFEN(charptrT s); 94 | charptrT EPDEncodeFEN(epdptrT epdptr); 95 | epdptrT EPDDecode(charptrT s); 96 | charptrT EPDEncode(epdptrT epdptr); 97 | void EPDRealize(epdptrT epdptr); 98 | void EPDInitArray(void); 99 | charptrT EPDPlayerString(cT c); 100 | void EPDSANEncode(mptrT mptr, sanT san); 101 | mptrT EPDSANDecodeAux(sanT san, siT strict); 102 | siT EPDIsLegal(void); 103 | siT EPDIsCheckmate(void); 104 | siT EPDIsStalemate(void); 105 | siT EPDIsInsufficientMaterial(void); 106 | siT EPDIsFiftyMoveDraw(void); 107 | siT EPDIsThirdRepeatIndex(gamptrT gamptr); 108 | siT EPDIsDraw(gamptrT gamptr); 109 | mptrT EPDMateInOne(void); 110 | void EPDExecuteUpdate(mptrT mptr); 111 | void EPDRetractUpdate(void); 112 | void EPDRetractAll(void); 113 | void EPDCollapse(void); 114 | void EPDReset(void); 115 | void EPDGenMoves(void); 116 | siT EPDFetchMoveCount(void); 117 | mptrT EPDFetchMove(siT index); 118 | void EPDSetMoveFlags(mptrT mptr); 119 | void EPDSortSAN(void); 120 | siT EPDPurgeOpFile(charptrT opsym, charptrT fn0, charptrT fn1); 121 | siT EPDRepairEPD(epdptrT epdptr); 122 | siT EPDRepairFile(charptrT fn0, charptrT fn1); 123 | siT EPDNormalizeFile(charptrT fn0, charptrT fn1); 124 | siT EPDScoreFile(charptrT fn, bmsptrT bmsptr); 125 | siT EPDEnumerateFile(siT depth, charptrT fn0, charptrT fn1, liptrT totalptr); 126 | charptrT EPDMoveList(gamptrT gamptr); 127 | pgnstrT EPDPGNFetchTagIndex(charptrT s); 128 | charptrT EPDPGNFetchTagName(pgnstrT pgnstr); 129 | charptrT EPDPGNGetSTR(gamptrT gamptr, pgnstrT pgnstr); 130 | void EPDPGNPutSTR(gamptrT gamptr, pgnstrT pgnstr, charptrT s); 131 | charptrT EPDPGNGenSTR(gamptrT gamptr); 132 | charptrT EPDPGNHistory(gamptrT gamptr); 133 | void EPDCopyInPTP(gamptrT gamptr, epdptrT epdptr); 134 | void EPDCopyOutPTP(gamptrT gamptr, epdptrT epdptr); 135 | charptrT EPDFetchRefcomStr(refcomT refcom); 136 | charptrT EPDFetchRefreqStr(refreqT refreq); 137 | refcomT EPDFetchRefcomIndex(charptrT s); 138 | refreqT EPDFetchRefreqIndex(charptrT s); 139 | refcomT EPDExtractRefcomIndex(epdptrT epdptr); 140 | refreqT EPDExtractRefreqIndex(epdptrT epdptr); 141 | siT EPDComm(refintptrT refintptr, charptrT pipebase); 142 | void EPDInit(void); 143 | void EPDTerm(void); 144 | /* *INDENT-ON* */ 145 | -------------------------------------------------------------------------------- /source/epdglue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* *INDENT-OFF* */ 3 | /*>>> epdglue.h: subprogram prototypes for epdglue.c */ 4 | /* Revised: 1995.12.11 */ 5 | /* 6 | Copyright (C) 1995 by Steven J. Edwards (sje@mv.mv.com) 7 | All rights reserved. This code may be freely redistibuted and used by 8 | both research and commerical applications. No warranty exists. 9 | */ 10 | /* 11 | The contents of this include file are the prototypes for the glue 12 | routines (epdglue.c) that form the programmatic interface between the 13 | host program Crafty and the EPD Kit. Therefore, this file may have 14 | to be changed (as will epdglue.c) if used with a different host program. 15 | The contents of the other source files in the EPD Kit (epddefs.h, 16 | epd.h, and epd.c) should not have to be changed for different hosts. 17 | */ 18 | /* 19 | This file was originally prepared on an Apple Macintosh using the 20 | Metrowerks CodeWarrior 6 ANSI C compiler. Tabs are set at every 21 | four columns. Further testing and development was performed on a 22 | generic PC running Linux 1.2.9 and using the gcc 2.6.3 compiler. 23 | */ 24 | int EGCommandParmCount(char *s); 25 | int EGCommandCheck(char *s); 26 | int EGCommand(char *s); 27 | void EGInit(void); 28 | void EGTerm(void); 29 | /* *INDENT-ON* */ 30 | -------------------------------------------------------------------------------- /source/evtest.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 02/26/14 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * EVTest() is used to test the program against a suite of test positions to * 8 | * measure its performance on a particular machine, or to evaluate its skill * 9 | * after modifying it in some way. * 10 | * * 11 | * The test is initiated by using the "evtest " command to read in * 12 | * the suite of problems from file . The format of this file is * 13 | * as follows: * 14 | * * 15 | * This sets the board position using the usual Forsythe * 16 | * notation (see module SetBoard() for a full explanation explanation of the * 17 | * syntax). * 18 | * * 19 | * After reading this position, the program then calls Evaluate() to produce * 20 | * a positional evaluation, along with any debug output from Evaluate(), and * 21 | * then goes on to the next position. * 22 | * * 23 | * A common use (with code included below) is to take a position, and then * 24 | * "flip" it (reverse the board rank by rank changing the color of each * 25 | * piece as this is done, giving us two positions. We then take each of * 26 | * these and mirror them, which reverses them file by file, which now gives * 27 | * four positions in total. We run these thru Evaluate() to make sure that * 28 | * all produce exactly the same score, except for the color change for the * 29 | * two that changed the piece colors. This is used to make certain that the * 30 | * evaluation is not asymmetric with respect to the colors, or to the side * 31 | * of the board, which catches errors that are difficult to spot otherwise. * 32 | * * 33 | ******************************************************************************* 34 | */ 35 | void EVTest(char *filename) { 36 | FILE *test_input; 37 | char *eof; 38 | char buff[4096]; 39 | TREE *const tree = block[0]; 40 | 41 | /* 42 | ************************************************************ 43 | * * 44 | * Read in the position * 45 | * * 46 | ************************************************************ 47 | */ 48 | if (!(test_input = fopen(filename, "r"))) { 49 | printf("file %s does not exist.\n", filename); 50 | return; 51 | } 52 | while (FOREVER) { 53 | eof = fgets(buffer, 4096, test_input); 54 | if (eof) { 55 | char *delim; 56 | 57 | delim = strchr(buffer, '\n'); 58 | if (delim) 59 | *delim = 0; 60 | delim = strchr(buffer, '\r'); 61 | if (delim) 62 | *delim = ' '; 63 | } else 64 | break; 65 | strcpy(buff, buffer); 66 | nargs = ReadParse(buffer, args, " \t;"); 67 | if (!strcmp(args[0], "end")) 68 | break; 69 | /* 70 | ************************************************************ 71 | * * 72 | * Now for the asymmetry tests. We use the built-in * 73 | * commands "flip" and "flop" to create the four * 74 | * positions, and call Evaluate() for each. * 75 | * * 76 | ************************************************************ 77 | */ 78 | else { 79 | int s1, s2, s3, s4, id; 80 | 81 | for (id = 2; id < nargs; id++) 82 | if (!strcmp(args[id], "id")) 83 | break; 84 | if (id >= nargs) 85 | id = 0; 86 | SetBoard(tree, nargs, args, 0); 87 | Castle(0, white) = 0; 88 | Castle(1, white) = 0; 89 | Castle(0, black) = 0; 90 | Castle(1, black) = 0; 91 | tree->pawn_score.key = 0; 92 | root_wtm = game_wtm; 93 | s1 = Evaluate(tree, 0, game_wtm, -99999, 99999); 94 | strcpy(buffer, "flop"); 95 | Option(tree); 96 | tree->pawn_score.key = 0; 97 | root_wtm = game_wtm; 98 | s2 = Evaluate(tree, 0, game_wtm, -99999, 99999); 99 | strcpy(buffer, "flip"); 100 | Option(tree); 101 | tree->pawn_score.key = 0; 102 | root_wtm = game_wtm; 103 | s3 = Evaluate(tree, 0, game_wtm, -99999, 99999); 104 | strcpy(buffer, "flop"); 105 | Option(tree); 106 | tree->pawn_score.key = 0; 107 | root_wtm = game_wtm; 108 | s4 = Evaluate(tree, 0, game_wtm, -99999, 99999); 109 | /* 110 | ************************************************************ 111 | * * 112 | * If the evaluations (with sign corrected if piece color * 113 | * was changed) are different, we display the four values * 114 | * and then display the original board position so that we * 115 | * can debug the cause of the asymmetry. * 116 | * * 117 | ************************************************************ 118 | */ 119 | if (s1 != s2 || s1 != s3 || s1 != s4 || s2 != s3 || s2 != s4 || 120 | s3 != s4) { 121 | strcpy(buffer, "flip"); 122 | Option(tree); 123 | printf("FEN = %s\n", buff); 124 | DisplayChessBoard(stdout, tree->position); 125 | if (id) 126 | Print(4095, "id=%s ", args[id + 1]); 127 | Print(4095, "wtm=%d score=%d %d (flop) %d (flip) %d (flop)\n", 128 | game_wtm, s1, s2, s3, s4); 129 | } 130 | } 131 | } 132 | input_stream = stdin; 133 | } 134 | -------------------------------------------------------------------------------- /source/history.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 08/15/15 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * History() is used to maintain the two killer moves for each ply. The * 8 | * most recently used killer is always first in the list. * 9 | * * 10 | * History() also maintains two counter-moves. These are moves that are * 11 | * directly used to refute a specific move at the previous ply, rather than * 12 | * just a plain killer that has caused cutoffs previously. * 13 | * * 14 | * History() finally remembers two moves that were played after a specific * 15 | * move at ply-2 (ie the two moves together create some sort of "plan".) * 16 | * * 17 | * History() also maintains the history counters. Each time a move fails * 18 | * high, it's history count is increased, while all other moves that were * 19 | * searched will have their counts reduced. The current counter is a * 20 | * saturating counter in the range 0 <= N <= 2047. * 21 | * * 22 | ******************************************************************************* 23 | */ 24 | void History(TREE * RESTRICT tree, int ply, int depth, int side, int move, 25 | int searched[]) { 26 | int i, index, mindex; 27 | /* 28 | ************************************************************ 29 | * * 30 | * Now, add this move to the current killer moves if it is * 31 | * not already there. If the move is already first in the * 32 | * list, leave it there, otherwise move the first one down * 33 | * to slot two and insert this move into slot one. * 34 | * * 35 | * If the best move so far is a capture or a promotion, * 36 | * we skip updating the killers (but still update history * 37 | * information) since we search good captures first, which * 38 | * happens before killers are tried, making capture moves * 39 | * useless here. * 40 | * * 41 | * The update value does not depend on depth. I used a * 42 | * function of depth for years, but as I examined more and * 43 | * more trees, that seemed to be lacking because it gives * 44 | * a really large bias towards moves that work near the * 45 | * root, while most of the nodes searched are near the * 46 | * tips. One unusual characteristic of this counter is * 47 | * that it is a software-based saturating counter. That * 48 | * is, it can never exceed 2047, nor can it ever drop * 49 | * below zero. * 50 | * * 51 | ************************************************************ 52 | */ 53 | if (!CaptureOrPromote(move)) { 54 | if (tree->killers[ply].move1 != move) { 55 | tree->killers[ply].move2 = tree->killers[ply].move1; 56 | tree->killers[ply].move1 = move; 57 | } 58 | /* 59 | ************************************************************ 60 | * * 61 | * Set the counter-move for the move at the previous ply * 62 | * to be this move that caused the fail-high. * 63 | * * 64 | ************************************************************ 65 | */ 66 | if (tree->counter_move[tree->curmv[ply - 1] & 4095].move1 != move) { 67 | tree->counter_move[tree->curmv[ply - 1] & 4095].move2 = 68 | tree->counter_move[tree->curmv[ply - 1] & 4095].move1; 69 | tree->counter_move[tree->curmv[ply - 1] & 4095].move1 = move; 70 | } 71 | /* 72 | ************************************************************ 73 | * * 74 | * Set the move-pair for the move two plies back so that * 75 | * if we play that move again, we will follow up with this * 76 | * move to continue the "plan". * 77 | * * 78 | ************************************************************ 79 | */ 80 | if (ply > 2) { 81 | if (tree->move_pair[tree->curmv[ply - 2] & 4095].move1 != move) { 82 | tree->move_pair[tree->curmv[ply - 2] & 4095].move2 = 83 | tree->move_pair[tree->curmv[ply - 2] & 4095].move1; 84 | tree->move_pair[tree->curmv[ply - 2] & 4095].move1 = move; 85 | } 86 | } 87 | /* 88 | ************************************************************ 89 | * * 90 | * Adjust the history counter for the move that caused the * 91 | * fail-high, limiting the max value to 2048. * 92 | * * 93 | ************************************************************ 94 | */ 95 | if (depth > 5) { 96 | mindex = HistoryIndex(side, move); 97 | history[mindex] += (2048 - history[mindex]) >> 5; 98 | /* 99 | ************************************************************ 100 | * * 101 | * Adjust the history counters for the moves that were * 102 | * searched but did not cause a fail-high, limiting the * 103 | * min value to 0. * 104 | * * 105 | ************************************************************ 106 | */ 107 | for (i = 1; i <= searched[0]; i++) { 108 | index = HistoryIndex(side, searched[i]); 109 | if (index != mindex) 110 | history[index] -= history[index] >> 5; 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /source/interrupt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "chess.h" 3 | #include "data.h" 4 | /* last modified 01/17/09 */ 5 | /* 6 | ******************************************************************************* 7 | * * 8 | * Interrupt() is used to read in a move when the operator types something * 9 | * while a search is in progress (during pondering as one example.) This * 10 | * routine reads in a command (move) and then makes two attempts to use this * 11 | * input: (1) call Option() to see if the command can be executed; (2) try * 12 | * InputMove() to see if this input is a legal move; If so, and we are * 13 | * pondering see if it matches the move we are pondering. * 14 | * * 15 | ******************************************************************************* 16 | */ 17 | void Interrupt(int ply) { 18 | TREE *const tree = block[0]; 19 | int temp, i, left = 0, readstat, result, time_used, save_move_number; 20 | 21 | /* 22 | ************************************************************ 23 | * * 24 | * If trying to find a move to ponder, and the operator * 25 | * types a command, exit a.s.a.p. * 26 | * * 27 | ************************************************************ 28 | */ 29 | if (puzzling) 30 | abort_search = 2; 31 | /* 32 | ************************************************************ 33 | * * 34 | * First check to see if this is a command by calling * 35 | * Option(). Option() will return a 0 if it didn't * 36 | * recognize the command; otherwise it returns a 1 if the * 37 | * command was executed, or a 2 if we need to abort the * 38 | * search to execute the command. * 39 | * * 40 | ************************************************************ 41 | */ 42 | else 43 | while (FOREVER) { 44 | readstat = Read(0, buffer); 45 | if (readstat <= 0) 46 | break; 47 | nargs = ReadParse(buffer, args, " \t;"); 48 | if (nargs == 0) { 49 | Print(32, "ok.\n"); 50 | break; 51 | } 52 | if (strcmp(args[0], ".")) { 53 | save_move_number = move_number; 54 | if (!game_wtm) 55 | move_number--; 56 | if (root_wtm) 57 | Print(32, "Black(%d): %s\n", move_number, buffer); 58 | else 59 | Print(32, "White(%d): %s\n", move_number, buffer); 60 | move_number = save_move_number; 61 | } 62 | /* 63 | ************************************************************ 64 | * * 65 | * "." command displays status of current search (this is * 66 | * part of winboard protocol.) * 67 | * * 68 | ************************************************************ 69 | */ 70 | if (!strcmp(args[0], ".")) { 71 | if (xboard) { 72 | end_time = ReadClock(); 73 | time_used = (end_time - start_time); 74 | printf("stat01: %d ", time_used); 75 | printf("%" PRIu64 " ", tree->nodes_searched); 76 | printf("%d ", iteration); 77 | for (i = 0; i < n_root_moves; i++) 78 | if (!(root_moves[i].status & 8)) 79 | left++; 80 | printf("%d %d\n", left, n_root_moves); 81 | fflush(stdout); 82 | break; 83 | } else { 84 | end_time = ReadClock(); 85 | time_used = (end_time - start_time); 86 | printf("time:%s ", DisplayTime(time_used)); 87 | printf("nodes:%" PRIu64 "\n", tree->nodes_searched); 88 | #if (CPUS > 1) 89 | ThreadTrace(block[0], 0, 1); 90 | #endif 91 | } 92 | } 93 | /* 94 | ************************************************************ 95 | * * 96 | * "?" command says "move now!" * 97 | * * 98 | ************************************************************ 99 | */ 100 | else if (!strcmp(args[0], "?")) { 101 | if (thinking) { 102 | abort_search = 1; 103 | } 104 | } 105 | /* 106 | ************************************************************ 107 | * * 108 | * "@" command says "assume ponder move was played." * 109 | * * 110 | ************************************************************ 111 | */ 112 | else if (!strcmp(args[0], "@")) { 113 | if (pondering) { 114 | predicted++; 115 | input_status = 1; 116 | pondering = 0; 117 | thinking = 1; 118 | opponent_end_time = ReadClock(); 119 | program_start_time = ReadClock(); 120 | Print(32, "predicted move made.\n"); 121 | } 122 | } 123 | /* 124 | ************************************************************ 125 | * * 126 | * Next see if Option() recognizes this as a command. Note * 127 | * some commands can't be executed in the middle of a * 128 | * search. Option() returns a value >= 2 in such cases. * 129 | * If we are pondering, we can back out of the search and * 130 | * execute the command, otherwise we produce an error and * 131 | * continue searching for our move. * 132 | * * 133 | ************************************************************ 134 | */ 135 | else { 136 | save_move_number = move_number; 137 | if (!analyze_mode && !game_wtm) 138 | move_number--; 139 | result = Option(tree); 140 | move_number = save_move_number; 141 | if (result >= 2) { 142 | if (thinking && result != 3) 143 | Print(32, "command not legal now.\n"); 144 | else { 145 | abort_search = 2; 146 | input_status = 2; 147 | break; 148 | } 149 | } else if ((result != 1) && analyze_mode) { 150 | abort_search = 1; 151 | input_status = 2; 152 | break; 153 | } 154 | /* 155 | ************************************************************ 156 | * * 157 | * Option() didn't recognize the input as a command so now * 158 | * we check to see if the operator typed a move. If so, * 159 | * and it matched the predicted move, switch from * 160 | * pondering to thinking to start the timer. If this is a * 161 | * move, but not the predicted move, abort the search and * 162 | * start over with the right move. * 163 | * * 164 | ************************************************************ 165 | */ 166 | else if (!result) { 167 | if (pondering) { 168 | nargs = ReadParse(buffer, args, " \t;"); 169 | temp = InputMove(tree, 0, Flip(root_wtm), 1, 1, args[0]); 170 | if (temp) { 171 | if ((From(temp) == From(ponder_move)) 172 | && (To(temp) == To(ponder_move)) 173 | && (Piece(temp) == Piece(ponder_move)) 174 | && (Captured(temp) == Captured(ponder_move)) 175 | && (Promote(temp) == Promote(ponder_move))) { 176 | predicted++; 177 | input_status = 1; 178 | pondering = 0; 179 | thinking = 1; 180 | opponent_end_time = ReadClock(); 181 | program_start_time = ReadClock(); 182 | Print(32, "predicted move made.\n"); 183 | } else { 184 | input_status = 2; 185 | abort_search = 2; 186 | break; 187 | } 188 | } else if (!strcmp(args[0], "go") || !strcmp(args[0], "move") 189 | || !strcmp(args[0], "SP")) { 190 | abort_search = 2; 191 | break; 192 | } else 193 | Print(4095, "Illegal move: %s\n", args[0]); 194 | } else 195 | Print(4095, "unrecognized/illegal command: %s\n", args[0]); 196 | } 197 | } 198 | } 199 | if (log_file) 200 | fflush(log_file); 201 | } 202 | -------------------------------------------------------------------------------- /source/learn.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "chess.h" 4 | #include "data.h" 5 | #if defined(UNIX) 6 | # include 7 | #endif 8 | 9 | /* last modified 02/24/14 */ 10 | /* 11 | ******************************************************************************* 12 | * * 13 | * LearnAdjust() us used to scale the learn value, which can be used to * 14 | * limit the aggressiveness of the learning algorithm. All we do here is * 15 | * divide the learn value passed in by "learning / 10". * 16 | * * 17 | ******************************************************************************* 18 | */ 19 | int LearnAdjust(int value) { 20 | 21 | if (learning / 10 > 0) 22 | return value / (learning / 10); 23 | else 24 | return 0; 25 | } 26 | 27 | /* last modified 02/24/14 */ 28 | /* 29 | ******************************************************************************* 30 | * * 31 | * LearnBook() is used to update the book database when a game ends for any * 32 | * reason. It uses the global "learn_value" variable and updates the book * 33 | * based on the moves played and the value that was "learned". * 34 | * * 35 | * The global learn_value has two possible sources. If a game ends with a * 36 | * real result (win, lose or draw) then the learrn_value will be set to a * 37 | * number in the interval {-300, 300} depending on the result. If there is * 38 | * no result (the operator exits the program prior to reaching a conclusion * 39 | * (quit, end, ^C) then we will use the values from the first few searches * 40 | * after leaving book to compute a learrn_value (see LearnValue() comments * 41 | * later in this file). * 42 | * * 43 | ******************************************************************************* 44 | */ 45 | void LearnBook() { 46 | float book_learn[64], t_learn_value; 47 | int nplies = 0, thisply = 0, i, j, v, cluster; 48 | unsigned char buf32[4]; 49 | 50 | /* 51 | ************************************************************ 52 | * * 53 | * If we have not been "out of book" for N moves, all we * 54 | * we need to do is take the search evaluation for the * 55 | * search just completed and tuck it away in the book * 56 | * learning array (book_learn_eval[]) for use later. * 57 | * * 58 | ************************************************************ 59 | */ 60 | if (!book_file) 61 | return; 62 | if (!learn) 63 | return; 64 | if (Abs(learn_value) != learning) 65 | learn_value = LearnAdjust(learn_value); 66 | learn = 0; 67 | Print(32, "LearnBook() updating book database\n"); 68 | /* 69 | ************************************************************ 70 | * * 71 | * Now we build a vector of book learning results. We * 72 | * give every book move below the last point where there * 73 | * were alternatives 100% of the learned score. We give * 74 | * the book move played at that point 100% of the learned * 75 | * score as well. Then we divide the learned score by the * 76 | * number of alternatives, and propagate this score back * 77 | * until there was another alternative, where we do this * 78 | * again and again until we reach the top of the book * 79 | * tree. * 80 | * * 81 | ************************************************************ 82 | */ 83 | t_learn_value = ((float) learn_value) / 100.0; 84 | for (i = 0; i < 64; i++) 85 | if (learn_nmoves[i] > 1) 86 | nplies++; 87 | nplies = Max(nplies, 1); 88 | for (i = 0; i < 64; i++) { 89 | if (learn_nmoves[i] > 1) 90 | thisply++; 91 | book_learn[i] = t_learn_value * (float) thisply / (float) nplies; 92 | } 93 | /* 94 | ************************************************************ 95 | * * 96 | * Now find the appropriate cluster, find the key we were * 97 | * passed, and update the resulting learn value. * 98 | * * 99 | ************************************************************ 100 | */ 101 | for (i = 0; i < 64 && learn_seekto[i]; i++) { 102 | if (learn_seekto[i] > 0) { 103 | fseek(book_file, learn_seekto[i], SEEK_SET); 104 | v = fread(buf32, 4, 1, book_file); 105 | if (v <= 0) 106 | perror("Learn() fread error: "); 107 | cluster = BookIn32(buf32); 108 | if (cluster) 109 | BookClusterIn(book_file, cluster, book_buffer); 110 | for (j = 0; j < cluster; j++) 111 | if (!(learn_key[i] ^ book_buffer[j].position)) 112 | break; 113 | if (j >= cluster) 114 | return; 115 | if (fabs(book_buffer[j].learn) < 0.0001) 116 | book_buffer[j].learn = book_learn[i]; 117 | else 118 | book_buffer[j].learn = (book_buffer[j].learn + book_learn[i]) / 2.0; 119 | fseek(book_file, learn_seekto[i] + 4, SEEK_SET); 120 | if (cluster) 121 | BookClusterOut(book_file, cluster, book_buffer); 122 | fflush(book_file); 123 | } 124 | } 125 | } 126 | 127 | /* last modified 02/24/14 */ 128 | /* 129 | ******************************************************************************* 130 | * * 131 | * LearnFunction() is called to compute the adjustment value added to the * 132 | * learn counter in the opening book. It takes three pieces of information * 133 | * into consideration to do this: the search value, the search depth that * 134 | * produced this value, and the rating difference (Crafty-opponent) so that * 135 | * + numbers means Crafty is expected to win, - numbers mean Crafty is ex- * 136 | * pected to lose. * 137 | * * 138 | ******************************************************************************* 139 | */ 140 | int LearnFunction(int sv, int search_depth, int rating_difference, 141 | int trusted_value) { 142 | float multiplier; 143 | static const float rating_mult_t[11] = { .00625, .0125, .025, .05, .075, .1, 144 | 0.15, 0.2, 0.25, 0.3, 0.35 145 | }; 146 | static const float rating_mult_ut[11] = { .25, .2, .15, .1, .05, .025, .012, 147 | .006, .003, .001 148 | }; 149 | int sd, rd, value; 150 | 151 | sd = Max(Min(search_depth - 10, 19), 0); 152 | rd = Max(Min(rating_difference / 200, 5), -5) + 5; 153 | if (trusted_value) 154 | multiplier = rating_mult_t[rd] * sd; 155 | else 156 | multiplier = rating_mult_ut[rd] * sd; 157 | sv = Max(Min(sv, 5 * learning), -5 * learning); 158 | value = (int) (sv * multiplier); 159 | return value; 160 | } 161 | 162 | /* last modified 02/24/14 */ 163 | /* 164 | ******************************************************************************* 165 | * * 166 | * LearnValue() is used to monitor the scores over the first N moves out of * 167 | * book. After these moves have been played, the evaluations are then used * 168 | * to decide whether the last book move played was a reasonable choice or * 169 | * not. (N is set by the #define LEARN_INTERVAL definition.) * 170 | * * 171 | * This procedure does not directly update the book. Rather, it sets the * 172 | * global learn_value variable to represent the goodness or badness of the * 173 | * position where we left the opening book. This will be used later to * 174 | * update the book in the event the game ends without any sort of actual * 175 | * result. In a normal situation, we will base our learning on the result * 176 | * of the game, win-lose-draw. But it is possible that the game ends before * 177 | * the final result is known. In this case, we will use the score from the * 178 | * learn_value we compute here so that we learn _something_ from playing a * 179 | * game fragment. * 180 | * * 181 | * There are three cases to be handled. (1) If the evaluation is bad right * 182 | * out of book, or it drops enough to be considered a bad line, then the * 183 | * book move will have its "learn" value reduced to discourage playing this * 184 | * move again. (2) If the evaluation is even after N moves, then the learn * 185 | * value will be increased, but by a relatively modest amount, so that a few * 186 | * even results will offset one bad result. (3) If the evaluation is very * 187 | * good after N moves, the learn value will be increased by a large amount * 188 | * so that this move will be favored the next time the game is played. * 189 | * * 190 | ******************************************************************************* 191 | */ 192 | void LearnValue(int search_value, int search_depth) { 193 | int i, interval; 194 | int best_eval = -999999, best_eval_p = 0; 195 | int worst_eval = 999999, worst_eval_p = 0; 196 | int best_after_worst_eval = -999999, worst_after_best_eval = 999999; 197 | 198 | /* 199 | ************************************************************ 200 | * * 201 | * If we have not been "out of book" for N moves, all we * 202 | * need to do is take the search evaluation for the search * 203 | * just completed and tuck it away in the book learning * 204 | * array (book_learn_eval[]) for use later. * 205 | * * 206 | ************************************************************ 207 | */ 208 | if (!book_file) 209 | return; 210 | if (!learn || learn_value != 0) 211 | return; 212 | if (moves_out_of_book <= LEARN_INTERVAL) { 213 | if (moves_out_of_book) { 214 | book_learn_eval[moves_out_of_book - 1] = search_value; 215 | book_learn_depth[moves_out_of_book - 1] = search_depth; 216 | } 217 | } 218 | /* 219 | ************************************************************ 220 | * * 221 | * Check the evaluations we've seen so far. If they are * 222 | * within reason (+/- 1/3 of a pawn or so) we simply keep * 223 | * playing and leave the book alone. If the eval is much * 224 | * better or worse, we need to update the learning data. * 225 | * * 226 | ************************************************************ 227 | */ 228 | else if (moves_out_of_book == LEARN_INTERVAL + 1) { 229 | if (moves_out_of_book < 1) 230 | return; 231 | interval = Min(LEARN_INTERVAL, moves_out_of_book); 232 | if (interval < 2) 233 | return; 234 | for (i = 0; i < interval; i++) { 235 | if (book_learn_eval[i] > best_eval) { 236 | best_eval = book_learn_eval[i]; 237 | best_eval_p = i; 238 | } 239 | if (book_learn_eval[i] < worst_eval) { 240 | worst_eval = book_learn_eval[i]; 241 | worst_eval_p = i; 242 | } 243 | } 244 | if (best_eval_p < interval - 1) { 245 | for (i = best_eval_p; i < interval; i++) 246 | if (book_learn_eval[i] < worst_after_best_eval) 247 | worst_after_best_eval = book_learn_eval[i]; 248 | } else 249 | worst_after_best_eval = book_learn_eval[interval - 1]; 250 | if (worst_eval_p < interval - 1) { 251 | for (i = worst_eval_p; i < interval; i++) 252 | if (book_learn_eval[i] > best_after_worst_eval) 253 | best_after_worst_eval = book_learn_eval[i]; 254 | } else 255 | best_after_worst_eval = book_learn_eval[interval - 1]; 256 | /* 257 | ************************************************************ 258 | * * 259 | * We now have the best eval for the first N moves out of * 260 | * book, the worst eval for the first N moves out of book, * 261 | * and the worst eval that follows the best eval. This * 262 | * will be used to recognize the following cases of * 263 | * results that follow a book move: * 264 | * * 265 | ************************************************************ 266 | */ 267 | /* 268 | ************************************************************ 269 | * * 270 | * (1) The best score is very good, and it doesn't drop * 271 | * after following the game further. This case detects * 272 | * those moves in book that are "good" and should be * 273 | * played whenever possible, while avoiding the sound * 274 | * gambits that leave us ahead in material for a short * 275 | * while until the score starts to drop as the gambit * 276 | * begins to show its effect. * 277 | * * 278 | ************************************************************ 279 | */ 280 | if (best_eval == best_after_worst_eval) { 281 | learn_value = best_eval; 282 | for (i = 0; i < interval; i++) 283 | if (learn_value == book_learn_eval[i]) 284 | search_depth = Max(search_depth, book_learn_depth[i]); 285 | } 286 | /* 287 | ************************************************************ 288 | * * 289 | * (2) The worst score is bad, and doesn't improve any * 290 | * after the worst point, indicating that the book move * 291 | * chosen was "bad" and should be avoided in the future. * 292 | * * 293 | ************************************************************ 294 | */ 295 | else if (worst_eval == worst_after_best_eval) { 296 | learn_value = worst_eval; 297 | for (i = 0; i < interval; i++) 298 | if (learn_value == book_learn_eval[i]) 299 | search_depth = Max(search_depth, book_learn_depth[i]); 300 | } 301 | /* 302 | ************************************************************ 303 | * * 304 | * (3) Things seem even out of book and remain that way * 305 | * for N moves. We will just average the 10 scores and * 306 | * use that as an approximation. * 307 | * * 308 | ************************************************************ 309 | */ 310 | else { 311 | learn_value = 0; 312 | search_depth = 0; 313 | for (i = 0; i < interval; i++) { 314 | learn_value += book_learn_eval[i]; 315 | search_depth += book_learn_depth[i]; 316 | } 317 | learn_value /= interval; 318 | search_depth /= interval; 319 | } 320 | learn_value = 321 | LearnFunction(learn_value, search_depth, 322 | crafty_rating - opponent_rating, learn_value < 0); 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /source/lock.h: -------------------------------------------------------------------------------- 1 | /* *INDENT-OFF* */ 2 | #if (CPUS > 1) 3 | # if !defined(UNIX) 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * this is a Microsoft windows-based operating system. * 8 | * * 9 | ******************************************************************************* 10 | */ 11 | # include 12 | # define pthread_attr_t HANDLE 13 | # define pthread_t HANDLE 14 | # define thread_t HANDLE 15 | extern pthread_t NumaStartThread(void *func, void *args); 16 | 17 | # include 18 | typedef volatile LONG lock_t[1]; 19 | 20 | # define LockInit(v) ((v)[0] = 0) 21 | # define LockFree(v) ((v)[0] = 0) 22 | # define Unlock(v) ((v)[0] = 0) 23 | __forceinline void Lock(volatile LONG * hPtr) { 24 | int iValue; 25 | 26 | for (;;) { 27 | iValue = _InterlockedExchange((LPLONG) hPtr, 1); 28 | if (0 == iValue) 29 | return; 30 | while (*hPtr); 31 | } 32 | } 33 | void Pause() { 34 | } 35 | # else 36 | /* 37 | ******************************************************************************* 38 | * * 39 | * this is a Unix-based operating system. define the spinlock code as needed * 40 | * for SMP synchronization. * 41 | * * 42 | ******************************************************************************* 43 | */ 44 | static void __inline__ LockX86(volatile int *lock) { 45 | int dummy; 46 | asm __volatile__( 47 | "1: movl $1, %0" "\n\t" 48 | " xchgl (%1), %0" "\n\t" 49 | " testl %0, %0" "\n\t" 50 | " jz 3f" "\n\t" 51 | "2: pause" "\n\t" 52 | " movl (%1), %0" "\n\t" 53 | " testl %0, %0" "\n\t" 54 | " jnz 2b" "\n\t" 55 | " jmp 1b" "\n\t" 56 | "3:" "\n\t" 57 | :"=&q"(dummy) 58 | :"q"(lock) 59 | :"cc", "memory"); 60 | } 61 | static void __inline__ Pause() { 62 | asm __volatile__( 63 | " pause" "\n\t"); 64 | } 65 | static void __inline__ UnlockX86(volatile int *lock) { 66 | asm __volatile__( 67 | "movl $0, (%0)" 68 | : 69 | :"q"(lock) 70 | :"memory"); 71 | } 72 | 73 | # define LockInit(p) (p=0) 74 | # define LockFree(p) (p=0) 75 | # define Unlock(p) (UnlockX86(&p)) 76 | # define Lock(p) (LockX86(&p)) 77 | # define lock_t volatile int 78 | # endif 79 | #else 80 | # define LockInit(p) 81 | # define LockFree(p) 82 | # define Lock(p) 83 | # define Unlock(p) 84 | # define lock_t volatile int 85 | #endif /* SMP code */ 86 | /* *INDENT-ON* */ 87 | -------------------------------------------------------------------------------- /source/make.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 01/06/16 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * MakeMove() is responsible for updating the position database whenever a * 8 | * piece is moved. It performs the following operations: (1) update the * 9 | * board structure itself by moving the piece and removing any captured * 10 | * piece. (2) update the hash keys. (3) update material counts. (4) then * 11 | * update castling status. (5) and finally update number of moves since * 12 | * last reversible move. * 13 | * * 14 | * There are some special-cases handled here, such as en passant captures * 15 | * where the enemy pawn is not on the square, castling which moves * 16 | * both the king and rook, and then rook moves/captures which give up the * 17 | * castling right to that side when the rook is moved. * 18 | * * 19 | * note: side = 1 if white is to move, 0 otherwise. enemy is the opposite * 20 | * and is 1 if it is not white to move, 0 otherwise. * 21 | * * 22 | ******************************************************************************* 23 | */ 24 | void MakeMove(TREE * RESTRICT tree, int ply, int side, int move) { 25 | uint64_t bit_move; 26 | int piece, from, to, captured, promote, enemy = Flip(side), cpiece; 27 | #if defined(DEBUG) 28 | int i; 29 | #endif 30 | 31 | /* 32 | ************************************************************ 33 | * * 34 | * First, some basic information is updated for all moves * 35 | * before we do the piece-specific stuff. We need to save * 36 | * the current position and both hash signatures, and add * 37 | * the current position to the repetition-list for the * 38 | * side on move, before the move is actually made on the * 39 | * board. We also update the 50 move rule counter which * 40 | * will be reset if a capture or pawn move is made here. * 41 | * * 42 | * If the en passant flag was set the previous ply, we * 43 | * have already used it to generate moves at this ply and * 44 | * we need to clear it before continuing. If it is set, * 45 | * we also need to update the hash signature since the EP * 46 | * opportunity no longer exists after making any move at * 47 | * this ply (one ply deeper than when a pawn was advanced * 48 | * two squares). * 49 | * * 50 | ************************************************************ 51 | */ 52 | #if defined(DEBUG) 53 | ValidatePosition(tree, ply, move, "MakeMove(1)"); 54 | #endif 55 | tree->status[ply + 1] = tree->status[ply]; 56 | tree->save_hash_key[ply] = HashKey; 57 | tree->save_pawn_hash_key[ply] = PawnHashKey; 58 | if (EnPassant(ply + 1)) { 59 | HashEP(EnPassant(ply + 1)); 60 | EnPassant(ply + 1) = 0; 61 | } 62 | Reversible(ply + 1)++; 63 | /* 64 | ************************************************************ 65 | * * 66 | * Now do the things that are common to all pieces, such * 67 | * as updating the bitboards and hash signature. * 68 | * * 69 | ************************************************************ 70 | */ 71 | piece = Piece(move); 72 | from = From(move); 73 | to = To(move); 74 | captured = Captured(move); 75 | promote = Promote(move); 76 | bit_move = SetMask(from) | SetMask(to); 77 | cpiece = PcOnSq(to); 78 | ClearSet(bit_move, Pieces(side, piece)); 79 | ClearSet(bit_move, Occupied(side)); 80 | Hash(side, piece, from); 81 | Hash(side, piece, to); 82 | PcOnSq(from) = 0; 83 | PcOnSq(to) = pieces[side][piece]; 84 | /* 85 | ************************************************************ 86 | * * 87 | * Now do the piece-specific things by jumping to the * 88 | * appropriate routine. * 89 | * * 90 | ************************************************************ 91 | */ 92 | switch (piece) { 93 | case pawn: 94 | HashP(side, from); 95 | HashP(side, to); 96 | Reversible(ply + 1) = 0; 97 | if (captured == 1 && !cpiece) { 98 | Clear(to + epsq[side], Pawns(enemy)); 99 | Clear(to + epsq[side], Occupied(enemy)); 100 | Hash(enemy, pawn, to + epsq[side]); 101 | HashP(enemy, to + epsq[side]); 102 | PcOnSq(to + epsq[side]) = 0; 103 | Material -= PieceValues(enemy, pawn); 104 | TotalPieces(enemy, pawn)--; 105 | TotalAllPieces--; 106 | captured = 0; 107 | } 108 | if (promote) { 109 | TotalPieces(side, pawn)--; 110 | Material -= PieceValues(side, pawn); 111 | Clear(to, Pawns(side)); 112 | Hash(side, pawn, to); 113 | HashP(side, to); 114 | Hash(side, promote, to); 115 | PcOnSq(to) = pieces[side][promote]; 116 | TotalPieces(side, occupied) += p_vals[promote]; 117 | TotalPieces(side, promote)++; 118 | Material += PieceValues(side, promote); 119 | Set(to, Pieces(side, promote)); 120 | } else if ((Abs(to - from) == 16) && (mask_eptest[to] & Pawns(enemy))) { 121 | EnPassant(ply + 1) = to + epsq[side]; 122 | HashEP(to + epsq[side]); 123 | } 124 | break; 125 | case knight: 126 | case bishop: 127 | case queen: 128 | break; 129 | case rook: 130 | if (Castle(ply + 1, side) > 0) { 131 | if ((from == rook_A[side]) && (Castle(ply + 1, side) & 2)) { 132 | Castle(ply + 1, side) &= 1; 133 | HashCastle(1, side); 134 | } else if ((from == rook_H[side]) && (Castle(ply + 1, side) & 1)) { 135 | Castle(ply + 1, side) &= 2; 136 | HashCastle(0, side); 137 | } 138 | } 139 | break; 140 | case king: 141 | KingSQ(side) = to; 142 | if (Castle(ply + 1, side) > 0) { 143 | if (Castle(ply + 1, side) & 2) 144 | HashCastle(1, side); 145 | if (Castle(ply + 1, side) & 1) 146 | HashCastle(0, side); 147 | if (Abs(to - from) == 2) { 148 | Castle(ply + 1, side) = -1; 149 | piece = rook; 150 | if (to == rook_G[side]) { 151 | from = rook_H[side]; 152 | to = rook_F[side]; 153 | } else { 154 | from = rook_A[side]; 155 | to = rook_D[side]; 156 | } 157 | bit_move = SetMask(from) | SetMask(to); 158 | ClearSet(bit_move, Rooks(side)); 159 | ClearSet(bit_move, Occupied(side)); 160 | Hash(side, rook, from); 161 | Hash(side, rook, to); 162 | PcOnSq(from) = 0; 163 | PcOnSq(to) = pieces[side][rook]; 164 | } else 165 | Castle(ply + 1, side) = 0; 166 | } 167 | break; 168 | } 169 | /* 170 | ************************************************************ 171 | * * 172 | * If this is a capture move, we also have to update the * 173 | * information that must change when a piece is removed * 174 | * from the board. * 175 | * * 176 | ************************************************************ 177 | */ 178 | if (captured) { 179 | Reversible(ply + 1) = 0; 180 | TotalAllPieces--; 181 | if (promote) 182 | piece = promote; 183 | Hash(enemy, captured, to); 184 | Clear(to, Pieces(enemy, captured)); 185 | Clear(to, Occupied(enemy)); 186 | Material -= PieceValues(enemy, captured); 187 | TotalPieces(enemy, captured)--; 188 | if (captured != pawn) 189 | TotalPieces(enemy, occupied) -= p_vals[captured]; 190 | switch (captured) { 191 | case pawn: 192 | HashP(enemy, to); 193 | break; 194 | case knight: 195 | case bishop: 196 | case queen: 197 | break; 198 | case rook: 199 | if (Castle(ply + 1, enemy) > 0) { 200 | if ((to == rook_A[enemy]) && (Castle(ply + 1, enemy) & 2)) { 201 | Castle(ply + 1, enemy) &= 1; 202 | HashCastle(1, enemy); 203 | } else if ((to == rook_H[enemy]) && (Castle(ply + 1, enemy) & 1)) { 204 | Castle(ply + 1, enemy) &= 2; 205 | HashCastle(0, enemy); 206 | } 207 | } 208 | break; 209 | case king: 210 | #if defined(DEBUG) 211 | Print(2048, "captured a king (Make)\n"); 212 | for (i = 1; i <= ply; i++) 213 | Print(2048, 214 | "ply=%2d, phase=%d, piece=%2d,from=%2d,to=%2d,captured=%2d\n", 215 | i, tree->phase[i], Piece(tree->curmv[i]), From(tree->curmv[i]), 216 | To(tree->curmv[i]), Captured(tree->curmv[i])); 217 | Print(2048, "ply=%2d, piece=%2d,from=%2d,to=%2d,captured=%2d\n", i, 218 | piece, from, to, captured); 219 | if (log_file) 220 | DisplayChessBoard(log_file, tree->position); 221 | #endif 222 | break; 223 | } 224 | } 225 | #if defined(DEBUG) 226 | ValidatePosition(tree, ply + 1, move, "MakeMove(2)"); 227 | #endif 228 | return; 229 | } 230 | 231 | /* last modified 01/06/16 */ 232 | /* 233 | ******************************************************************************* 234 | * * 235 | * MakeMoveRoot() is used to make a move at the root of the game tree, * 236 | * before any searching is done. It uses MakeMove() to execute the move, * 237 | * but then copies the resulting position back to position[0], the actual * 238 | * board position. It handles the special-case of the draw-by-repetition * 239 | * rule by clearing the repetition list when a non-reversible move is made, * 240 | * since no repetitions are possible once such a move is played. * 241 | * * 242 | ******************************************************************************* 243 | */ 244 | void MakeMoveRoot(TREE * RESTRICT tree, int side, int move) { 245 | int player; 246 | 247 | /* 248 | ************************************************************ 249 | * * 250 | * First, make the move and then reset the repetition * 251 | * index if the 50 move rule counter was reset to zero. * 252 | * * 253 | ************************************************************ 254 | */ 255 | MakeMove(tree, 0, side, move); 256 | if (Reversible(1) == 0) 257 | rep_index = -1; 258 | tree->rep_list[++rep_index] = HashKey; 259 | /* 260 | ************************************************************ 261 | * * 262 | * One odd action is to note if the castle status is * 263 | * currently negative, which indicates that that side * 264 | * castled during the previous search. We simply set the * 265 | * castle status for that side to zero and we are done. * 266 | * * 267 | * We then copy this back to ply=0 status (which is the * 268 | * permanent game-board ply). * 269 | * * 270 | ************************************************************ 271 | */ 272 | for (player = black; player <= white; player++) 273 | Castle(1, player) = Max(0, Castle(1, player)); 274 | tree->status[0] = tree->status[1]; 275 | } 276 | -------------------------------------------------------------------------------- /source/output.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 02/24/14 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * OutputMove() is responsible for converting the internal move format to a * 8 | * string that can be displayed. First, it simply converts the from/to * 9 | * squares to fully-qualified algebraic (which includes O-O and O-O-O for * 10 | * castling moves). Next, we try several "shortcut" forms and call * 11 | * input_move(silent=1) to let it silently check the move for uniqueness. * 12 | * as soon as we get a non-ambiguous move, we return that text string. * 13 | * * 14 | ******************************************************************************* 15 | */ 16 | char *OutputMove(TREE * RESTRICT tree, int ply, int wtm, int move) { 17 | static char text_move[10], new_text[10]; 18 | unsigned *mvp; 19 | char *text = text_move; 20 | static const char piece_names[7] = { ' ', 'P', 'N', 'B', 'R', 'Q', 'K' }; 21 | /* 22 | ************************************************************ 23 | * * 24 | * Special case for null-move which will only be used in a * 25 | * search trace which dumps the entire tree. * 26 | * * 27 | ************************************************************ 28 | */ 29 | if (move == 0) { 30 | strcpy(text, "null"); 31 | return text; 32 | } 33 | do { 34 | /* 35 | ************************************************************ 36 | * * 37 | * Check for castling moves first. * 38 | * * 39 | ************************************************************ 40 | */ 41 | if ((Piece(move) == king) && (Abs(From(move) - To(move)) == 2)) { 42 | if (wtm) { 43 | if (To(move) == 2) 44 | strcpy(text_move, "O-O-O"); 45 | else 46 | strcpy(text_move, "O-O"); 47 | } else { 48 | if (To(move) == 58) 49 | strcpy(text_move, "O-O-O"); 50 | else 51 | strcpy(text_move, "O-O"); 52 | } 53 | break; 54 | } 55 | /* 56 | ************************************************************ 57 | * * 58 | * Not a castling move. Convert the move to a fully- * 59 | * qualified algebraic move as a starting point. * 60 | * * 61 | ************************************************************ 62 | */ 63 | text = new_text; 64 | if ((int) Piece(move) > pawn) 65 | *text++ = piece_names[Piece(move)]; 66 | *text++ = File(From(move)) + 'a'; 67 | *text++ = Rank(From(move)) + '1'; 68 | if (Captured(move)) 69 | *text++ = 'x'; 70 | *text++ = File(To(move)) + 'a'; 71 | *text++ = Rank(To(move)) + '1'; 72 | if (Promote(move)) { 73 | *text++ = '='; 74 | *text++ = piece_names[Promote(move)]; 75 | } 76 | *text = '\0'; 77 | strcpy(text_move, new_text); 78 | if (output_format > 0) 79 | break; 80 | /* 81 | ************************************************************ 82 | * * 83 | * Now we try some short forms. If this is a pawn move * 84 | * (first character is "P") and the move is not a capture * 85 | * move, we can try just the destination square (Pe2e4 * 86 | * becomes e4). * 87 | * * 88 | ************************************************************ 89 | */ 90 | if (Piece(move) == pawn) { 91 | if (!Captured(move)) { 92 | strcpy(text_move, new_text + 2); 93 | if (InputMove(tree, ply, wtm, 1, 0, text_move)) 94 | break; 95 | } 96 | /* 97 | ************************************************************ 98 | * * 99 | * If this is a pawn and it is capturing something, try * 100 | * the usual pawn capture format (Pe4xd5 becomes exd5). * 101 | * * 102 | ************************************************************ 103 | */ 104 | text_move[0] = new_text[0]; 105 | strcpy(text_move + 1, new_text + 2); 106 | if (InputMove(tree, ply, wtm, 1, 0, text_move)) 107 | break; 108 | /* 109 | ************************************************************ 110 | * * 111 | * It is a pawn move and we can't find a shorter form, so * 112 | * leave it as a fully-qualified move and go with it as * 113 | * is. (this will not normally happen). * 114 | * * 115 | ************************************************************ 116 | */ 117 | strcpy(text_move, new_text); 118 | break; 119 | } 120 | /* 121 | ************************************************************ 122 | * * 123 | * If the move is a normal piece move, and does not * 124 | * capture anything, we try the piece + destination format * 125 | * first (Ng1f3 becomes Nf3). * 126 | * * 127 | ************************************************************ 128 | */ 129 | if (!Captured(move)) { 130 | text_move[0] = new_text[0]; 131 | strcpy(text_move + 1, new_text + 3); 132 | if (InputMove(tree, ply, wtm, 1, 0, text_move)) 133 | break; 134 | /* 135 | ************************************************************ 136 | * * 137 | * If that is ambiguous, we will try two alternatives: * 138 | * (1) add in the origin file; (2) add in the origin rank * 139 | * (Ng1f3 becomes Ngf3 or N1f3). * 140 | * * 141 | ************************************************************ 142 | */ 143 | text_move[0] = new_text[0]; 144 | text_move[1] = new_text[1]; 145 | strcpy(text_move + 2, new_text + 3); 146 | if (InputMove(tree, ply, wtm, 1, 0, text_move)) 147 | break; 148 | text_move[0] = new_text[0]; 149 | strcpy(text_move + 1, new_text + 2); 150 | if (InputMove(tree, ply, wtm, 1, 0, text_move)) 151 | break; 152 | /* 153 | ************************************************************ 154 | * * 155 | * Nothing worked, so we go with the fully-qualified move. * 156 | * * 157 | ************************************************************ 158 | */ 159 | strcpy(text_move, new_text); 160 | break; 161 | } else { 162 | /* 163 | ************************************************************ 164 | * * 165 | * If this is a capture, we try the short form of a * 166 | * capture move (Ng1xf3 becomes Nxf3) * 167 | * * 168 | ************************************************************ 169 | */ 170 | text_move[0] = new_text[0]; 171 | strcpy(text_move + 1, new_text + 3); 172 | if (InputMove(tree, ply, wtm, 1, 0, text_move)) 173 | break; 174 | /* 175 | ************************************************************ 176 | * * 177 | * If that didn't work, we try adding in the origin file * 178 | * or the origin rank (Ng1xf3 becomes Ngxf3 or N1xf3). * 179 | * * 180 | ************************************************************ 181 | */ 182 | text_move[0] = new_text[0]; 183 | text_move[1] = new_text[1]; 184 | strcpy(text_move + 2, new_text + 3); 185 | if (InputMove(tree, ply, wtm, 1, 0, text_move)) 186 | break; 187 | text_move[0] = new_text[0]; 188 | strcpy(text_move + 1, new_text + 2); 189 | if (InputMove(tree, ply, wtm, 1, 0, text_move)) 190 | break; 191 | /* 192 | ************************************************************ 193 | * * 194 | * Nothing worked, return the fully-qualified move. * 195 | * * 196 | ************************************************************ 197 | */ 198 | strcpy(text_move, new_text); 199 | break; 200 | } 201 | } while (0); 202 | /* 203 | ************************************************************ 204 | * * 205 | * If the move is a check, or mate, append either "+" or * 206 | * "#" as appropriate. * 207 | * * 208 | ************************************************************ 209 | */ 210 | if (output_format == 0) { 211 | text = text_move + strlen(text_move); 212 | tree->status[MAXPLY] = tree->status[ply]; 213 | MakeMove(tree, MAXPLY, wtm, move); 214 | if (Check(Flip(wtm))) { 215 | mvp = 216 | GenerateCheckEvasions(tree, MAXPLY + 1, Flip(wtm), 217 | tree->move_list + 4800); 218 | if (mvp == (tree->move_list + 4800)) 219 | *text++ = '#'; 220 | else 221 | *text++ = '+'; 222 | } 223 | UnmakeMove(tree, MAXPLY, wtm, move); 224 | *text = 0; 225 | } 226 | return text_move; 227 | } 228 | -------------------------------------------------------------------------------- /source/ponder.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 08/03/16 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * Ponder() is the driver for "pondering" (thinking on the opponent's time.) * 8 | * its operation is simple: Find a predicted move by (a) taking the second * 9 | * move from the principal variation, or (b) call lookup to see if it finds * 10 | * a suggested move from the transposition table. Then, make this move and * 11 | * do a search from the resulting position. While pondering, one of three * 12 | * things can happen: (1) A move is entered, and it matches the predicted * 13 | * move. We then switch from pondering to thinking and search as normal; * 14 | * (2) A move is entered, but it does not match the predicted move. We then * 15 | * abort the search, unmake the pondered move, and then restart with the * 16 | * move entered. (3) A command is entered. If it is a simple command, it * 17 | * can be done without aborting the search or losing time. If not, we abort * 18 | * the search, execute the command, and then attempt to restart pondering if * 19 | * the command didn't make that impossible. * 20 | * * 21 | ******************************************************************************* 22 | */ 23 | int Ponder(int wtm) { 24 | TREE *const tree = block[0]; 25 | int dalpha = -999999, dbeta = 999999, i; 26 | unsigned *n_ponder_moves, *mv; 27 | int save_move_number, tlom, value, illegal = 0; 28 | 29 | /* 30 | ************************************************************ 31 | * * 32 | * First, let's check to see if pondering is allowed, or * 33 | * if we should avoid pondering on this move since it is * 34 | * the first move of a game, or if the game is over, or * 35 | * "force" mode is active, or there is input in the queue * 36 | * that needs to be read and processed. * 37 | * * 38 | ************************************************************ 39 | */ 40 | if (!ponder || force || over || CheckInput()) 41 | return 0; 42 | save_move_number = move_number; 43 | /* 44 | ************************************************************ 45 | * * 46 | * Check the ponder move for legality. If it is not a * 47 | * legal move, we have to take action to find something to * 48 | * ponder. * 49 | * * 50 | ************************************************************ 51 | */ 52 | strcpy(ponder_text, "none"); 53 | if (ponder_move) { 54 | if (!VerifyMove(tree, 1, wtm, ponder_move)) { 55 | ponder_move = 0; 56 | Print(4095, "ERROR. ponder_move is illegal (1).\n"); 57 | Print(4095, "ERROR. PV pathl=%d\n", last_pv.pathl); 58 | Print(4095, "ERROR. move=%d %x\n", ponder_move, ponder_move); 59 | } 60 | } 61 | /* 62 | ************************************************************ 63 | * * 64 | * First attempt, do a hash probe. However, since a hash * 65 | * collision is remotely possible, we still need to verify * 66 | * that the transposition/refutation best move is actually * 67 | * legal. * 68 | * * 69 | ************************************************************ 70 | */ 71 | if (!ponder_move) { 72 | HashProbe(tree, 0, 0, wtm, dalpha, dbeta, &value); 73 | if (tree->hash_move[0]) 74 | ponder_move = tree->hash_move[0]; 75 | if (ponder_move) { 76 | if (!VerifyMove(tree, 1, wtm, ponder_move)) { 77 | Print(4095, "ERROR. ponder_move is illegal (2).\n"); 78 | Print(4095, "ERROR. move=%d %x\n", ponder_move, ponder_move); 79 | ponder_move = 0; 80 | } 81 | } 82 | } 83 | /* 84 | ************************************************************ 85 | * * 86 | * Second attempt. If that didn't work, then we try what * 87 | * I call a "puzzling" search. Which is simply a shorter * 88 | * time-limit search for the other side, to find something * 89 | * to ponder. * 90 | * * 91 | ************************************************************ 92 | */ 93 | if (!ponder_move) { 94 | TimeSet(puzzle); 95 | if (time_limit < 20) 96 | return 0; 97 | puzzling = 1; 98 | Print(32, " puzzling over a move to ponder.\n"); 99 | last_pv.pathl = 0; 100 | last_pv.pathd = 0; 101 | for (i = 0; i < MAXPLY; i++) { 102 | tree->killers[i].move1 = 0; 103 | tree->killers[i].move2 = 0; 104 | } 105 | Iterate(wtm, puzzle, 0); 106 | for (i = 0; i < MAXPLY; i++) { 107 | tree->killers[i].move1 = 0; 108 | tree->killers[i].move2 = 0; 109 | } 110 | puzzling = 0; 111 | if (tree->pv[0].pathl) 112 | ponder_move = tree->pv[0].path[1]; 113 | if (!ponder_move) 114 | return 0; 115 | for (i = 1; i < (int) tree->pv[0].pathl; i++) 116 | last_pv.path[i] = tree->pv[0].path[i + 1]; 117 | last_pv.pathl = tree->pv[0].pathl - 1; 118 | last_pv.pathd = 0; 119 | if (!VerifyMove(tree, 1, wtm, ponder_move)) { 120 | ponder_move = 0; 121 | Print(4095, "ERROR. ponder_move is illegal (3).\n"); 122 | Print(4095, "ERROR. PV pathl=%d\n", last_pv.pathl); 123 | return 0; 124 | } 125 | } 126 | /* 127 | ************************************************************ 128 | * * 129 | * Display the move we are going to "ponder". * 130 | * * 131 | ************************************************************ 132 | */ 133 | if (wtm) 134 | Print(32, "White(%d): %s [pondering]\n", move_number, OutputMove(tree, 0, 135 | wtm, ponder_move)); 136 | else 137 | Print(32, "Black(%d): %s [pondering]\n", move_number, OutputMove(tree, 0, 138 | wtm, ponder_move)); 139 | sprintf(ponder_text, "%s", OutputMove(tree, 0, wtm, ponder_move)); 140 | if (post) 141 | printf("Hint: %s\n", ponder_text); 142 | /* 143 | ************************************************************ 144 | * * 145 | * Set the ponder move list and eliminate illegal moves. * 146 | * This list is used to test the move entered while we are * 147 | * pondering, since we need a move list for the input * 148 | * screening process. * 149 | * * 150 | ************************************************************ 151 | */ 152 | n_ponder_moves = GenerateCaptures(tree, 0, wtm, ponder_moves); 153 | num_ponder_moves = 154 | GenerateNoncaptures(tree, 0, wtm, n_ponder_moves) - ponder_moves; 155 | for (mv = ponder_moves; mv < ponder_moves + num_ponder_moves; mv++) { 156 | MakeMove(tree, 0, wtm, *mv); 157 | illegal = Check(wtm); 158 | UnmakeMove(tree, 0, wtm, *mv); 159 | if (illegal) 160 | *mv = 0; 161 | } 162 | /* 163 | ************************************************************ 164 | * * 165 | * Now, perform an iterated search, but with the special * 166 | * "pondering" flag set which changes the time controls * 167 | * since there is no need to stop searching until the * 168 | * opponent makes a move. * 169 | * * 170 | ************************************************************ 171 | */ 172 | MakeMove(tree, 0, wtm, ponder_move); 173 | tree->curmv[0] = ponder_move; 174 | tree->rep_list[++rep_index] = HashKey; 175 | tlom = last_opponent_move; 176 | last_opponent_move = ponder_move; 177 | if (kibitz) 178 | strcpy(kibitz_text, "n/a"); 179 | thinking = 0; 180 | pondering = 1; 181 | if (!wtm) 182 | move_number++; 183 | ponder_value = Iterate(Flip(wtm), think, 0); 184 | rep_index--; 185 | move_number = save_move_number; 186 | pondering = 0; 187 | thinking = 0; 188 | last_opponent_move = tlom; 189 | UnmakeMove(tree, 0, wtm, ponder_move); 190 | /* 191 | ************************************************************ 192 | * * 193 | * Search completed. the possible return values are: * 194 | * * 195 | * (0) No pondering was done, period. * 196 | * * 197 | * (1) Pondering was done, opponent made the predicted * 198 | * move, and we searched until time ran out in a * 199 | * normal manner. * 200 | * * 201 | * (2) Pondering was done, but the ponder search * 202 | * terminated due to either finding a mate, or the * 203 | * maximum search depth was reached. The result of * 204 | * this ponder search are valid, but only if the * 205 | * opponent makes the correct (predicted) move. * 206 | * * 207 | * (3) Pondering was done, but the opponent either made a * 208 | * different move, or entered a command that has to * 209 | * interrupt the pondering search before the command * 210 | * (or move) can be processed. This forces Main() to * 211 | * avoid reading in a move/command since one has been * 212 | * read into the command buffer already. * 213 | * * 214 | ************************************************************ 215 | */ 216 | if (input_status == 1) 217 | return 1; 218 | if (input_status == 2) 219 | return 3; 220 | return 2; 221 | } 222 | -------------------------------------------------------------------------------- /source/repeat.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 08/03/16 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * Repeat() is used to detect a draw by repetition. The repetition list is * 8 | * a simple 1d array that contains the Zobrist signatures for each position * 9 | * reached in the game since the last irreversable moves at the root. New * 10 | * positions are only added to this list here in Repeat(). * 11 | * * 12 | * Repeat() scans the list to determine if this position has occurred at * 13 | * once previously (two-fold repetition.) If so, this will be treated as a * 14 | * draw by Search()/Quiesce(). * 15 | * * 16 | * Repeat() also handles 50-move draws. The position[] structure contains * 17 | * the count of moves since the last capture or pawn push. When this value * 18 | * reaches 100 (plies, which is 50 moves) the score is set to DRAW. * 19 | * * 20 | * Repeat() has one tricky issue. MakeMoveRoot() has to insert moves into * 21 | * the repetition list so that things will work well. We end up with the * 22 | * list having entries from 0 through "rep_index" where the last entry is * 23 | * the entry stored after the last actual move in the game. Search() will * 24 | * call Repeat() at ply=1, which stores the SAME position at position * 25 | * rep_index + 1. That gets the list out of sync. To solve this, Iterate() * 26 | * decrements rep_index immediately prior to calling Search(), and it then * 27 | * increments it immediately after Search() returns. This causes Search() * 28 | * to overwrite the entry with the same value for ply=1, but now the list is * 29 | * in a sane state for the rest of the search. Do NOT remove those to lines * 30 | * in Iterate() or repetition detection will be broken. * 31 | * * 32 | ******************************************************************************* 33 | */ 34 | int Repeat(TREE * RESTRICT tree, int ply) { 35 | int where, count; 36 | 37 | /* 38 | ************************************************************ 39 | * * 40 | * If the 50-move rule has been reached, then adjust the * 41 | * score to reflect the impending draw. If we have not * 42 | * made 2 moves for each side (or more) since the last * 43 | * irreversible move, there is no way to repeat a prior * 44 | * position. * 45 | * * 46 | ************************************************************ 47 | */ 48 | tree->rep_list[rep_index + ply] = HashKey; 49 | if (Reversible(ply) < 4) 50 | return 0; 51 | if (Reversible(ply) > 99) 52 | return 3; 53 | /* 54 | ************************************************************ 55 | * * 56 | * Now we scan the right part of the repetition list, * 57 | * which is to search backward from the entry for 2 plies * 58 | * back (no point in checking current position, we KNOW * 59 | * that is a match but it is not a repetition, it is just * 60 | * the current position's entry which we don't want to * 61 | * check). We can use the reversible move counter as a * 62 | * limit since there is no point in scanning the list back * 63 | * beyond the last capture/pawn move since there can not * 64 | * possibly be a repeat beyond that point. * 65 | * * 66 | ************************************************************ 67 | */ 68 | count = Reversible(ply) / 2 - 1; 69 | for (where = rep_index + ply - 4; count; where -= 2, count--) { 70 | if (HashKey == tree->rep_list[where]) 71 | return 2; 72 | } 73 | return 0; 74 | } 75 | 76 | /* last modified 08/03/16 */ 77 | /* 78 | ******************************************************************************* 79 | * * 80 | * Repeat3x() is used to detect a real draw by repetition. This routine is * 81 | * only called from Main() and simply scans the complete list searching for * 82 | * exactly three repetitions (two additional repetitions of the current * 83 | * position.) This is used to actually claim a draw by repetition or by the * 84 | * 50 move rule. * 85 | * * 86 | ******************************************************************************* 87 | */ 88 | int Repeat3x(TREE * RESTRICT tree) { 89 | int reps = 0, where; 90 | 91 | /* 92 | ************************************************************ 93 | * * 94 | * If the 50-move rule has been reached, then return an * 95 | * indicator that a draw can be claimed if wanted. * 96 | * * 97 | ************************************************************ 98 | */ 99 | if (Reversible(0) > 99) 100 | return 2; 101 | /* 102 | ************************************************************ 103 | * * 104 | * Scan the repetition list to determine if this position * 105 | * has occurred two times previously, making this an * 106 | * actual 3-fold repetition. Note we only check every * 107 | * other entry since the position has to be repeated 3x * 108 | * with the SAME side on move. * 109 | * * 110 | ************************************************************ 111 | */ 112 | for (where = rep_index % 2; where < rep_index; where += 2) 113 | if (HashKey == tree->rep_list[where]) 114 | reps++; 115 | return reps == 2; 116 | } 117 | -------------------------------------------------------------------------------- /source/resign.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 08/03/16 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * ResignOrDraw() is used to determine if the program should either resign * 8 | * or offer a draw. This decision is based on two criteria: (1) current * 9 | * search evaluation and (2) time remaining on opponent's clock. * 10 | * * 11 | * The evaluation returned by the last search must be less than the resign * 12 | * threshold to trigger the resign code, or else must be exactly equal to * 13 | * the draw score to trigger the draw code. * 14 | * * 15 | * The opponent must have enough time to be able to win or draw the game if * 16 | * it were played out as well. * 17 | * * 18 | ******************************************************************************* 19 | */ 20 | void ResignOrDraw(TREE * RESTRICT tree, int value) { 21 | int v, result = 0; 22 | 23 | /* 24 | ************************************************************ 25 | * * 26 | * If the game is a technical draw, where there are no * 27 | * pawns and material is balanced, then offer a draw. * 28 | * * 29 | ************************************************************ 30 | */ 31 | if (Drawn(tree, value) == 1) 32 | result = 2; 33 | /* 34 | ************************************************************ 35 | * * 36 | * First check to see if the increment is 2 seconds or * 37 | * more. If so, then the game is being played slowly * 38 | * enough that a draw offer or resignation is worth * 39 | * consideration. Otherwise, if the opponent has at least * 40 | * 30 seconds left, he can probably play the draw or win * 41 | * out. * 42 | * * 43 | * If the value is below the resignation threshold, then * 44 | * Crafty should resign and get on to the next game. Note * 45 | * that it is necessary to have a bad score for * 46 | * moves in a row before resigning. * 47 | * * 48 | * Note that we don't resign for "deep mates" since we do * 49 | * not know if the opponent actually saw that result. We * 50 | * play on until it becomes obvious he "sees it." * 51 | * * 52 | ************************************************************ 53 | */ 54 | if ((tc_increment > 200) || (tc_time_remaining[Flip(root_wtm)] >= 3000)) { 55 | if (resign) { 56 | if (value < -(MATE - 15)) { 57 | if (++resign_counter >= resign_count) 58 | result = 1; 59 | } else if (value < -resign * 100 && value > -32000) { 60 | if (++resign_counter >= resign_count) 61 | result = 1; 62 | } else 63 | resign_counter = 0; 64 | } 65 | } 66 | /* 67 | ************************************************************ 68 | * * 69 | * If the value is almost equal to the draw score, then * 70 | * Crafty should offer the opponent a draw. Note that it * 71 | * is necessary that the draw score occur on exactly * 72 | * moves in a row before making the offer. * 73 | * Note also that the draw offer will be repeated every * 74 | * moves so setting this value too low can * 75 | * make the program behave "obnoxiously." * 76 | * * 77 | ************************************************************ 78 | */ 79 | if ((tc_increment > 200) || (tc_time_remaining[Flip(root_wtm)] >= 3000)) { 80 | if (Abs(Abs(value) - Abs(DrawScore(game_wtm))) < 2 && 81 | moves_out_of_book > 3) { 82 | if (++draw_counter >= draw_count) { 83 | draw_counter = 0; 84 | result = 2; 85 | } 86 | } else 87 | draw_counter = 0; 88 | } 89 | /* 90 | ************************************************************ 91 | * * 92 | * Now print the draw offer or resignation if appropriate * 93 | * but be sure and do it in a form that ICC/FICS will * 94 | * understand if the "xboard" flag is set. * 95 | * * 96 | * Note that we also use the "speak" facility to verbally * 97 | * offer draws or resign if the "speech" variable has been * 98 | * set to 1 by entering "speech on". * 99 | * * 100 | ************************************************************ 101 | */ 102 | if (result == 1) { 103 | learn_value = (crafty_is_white) ? -300 : 300; 104 | LearnBook(); 105 | if (xboard) 106 | Print(4095, "resign\n"); 107 | if (audible_alarm) 108 | printf("%c", audible_alarm); 109 | if (speech) { 110 | char announce[128]; 111 | 112 | strcpy(announce, "./speak "); 113 | strcat(announce, "Resign"); 114 | v = system(announce); 115 | if (v <= 0) 116 | perror("ResignOrDraw() system() error: "); 117 | } 118 | if (crafty_is_white) { 119 | Print(4095, "0-1 {White resigns}\n"); 120 | strcpy(pgn_result, "0-1"); 121 | } else { 122 | Print(4095, "1-0 {Black resigns}\n"); 123 | strcpy(pgn_result, "1-0"); 124 | } 125 | } 126 | if (offer_draws && result == 2) { 127 | draw_offered = 1; 128 | if (!xboard) { 129 | Print(1, "\nI offer a draw.\n\n"); 130 | if (audible_alarm) 131 | printf("%c", audible_alarm); 132 | if (speech) { 133 | char announce[128]; 134 | 135 | strcpy(announce, "./speak "); 136 | strcat(announce, "Drawoffer"); 137 | v = system(announce); 138 | if (v <= 0) 139 | perror("ResignOrDraw() system() error: "); 140 | } 141 | } else if (xboard) 142 | Print(4095, "offer draw\n"); 143 | else 144 | Print(4095, "\n*draw\n"); 145 | } else 146 | draw_offered = 0; 147 | } 148 | -------------------------------------------------------------------------------- /source/see.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 05/16/14 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * SEE() is used to analyze capture moves to see whether or not they appear * 8 | * to be profitable. The basic algorithm is extremely fast since it uses * 9 | * the bitmaps to determine which squares are attacking the [target] square. * 10 | * * 11 | * The algorithm is quite simple. Using the attack bitmaps, we enumerate * 12 | * all the pieces that are attacking [target] for either side. Then we * 13 | * simply use the lowest piece (value) for the correct side to capture on * 14 | * [target]. we continually "flip" sides taking the lowest piece each time. * 15 | * * 16 | * As a piece is used, if it is a sliding piece (pawn, bishop, rook or * 17 | * queen) we remove the piece, then generate moves of bishop/queen or * 18 | * rook/queen and then add those in to the attackers, removing any attacks * 19 | * that have already been used. * 20 | * * 21 | ******************************************************************************* 22 | */ 23 | int SEE(TREE * RESTRICT tree, int wtm, int move) { 24 | uint64_t attacks, temp = 0, toccupied = OccupiedSquares; 25 | uint64_t bsliders = 26 | Bishops(white) | Bishops(black) | Queens(white) | Queens(black); 27 | uint64_t rsliders = 28 | Rooks(white) | Rooks(black) | Queens(white) | Queens(black); 29 | int attacked_piece, piece, nc = 1, see_list[32]; 30 | int source = From(move), target = To(move); 31 | 32 | /* 33 | ************************************************************ 34 | * * 35 | * Determine which squares attack for each side. * 36 | * initialize by placing the piece on first in * 37 | * the list as it is being captured to start things off. * 38 | * * 39 | ************************************************************ 40 | */ 41 | attacks = AttacksTo(tree, target); 42 | attacked_piece = pcval[Captured(move)]; 43 | /* 44 | ************************************************************ 45 | * * 46 | * The first piece to capture on is the piece * 47 | * standing on . * 48 | * * 49 | ************************************************************ 50 | */ 51 | wtm = Flip(wtm); 52 | see_list[0] = attacked_piece; 53 | piece = Piece(move); 54 | attacked_piece = pcval[piece]; 55 | Clear(source, toccupied); 56 | if (piece & 1) 57 | attacks |= BishopAttacks(target, toccupied) & bsliders; 58 | if (piece != king && (piece == pawn || piece & 4)) 59 | attacks |= RookAttacks(target, toccupied) & rsliders; 60 | /* 61 | ************************************************************ 62 | * * 63 | * Now pick out the least valuable piece for the correct * 64 | * side that is bearing on . As we find one, we * 65 | * update the attacks (if this is a sliding piece) to get * 66 | * the attacks for any sliding piece that is lined up * 67 | * behind the attacker we are removing. * 68 | * * 69 | * Once we know there is a piece attacking the last * 70 | * capturing piece, add it to the see list and repeat * 71 | * until one side has no more captures. * 72 | * * 73 | ************************************************************ 74 | */ 75 | for (attacks &= toccupied; attacks; attacks &= toccupied) { 76 | for (piece = pawn; piece <= king; piece++) 77 | if ((temp = Pieces(wtm, piece) & attacks)) 78 | break; 79 | if (piece > king) 80 | break; 81 | toccupied ^= (temp & -temp); 82 | if (piece & 1) 83 | attacks |= BishopAttacks(target, toccupied) & bsliders; 84 | if (piece != king && piece & 4) 85 | attacks |= RookAttacks(target, toccupied) & rsliders; 86 | see_list[nc] = -see_list[nc - 1] + attacked_piece; 87 | attacked_piece = pcval[piece]; 88 | if (see_list[nc++] - attacked_piece > 0) 89 | break; 90 | wtm = Flip(wtm); 91 | } 92 | /* 93 | ************************************************************ 94 | * * 95 | * Starting at the end of the sequence of values, use a * 96 | * "minimax" like procedure to decide where the captures * 97 | * will stop. * 98 | * * 99 | ************************************************************ 100 | */ 101 | while (--nc) 102 | see_list[nc - 1] = -Max(-see_list[nc - 1], see_list[nc]); 103 | return see_list[0]; 104 | } 105 | 106 | /* last modified 05/16/14 */ 107 | /* 108 | ******************************************************************************* 109 | * * 110 | * SEEO() is used to analyze a move already made to see if it appears to be * 111 | * safe. It is similar to SEE() except that the move has already been made * 112 | * and we are checking to see whether the opponent can gain material by * 113 | * capturing the piece just moved. * 114 | * * 115 | ******************************************************************************* 116 | */ 117 | int SEEO(TREE * RESTRICT tree, int wtm, int move) { 118 | uint64_t attacks, temp = 0, toccupied = OccupiedSquares; 119 | uint64_t bsliders = 120 | Bishops(white) | Bishops(black) | Queens(white) | Queens(black); 121 | uint64_t rsliders = 122 | Rooks(white) | Rooks(black) | Queens(white) | Queens(black); 123 | int attacked_piece, piece, nc = 1, see_list[32], target = To(move); 124 | 125 | /* 126 | ************************************************************ 127 | * * 128 | * Determine which squares attack for each side. * 129 | * initialize by placing the piece on first in * 130 | * the list as it is being captured to start things off. * 131 | * * 132 | ************************************************************ 133 | */ 134 | attacks = AttacksTo(tree, target); 135 | attacked_piece = pcval[Piece(move)]; 136 | /* 137 | ************************************************************ 138 | * * 139 | * The first piece to capture on is the piece * 140 | * standing on that square. We have to find out the least * 141 | * valuable attacker for that square first. * 142 | * * 143 | ************************************************************ 144 | */ 145 | wtm = Flip(wtm); 146 | see_list[0] = attacked_piece; 147 | for (piece = pawn; piece <= king; piece++) 148 | if ((temp = Pieces(wtm, piece) & attacks)) 149 | break; 150 | if (piece > king) 151 | return 0; 152 | toccupied ^= (temp & -temp); 153 | if (piece & 1) 154 | attacks |= BishopAttacks(target, toccupied) & bsliders; 155 | if (piece != king && piece & 4) 156 | attacks |= RookAttacks(target, toccupied) & rsliders; 157 | attacked_piece = pcval[piece]; 158 | wtm = Flip(wtm); 159 | /* 160 | ************************************************************ 161 | * * 162 | * Now pick out the least valuable piece for the correct * 163 | * side that is bearing on . As we find one, we * 164 | * update the attacks (if this is a sliding piece) to get * 165 | * the attacks for any sliding piece that is lined up * 166 | * behind the attacker we are removing. * 167 | * * 168 | * Once we know there is a piece attacking the last * 169 | * capturing piece, add it to the see list and repeat * 170 | * until one side has no more captures. * 171 | * * 172 | ************************************************************ 173 | */ 174 | for (attacks &= toccupied; attacks; attacks &= toccupied) { 175 | for (piece = pawn; piece <= king; piece++) 176 | if ((temp = Pieces(wtm, piece) & attacks)) 177 | break; 178 | if (piece > king) 179 | break; 180 | toccupied ^= (temp & -temp); 181 | if (piece & 1) 182 | attacks |= BishopAttacks(target, toccupied) & bsliders; 183 | if (piece != king && piece & 4) 184 | attacks |= RookAttacks(target, toccupied) & rsliders; 185 | see_list[nc] = -see_list[nc - 1] + attacked_piece; 186 | attacked_piece = pcval[piece]; 187 | if (see_list[nc++] - attacked_piece > 0) 188 | break; 189 | wtm = Flip(wtm); 190 | } 191 | /* 192 | ************************************************************ 193 | * * 194 | * Starting at the end of the sequence of values, use a * 195 | * "minimax" like procedure to decide where the captures * 196 | * will stop. * 197 | * * 198 | ************************************************************ 199 | */ 200 | while (--nc) 201 | see_list[nc - 1] = -Max(-see_list[nc - 1], see_list[nc]); 202 | return see_list[0]; 203 | } 204 | -------------------------------------------------------------------------------- /source/setboard.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 05/08/14 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * SetBoard() is used to set up the board in any position desired. It uses * 8 | * a forsythe-like string of characters to describe the board position. * 9 | * * 10 | * The standard piece codes p,n,b,r,q,k are used to denote the type of piece * 11 | * on a square, upper/lower case are used to indicate the side (program/ * 12 | * opponent) of the piece. * 13 | * * 14 | * The pieces are entered with square a8 first, then b8, ... until the full * 15 | * 8th rank is completed. A "/" terminates that rank. This is repeated for * 16 | * each of the 8 ranks, with the last (1st) rank not needing a terminating * 17 | * "/". For empty squares, a number between 1 and 8 can be used to indicate * 18 | * the number of adjacent empty squares. * 19 | * * 20 | * That board description must be followed by a "b" or "w" to indicate which * 21 | * side is on move. * 22 | * * 23 | * Next, up to 4 characters are used to indicate which side can castle and * 24 | * to which side. An uppercase K means white can castle kingside, while a * 25 | * lowercase q means black can castle queenside. * 26 | * * 27 | * Finally, if there is an enpassant capture possible (the last move was a * 28 | * double pawn move and there was an enemy pawn that could capture it. The * 29 | * square is the square the capturing pawn ends up on. * 30 | * * 31 | * K2R/PPP////q/5ppp/7k/ b - - * 32 | * * 33 | * this assumes that k represents a white king and -q represents a black * 34 | * queen. * 35 | * * 36 | * k * * r * * * * * 37 | * p p p * * * * * * 38 | * * * * * * * * * * 39 | * * * * * * * * * * 40 | * * * * * * * * * * 41 | * -q * * * * * * * * 42 | * * * * * * -p -p -p * 43 | * * * * * * * * -k * 44 | * * 45 | ******************************************************************************* 46 | */ 47 | void SetBoard(TREE * tree, int nargs, char *args[], int special) { 48 | int twtm, i, match, num, pos, square, tboard[64]; 49 | int bcastle, ep, wcastle, error = 0, whichsq; 50 | char input[80]; 51 | static const char bdinfo[] = 52 | { 'k', 'q', 'r', 'b', 'n', 'p', '*', 'P', 'N', 'B', 53 | 'R', 'Q', 'K', '*', '1', '2', '3', '4', 54 | '5', '6', '7', '8', '/' 55 | }; 56 | static const char status[13] = 57 | { 'K', 'Q', 'k', 'q', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 58 | 'h', ' ' 59 | }; 60 | static const int firstsq[8] = { 56, 48, 40, 32, 24, 16, 8, 0 }; 61 | 62 | if (special) 63 | strcpy(input, initial_position); 64 | else 65 | strcpy(input, args[0]); 66 | for (i = 0; i < 64; i++) 67 | tboard[i] = 0; 68 | /* 69 | ************************************************************ 70 | * * 71 | * Scan the input string searching for pieces, numbers * 72 | * [empty squares], slashes [end-of-rank] and a blank * 73 | * [end of board, start of castle status]. * 74 | * * 75 | ************************************************************ 76 | */ 77 | whichsq = 0; 78 | square = firstsq[whichsq]; 79 | num = 0; 80 | for (pos = 0; pos < (int) strlen(args[0]); pos++) { 81 | for (match = 0; match < 23 && args[0][pos] != bdinfo[match]; match++); 82 | if (match > 22) 83 | break; 84 | /* 85 | "/" -> end of this rank. 86 | */ 87 | else if (match == 22) { 88 | num = 0; 89 | if (whichsq > 6) 90 | break; 91 | square = firstsq[++whichsq]; 92 | } 93 | /* 94 | "1-8" -> empty squares. 95 | */ 96 | else if (match >= 14) { 97 | num += match - 13; 98 | square += match - 13; 99 | if (num > 8) { 100 | printf("more than 8 squares on one rank\n"); 101 | error = 1; 102 | break; 103 | } 104 | continue; 105 | } 106 | /* 107 | piece codes. 108 | */ 109 | else { 110 | if (++num > 8) { 111 | printf("more than 8 squares on one rank\n"); 112 | error = 1; 113 | break; 114 | } 115 | tboard[square++] = match - 6; 116 | } 117 | } 118 | /* 119 | ************************************************************ 120 | * * 121 | * Now extract (a) side to move [w/b], (b) castle status * 122 | * [KkQq for white/black king-side ok, white/black queen- * 123 | * side ok], (c) enpassant target square. * 124 | * * 125 | ************************************************************ 126 | */ 127 | twtm = 0; 128 | ep = 0; 129 | wcastle = 0; 130 | bcastle = 0; 131 | /* 132 | ************************************************************ 133 | * * 134 | * Side to move. * 135 | * * 136 | ************************************************************ 137 | */ 138 | if (args[1][0] == 'w') 139 | twtm = 1; 140 | else if (args[1][0] == 'b') 141 | twtm = 0; 142 | else { 143 | printf("side to move is bad\n"); 144 | error = 1; 145 | } 146 | /* 147 | ************************************************************ 148 | * * 149 | * Castling/enpassant status. * 150 | * * 151 | ************************************************************ 152 | */ 153 | if (nargs > 2 && strlen(args[2])) { 154 | if (strcmp(args[2], "-")) { 155 | for (pos = 0; pos < (int) strlen(args[2]); pos++) { 156 | for (match = 0; (match < 13) && (args[2][pos] != status[match]); 157 | match++); 158 | if (match == 0) 159 | wcastle += 1; 160 | else if (match == 1) 161 | wcastle += 2; 162 | else if (match == 2) 163 | bcastle += 1; 164 | else if (match == 3) 165 | bcastle += 2; 166 | else if (args[2][0] != '-') { 167 | printf("castling status is bad.\n"); 168 | error = 1; 169 | } 170 | } 171 | } 172 | } 173 | if (nargs > 3 && strlen(args[3])) { 174 | if (strcmp(args[3], "-")) { 175 | if (args[3][0] >= 'a' && args[3][0] <= 'h' && args[3][1] > '0' && 176 | args[3][1] < '9') { 177 | ep = (args[3][1] - '1') * 8 + args[3][0] - 'a'; 178 | } else if (args[3][0] != '-') { 179 | printf("enpassant status is bad.\n"); 180 | error = 1; 181 | } 182 | } 183 | } 184 | for (i = 0; i < 64; i++) 185 | PcOnSq(i) = tboard[i]; 186 | Castle(0, white) = wcastle; 187 | Castle(0, black) = bcastle; 188 | EnPassant(0) = 0; 189 | if (ep) { 190 | do { 191 | if (twtm && Rank(ep) == RANK6 && PcOnSq(ep - 8) == -pawn) { 192 | if (File(ep) != 7 && PcOnSq(ep - 7) == pawn) 193 | break; 194 | if (File(ep) != 0 && PcOnSq(ep - 9) == pawn) 195 | break; 196 | } else if (!twtm && Rank(ep) == RANK3 && PcOnSq(ep + 8) == pawn) { 197 | if (File(ep) != 0 && PcOnSq(ep + 7) == -pawn) 198 | break; 199 | if (File(ep) != 7 && PcOnSq(ep + 9) == -pawn) 200 | break; 201 | } else { 202 | ep = 0; 203 | } 204 | if (!ep) { 205 | printf("enpassant status is bad.\n"); 206 | ep = 0; 207 | error = 1; 208 | } 209 | } while (0); 210 | EnPassant(0) = ep; 211 | } 212 | /* 213 | ************************************************************ 214 | * * 215 | * Now check the castling status status to make sure that * 216 | * the board is in a state that matches. * 217 | * * 218 | ************************************************************ 219 | */ 220 | if (((Castle(0, white) & 2) && (PcOnSq(A1) != rook)) 221 | || ((Castle(0, white) & 1) && (PcOnSq(H1) != rook)) 222 | || ((Castle(0, black) & 2) && (PcOnSq(A8) != -rook)) 223 | || ((Castle(0, black) & 1) && (PcOnSq(H8) != -rook))) { 224 | printf("ERROR-- castling status does not match board position\n"); 225 | error = 1; 226 | } 227 | /* 228 | ************************************************************ 229 | * * 230 | * Now set the bitboards so that error tests can be done. * 231 | * * 232 | ************************************************************ 233 | */ 234 | SetChessBitBoards(tree); 235 | /* 236 | ************************************************************ 237 | * * 238 | * Now check the position for a sane position, which means * 239 | * no more than 8 pawns, no more than 10 knights, bishops * 240 | * or rooks, no more than 9 queens, no pawns on 1st or 8th * 241 | * rank, etc. * 242 | * * 243 | ************************************************************ 244 | */ 245 | game_wtm = twtm; 246 | error += InvalidPosition(tree); 247 | if (!error) { 248 | if (log_file) 249 | DisplayChessBoard(log_file, tree->position); 250 | rep_index = 0; 251 | tree->rep_list[0] = HashKey; 252 | Reversible(0) = 0; 253 | if (!special) { 254 | last_mate_score = 0; 255 | InitializeKillers(); 256 | last_pv.pathd = 0; 257 | last_pv.pathl = 0; 258 | tree->pv[0].pathd = 0; 259 | tree->pv[0].pathl = 0; 260 | moves_out_of_book = 0; 261 | } 262 | } else { 263 | if (special) 264 | Print(4095, "bad string = \"%s\"\n", initial_position); 265 | else 266 | Print(4095, "bad string = \"%s\"\n", args[0]); 267 | InitializeChessBoard(tree); 268 | Print(4095, "Illegal position, using normal initial chess position\n"); 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /source/speak: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # 3 | # Announce a move from Crafty using some soundfiles 4 | # 5 | 6 | # The move sent by crafty 7 | my $move = $ARGV[0]; 8 | 9 | # First check some specials 10 | if ($move eq "O-O") { 11 | system("say castle king side"); 12 | } 13 | elsif ($move eq "O-O-O") { 14 | system("say castle queen side"); 15 | } 16 | elsif ($move eq "Stalemate") { 17 | system("say stalemate"); 18 | } 19 | elsif ($move eq "Drawaccept") { 20 | system("say I accept your draw offer"); 21 | } 22 | elsif ($move eq "Drawdecline") { 23 | system("say I decline your draw offer"); 24 | } 25 | elsif ($move eq "Drawoffer") { 26 | system("say I offer a draw"); 27 | } 28 | elsif ($move eq "Resign") { 29 | system("say I resign"); 30 | } 31 | elsif ($move eq "Checkmate") { 32 | system("say checkmate"); 33 | } 34 | # Handle all normal moves. All that needs to be done is announce 35 | # each character sent by crafty alone. Set some pause beteween each 36 | # char for clearer understanding. 37 | # 38 | # NOTE 1: Crafty uses short notation, so short notation is 39 | # announced. For long notation announcements the crafty engine has 40 | # to be hacked and pgn output as well as logging would get broken. 41 | # 42 | # NOTE 2: There has to exist a sound file named for each row, 43 | # column and piece. That is there has to be sound files like a.wav, 44 | # 1.wav, R.wav and so on. One can easily rename the files 45 | # distributed for free eg. at the "Arena" Website 46 | # (http://www.playwitharena.com). Soundfiles from Fritz are not 47 | # suitable, at the moment, but the script can easily be rewritten 48 | # to handle them as well. 49 | else { 50 | for (my $i=0; $i or it is not 33 | * available. 34 | */ 35 | /* #define TB_NO_STDINT */ 36 | 37 | /* 38 | * Define TB_NO_STDBOOL if you do not want to use or it is not 39 | * available or unnecessary (e.g. C++). 40 | */ 41 | /* #define TB_NO_STDBOOL */ 42 | 43 | /* 44 | * Define TB_NO_THREADS if your program is not multi-threaded. 45 | */ 46 | /* #define TB_NO_THREADS */ 47 | # define TB_HAVE_THREADS 48 | 49 | /* 50 | * Define TB_NO_HELPER_API if you do not need the helper API. 51 | */ 52 | /* #define TB_NO_HELPER_API */ 53 | 54 | /***************************************************************************/ 55 | /* ENGINE INTEGRATION CONFIG */ 56 | /***************************************************************************/ 57 | 58 | /* 59 | * If you are integrating tbprobe into an engine, you can replace some of 60 | * tbprobe's built-in functionality with that already provided by the engine. 61 | * This is OPTIONAL. If no definition are provided then tbprobe will use its 62 | * own internal defaults. That said, for engines it is generally a good idea 63 | * to avoid redundancy. 64 | */ 65 | 66 | # include "chess.h" 67 | # include "data.h" 68 | 69 | /* 70 | * Define TB_KING_ATTACKS(square) to return the king attacks bitboard for a 71 | * king at `square'. 72 | */ 73 | # define TB_KING_ATTACKS(square) KingAttacks(square) 74 | 75 | /* 76 | * Define TB_KNIGHT_ATTACKS(square) to return the knight attacks bitboard for 77 | * a knight at `square'. 78 | */ 79 | # define TB_KNIGHT_ATTACKS(square) KnightAttacks(square) 80 | 81 | /* 82 | * Define TB_ROOK_ATTACKS(square, occ) to return the rook attacks bitboard 83 | * for a rook at `square' assuming the given `occ' occupancy bitboard. 84 | */ 85 | # define TB_ROOK_ATTACKS(square, occ) RookAttacks(square, occ) 86 | 87 | /* 88 | * Define TB_BISHOP_ATTACKS(square, occ) to return the bishop attacks bitboard 89 | * for a bishop at `square' assuming the given `occ' occupancy bitboard. 90 | */ 91 | # define TB_BISHOP_ATTACKS(square, occ) BishopAttacks(square, occ) 92 | 93 | /* 94 | * Define TB_QUEEN_ATTACKS(square, occ) to return the queen attacks bitboard 95 | * for a queen at `square' assuming the given `occ' occupancy bitboard. 96 | * NOTE: If no definition is provided then tbprobe will use: 97 | * TB_ROOK_ATTACKS(square, occ) | TB_BISHOP_ATTACKS(square, occ) 98 | */ 99 | # define TB_QUEEN_ATTACKS(square, occ) QueenAttacks(square, occ) 100 | 101 | /* 102 | * Define TB_PAWN_ATTACKS(square, color) to return the pawn attacks bitboard 103 | * for a `color' pawn at `square'. 104 | * NOTE: This definition must work for pawns on ranks 1 and 8. For example, 105 | * a white pawn on e1 attacks d2 and f2. A black pawn on e1 attacks 106 | * nothing. Etc. 107 | * NOTE: This definition must not include en passant captures. 108 | */ 109 | # define TB_PAWN_ATTACKS(square, color) PawnAttacks(color, square) 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /source/tbcore.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011-2015 Ronald de Man 3 | */ 4 | 5 | #ifndef TBCORE_H 6 | # define TBCORE_H 7 | 8 | # ifndef __WIN32__ 9 | # include 10 | # define SEP_CHAR ':' 11 | # define FD int 12 | # define FD_ERR -1 13 | # else 14 | # include 15 | # define SEP_CHAR ';' 16 | # define FD HANDLE 17 | # define FD_ERR INVALID_HANDLE_VALUE 18 | # endif 19 | 20 | # ifdef TB_HAVE_THREADS 21 | # ifndef __WIN32__ 22 | # define LOCK_T pthread_mutex_t 23 | # define LOCK_INIT(x) pthread_mutex_init(&(x), NULL) 24 | # define LOCK(x) pthread_mutex_lock(&(x)) 25 | # define UNLOCK(x) pthread_mutex_unlock(&(x)) 26 | # else 27 | # define LOCK_T HANDLE 28 | # define LOCK_INIT(x) do { x = CreateMutex(NULL, FALSE, NULL); } while (0) 29 | # define LOCK(x) WaitForSingleObject(x, INFINITE) 30 | # define UNLOCK(x) ReleaseMutex(x) 31 | # endif 32 | # else /* !TB_HAVE_THREADS */ 33 | # define LOCK_T int 34 | # define LOCK_INIT(x) 35 | /* NOP */ 36 | # define LOCK(x) 37 | /* NOP */ 38 | # define UNLOCK(x) 39 | /* NOP */ 40 | # endif 41 | 42 | # define WDLSUFFIX ".rtbw" 43 | # define DTZSUFFIX ".rtbz" 44 | # define WDLDIR "RTBWDIR" 45 | # define DTZDIR "RTBZDIR" 46 | # define TBPIECES 6 47 | 48 | # define WDL_MAGIC 0x5d23e871 49 | # define DTZ_MAGIC 0xa50c66d7 50 | 51 | # define TBHASHBITS 10 52 | 53 | typedef unsigned long long uint64; 54 | typedef unsigned int uint32; 55 | typedef unsigned char ubyte; 56 | typedef unsigned short ushort; 57 | 58 | struct TBHashEntry; 59 | 60 | # ifdef DECOMP64 61 | typedef uint64 base_t; 62 | # else 63 | typedef uint32 base_t; 64 | # endif 65 | 66 | struct PairsData { 67 | char *indextable; 68 | ushort *sizetable; 69 | ubyte *data; 70 | ushort *offset; 71 | ubyte *symlen; 72 | ubyte *sympat; 73 | int blocksize; 74 | int idxbits; 75 | int min_len; 76 | base_t base[1]; // C++ complains about base[]... 77 | }; 78 | 79 | struct TBEntry { 80 | char *data; 81 | uint64 key; 82 | uint64 mapping; 83 | ubyte ready; 84 | ubyte num; 85 | ubyte symmetric; 86 | ubyte has_pawns; 87 | } __attribute__ ((__may_alias__)); 88 | 89 | struct TBEntry_piece { 90 | char *data; 91 | uint64 key; 92 | uint64 mapping; 93 | ubyte ready; 94 | ubyte num; 95 | ubyte symmetric; 96 | ubyte has_pawns; 97 | ubyte enc_type; 98 | struct PairsData *precomp[2]; 99 | int factor[2][TBPIECES]; 100 | ubyte pieces[2][TBPIECES]; 101 | ubyte norm[2][TBPIECES]; 102 | }; 103 | 104 | struct TBEntry_pawn { 105 | char *data; 106 | uint64 key; 107 | uint64 mapping; 108 | ubyte ready; 109 | ubyte num; 110 | ubyte symmetric; 111 | ubyte has_pawns; 112 | ubyte pawns[2]; 113 | struct { 114 | struct PairsData *precomp[2]; 115 | int factor[2][TBPIECES]; 116 | ubyte pieces[2][TBPIECES]; 117 | ubyte norm[2][TBPIECES]; 118 | } file[4]; 119 | }; 120 | 121 | struct DTZEntry_piece { 122 | char *data; 123 | uint64 key; 124 | uint64 mapping; 125 | ubyte ready; 126 | ubyte num; 127 | ubyte symmetric; 128 | ubyte has_pawns; 129 | ubyte enc_type; 130 | struct PairsData *precomp; 131 | int factor[TBPIECES]; 132 | ubyte pieces[TBPIECES]; 133 | ubyte norm[TBPIECES]; 134 | ubyte flags; // accurate, mapped, side 135 | ushort map_idx[4]; 136 | ubyte *map; 137 | }; 138 | 139 | struct DTZEntry_pawn { 140 | char *data; 141 | uint64 key; 142 | uint64 mapping; 143 | ubyte ready; 144 | ubyte num; 145 | ubyte symmetric; 146 | ubyte has_pawns; 147 | ubyte pawns[2]; 148 | struct { 149 | struct PairsData *precomp; 150 | int factor[TBPIECES]; 151 | ubyte pieces[TBPIECES]; 152 | ubyte norm[TBPIECES]; 153 | } file[4]; 154 | ubyte flags[4]; 155 | ushort map_idx[4][4]; 156 | ubyte *map; 157 | }; 158 | 159 | struct TBHashEntry { 160 | uint64 key; 161 | struct TBEntry *ptr; 162 | }; 163 | 164 | struct DTZTableEntry { 165 | uint64 key1; 166 | uint64 key2; 167 | struct TBEntry *entry; 168 | }; 169 | 170 | #endif 171 | -------------------------------------------------------------------------------- /source/tbprobe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * tbprobe.h 3 | * (C) 2015 basil, all rights reserved, 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a 6 | * copy of this software and associated documentation files (the "Software"), 7 | * to deal in the Software without restriction, including without limitation 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | * and/or sell copies of the Software, and to permit persons to whom the 10 | * Software is furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | * DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef TBPROBE_H 25 | # define TBPROBE_H 26 | 27 | # include "tbconfig.h" 28 | 29 | # ifdef __cplusplus 30 | extern "C" { 31 | # endif 32 | 33 | # ifndef TB_NO_STDINT 34 | # include 35 | # else 36 | typedef unsigned char uint8_t; 37 | typedef unsigned short uint16_t; 38 | typedef unsigned uint32_t; 39 | typedef long long unsigned uint64_t; 40 | typedef char int8_t; 41 | typedef short int16_t; 42 | typedef int int32_t; 43 | typedef long long int64_t; 44 | # endif 45 | 46 | # ifndef TB_NO_STDBOOL 47 | # include 48 | # else 49 | # ifndef __cplusplus 50 | typedef uint8_t bool; 51 | # define true 1 52 | # define false 0 53 | # endif 54 | # endif 55 | 56 | /* 57 | * Internal definitions. Do not call these functions directly. 58 | */ 59 | extern bool tb_init_impl(const char *_path); 60 | extern unsigned tb_probe_wdl_impl(uint64_t _white, uint64_t _black, 61 | uint64_t _kings, uint64_t _queens, uint64_t _rooks, uint64_t _bishops, 62 | uint64_t _knights, uint64_t _pawns, unsigned _ep, bool _turn, 63 | uint64_t _hash); 64 | extern unsigned tb_probe_root_impl(uint64_t _white, uint64_t _black, 65 | uint64_t _kings, uint64_t _queens, uint64_t _rooks, uint64_t _bishops, 66 | uint64_t _knights, uint64_t _pawns, unsigned _rule50, unsigned _ep, 67 | bool _turn, unsigned *_results); 68 | 69 | /****************************************************************************/ 70 | /* MAIN API */ 71 | /****************************************************************************/ 72 | 73 | # define TB_MAX_MOVES (192+1) 74 | 75 | # define TB_CASTLING_K 0x1 76 | /* White king-side. */ 77 | # define TB_CASTLING_Q 0x2 78 | /* White queen-side. */ 79 | # define TB_CASTLING_k 0x4 80 | /* Black king-side. */ 81 | # define TB_CASTLING_q 0x8 82 | /* Black queen-side. */ 83 | 84 | # define TB_LOSS 0 85 | /* LOSS */ 86 | # define TB_BLESSED_LOSS 1 87 | /* LOSS but 50-move draw */ 88 | # define TB_DRAW 2 89 | /* DRAW */ 90 | # define TB_CURSED_WIN 3 91 | /* WIN but 50-move draw */ 92 | # define TB_WIN 4 93 | /* WIN */ 94 | 95 | # define TB_PROMOTES_NONE 0 96 | # define TB_PROMOTES_QUEEN 1 97 | # define TB_PROMOTES_ROOK 2 98 | # define TB_PROMOTES_BISHOP 3 99 | # define TB_PROMOTES_KNIGHT 4 100 | 101 | # define TB_RESULT_WDL_MASK 0x0000000F 102 | # define TB_RESULT_TO_MASK 0x000003F0 103 | # define TB_RESULT_FROM_MASK 0x0000FC00 104 | # define TB_RESULT_PROMOTES_MASK 0x00070000 105 | # define TB_RESULT_EP_MASK 0x00080000 106 | # define TB_RESULT_DTZ_MASK 0xFFF00000 107 | # define TB_RESULT_WDL_SHIFT 0 108 | # define TB_RESULT_TO_SHIFT 4 109 | # define TB_RESULT_FROM_SHIFT 10 110 | # define TB_RESULT_PROMOTES_SHIFT 16 111 | # define TB_RESULT_EP_SHIFT 19 112 | # define TB_RESULT_DTZ_SHIFT 20 113 | 114 | # define TB_GET_WDL(_res) \ 115 | (((_res) & TB_RESULT_WDL_MASK) >> TB_RESULT_WDL_SHIFT) 116 | # define TB_GET_TO(_res) \ 117 | (((_res) & TB_RESULT_TO_MASK) >> TB_RESULT_TO_SHIFT) 118 | # define TB_GET_FROM(_res) \ 119 | (((_res) & TB_RESULT_FROM_MASK) >> TB_RESULT_FROM_SHIFT) 120 | # define TB_GET_PROMOTES(_res) \ 121 | (((_res) & TB_RESULT_PROMOTES_MASK) >> TB_RESULT_PROMOTES_SHIFT) 122 | # define TB_GET_EP(_res) \ 123 | (((_res) & TB_RESULT_EP_MASK) >> TB_RESULT_EP_SHIFT) 124 | # define TB_GET_DTZ(_res) \ 125 | (((_res) & TB_RESULT_DTZ_MASK) >> TB_RESULT_DTZ_SHIFT) 126 | 127 | # define TB_SET_WDL(_res, _wdl) \ 128 | (((_res) & ~TB_RESULT_WDL_MASK) | \ 129 | (((_wdl) << TB_RESULT_WDL_SHIFT) & TB_RESULT_WDL_MASK)) 130 | # define TB_SET_TO(_res, _to) \ 131 | (((_res) & ~TB_RESULT_TO_MASK) | \ 132 | (((_to) << TB_RESULT_TO_SHIFT) & TB_RESULT_TO_MASK)) 133 | # define TB_SET_FROM(_res, _from) \ 134 | (((_res) & ~TB_RESULT_FROM_MASK) | \ 135 | (((_from) << TB_RESULT_FROM_SHIFT) & TB_RESULT_FROM_MASK)) 136 | # define TB_SET_PROMOTES(_res, _promotes) \ 137 | (((_res) & ~TB_RESULT_PROMOTES_MASK) | \ 138 | (((_promotes) << TB_RESULT_PROMOTES_SHIFT) & TB_RESULT_PROMOTES_MASK)) 139 | # define TB_SET_EP(_res, _ep) \ 140 | (((_res) & ~TB_RESULT_EP_MASK) | \ 141 | (((_ep) << TB_RESULT_EP_SHIFT) & TB_RESULT_EP_MASK)) 142 | # define TB_SET_DTZ(_res, _dtz) \ 143 | (((_res) & ~TB_RESULT_DTZ_MASK) | \ 144 | (((_dtz) << TB_RESULT_DTZ_SHIFT) & TB_RESULT_DTZ_MASK)) 145 | 146 | # define TB_RESULT_CHECKMATE TB_SET_WDL(0, TB_WIN) 147 | # define TB_RESULT_STALEMATE TB_SET_WDL(0, TB_DRAW) 148 | # define TB_RESULT_FAILED 0xFFFFFFFF 149 | 150 | /* 151 | * The tablebase can be probed for any position where #pieces <= TB_LARGEST. 152 | */ 153 | extern unsigned TB_LARGEST; 154 | 155 | /* 156 | * Initialize the tablebase. 157 | * 158 | * PARAMETERS: 159 | * - path: 160 | * The tablebase PATH string. 161 | * 162 | * RETURN: 163 | * - true=succes, false=failed. The TB_LARGEST global will also be 164 | * initialized. If no tablebase files are found, then `true' is returned 165 | * and TB_LARGEST is set to zero. 166 | */ 167 | static inline bool tb_init(const char *_path) { 168 | return tb_init_impl(_path); 169 | } 170 | /* 171 | * Probe the Win-Draw-Loss (WDL) table. 172 | * 173 | * PARAMETERS: 174 | * - white, black, kings, queens, rooks, bishops, knights, pawns: 175 | * The current position (bitboards). 176 | * - rule50: 177 | * The 50-move half-move clock. 178 | * - castling: 179 | * Castling rights. Set to zero if no castling is possible. 180 | * - ep: 181 | * The en passant square (if exists). Set to zero if there is no en passant 182 | * square. 183 | * - turn: 184 | * true=white, false=black 185 | * 186 | * RETURN: 187 | * - One of {TB_LOSS, TB_BLESSED_LOSS, TB_DRAW, TB_CURSED_WIN, TB_WIN}. 188 | * Otherwise returns TB_RESULT_FAILED if the probe failed. 189 | * 190 | * NOTES: 191 | * - Engines should use this function during search. 192 | * - This function is thread safe assuming TB_NO_THREADS is disabled. 193 | */ static inline unsigned tb_probe_wdl( 194 | uint64_t _white, uint64_t _black, uint64_t _kings, uint64_t _queens, 195 | uint64_t _rooks, uint64_t _bishops, uint64_t _knights, uint64_t _pawns, 196 | unsigned _rule50, unsigned _castling, unsigned _ep, bool _turn, 197 | uint64_t _hash) { 198 | if (_castling != 0) 199 | return TB_RESULT_FAILED; 200 | if (_rule50 != 0) 201 | return TB_RESULT_FAILED; 202 | return tb_probe_wdl_impl(_white, _black, _kings, _queens, _rooks, 203 | _bishops, _knights, _pawns, _ep, _turn, _hash); 204 | } 205 | 206 | /* 207 | * Probe the Distance-To-Zero (DTZ) table. 208 | * 209 | * PARAMETERS: 210 | * - white, black, kings, queens, rooks, bishops, knights, pawns: 211 | * The current position (bitboards). 212 | * - rule50: 213 | * The 50-move half-move clock. 214 | * - castling: 215 | * Castling rights. Set to zero if no castling is possible. 216 | * - ep: 217 | * The en passant square (if exists). Set to zero if there is no en passant 218 | * square. 219 | * - turn: 220 | * true=white, false=black 221 | * - results (OPTIONAL): 222 | * Alternative results, one for each possible legal move. The passed array 223 | * must be TB_MAX_MOVES in size. 224 | * If alternative results are not desired then set results=NULL. 225 | * 226 | * RETURN: 227 | * - A TB_RESULT value comprising: 228 | * 1) The WDL value (TB_GET_WDL) 229 | * 2) The suggested move (TB_GET_FROM, TB_GET_TO, TB_GET_PROMOTES, TB_GET_EP) 230 | * 3) The DTZ value (TB_GET_DTZ) 231 | * The suggested move is guaranteed to preserved the WDL value. 232 | * 233 | * Otherwise: 234 | * 1) TB_RESULT_STALEMATE is returned if the position is in stalemate. 235 | * 2) TB_RESULT_CHECKMATE is returned if the position is in checkmate. 236 | * 3) TB_RESULT_FAILED is returned if the probe failed. 237 | * 238 | * If results!=NULL, then a TB_RESULT for each legal move will be generated 239 | * and stored in the results array. The results array will be terminated 240 | * by TB_RESULT_FAILED. 241 | * 242 | * NOTES: 243 | * - Engines can use this function to probe at the root. This function should 244 | * not be used during search. 245 | * - DTZ tablebases can suggest unnatural moves, especially for losing 246 | * positions. Engines may prefer to traditional search combined with WDL 247 | * move filtering using the alternative results array. 248 | * - This function is NOT thread safe. For engines this function should only 249 | * be called once at the root per search. 250 | */ 251 | static inline unsigned tb_probe_root(uint64_t _white, uint64_t _black, 252 | uint64_t _kings, uint64_t _queens, uint64_t _rooks, uint64_t _bishops, 253 | uint64_t _knights, uint64_t _pawns, unsigned _rule50, 254 | unsigned _castling, unsigned _ep, bool _turn, unsigned *_results) { 255 | if (_castling != 0) 256 | return TB_RESULT_FAILED; 257 | return tb_probe_root_impl(_white, _black, _kings, _queens, _rooks, 258 | _bishops, _knights, _pawns, _rule50, _ep, _turn, _results); 259 | } 260 | 261 | /****************************************************************************/ 262 | /* HELPER API */ 263 | /****************************************************************************/ 264 | 265 | /* 266 | * The HELPER API provides some useful additional functions. It is optional 267 | * and can be disabled by defining TB_NO_HELPER_API. Engines should disable 268 | * the HELPER API. 269 | */ 270 | 271 | # ifndef TB_NO_HELPER_API 272 | 273 | extern unsigned tb_pop_count(uint64_t _bb); 274 | extern unsigned tb_lsb(uint64_t _bb); 275 | extern uint64_t tb_pop_lsb(uint64_t _bb); 276 | extern uint64_t tb_king_attacks(unsigned _square); 277 | extern uint64_t tb_queen_attacks(unsigned _square, uint64_t _occ); 278 | extern uint64_t tb_rook_attacks(unsigned _square, uint64_t _occ); 279 | extern uint64_t tb_bishop_attacks(unsigned _square, uint64_t _occ); 280 | extern uint64_t tb_knight_attacks(unsigned _square); 281 | extern uint64_t tb_pawn_attacks(unsigned _square, bool _color); 282 | 283 | # endif 284 | 285 | # ifdef __cplusplus 286 | } 287 | # endif 288 | 289 | #endif 290 | -------------------------------------------------------------------------------- /source/unmake.c: -------------------------------------------------------------------------------- 1 | #include "chess.h" 2 | #include "data.h" 3 | /* last modified 01/06/16 */ 4 | /* 5 | ******************************************************************************* 6 | * * 7 | * UnmakeMove() is responsible for updating the position database whenever a * 8 | * move is retracted. It is the exact inverse of MakeMove(). The hash * 9 | * signature(s) are not updated, they are just restored to their status that * 10 | * was saved before the move was made, to save time. * 11 | * * 12 | ******************************************************************************* 13 | */ 14 | void UnmakeMove(TREE * RESTRICT tree, int ply, int side, int move) { 15 | uint64_t bit_move; 16 | int piece, from, to, captured, promote, enemy = Flip(side); 17 | 18 | /* 19 | ************************************************************ 20 | * * 21 | * First, restore the hash signatures to their state prior * 22 | * to this move being made by simply copying the old * 23 | * values. * 24 | * * 25 | ************************************************************ 26 | */ 27 | HashKey = tree->save_hash_key[ply]; 28 | PawnHashKey = tree->save_pawn_hash_key[ply]; 29 | /* 30 | ************************************************************ 31 | * * 32 | * Now do the things that are common to all pieces, such * 33 | * as updating the bitboards and hash signature. * 34 | * * 35 | ************************************************************ 36 | */ 37 | piece = Piece(move); 38 | from = From(move); 39 | to = To(move); 40 | captured = Captured(move); 41 | promote = Promote(move); 42 | bit_move = SetMask(from) | SetMask(to); 43 | ClearSet(bit_move, Pieces(side, piece)); 44 | ClearSet(bit_move, Occupied(side)); 45 | PcOnSq(to) = 0; 46 | PcOnSq(from) = pieces[side][piece]; 47 | /* 48 | ************************************************************ 49 | * * 50 | * Now do the piece-specific things by jumping to the * 51 | * appropriate routine (this only has to deal with pawns * 52 | * and king moves that are castling moves. * 53 | * * 54 | ************************************************************ 55 | */ 56 | switch (piece) { 57 | case pawn: 58 | if (captured == 1) { 59 | if (EnPassant(ply) == to) { 60 | TotalAllPieces++; 61 | Set(to + epsq[side], Pawns(enemy)); 62 | Set(to + epsq[side], Occupied(enemy)); 63 | PcOnSq(to + epsq[side]) = pieces[enemy][pawn]; 64 | Material -= PieceValues(side, pawn); 65 | TotalPieces(enemy, pawn)++; 66 | captured = 0; 67 | } 68 | } 69 | if (promote) { 70 | TotalPieces(side, pawn)++; 71 | Clear(to, Pawns(side)); 72 | Clear(to, Occupied(side)); 73 | Clear(to, Pieces(side, promote)); 74 | Material -= PieceValues(side, promote); 75 | Material += PieceValues(side, pawn); 76 | TotalPieces(side, occupied) -= p_vals[promote]; 77 | TotalPieces(side, promote)--; 78 | } 79 | break; 80 | case knight: 81 | case bishop: 82 | case rook: 83 | case queen: 84 | break; 85 | case king: 86 | KingSQ(side) = from; 87 | if (Abs(to - from) == 2) { 88 | if (to == rook_G[side]) { 89 | from = rook_H[side]; 90 | to = rook_F[side]; 91 | } else { 92 | from = rook_A[side]; 93 | to = rook_D[side]; 94 | } 95 | bit_move = SetMask(from) | SetMask(to); 96 | ClearSet(bit_move, Rooks(side)); 97 | ClearSet(bit_move, Occupied(side)); 98 | PcOnSq(to) = 0; 99 | PcOnSq(from) = pieces[side][rook]; 100 | } 101 | break; 102 | } 103 | /* 104 | ************************************************************ 105 | * * 106 | * Next we restore information related to a piece that was * 107 | * captured and is now being returned to the board. * 108 | * * 109 | ************************************************************ 110 | */ 111 | if (captured) { 112 | TotalAllPieces++; 113 | Set(to, Pieces(enemy, captured)); 114 | Set(to, Occupied(enemy)); 115 | Material += PieceValues(enemy, captured); 116 | PcOnSq(to) = pieces[enemy][captured]; 117 | TotalPieces(enemy, captured)++; 118 | if (captured != pawn) 119 | TotalPieces(enemy, occupied) += p_vals[captured]; 120 | } 121 | #if defined(DEBUG) 122 | ValidatePosition(tree, ply, move, "UnmakeMove(1)"); 123 | #endif 124 | return; 125 | } 126 | -------------------------------------------------------------------------------- /source/validate.c: -------------------------------------------------------------------------------- 1 | #if defined(DEBUG) 2 | # include "chess.h" 3 | # include "data.h" 4 | /* last modified 02/26/14 */ 5 | /* 6 | ******************************************************************************* 7 | * * 8 | * ValidatePosition() is a debugging tool that is enabled by using the * 9 | * -DDEBUG compilation flag. This procedure tests the various data * 10 | * structures used in Crafty related to the chess board and incrementally * 11 | * updated values like hash signatures and so forth. It simply looks for * 12 | * consistency between the various bitboards, and recomputes the hash * 13 | * signatures to determine if they are correct. If anything fails to pass * 14 | * the validation test, we print out a dump of the moves made in this path * 15 | * through the tree, and then exit since things are corrupted. * 16 | * * 17 | * This greatly slows the program down, because ValidatePosition() is called * 18 | * after each Make()/Unmake() (these are the functions that modify the * 19 | * primary data structures). In general, this will not be used by users * 20 | * unless they are modifying the source code themselves. * 21 | * * 22 | ******************************************************************************* 23 | */ 24 | void ValidatePosition(TREE * RESTRICT tree, int ply, int move, char *caller) { 25 | uint64_t temp, temp1, temp_occ; 26 | uint64_t temp_occx; 27 | int i, square, error = 0; 28 | int side, piece, temp_score; 29 | 30 | /* 31 | ************************************************************ 32 | * * 33 | * First, test occupied[side] which should match the OR * 34 | * result of all pieces[side]. * 35 | * * 36 | ************************************************************ 37 | */ 38 | for (side = black; side <= white; side++) { 39 | temp_occ = 40 | Pawns(side) | Knights(side) | Bishops(side) | Rooks(side) | 41 | Queens(side) 42 | | Kings(side); 43 | if (Occupied(side) ^ temp_occ) { 44 | if (!error) 45 | Print(2048, "\n"); 46 | Print(2048, "ERROR %s occupied squares is bad!\n", 47 | (side) ? "white" : "black"); 48 | Display2BitBoards(temp_occ, Occupied(white)); 49 | error = 1; 50 | } 51 | } 52 | /* 53 | ************************************************************ 54 | * * 55 | * Now we do some sanity tests on the actual chess board * 56 | * information. The first test is to make sure that no * 57 | * bitmap square is set in more than one bitmap, which * 58 | * would imply two different pieces on the same square. * 59 | * * 60 | ************************************************************ 61 | */ 62 | temp_occ = 63 | Pawns(white) ^ Knights(white) ^ Bishops(white) ^ Rooks(white) ^ 64 | Queens(white) ^ Pawns(black) ^ Knights(black) ^ Bishops(black) ^ 65 | Rooks(black) ^ Queens(black) ^ Kings(white) ^ Kings(black); 66 | temp_occx = 67 | Pawns(white) | Knights(white) | Bishops(white) | Rooks(white) | 68 | Queens(white) | Pawns(black) | Knights(black) | Bishops(black) | 69 | Rooks(black) | Queens(black) | Kings(white) | Kings(black); 70 | if (temp_occ ^ temp_occx) { 71 | if (!error) 72 | Print(2048, "\n"); 73 | Print(2048, "ERROR two pieces on same square\n"); 74 | error = 1; 75 | } 76 | /* 77 | ************************************************************ 78 | * * 79 | * Add up all the pieces (material values) to see if this * 80 | * matches the incrementally updated value. * 81 | * * 82 | ************************************************************ 83 | */ 84 | temp_score = 0; 85 | for (side = black; side <= white; side++) 86 | for (piece = pawn; piece < king; piece++) 87 | temp_score += PopCnt(Pieces(side, piece)) * PieceValues(side, piece); 88 | if (temp_score != Material) { 89 | if (!error) 90 | Print(2048, "\n"); 91 | Print(2048, "ERROR material evaluation is wrong, good=%d, bad=%d\n", 92 | temp_score, Material); 93 | error = 1; 94 | } 95 | /* 96 | ************************************************************ 97 | * * 98 | * Next, check the incrementally updated piece counts for * 99 | * both sides. ditto for pawn counts. * 100 | * * 101 | ************************************************************ 102 | */ 103 | for (side = black; side <= white; side++) { 104 | temp_score = 0; 105 | for (piece = knight; piece < king; piece++) 106 | temp_score += PopCnt(Pieces(side, piece)) * p_vals[piece]; 107 | if (temp_score != TotalPieces(side, occupied)) { 108 | if (!error) 109 | Print(2048, "\n"); 110 | Print(2048, "ERROR %s pieces is wrong, good=%d, bad=%d\n", 111 | (side) ? "white" : "black", temp_score, TotalPieces(side, 112 | occupied)); 113 | error = 1; 114 | } 115 | } 116 | for (side = black; side <= white; side++) { 117 | temp_score = PopCnt(Pawns(side)); 118 | if (temp_score != TotalPieces(side, pawn)) { 119 | if (!error) 120 | Print(2048, "\n"); 121 | Print(2048, "ERROR %s pawns is wrong, good=%d, bad=%d\n", 122 | (side) ? "white" : "black", temp_score, TotalPieces(side, pawn)); 123 | error = 1; 124 | } 125 | } 126 | i = PopCnt(OccupiedSquares); 127 | if (i != TotalAllPieces) { 128 | if (!error) 129 | Print(2048, "\n"); 130 | Print(2048, "ERROR! TotalAllPieces is wrong, correct=%d bad=%d\n", i, 131 | TotalAllPieces); 132 | error = 1; 133 | } 134 | /* 135 | ************************************************************ 136 | * * 137 | * Now we cycle through each different chessboard bitmap * 138 | * and verify that each piece in a bitmap matches the same * 139 | * piece type in the board[64] array. * 140 | * * 141 | ************************************************************ 142 | */ 143 | for (side = black; side <= white; side++) 144 | for (piece = pawn; piece <= king; piece++) { 145 | temp = Pieces(side, piece); 146 | while (temp) { 147 | square = LSB(temp); 148 | if (PcOnSq(square) != pieces[side][piece]) { 149 | if (!error) 150 | Print(2048, "\n"); 151 | Print(2048, "ERROR! board[%d]=%d, should be %d\n", square, 152 | PcOnSq(square), pieces[side][piece]); 153 | error = 1; 154 | } 155 | temp &= temp - 1; 156 | } 157 | } 158 | /* 159 | ************************************************************ 160 | * * 161 | * And then we look at the board[64] array and make sure * 162 | * that any non-zero piece matches the proper bitmap for * 163 | * that particular piece type. * 164 | * * 165 | ************************************************************ 166 | */ 167 | for (i = 0; i < 64; i++) { 168 | if (!PcOnSq(i)) 169 | continue; 170 | side = (PcOnSq(i) > 0) ? 1 : 0; 171 | if (SetMask(i) & Pieces(side, Abs(PcOnSq(i)))) 172 | continue; 173 | if (!error) 174 | Print(2048, "\n"); 175 | Print(2048, "ERROR! bitboards/board[%d] don't agree!\n", i); 176 | error = 1; 177 | break; 178 | } 179 | /* 180 | ************************************************************ 181 | * * 182 | * The last chess board test is to make sure that any * 183 | * square that is empty according to board[64] is also * 184 | * empty according to the occupied squares bitmap. * 185 | * * 186 | ************************************************************ 187 | */ 188 | temp = ~(temp_occ | temp_occx); 189 | while (temp) { 190 | square = LSB(temp); 191 | if (PcOnSq(square)) { 192 | if (!error) 193 | Print(2048, "\n"); 194 | Print(2048, "ERROR! board[%d]=%d, should be 0\n", square, 195 | PcOnSq(square)); 196 | error = 1; 197 | } 198 | temp &= temp - 1; 199 | } 200 | /* 201 | ************************************************************ 202 | * * 203 | * Finally, we re-compute the pawn hash signature and the * 204 | * normal hash signature and verify that they match the * 205 | * incrementally updated values. * 206 | * * 207 | ************************************************************ 208 | */ 209 | temp = 0; 210 | temp1 = 0; 211 | for (i = 0; i < 64; i++) { 212 | side = (PcOnSq(i) > 0) ? 1 : 0; 213 | temp ^= randoms[side][Abs(PcOnSq(i))][i]; 214 | if (Abs(PcOnSq(i)) == pawn) 215 | temp1 ^= randoms[side][Abs(PcOnSq(i))][i]; 216 | } 217 | if (EnPassant(ply)) 218 | temp ^= enpassant_random[EnPassant(ply)]; 219 | for (side = black; side <= white; side++) { 220 | if (Castle(ply, side) < 0 || !(Castle(ply, side) & 1)) 221 | temp ^= castle_random[0][side]; 222 | if (Castle(ply, side) < 0 || !(Castle(ply, side) & 2)) 223 | temp ^= castle_random[1][side]; 224 | } 225 | if (temp ^ HashKey) { 226 | if (!error) 227 | Print(2048, "\n"); 228 | Print(2048, "ERROR! hash_key is bad.\n"); 229 | error = 1; 230 | } 231 | if (temp1 ^ PawnHashKey) { 232 | if (!error) 233 | Print(2048, "\n"); 234 | Print(2048, "ERROR! pawn_hash_key is bad.\n"); 235 | error = 1; 236 | } 237 | /* 238 | ************************************************************ 239 | * * 240 | * If any inconsistencies/errors were found, we are going * 241 | * to dump as much debugging information as possible to * 242 | * help pinpoint the source of the problem. * 243 | * * 244 | ************************************************************ 245 | */ 246 | if (error) { 247 | Lock(lock_smp); 248 | Unlock(lock_smp); 249 | Print(2048, "ply=%d\n", tree->ply); 250 | Print(2048, "phase[%d]=%d current move:\n", ply, tree->phase[ply]); 251 | DisplayChessMove("move=", move); 252 | DisplayChessBoard(stdout, tree->position); 253 | Print(2048, "called from %s, ply=%d\n", caller, ply); 254 | Print(2048, "node=%" PRIu64 "\n", tree->nodes_searched); 255 | Print(2048, "active path:\n"); 256 | for (i = 1; i <= ply; i++) { 257 | Print(2048, "ply=%d ", i); 258 | DisplayChessMove("move=", tree->curmv[i]); 259 | } 260 | CraftyExit(1); 261 | } 262 | } 263 | #endif 264 | -------------------------------------------------------------------------------- /source/vcinline.h: -------------------------------------------------------------------------------- 1 | #if _MSC_VER >= 1200 2 | # define FORCEINLINE __forceinline 3 | #else 4 | # define FORCEINLINE __inline 5 | #endif 6 | FORCEINLINE int PopCnt(uint64_t a) { 7 | /* Because Crafty bitboards are typically sparsely populated, we use a 8 | streamlined version of the boolean.c algorithm instead of the one in x86.s */ 9 | __asm { 10 | mov ecx, dword ptr a xor eax, eax test ecx, ecx jz l1 l0:lea edx,[ecx - 1] 11 | inc eax and ecx, edx jnz l0 l1:mov ecx, dword ptr a + 4 test ecx, 12 | ecx jz l3 l2:lea edx,[ecx - 1] 13 | inc eax and ecx, edx jnz l2 l3:}} FORCEINLINE int MSB(uint64_t a) { 14 | __asm { 15 | bsr edx, dword ptr a + 4 mov eax, 31 jnz l1 bsr edx, dword ptr a mov eax, 16 | 63 jnz l1 mov edx, -1 l1:sub eax, 17 | edx}} FORCEINLINE int LSB(uint64_t a) { 18 | __asm { 19 | bsf edx, dword ptr a mov eax, 63 jnz l1 bsf edx, dword ptr a + 4 mov eax, 20 | 31 jnz l1 mov edx, -33 l1:sub eax, edx}} 21 | --------------------------------------------------------------------------------