├── src ├── cui.h ├── cui.cpp ├── debugtree.cpp ├── main.cpp ├── evalptn.h ├── util.cpp ├── debugtree.h ├── judger.cpp ├── online.cpp ├── board.cpp ├── search.h ├── util.h ├── board.h ├── gendata.cpp ├── evalptn.cpp ├── test.cpp ├── search.cpp └── linreg.cpp ├── .gitignore ├── doc ├── logs │ ├── bwcore1.5_mpc.log │ ├── bwcore1.5_nompc.log │ ├── bwcore1.5_badmpc.log │ ├── bwcore1.5_dep8_pvs.log │ ├── bwcore1.5_dep8_nopvs.log │ ├── bwcore1.5_iddep8_pvs.log │ ├── bwcore1.5_iddep8_pres_pvs.log │ ├── bwcore1.5_iddep8_pvs_hash.log │ ├── bwcore1.5_iddep8_full_nompc.log │ ├── bwcore1.5_td_full_20210220.log │ ├── bwcore1.5_iddep8_pres_pvs_hash.log │ ├── bwcore1.5_iddep8_pres_pvs_hashx.log │ ├── match.log │ └── rawdata2_trainlog.log └── bwcore_TODO.md ├── tools ├── mpc_reg.py ├── hexboard.py ├── merge.py ├── ptndatainfo.cpp ├── metacode.cpp ├── boardhex.html ├── olddata_transfer.cpp ├── match.py └── linreg_old.cpp ├── makefile ├── README.md └── botzone.cpp /src/cui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void runConsole(); 4 | -------------------------------------------------------------------------------- /src/cui.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/src/cui.cpp -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.exe 3 | *.out 4 | /*.log 5 | /.vscode 6 | /data/* 7 | debugtree.html 8 | -------------------------------------------------------------------------------- /src/debugtree.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/src/debugtree.cpp -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_mpc.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_mpc.log -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_nompc.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_nompc.log -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_badmpc.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_badmpc.log -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_dep8_pvs.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_dep8_pvs.log -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_dep8_nopvs.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_dep8_nopvs.log -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_iddep8_pvs.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_iddep8_pvs.log -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_iddep8_pres_pvs.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_iddep8_pres_pvs.log -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_iddep8_pvs_hash.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_iddep8_pvs_hash.log -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_iddep8_full_nompc.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_iddep8_full_nompc.log -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_td_full_20210220.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_td_full_20210220.log -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_iddep8_pres_pvs_hash.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_iddep8_pres_pvs_hash.log -------------------------------------------------------------------------------- /doc/logs/bwcore1.5_iddep8_pres_pvs_hashx.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffasttime/Reversi-bwcore/HEAD/doc/logs/bwcore1.5_iddep8_pres_pvs_hashx.log -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "board.h" 2 | #include 3 | #include "cui.h" 4 | #include "search.h" 5 | #include "evalptn.h" 6 | using std::cin; using std::cout; 7 | 8 | void global_init(){ 9 | initPtnConfig(); 10 | loadPtnData(); 11 | loadPCData(); 12 | } 13 | 14 | int main(){ 15 | srand(0); 16 | global_init(); 17 | search_delta=0.5; 18 | runConsole(); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /src/evalptn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "board.h" 3 | 4 | constexpr unsigned short pow3[]={1,3,9,27,81,243,729,2187,6561,19683,59049}; 5 | 6 | #ifndef ONLINE 7 | 8 | extern unsigned short pow4to3_10[1<<20], pow4to3_9[1<<18]; 9 | int pow3to4(int len, int x); 10 | 11 | void readShort(FILE *stream, short &tar); 12 | void initPtnConfig(); 13 | void loadPtnData(); 14 | int evalPtn(const Board &board); 15 | 16 | extern unsigned short pow4to3_10[1<<20], pow4to3_9[1<<18]; 17 | 18 | #endif //ONLINE 19 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "util.h" 5 | 6 | #ifdef DEBUG 7 | void assertprintf(bool val, const char *fmt, ...){ 8 | if (!val){ 9 | va_list args; 10 | va_start(args, fmt); 11 | printf("assertion failed!\n"); 12 | vprintf(fmt, args); 13 | va_end(args); 14 | fflush(stdout); 15 | abort(); 16 | } 17 | } 18 | #endif 19 | #ifndef ONLINE 20 | void showMask(u64 x){ 21 | for(int i=0;i<8;i++,puts("")) 22 | for(int j=0;j<8;j++) 23 | putchar((x>>(i*8+j)&1)?'*':'.'); 24 | } 25 | #endif //ONLINE 26 | -------------------------------------------------------------------------------- /tools/mpc_reg.py: -------------------------------------------------------------------------------- 1 | # an experimental linear regression for probcut 2 | import numpy as np 3 | 4 | full_data=np.loadtxt("data/pc_ndata.txt", delimiter=',') 5 | 6 | # w, b, sigma 7 | result=np.ndarray([64,15,3]) 8 | result[:,:]=[1,0,100] 9 | 10 | for cnt in range(6,60): 11 | for dep in range(3,15): 12 | data=full_data[full_data[:,0]==dep] 13 | data=data[data[:,1]==cnt] 14 | print(f"dep:{dep}, cnt:{cnt}, len:{len(data)}") 15 | if (len(data)<=5): 16 | if len(data)>=1: 17 | print("lack of data:") 18 | print(data) 19 | continue 20 | x=data[:,2] 21 | y=data[:,3] 22 | w,b=np.polyfit(x,y,1) 23 | sigma=np.std(w*x+b-y, ddof=1) + 10/len(data) 24 | print(f"w:{w}, b:{b}, sigma:{sigma}") 25 | result[cnt,dep] = [w, b, sigma] 26 | 27 | np.savetxt("data/pc_coeff.txt", result.reshape([-1,3]), fmt="%.4f") 28 | -------------------------------------------------------------------------------- /tools/hexboard.py: -------------------------------------------------------------------------------- 1 | '''hexboard.py 2 | A simple script show hexdecimal code board 3 | usage: 4 | 5 | or 6 | ''' 7 | 8 | def showMask(x): 9 | for i in range(64): 10 | print('*' if x>>i&1 else '.', end='') 11 | if i%8==7: print() 12 | 13 | def showBoard(b, w): 14 | for i in range(64): 15 | if (b&w)>>i&1: 16 | raise 17 | elif b>>i&1: 18 | print('\u25cf',end='') 19 | elif w>>i&1: 20 | print('\u25cb',end='') 21 | else: 22 | print('\u00b7',end='') 23 | 24 | if i%8==7: print() 25 | 26 | print(__doc__) 27 | 28 | while True: 29 | print('>', end=' ') 30 | 31 | try: 32 | s=input().replace(',',' ').split() 33 | except KeyboardInterrupt: 34 | break 35 | 36 | if len(s)==1: 37 | showMask(int(s[0], 16)) 38 | else: 39 | showBoard(int(s[0], 16), int(s[1], 16)) 40 | -------------------------------------------------------------------------------- /src/debugtree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DEBUGTREE_WARPPER_BEGIN \ 4 | if (debug_tree)\ 5 | debug_tree->step_in(__func__,depth, cboard, alpha, beta);\ 6 | auto warpper=[&]()->Val{ 7 | #define DEBUGTREE_WARPPER_END \ 8 | }; auto ret=warpper(); if (debug_tree) debug_tree->step_out(ret); return ret; 9 | 10 | #include "board.h" 11 | #include 12 | 13 | typedef float Val; 14 | 15 | struct DebugTreeNode{ 16 | std::string fun_name; 17 | Board board; 18 | int depth; 19 | Val alpha, beta; 20 | Val ret; 21 | std::vector ch; 22 | DebugTreeNode* fa; 23 | DebugTreeNode(){} 24 | ~DebugTreeNode(){for(auto p:ch) delete p;} 25 | void write_board(FILE *out); 26 | void write_html(FILE *out, int d, int d_limit); 27 | }; 28 | struct DebugTree{ 29 | DebugTreeNode *root; 30 | DebugTreeNode *cur; 31 | void step_in(const std::string &fun_name, int depth, const Board &board, Val alpha, Val beta); 32 | void step_out(Val ret){ 33 | cur->ret=ret; 34 | cur=cur->fa; 35 | } 36 | DebugTree(){ 37 | root=cur=nullptr; 38 | } 39 | ~DebugTree(){delete root;} 40 | void write_html(std::string file, int d_limit=5); 41 | }; 42 | extern DebugTree *debug_tree; 43 | -------------------------------------------------------------------------------- /src/judger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | judger.cpp 3 | botzone simplified interactive judger for reversi game 4 | */ 5 | #include 6 | #include 7 | #include "util.h" 8 | #include "board.h" 9 | using std::cin, std::cout, std::endl; 10 | 11 | int main(){ 12 | Game game; 13 | cout<<"request\n-1 -1"<>x>>y; 16 | if (x==-1 && y==-1){ 17 | if (game.hasmove()){ 18 | cout<<"finish\n"<=8 || y<0 || y>=8 || !game.testmove(pos(x,y))){ 25 | cout<<"finish\n"<()){ 30 | cout<<"finish\n"< 16 | ''' 17 | source = '' 18 | 19 | for file in files: 20 | with open('src/'+file, 'r') as f: 21 | source += f.read() + '\n' 22 | 23 | source = re.sub(r"#include.*\n", "", source) 24 | source = re.sub(r"#pragma once.*\n", "", source) 25 | 26 | source = re.sub(r"#ifndef ONLINE(.|\n)*?#endif //ONLINE","",source) 27 | source = re.sub(r"#if 1([\s\S]*?)#endif //1","\g<1>", source); 28 | source = re.sub(r"#ifdef GENDATA_PC(.|\n)*?#endif //GENDATA_PC","", source); 29 | source = re.sub(r"(.*)//.*\n",r"\g<1>\n",source) 30 | source = re.sub(r"\/\*[^\/]*\*\/","",source) 31 | source = re.sub(r"assertprintf\(.*?\);","",source) 32 | source = re.sub(r"#ifdef DEBUGTREE(.|\n)*?#endif","",source) 33 | source = re.sub(r"#ifdef DEBUG(.|\n)*?#endif","",source) 34 | 35 | source = re.sub(r"#ifdef RUN_BY_STEP([\s\S]*)#else([\s\S]*)#endif","\g<2>", source); 36 | 37 | source = re.sub(r"\s*\n","\n",source) 38 | source = re.sub(r"\n+","\n",source) 39 | 40 | with open('botzone.cpp', 'w') as f: 41 | f.write(head + source) 42 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | PYTHON3 = python 2 | VPATH = src 3 | 4 | ifdef def 5 | def = -D$(def) 6 | endif 7 | 8 | ifdef debug 9 | CXXFLAGS = -std=c++17 -g -Wall -DDEBUG $(def) -lpthread 10 | objects = util.o board.o evalptn.o search.o debugtree.o 11 | else 12 | CXXFLAGS = -std=c++17 -Wall -O2 $(def) -lpthread 13 | objects = util.o board.o evalptn.o search.o 14 | endif 15 | 16 | ifndef o 17 | output = $@.exe 18 | else 19 | output = $(o).exe 20 | endif 21 | 22 | all: $(objects) cui.o 23 | $(CXX) $(objects) cui.o src/main.cpp $(CXXFLAGS) -o bwcore1.5.exe 24 | 25 | online: $(objects) 26 | $(CXX) $(objects) src/online.cpp $(CXXFLAGS) -o $(output) 27 | 28 | test: $(objects) 29 | $(CXX) $(objects) src/test.cpp $(CXXFLAGS) -o $(output) 30 | 31 | gendata: $(objects) 32 | $(CXX) $(objects) src/gendata.cpp $(CXXFLAGS) -o $(output) 33 | 34 | judger: util.o board.o 35 | $(CXX) util.o board.o src/judger.cpp $(CXXFLAGS) -o $(output) 36 | 37 | linreg: util.o board.o evalptn.o 38 | $(CXX) util.o board.o evalptn.o src/linreg.cpp $(CXXFLAGS) -o $(output) 39 | 40 | $(objects): util.h 41 | cui.o: board.h search.h evalptn.h 42 | evalptn.o: board.h evalptn.h 43 | search.o: board.h search.h debugtree.h evalptn.h 44 | debugtree.o: board.h 45 | board.o: board.h 46 | util.o: 47 | 48 | bwcore14: 49 | $(CXX) -O2 tools/bwcore_online1.4.cpp -o bwcore_online1.4.exe 50 | 51 | .PHONY: botzone 52 | botzone: 53 | $(PYTHON3) tools/merge.py 54 | $(CXX) $(CXXFLAGS) botzone.cpp -o botzone.exe 55 | 56 | .PHONY: clean 57 | clean: 58 | $(RM) *.o bwcore1.5.exe online.exe test.exe gendata.exe judger.exe linreg.exe debugtree.html 59 | -------------------------------------------------------------------------------- /src/online.cpp: -------------------------------------------------------------------------------- 1 | #include "board.h" 2 | #include "search.h" 3 | #include "evalptn.h" 4 | #include "time.h" 5 | #include 6 | 7 | int main(){ 8 | srand(time(nullptr)); 9 | initPtnConfig(); 10 | loadPtnData(); 11 | loadPCData(); 12 | Game game; 13 | #ifdef RUN_BY_STEP 14 | int n,x,y; scanf("%d", &n); 15 | inc(i,2*n-1){ 16 | scanf("%d%d",&x,&y); 17 | if (x!=-1) game.makemove(pos(x,y), 0); // no auto pass 18 | else if (i) game.col=!game.col; // opp pass 19 | } 20 | if (game.hasmove()){ 21 | int sp=think_choice_td(game.board, game.col); 22 | printf("%d %d\n", sp/8, sp%8); 23 | } 24 | else puts("-1 -1"); 25 | //debug 26 | printf("%s, %s\n", game.repr().c_str(), searchstat.str().c_str()); 27 | #else 28 | int n,x,y; scanf("%d", &n); // skip 29 | for(n=0;;n++){ 30 | scanf("%d%d", &x, &y); 31 | if (x!=-1) game.makemove(pos(x,y), 0); 32 | else if (n) game.col=!game.col, game.board.cswap(); 33 | if (game.hasmove()){ 34 | int sp=think_choice_td(game.board); 35 | printf("%d %d\n", sp/8, sp%8); 36 | printf("%s, %s, st:%d, scnt: %d\n", game.repr().c_str(), searchstat.str().c_str(), 37 | searchstat_sum.tl, searchstat_sum.leafcnt); 38 | game.makemove(sp, 0); 39 | } 40 | else puts("-1 -1"),puts(""), game.col=!game.col, game.board.cswap(); 41 | //debug 42 | puts("\n"); // data + global data 43 | printf(">>>BOTZONE_REQUEST_KEEP_RUNNING<<<\n"); 44 | fflush(stdout); 45 | } 46 | #endif //RUN_BY_STEP 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /src/board.cpp: -------------------------------------------------------------------------------- 1 | #include "board.h" 2 | #include 3 | #include 4 | #include 5 | 6 | std::string Board::repr() const{ 7 | std::ostringstream o; 8 | o< 6 | using namespace std; 7 | #define inc(i,n) for (int i=0;i10) len=plen[k]; 41 | else len=pow3[plen[k]]; 42 | inc(j, len) rdc(x); // load & check 43 | } 44 | short file_checksum; readShort(in, file_checksum); 45 | printf("checksum=%d,",(int)checksum); 46 | if (checksum!=file_checksum) printf("fail!\n"); 47 | else printf("ok\n"); 48 | 49 | char desc[200]; 50 | short desc_len; readShort(in, desc_len); 51 | if (desc_len>200){ 52 | puts("wrong desc_len"); 53 | exit(1); 54 | } 55 | fread(desc, desc_len, 1, in); 56 | puts("description:"); 57 | puts(desc); 58 | 59 | long long tm; 60 | fread(&tm, sizeof tm, 1, in); 61 | printf("gen timestamp: %lld , %s",tm, ctime(&tm)); 62 | 63 | fclose(in); 64 | } 65 | 66 | int main(int argc, char **argv){ 67 | if (argc==1) return puts("args: input_filename"),0; 68 | EVAL_FILE=argv[1]; 69 | loadPtnData(); 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/search.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "board.h" 4 | #include "time.h" 5 | #include 6 | #include 7 | #include 8 | using std::max; 9 | 10 | typedef float Val; 11 | constexpr int INF=256; 12 | typedef std::pair PosVal; 13 | 14 | #ifdef DEBUGTREE 15 | #include "debugtree.h" 16 | #endif 17 | 18 | Val search_end2(CBoard board); 19 | inline Val eval_end(CBoard board){return board.cnt0()-board.cnt1();} 20 | 21 | template 22 | Val search_end(CBoard cboard, Val alpha, Val beta, bool pass){ 23 | #ifdef DEBUGTREE 24 | DEBUGTREE_WARPPER_BEGIN 25 | #endif 26 | u64 move=cboard.genmove(); 27 | if (!move){ 28 | if (pass) return eval_end(cboard); 29 | return -search_end(cboard.cswap_r(), -beta, -alpha, 1); 30 | } 31 | Val val=-INF; 32 | for (auto p:u64iter(move)){ 33 | if constexpr (depth==3) val=max(val, -search_end2(cboard.cmakemove_r(p))); 34 | else val=max(val, -search_end(cboard.cmakemove_r(p), -beta, -alpha, 0)); 35 | if (val>=beta) return val; 36 | if (val>alpha) alpha=val; 37 | } 38 | return val; 39 | #ifdef DEBUGTREE 40 | DEBUGTREE_WARPPER_END 41 | #endif 42 | } 43 | 44 | struct SearchStat{ 45 | int depth, leafcnt; 46 | int t_start, tl; 47 | int hashhit; 48 | std::vector pv; 49 | Val maxv; 50 | void timing(){ 51 | tl=clock()-t_start; 52 | t_start=clock(); 53 | } 54 | void reset(int _depth){leafcnt=hashhit=0; depth=_depth; t_start=clock(); maxv=-INF;} 55 | std::string str(); 56 | }; 57 | 58 | int search_root(int depth, CBoard cboard, int suggestp=-1); 59 | Val search_normal(int depth, CBoard cboard, Val alpha, Val beta, bool pass=0); 60 | 61 | constexpr int MPC_MAXD=14; 62 | 63 | #ifndef ONLINE 64 | extern u64 debug_flag; 65 | extern std::ostringstream debugout; 66 | 67 | Val search_exact(int depth, CBoard cboard, Val alpha, Val beta, bool pass=0); 68 | int random_choice(CBoard board); 69 | int think_choice(CBoard board); 70 | int think_choice_td(CBoard board); 71 | void loadPCData(); 72 | 73 | extern SearchStat searchstat; 74 | extern SearchStat searchstat_sum; 75 | extern std::ostringstream debugout; 76 | 77 | extern Val search_delta; 78 | extern int think_maxd; //think_choice() maxd midgame 79 | extern int think_checktime, think_maxtime; 80 | 81 | #endif //ONLINE 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reversi-bwcore 2 | 3 | A simple but relative strong Reversi (aka Othello) bot running on [botzone.org](https://botzone.org) 4 | 5 | version: 1.5.2 ("strongbwcore" on botzone) 6 | 7 | For detail introduction and rule of the game, see [https://en.wikipedia.org/wiki/Reversi](https://en.wikipedia.org/wiki/Reversi). 8 | 9 | This project is writing for fun, and in memory of my first contact with programming. 10 | 11 | The main algorithm of this bot is minimax search with alpha-beta pruning. Some famous optimization of minimax including PVS, hash table and zero window search are used. Bitwise operations are used to speed up board operation. Pattern evaluation is a important way to estimate on middle game. I tried to implement all of them by simple code, so it looks easier to read. 12 | 13 | ## Usage 14 | 15 | ### requirements 16 | 17 | * g++ >= 7 18 | 19 | These code was developed under g++17. MinGW-w64 is suggested on Windows. 20 | 21 | * Python3 22 | 23 | Some simple scripts including `tools/merge.py` `tools/match.py` requires Python3. 24 | 25 | * A modern x86-64 CPU 26 | 27 | A little assembly instructions are used to speed up board operation. 28 | 29 | ### build 30 | 31 | * Generate single file botzone program 32 | 33 | Run `make botzone` , it will call `tools/merge.py` to merge useful source files in `/src` into botzone.cpp, and try to build it. 34 | 35 | `make online` will build same program, but it is built by multiple files. 36 | 37 | * Run a simple console UI (currently only available on Windows) 38 | 39 | Run `make` , then there will be a bwcore1.5.exe. You can play game by clicking mouse. See `src/cui.cpp` if it can't display properly. 40 | 41 | * Run keyboard interaction mode 42 | 43 | Run `make test` and `test.exe`, you can run bot by keyboard commands. And there are some commands for debug, see [docs/TODO.md](doc/bwcore_TODO.md) 44 | 45 | Specially, run `test.exe --` will do some basic test 46 | 47 | * Match between two bots 48 | 49 | 1. Get any Reversi bot supporting Botzone keep running mode, for bwcore1.5 is `make online` or `make botzone` 50 | 2. Run `make judger` Make a Reversi judger, `src/judger.cpp` is a simple judger supports Botzone simplified interaction format 51 | 3. Run `python tools.py `, then result will be saved into `match.log` after all match games are finished 52 | 53 | * Generate selfplay data 54 | 55 | Run `make gendata` , board evaluation data can be generated by selfplay. Further more, you can modify `src/gendata.cpp` to generate learning data as you like. 56 | 57 | * Debug 58 | 59 | When building all binaries above, add `make debug=1` to build with debug flag, then the code will not be optimized. bwcore1.5 will also do some assertion check when running under debug mode. 60 | 61 | ## Tutorials 62 | 63 | Waiting... 64 | 65 | You can read [docs/TODO.md](doc/bwcore_TODO.md) for some details of this version 66 | 67 | ## Old version 68 | 69 | version: 1.4.x "impbwcore" ([https://github.com/fffasttime/Reversi-bwcore/tree/1.4.x](https://github.com/fffasttime/Reversi-bwcore/tree/1.4.x)) 70 | 71 | algorithm: minimax, ptn evaluation, ... 72 | 73 | Got 1st place on botzone real time ranklist in 2017-2019, 3rd in 2020 74 | -------------------------------------------------------------------------------- /doc/logs/match.log: -------------------------------------------------------------------------------- 1 | Match finished at Mon Feb 22 00:53:58 2021 2 | bot1:botzone.exe(pc open t=2.0) bot2:bwcore_online1.5.exe 3 | bot1 | win | loss | draw | score 4 | as_b | 24 | 20 | 6 | 27.0 / 50 5 | as_w | 35 | 12 | 3 | 36.5 / 50 6 | 63.5 / 100 7 | 8 | Match finished at Mon Feb 22 11:54:19 2021 9 | bot1:botzone.exe(pc open t=2.0) bot2:bwcore_online1.5.exe 10 | bot1 | win | loss | draw | score 11 | as_b | 55 | 39 | 6 | 58.0 / 100 12 | as_w | 55 | 35 | 10 | 60.0 / 100 13 | 118.0 / 200 14 | 15 | Match finished at Mon Feb 22 02:38:38 2021 16 | bot1:botzone.exe(pc no end) bot2:bwcore_online1.5.exe 17 | bot1 | win | loss | draw | score 18 | as_b | 54 | 38 | 8 | 58.0 / 100 19 | as_w | 58 | 34 | 8 | 62.0 / 100 20 | 120.0 / 200 21 | 22 | Match finished at Mon Feb 22 16:57:02 2021 23 | bot1:botzone.exe(pc t=1.2) bot2:bwcore_online1.5.exe 24 | bot1 | win | loss | draw | score 25 | as_b | 56 | 40 | 4 | 58.0 / 100 26 | as_w | 64 | 31 | 5 | 66.5 / 100 27 | 124.5 / 200 28 | 29 | Match finished at Mon Feb 22 17:00:15 2021 30 | bot1:botzone.exe(pc t=1.2) bot2:bwcore_online1.4.exe 31 | bot1 | win | loss | draw | score 32 | as_b | 44 | 5 | 1 | 44.5 / 50 33 | as_w | 40 | 5 | 5 | 42.5 / 50 34 | 87.0 / 100 35 | 36 | Match finished at Tue Mar 2 02:59:09 2021 37 | bot1:online.exe(rawdata1, nopc) bot2:online_olddata.exe 38 | bot1 | win | loss | draw | score 39 | as_b | 55 | 37 | 8 | 59.0 / 100 40 | as_w | 68 | 23 | 9 | 72.5 / 100 41 | 131.5 / 200 42 | 43 | Match finished at Tue Mar 2 03:08:41 2021 44 | bot1:online.exe(rawdata1, nopc) bot2:bwcore_online1.4.exe 45 | bot1 | win | loss | draw | score 46 | as_b | 70 | 25 | 5 | 72.5 / 100 47 | as_w | 90 | 9 | 1 | 90.5 / 100 48 | 163.0 / 200 49 | 50 | Match finished at Tue Mar 2 21:14:12 2021 51 | bot1:online.exe(rawdata1, pc old) bot2:bwcore_online1.4.exe 52 | bot1 | win | loss | draw | score 53 | as_b | 34 | 14 | 2 | 35.0 / 50 54 | as_w | 49 | 1 | 0 | 49.0 / 50 55 | 84.0 / 100 56 | 57 | Match finished at Tue Mar 2 22:55:06 2021 58 | bot1:online.exe(rawdata1, pc old) bot2:online_olddata.exe 59 | bot1 | win | loss | draw | score 60 | as_b | 70 | 24 | 6 | 73.0 / 100 61 | as_w | 72 | 21 | 7 | 75.5 / 100 62 | 148.5 / 200 63 | 64 | Match finished at Thu Mar 4 23:51:59 2021 65 | bot1:online.exe(rawdata2, nopc) bot2:bwcore_online1.4.exe 66 | bot1 | win | loss | draw | score 67 | as_b | 39 | 9 | 2 | 40.0 / 50 68 | as_w | 41 | 7 | 2 | 42.0 / 50 69 | 82.0 / 100 70 | 71 | Match finished at Fri Mar 5 10:33:56 2021 72 | bot1:online.exe(rawdata2, pc old) bot2:online_olddata.exe 73 | bot1 | win | loss | draw | score 74 | as_b | 74 | 21 | 5 | 76.5 / 100 75 | as_w | 75 | 11 | 14 | 82.0 / 100 76 | 158.5 / 200 77 | 78 | Match finished at Fri Mar 5 10:38:32 2021 79 | bot1:online_nopc.exe(rawdata2) bot2:online_olddata.exe 80 | bot1 | win | loss | draw | score 81 | as_b | 74 | 23 | 3 | 75.5 / 100 82 | as_w | 60 | 32 | 8 | 64.0 / 100 83 | 139.5 / 200 84 | 85 | warning: low var 86 | Match finished at Fri Mar 5 11:24:16 2021 87 | bot1:online.exe(rawdata2, pc old) bot2:bwcore_online1.4.exe 88 | bot1 | win | loss | draw | score 89 | as_b | 40 | 7 | 3 | 41.5 / 50 90 | as_w | 50 | 0 | 0 | 50.0 / 50 91 | 91.5 / 100 92 | 93 | -------------------------------------------------------------------------------- /doc/bwcore_TODO.md: -------------------------------------------------------------------------------- 1 | # bwcore1.5 2 | 3 | ## 目标 4 | 5 | 原有代码冗杂且效率低(全部代码4k+行),可维护性差,全部重构bwcore1.3~1.4(impbwcore)代码。 6 | 7 | bwcore1.5(strongbwcore)仍然使用较传统的搜索算法。主要为快速终局搜索、快速位运算估值、pvs搜索、置换表等。实验性的内容暂不考虑。 8 | 9 | ## 辅助文件 10 | 11 | * boardhex.html 用于构造棋盘的十六进制码。 12 | * match.py 用于比较两个bot 13 | * merge.py 合并代码到单文件 14 | * 调试时可生成搜索树debugtree.html。父节点的ret应为所有子节点ret取负中的最大值。若节点的ret>=beta,则剪枝。 15 | 16 | ## 棋盘数据结构 17 | 18 | 位运算棋盘操作board.cpp,封装常用操作。 19 | 20 | 包含以下操作: 21 | 22 | 生成可行位置、位运算棋步、对称变换、计数。 23 | 24 | * 更新:Board现在不区分黑白颜色,board.b[0]为先手方,board.b[1]为后手方。Game类保存颜色,仅用于显示。 25 | 26 | ### 可行位置生成 27 | 28 | 使用Zebra代码。使用纯位运算消除分支语句,并用分支技巧把8次位运算降低到3次。 29 | 30 | ### 位运算棋步 31 | 32 | 使用位运算或BMI2指令进行快速翻转。 33 | 34 | 把四个方向的翻转的每一条线,使用PEXT提取映射成一条线。这条线上的翻转可以构造查表。再PDEP放回。 35 | 36 | 1. 需要有pos映射后的翻转位置(0..63)=>(0..7)。该值为4*64个常数,直接打表查询。 37 | 2. 需要pos在各个位置上的mask。 38 | 3. 主查询表为在一条线上的翻转,有8*65536种状态,映射为2字节。耗内存1MB。 39 | 40 | ### 变换 41 | 42 | 棋盘有8种对称变换,可以下面方式计算全部的变换: 43 | 44 | `id` `flipv` `lrotate` `rrotate` 45 | 46 | `id+bsw(fliph)` `flipv+bsw(180 rotate)` `lrotate+bsw(transpose)` `rrotate+bsw` 47 | 48 | 其中`flipv`按中央竖线对称,`rotate`旋转90度,`transpose`按主对角线对称。 49 | 50 | 在x86-64上,`fliph`即指令`bswap`,按8位分组翻转64个位。`flip_v`可通过6次`&`和`<<`+3次`|`的位运算得到;`rotate`位运算类似。注:`transpose`也可由3次`zip`(2*32矩阵转置)得到,`zip`由两次`pext`得到。 51 | 52 | ## 估值 53 | 54 | ### 模板 55 | 56 | * e1(edga+2x),e2,e3,e4直线, k8,k7,k6,k5,k4斜线 57 | * c52, c33 角 58 | * wmob, wodd, wb 59 | 60 | 1.5.1更新:加入中心棋子数wcinner, 边棋子数wcedge,角ccor,(2,2)位置cx22 61 | 62 | 使用e2-e4,k8-k4时,无需对棋盘变换。c52需要全部8种变换。c33需要`flipv,fliph,r180`,e1需要`fliph,lrotate,rrotate`。 63 | 64 | 对长度9-10的模板进行三进制转换。约定默认黑色(先手方)在二进制下为高位,三进制下位权为2。二进制下低位在三进制下为高位。占内存约10MB。 65 | 66 | ### 文件格式 67 | 68 | 主要由16位整数构成。 69 | 70 | | version | part count | pattern type count | pattern type length | pattern coeff data | checksum | desc_str_len | desc_str | generate timestamp | 71 | | ------- | ---------- | ------------------ | -------------------------- | ------------------ | -------- | ------------ | -------- | ------------------ | 72 | | int16 | int16 | int16 | int16*(pattern type count) | int16* | int16 | int16 | cstring | int64 | 73 | 74 | ## 搜索 75 | 76 | `search_normal` 执行一般的中局搜索,以Negamax为框架。 77 | 78 | ### PVS 79 | 80 | 随机选取节点(第一个)做PVS,平均情况下比普通alphabeta节点略少约10% 81 | 82 | #### 试探搜索 83 | 84 | [deprecated] bwcore1.3~1.4只对浅层节点使用宽窗口的试探搜索完全排序,效果较好 85 | 86 | 现在只试探搜索找到pv作为主要节点,不对所有节点排序。(未实验排序,考虑到剪枝搜索无法准确排序,而且即使不准确时次大节点对PVS空窗验证影响不大) 87 | 88 | > 目前为递归执行。当搜索目的为空窗验证时,选取好的pv仍能有效剪枝。 89 | 90 | ### 置换表 91 | 92 | 黑白棋很少出现相同的搜索树分支。因此置换表只能用于减少迭代加深或试探搜索时出现的重复。用于pvs实验效果一般,命中率不到1%,只能减少5%左右的节点。 93 | 94 | 更新:在使用迭代加深和试探搜索时,置换表效果提高。 95 | 96 | ### 终局优化 97 | 98 | `seach_end2` 展开搜索剩余两个空 99 | 100 | `seach_end` 简单终局搜索,不用额外剪枝,建议6个左右空时使用 101 | 102 | ### 概率剪枝 103 | 104 | 变量cnt, height。参数为pc_depth, slope, bias, sigma。 105 | 106 | 概率剪枝会对上、下界做一次浅层的零窗搜索。 107 | 108 | > 数据生成:开gendata.cpp GENDATA_PC宏生成浅层搜索结果到pc_data.txt,再gendata.cpp remake()一次生成深层搜索结果pc_ndata.txt,然后执行回归。 109 | 110 | ## 测试程序 111 | 112 | `make test` 生成 `test.exe` 113 | 114 | `test.exe --` 执行检查。 115 | 116 | #### 命令行模式 117 | 118 | `test.exe ` 运行命令行测试模式 119 | 120 | `p` `print` 显示当前局面 121 | 122 | `ph` `printhex` 当前局面的十六进制编码 123 | 124 | `m x y` `makemove x y` 在x行y列下子,坐标从0,0开始 125 | 126 | `u` `unmakemove` 撤销一步 127 | 128 | `evalptn` 当前局面的模板估值 129 | 130 | `think` 思考下一步 131 | 132 | `play` 思考下一步并执行 133 | 134 | `tree depth` 搜索指定步数并保存html格式的搜索树 135 | 136 | `cnt` 统计黑白棋子数 137 | 138 | `reset` 重置为游戏开始 139 | 140 | `ld hex0 hex1` `board hex0 hex1` 设置当前局面为十六进制编码指定的局面 141 | 142 | `flipv` `fliph` `rotatel` `rotater` 变换棋盘 143 | 144 | `cswap` 交换棋盘黑白颜色 145 | 146 | `fcol` 交换当前玩家颜色 147 | 148 | `col 0` `col 1` 设置当前玩家颜色(0黑,1白) 149 | 150 | `savesgf filename` 以sgf格式保存当前棋谱 151 | 152 | `q` `quit` `exit` 退出 153 | 154 | -------------------------------------------------------------------------------- /tools/metacode.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | metacode.cpp 3 | generate constant arrays code 4 | - deprecated because BMI2 flip code is deprecated 5 | author: fffasttime 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | using namespace std; 12 | 13 | typedef unsigned long long ull; 14 | typedef ull u64; 15 | 16 | bool flghex; 17 | 18 | #define prtval(name) cout<< "constexpr " << #name << "[" << dec << name.size() << "]=" << name <<";\n"; 19 | #define inc(i,n) for (int i=0;i>(i*8+j)&1)?'*':'.'); 26 | puts(""); 27 | } 28 | } 29 | 30 | template 31 | ostream &operator<<(ostream &o, const vector &vec){ 32 | o<<"{"; 33 | for (size_t i=0;i h,v,d1,d2; 46 | 47 | void genCommonMask(){ 48 | inc(i,8){ 49 | ull hh=0,vv=0; 50 | inc(j,8) 51 | hh|=1ull<<(8*i+j), 52 | vv|=1ull<<(8*j+i); 53 | h.push_back(hh); 54 | v.push_back(vv); 55 | } 56 | inc(i,15){ 57 | ull dd1=0,dd2=0; 58 | inc(j,8) if (i-j>=0 && i-j<8){ 59 | int y=i-j; 60 | dd1|=1ull<<((7-j)*8+y); 61 | dd2|=1ull<<(j*8+y); 62 | } 63 | d1.push_back(dd1); 64 | d2.push_back(dd2); 65 | } 66 | reverse(begin(d1),end(d1)); 67 | flghex=1; 68 | prtval(h); prtval(v); 69 | prtval(d1); prtval(d2); 70 | flghex=0; 71 | } 72 | 73 | //position mask (4x64) 74 | vector pm_h,pm_v,pm_d1,pm_d2,p_umask; 75 | //position to line pos after PEXT (4x64) 76 | vector pl_h,pl_v,pl_d1,pl_d2; 77 | 78 | template constexpr const T& cmin(const T &a,const T &b){return a0)?0x8040201008040201<<((x/8-x%8)*8):0x8040201008040201>>((x%8-x/8)*8);} 86 | constexpr u64 fpm_d2(int x){return (x/8+x%8-7>0)?0x0102040810204080<<((x/8+x%8-7)*8):0x0102040810204080>>((7-x%8-x/8)*8);} 87 | constexpr u64 fp_umask(int x){return ~(fpm_h(x)|fpm_v(x)|fpm_d1(x)|fpm_d2(x));} 88 | bool equ(int n, function f, vector &v){ 89 | for (int i=0;i>p&1){ 99 | pm_h.push_back(hh); 100 | pl_h.push_back(getp(hh,p)); 101 | } 102 | for (auto vv:v) if (vv>>p&1){ 103 | pm_v.push_back(vv); 104 | pl_v.push_back(getp(vv,p)); 105 | } 106 | for (auto dd1:d1) if (dd1>>p&1){ 107 | pm_d1.push_back(dd1); 108 | pl_d1.push_back(getp(dd1,p)); 109 | } 110 | for (auto dd2:d2) if (dd2>>p&1){ 111 | pm_d2.push_back(dd2); 112 | pl_d2.push_back(getp(dd2,p)); 113 | } 114 | p_umask.push_back(~(pm_h.back()|pm_v.back()|pm_d1.back()|pm_d2.back())); 115 | } 116 | prtval(pl_h); prtval(pl_v); 117 | prtval(pl_d1); prtval(pl_d2); 118 | 119 | flghex=1; 120 | prtval(pm_h); prtval(pm_v); 121 | prtval(pm_d1); prtval(pm_d2); 122 | prtval(p_umask); 123 | 124 | #define CHK(ptn) cout<<#ptn<<" "<=0 && x<8 && y>=0 && y<8, "invalid pos\n"); return x*8+y;} 22 | 23 | inline int popcnt(cu64 x){ 24 | u64 ret; 25 | asm("popcnt %1,%0":"=r"(ret):"r"(x)); 26 | return ret; 27 | } 28 | inline u64 pext(cu64 x, cu64 mask){ 29 | u64 ret; 30 | asm("pext %1,%2,%0":"=r"(ret):"r"(mask), "r"(x)); 31 | return ret; 32 | } 33 | inline u64 pdep(cu64 x, cu64 mask){ 34 | u64 ret; 35 | asm("pdep %1,%2,%0":"=r"(ret):"r"(mask), "r"(x)); 36 | return ret; 37 | } 38 | inline u64 blsr(cu64 x){ 39 | u64 ret; 40 | asm("blsr %1,%0":"=r"(ret):"r"(x)); 41 | return ret; 42 | } 43 | inline bool bget(cu64 x, u64 p){ 44 | bool ret; 45 | asm( 46 | "bt %2,%1\n" 47 | "setc %0\n" 48 | :"=&r"(ret) :"r"(x), "r"(p)); 49 | return ret; 50 | } 51 | inline void bts(u64 &x, u64 p){asm("bts %1,%0\n":"+r"(x):"r"(p));} 52 | inline void btr(u64 &x, u64 p){asm("btr %1,%0\n":"+r"(x):"r"(p));} 53 | inline int ctz(u64 x){assertprintf(x, "ctz 0 is undefined behavior\n"); return __builtin_ctzll(x);} 54 | inline int tzcnt(u64 x){if (x==0) return 64; return __builtin_ctzll(x);} 55 | inline void flip_h(u64 &x){asm("bswap %0\n":"+r"(x));} 56 | inline void flip_v(u64 &x){ 57 | x = (x & 0xaaaaaaaaaaaaaaaa) >> 1 | (x & 0x5555555555555555) << 1; 58 | x = (x & 0xcccccccccccccccc) >> 2 | (x & 0x3333333333333333) << 2; 59 | x = (x & 0xf0f0f0f0f0f0f0f0) >> 4 | (x & 0x0f0f0f0f0f0f0f0f) << 4; 60 | } 61 | inline void rotate_r(u64 &x){ 62 | x = (x & 0xf0f0f0f000000000) >> 4 | (x & 0x0f0f0f0f00000000) >> 32 63 | | (x & 0x00000000f0f0f0f0) << 32 | (x & 0x000000000f0f0f0f) << 4; 64 | x = (x & 0xcccc0000cccc0000) >> 2 | (x & 0x3333000033330000) >> 16 65 | | (x & 0x0000cccc0000cccc) << 16 | (x & 0x0000333300003333) << 2; 66 | x = (x & 0xaa00aa00aa00aa00) >> 1 | (x & 0x5500550055005500) >> 8 67 | | (x & 0x00aa00aa00aa00aa) << 8 | (x & 0x0055005500550055) << 1; 68 | } 69 | inline void rotate_l(u64 &x){ 70 | x = (x & 0xf0f0f0f000000000) >> 32 | (x & 0x0f0f0f0f00000000) << 4 71 | | (x & 0x00000000f0f0f0f0) >> 4 | (x & 0x000000000f0f0f0f) << 32; 72 | x = (x & 0xcccc0000cccc0000) >> 16 | (x & 0x3333000033330000) << 2 73 | | (x & 0x0000cccc0000cccc) >> 2 | (x & 0x0000333300003333) << 16; 74 | x = (x & 0xaa00aa00aa00aa00) >> 8 | (x & 0x5500550055005500) << 1 75 | | (x & 0x00aa00aa00aa00aa) >> 1 | (x & 0x0055005500550055) << 8; 76 | } 77 | #ifndef ONLINE 78 | void showMask(u64 x); 79 | #endif //ONLINE 80 | struct u64iter{ 81 | u64 x; 82 | u64iter(u64 x):x(x){} 83 | struct iter; 84 | iter begin(); 85 | iter end(); 86 | }; 87 | struct u64iter::iter{ 88 | u64iter x; 89 | int operator*() const{return ctz(x.x);} 90 | bool operator!=(const iter &v) const{return x.x!=v.x.x;} 91 | iter &operator++(){ 92 | x.x=blsr(x.x); 93 | return *this; 94 | } 95 | }; 96 | inline u64iter::iter u64iter::begin(){return {*this};} 97 | inline u64iter::iter u64iter::end(){return {u64iter(0)};} 98 | 99 | #include 100 | namespace bitptn{ 101 | // generate conxtexpr arrays 102 | template 103 | constexpr auto make_array_helper(Function f, std::index_sequence) 104 | -> std::array::type, sizeof...(Indices)> { 105 | return {{ f(Indices)... }}; 106 | } 107 | template 108 | constexpr auto make_array(Function f) 109 | -> std::array::type, N>{ 110 | return make_array_helper(f, std::make_index_sequence{}); 111 | } 112 | #define CEXPR_ARRAY_DEF(name, N, expr) \ 113 | constexpr static auto f##name(int x){return (expr);}\ 114 | constexpr auto name = make_array(f##name) 115 | // common mask '-' '|' '\' '/', ascending order 116 | CEXPR_ARRAY_DEF(h, 8, 0xffull<<(x*8)); 117 | CEXPR_ARRAY_DEF(v, 8, 0x101010101010101ull<7?0x8040201008040201u<<((x-7)*8):0x8040201008040201u>>((7-x)*8)); 119 | CEXPR_ARRAY_DEF(d2, 15, x>7?0x0102040810204080u<<((x-7)*8):0x0102040810204080u>>((7-x)*8)); 120 | #undef CEXPR_ARRAY_DEF 121 | 122 | constexpr u64 edge2x=0x42ff, c33=0x70707, c52=0x1f1f, ccor=0x8100000000000081, 123 | cx22=0x0042000000004200, pedge=0x7e8181818181817e, pinner=0x003c7e7e7e7e3c00; 124 | } 125 | -------------------------------------------------------------------------------- /tools/boardhex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ReversiBoard-Hex 6 | 7 | 25 | 26 | 27 | 35 |
28 | 29 | 30 | 31 |
32 | 33 | 34 |
36 | 37 |
38 |

39 | occupy: 40 | 42 |

43 | board: 44 | 46 | 47 | 133 | 134 | -------------------------------------------------------------------------------- /tools/olddata_transfer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | transfer bwcore1.4 coeff file into bwcore1.5.0 coeff file 3 | deprecated because bwcore1.5.0 has made new coeff data, bwcore1.5.1 has new evalptn function 4 | */ 5 | #include 6 | using namespace std; 7 | 8 | struct CoeffPack{ 9 | short e1[59049], c52[59049], c33[19683], 10 | e2[6561], e3[6561], e4[6561], k8[6561], k7[2187], k6[729], k5[243], k4[81], 11 | wb, wodd, wmob; 12 | }pdata[12]; 13 | 14 | constexpr unsigned short pow3[]={1,3,9,27,81,243,729,2187,6561,19683,59049}; 15 | int new_e1_pos[59049]; 16 | 17 | char *EVAL_FILE, EVAL_FILE_S[50]; 18 | 19 | const short format_version=1; 20 | const char *data_desc="from old ptndata_botzone\n\ 21 | wb|wodd|wmob|e1|c52|c33|e2|e3|e4|k8|k7|k6|k5|k4\n"; 22 | const short plen[]= 23 | {0,0,0,10,10,9,8,8,8,8,7,6,5,4}; 24 | short COEFF_PARTCNT; 25 | 26 | unsigned short checksum; 27 | void readShort(FILE *f, short &x){ 28 | fread(&x, 2, 1, f); 29 | checksum^=x; 30 | } 31 | void writeShort(FILE *f, short x){ 32 | fwrite(&x, 2, 1, f); 33 | checksum^=x; 34 | } 35 | 36 | void loadPtnData(){ 37 | FILE *eval_stream=fopen(EVAL_FILE, "rb"); 38 | readShort(eval_stream, COEFF_PARTCNT); 39 | for (int i=0;i 5 | #include 6 | 7 | /* 8 | bitmove: 9 | UL U UR >>9 >>8 >>7 10 | L . R => >>1 . <<1 11 | DL D DR <<7 <<8 <<9 12 | */ 13 | class Board{ 14 | public: 15 | u64 b[2]; 16 | Board(){} 17 | Board(u64 _0, u64 _1):b{_0,_1}{} 18 | void setStart(){b[0]=0x810000000,b[1]=0x1008000000;} 19 | bool operator==(const Board &v) const{return b[0]==v.b[0] && b[1]==v.b[1];} 20 | std::string repr() const; 21 | #ifndef ONLINE 22 | std::string str(bool fcol=0) const; 23 | #endif //ONLINE 24 | template u64 genmove() const{ 25 | // This part of code is brought from Zebra & stdrick 26 | cu64 b_cur = b[col], b_opp = b[!col]; 27 | u64 b_opp_inner = b_opp & 0x7E7E7E7E7E7E7E7Eu; 28 | u64 moves = 0; 29 | u64 b_flip, b_opp_adj; 30 | 31 | #define GENMOVE(arrow, d, opp) \ 32 | b_flip = (b_cur arrow d) & opp; \ 33 | b_flip |= (b_flip arrow d) & opp; \ 34 | b_opp_adj = opp & (opp arrow d); \ 35 | b_flip |= (b_flip arrow (d+d)) & b_opp_adj; \ 36 | b_flip |= (b_flip arrow (d+d)) & b_opp_adj; \ 37 | moves |= b_flip arrow d 38 | GENMOVE(>>, 1, b_opp_inner); GENMOVE(<<, 1, b_opp_inner); 39 | GENMOVE(>>, 8, b_opp); GENMOVE(<<, 8, b_opp); 40 | GENMOVE(>>, 7, b_opp_inner); GENMOVE(<<, 7, b_opp_inner); 41 | GENMOVE(>>, 9, b_opp_inner); GENMOVE(<<, 9, b_opp_inner); 42 | #undef GENMOVE 43 | 44 | return moves & ~(b_cur | b_opp); 45 | } 46 | bool testmove(int p) const{return bget(genmove(),p);} 47 | template bool makemove(int p){ 48 | using namespace bitptn; 49 | u64 &b_cur = b[col]; u64 &b_opp = b[!col]; 50 | u64 b_flip, b_opp_adj, flips = 0; 51 | u64 b_opp_inner = b_opp & 0x7E7E7E7E7E7E7E7Eu; 52 | #define GENMOVE(arrow, d, opp) \ 53 | b_flip = ((1ull<>, 1, b_opp_inner); GENMOVE(<<, 1, b_opp_inner); 60 | GENMOVE(>>, 8, b_opp); GENMOVE(<<, 8, b_opp); 61 | GENMOVE(>>, 7, b_opp_inner); GENMOVE(<<, 7, b_opp_inner); 62 | GENMOVE(>>, 9, b_opp_inner); GENMOVE(<<, 9, b_opp_inner); 63 | #undef GENMOVE 64 | if(flips) b_cur^=flips, b_opp^=flips, bts(b_cur, p); 65 | return flips; 66 | } 67 | // prefix c_ swap color 68 | // suffix _r return a new borad 69 | bool cmakemove(int p){cswap();return makemove<1>(p);} 70 | Board makemove_r(int p) const{Board r=*this;r.makemove(p);return r;} 71 | Board cmakemove_r(int p) const{Board r(b[1],b[0]);r.makemove<1>(p);return r;} 72 | int cnt0() const{return popcnt(b[0]);} 73 | int cnt1() const{return popcnt(b[1]);} 74 | u64 hash() const{return ((b[0]*19260817)^b[1])%998244353;} 75 | 76 | Board cswap_r() const{return Board(b[1],b[0]);} 77 | void cswap(){std::swap(b[0],b[1]);} 78 | void flip_h(){::flip_h(b[0]);::flip_h(b[1]);} 79 | void flip_v(){::flip_v(b[0]);::flip_v(b[1]);} 80 | void rotate_l(){::rotate_l(b[0]);::rotate_l(b[1]);} 81 | void rotate_r(){::rotate_r(b[0]);::rotate_r(b[1]);} 82 | u64 emptys() const{return ~(b[0]|b[1]);} 83 | u64 occupys() const{return b[0]|b[1];} 84 | 85 | int operator[](int p) const{return 2-2*bget(b[0], p)-bget(b[1], p);} 86 | }; 87 | typedef const Board &CBoard; 88 | 89 | class Game{ 90 | public: 91 | #ifndef ONLINE 92 | std::string str() const; 93 | void savesgf(std::string filename); 94 | Board& board_begin(){return step?board_before[0]:board;} 95 | #endif //ONLINE 96 | std::string repr() const; 97 | Board board; 98 | Board board_before[60]; 99 | int col_before[60], move_before[60]; 100 | int step,col; 101 | Game(){ step=col=0; board.setStart(); } 102 | bool testmove(int p){return p>=0 && p<64 && board.testmove(p);} 103 | void makemove(int p, bool autopass=1){ 104 | assertprintf(p>=0 && p<64, "invalid move at %d\n", p); 105 | assertprintf(board.testmove(p),"invalid move at %d\n", p); 106 | assertprintf(step<60, "makemove unbelievably outbound\n"); 107 | col_before[step]=col; 108 | board_before[step]=board; 109 | move_before[step]=p; 110 | step++; 111 | board.cmakemove(p); col=!col; 112 | if (autopass && !hasmove()) col=!col, board.cswap(); 113 | } 114 | void unmakemove(){ 115 | assertprintf(step>=0, "unmakemove outbound\n"); 116 | --step; 117 | board=board_before[step]; 118 | col=col_before[step]; 119 | } 120 | void reset(){ 121 | step=0, col=0; 122 | board.setStart(); 123 | } 124 | bool isend(){return !hasmove();} 125 | int cnt(int _col) const{return popcnt(board.b[col^_col]);} 126 | int winner() const{ 127 | if (cnt(PBLACK)>cnt(PWHITE)) return PBLACK; 128 | else if (cnt(PBLACK)=0 && p<64, "invalid pos call at [%d]\n", p); 136 | return board[p]; 137 | } 138 | }; 139 | -------------------------------------------------------------------------------- /tools/match.py: -------------------------------------------------------------------------------- 1 | # match.py 2 | # a simple match dispatcher between two bots, only support botzone keep running communication mode 3 | from subprocess import Popen, PIPE 4 | import time 5 | import argparse 6 | 7 | verbose=False 8 | 9 | def debugprint(str, always=False): 10 | if verbose or always: 11 | if type(str)==bytes: 12 | str=str.decode() 13 | print(str.strip()) 14 | 15 | def runGame(cmd_bot1, cmd_bot2, cmd_judger): 16 | try: 17 | p_judge=Popen(cmd_judger, stdin=PIPE, stdout=PIPE) 18 | p_bot1=Popen(cmd_bot1, stdin=PIPE, stdout=PIPE) 19 | p_bot2=Popen(cmd_bot2, stdin=PIPE, stdout=PIPE) 20 | 21 | p_bot1.stdin.write(b'1\n') 22 | p_bot2.stdin.write(b'1\n') 23 | 24 | while True: 25 | # read from judger 26 | debugprint('[judger]') 27 | s_judge_cmd=p_judge.stdout.readline() 28 | debugprint(s_judge_cmd) 29 | s_judge_data=p_judge.stdout.readline() 30 | debugprint(s_judge_data) 31 | if (s_judge_cmd.strip()==b'finish'): 32 | winner=s_judge_data 33 | debugprint(p_judge.stdout.readline(), True) # reason 34 | break 35 | 36 | # bot run 37 | p_bot1.stdin.write(s_judge_data) 38 | p_bot1.stdin.flush() 39 | debugprint('[bot1]') 40 | resp1 = p_bot1.stdout.readline() 41 | if verbose: 42 | debugprint(resp1) 43 | else: 44 | print(f'({resp1.strip().decode()})',end=' ', flush=True) 45 | 46 | debugprint(p_bot1.stdout.readline()) # debug 47 | while (p_bot1.stdout.readline().strip()!=b'>>>BOTZONE_REQUEST_KEEP_RUNNING<<<'): pass 48 | 49 | p_judge.stdin.write(resp1) 50 | p_judge.stdin.flush() 51 | 52 | # read from judger 53 | debugprint('[judger]') 54 | s_judge_cmd=p_judge.stdout.readline() 55 | debugprint(s_judge_cmd) 56 | s_judge_data=p_judge.stdout.readline() 57 | debugprint(s_judge_data) 58 | if (s_judge_cmd.strip()==b'finish'): 59 | winner=s_judge_data 60 | debugprint(p_judge.stdout.readline(), True) # reason 61 | break 62 | 63 | # bot run 64 | p_bot2.stdin.write(s_judge_data) 65 | p_bot2.stdin.flush() 66 | debugprint('[bot2]') 67 | resp2 = p_bot2.stdout.readline() 68 | if verbose: 69 | debugprint(resp2) 70 | else: 71 | print(f'({resp2.strip().decode()})',end=' ', flush=True) 72 | 73 | debugprint(p_bot2.stdout.readline()) # debug 74 | while (p_bot2.stdout.readline().strip()!=b'>>>BOTZONE_REQUEST_KEEP_RUNNING<<<'): pass 75 | 76 | p_judge.stdin.write(resp2) 77 | p_judge.stdin.flush() 78 | 79 | winner=int(winner.decode().strip()) 80 | print('winner: ', winner) 81 | return winner 82 | 83 | finally: 84 | p_judge.kill() 85 | p_bot1.kill() 86 | p_bot2.kill() 87 | 88 | def runTests(bot1, bot2, judger, n_game): 89 | win_black, draw_black = 0, 0 90 | win_white, draw_white = 0, 0 91 | 92 | for i in range(n_game): 93 | print("Game ", i) 94 | winner=runGame(bot1, bot2, judger) 95 | if winner==0: win_black+=1 96 | elif winner==2: draw_black+=1 97 | loss_black=n_game-win_black-draw_black 98 | 99 | for i in range(n_game): 100 | print("Game ", i) 101 | winner=runGame(bot2, bot1, judger) 102 | if winner==1: win_white+=1 103 | elif winner==2: draw_white+=1 104 | loss_white=n_game-win_white-draw_white 105 | 106 | with open("match.log", "a") as f: 107 | f.write(f"Match finished at {time.ctime(time.time())}\n") 108 | f.write(f"bot1:{bot1} bot2:{bot2}\n") 109 | f.write("bot1 | win | loss | draw | score\n") 110 | f.write(f"as_b | {win_black:^3d} | {loss_black:^4d} | {draw_black:^4d} | ") 111 | f.write(f"{win_black+draw_black/2:^4.1f} / {n_game}\n") 112 | f.write(f"as_w | {win_white:^3d} | {loss_white:^4d} | {draw_white:^4d} | ") 113 | f.write(f"{win_white+draw_white/2:^4.1f} / {n_game}\n") 114 | f.write(f"{win_black+draw_black/2+win_white+draw_white/2:^4.1f} / {n_game*2}\n") 115 | f.write('\n') 116 | 117 | if __name__ == '__main__': 118 | parser=argparse.ArgumentParser() 119 | parser.add_argument('bot1',default='botzone.exe') 120 | parser.add_argument('bot2',default='botzone.exe') 121 | parser.add_argument('n_game', default='50', help='games play each side') 122 | parser.add_argument('-j', '--judger', default='judger.exe') 123 | parser.add_argument('-v', '--verbose', action='store_true', help='show communication') 124 | args=parser.parse_args() 125 | if (args.verbose): 126 | verbose=True 127 | runTests(args.bot1, args.bot2, args.judger, int(args.n_game)) 128 | -------------------------------------------------------------------------------- /src/gendata.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include "board.h" 3 | #include "evalptn.h" 4 | #include "search.h" 5 | #include 6 | #include 7 | using std::cin, std::cout; 8 | using std::ifstream, std::ofstream; 9 | 10 | void gendata_PC(){ 11 | int n; cout<<"n_games(10-100): "; cin>>n; 12 | Game game; 13 | think_checktime=6000; 14 | think_maxtime=15000; 15 | search_delta=4; 16 | for (int i=0;i>std::hex>>b0>>b1>>std::dec>>val>>d1>>d>>cnt){ 35 | pcnt[cnt][d]++; 36 | fout<=p_begin && remain<=p_end && i%(p_end-p_begin+1)+p_begin==remain){ 61 | search_delta=0; 62 | think_maxd=12; 63 | think_choice(game.board); 64 | int val=searchstat.maxv; 65 | fout<=p_begin && remain<=p_end && i%(p_end-p_begin+1)+p_begin==remain){ 89 | search_delta=0.15; 90 | think_maxd=9; 91 | if (phase>5) think_maxd=8; 92 | Board bcur=game.board; int bccol=game.col; 93 | int val; 94 | while (popcnt(game.board.emptys())>16){ 95 | int mv=think_choice(game.board); 96 | game.makemove(mv); 97 | if (!game.hasmove()) goto fail; 98 | } 99 | search_delta=0; 100 | think_maxd=12; 101 | think_choice(game.board); 102 | val=searchstat.maxv; 103 | if (game.col!=bccol) val=-val; 104 | fout< \n"; 130 | cout<<"folder: "; cin>>foldername; 131 | cout<<"phase: "; cin>>phase; 132 | cout<<"game_cnt: "; cin>>game_cnt; 133 | } 134 | foldername=std::string("data/")+foldername+"/"; 135 | int p_begin=p_begins[phase]; 136 | int p_end=p_ends[phase]; 137 | ofstream fout(foldername+"data"+std::to_string(p_begin)+"_"+std::to_string(p_end)+".txt", std::ios::app); 138 | if (!fout.is_open()){ 139 | puts("file doesn't exist"); 140 | exit(1); 141 | } 142 | if (phase<5) gendata_endgame(fout, phase, game_cnt); 143 | else gendata_midgame(fout, phase, game_cnt); 144 | } 145 | 146 | int main(int argc, char **argv){ 147 | srand(time(0)); 148 | initPtnConfig(); 149 | loadPtnData(); 150 | 151 | #ifdef GENDATA_PC 152 | gendata_PC(); 153 | remakedata_PC(); 154 | #else 155 | gendata(argc, argv); 156 | #endif 157 | return 0; 158 | } 159 | -------------------------------------------------------------------------------- /src/evalptn.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include "evalptn.h" 3 | #include 4 | 5 | #define EVAL_FILE "data/rawdata2/reversicoeff.bin" 6 | 7 | constexpr int Eval_PrTable[61]={-1,-1,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8,8,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10}; 8 | 9 | unsigned short pow4to3_10[1<<20], pow4to3_9[1<<18]; 10 | 11 | struct CoeffPack{ 12 | short e1[pow3[10]], c52[pow3[10]], c33[pow3[9]], //by pow4to3 13 | e2[1<<16], e3[1<<16], e4[1<<16], k8[1<<16], k7[1<<14], k6[1<<12], k5[1<<10], k4[1<<8], //directly 14 | wb, wodd, wmob, wcinner[65], wcedge, wedgeodd, ccor[1<<8], cx22[1<<8]; 15 | }pdata[12]; 16 | 17 | void readShort(FILE *stream, short &tar){fread(&tar, 2, 1, stream);} 18 | 19 | int pow3to4(int len, int x){ 20 | int b=0, w=0; 21 | inc(i,len){ 22 | b<<=1; w<<=1; 23 | int p=x/pow3[i]%3; 24 | if (p==2) b|=1; 25 | if (p==1) w|=1; 26 | } 27 | return b< 2 | #include 3 | #include 4 | #include "util.h" 5 | #include "board.h" 6 | #include "evalptn.h" 7 | #include "search.h" 8 | #include "debugtree.h" 9 | using std::cout; 10 | 11 | void check(bool val, const char *str){ 12 | if (!val) printf("fail "); 13 | else printf("pass "); 14 | printf(str); puts(""); 15 | if (!val) exit(1); 16 | } 17 | #define CHECK(expr) check(expr, #expr) 18 | 19 | int t_start, t_end; 20 | #define TSCTEST(expr) \ 21 | asm volatile("rdtsc":"=A" (t_start)::"%rdx");\ 22 | expr; \ 23 | asm volatile("rdtsc":"=A" (t_end)::"%rdx");\ 24 | printf(#expr); printf(" TSC=%d\n",t_end-t_start); 25 | 26 | int runTests(){ 27 | //util.h 28 | CHECK(popcnt(~0)==64); 29 | CHECK(popcnt( 0)==0); 30 | CHECK(pext(~0,0xFF)==0xFF); 31 | CHECK(pext(0xFF00,~0)==0xFF00); 32 | 33 | u64 x=2; 34 | bts(x,45); 35 | CHECK(x==(1ull<<45|2)); 36 | btr(x,1); 37 | CHECK(x==(1ull<<45)); 38 | 39 | x|=2; 40 | int cnt=0; 41 | for (auto p:u64iter(x)){ 42 | if (cnt==0) CHECK(p==1); 43 | else if (cnt==1) CHECK(p==45); 44 | cnt++; 45 | } 46 | CHECK(cnt==2); 47 | 48 | CHECK(bget(x,45)==1); 49 | CHECK(bget(x,46)==0); 50 | 51 | //board.h, on start board 52 | Board board; 53 | board.setStart(); 54 | 55 | CHECK(popcnt(board.genmove())); 56 | CHECK(board.testmove(pos(5,4))); 57 | 58 | auto testmoves=[&](Board &b){ 59 | u64 x=0; 60 | for (int i=0;i<64;i++) 61 | if (b.testmove(i)) 62 | bts(x, i); 63 | return x; 64 | }; 65 | 66 | inc(i,5){ 67 | TSCTEST(inc(j,10) board.testmove(j)); //optimized out! 68 | } 69 | inc(i,5){ 70 | TSCTEST(inc(j,10) board.makemove(44)); 71 | } 72 | inc(i,5){ 73 | TSCTEST(inc(j,10) x=board.genmove()); 74 | } 75 | Board b2; b2.b[0]=rand(); b2.b[1]=rand(); 76 | inc(i,20){ 77 | b2.b[0]+=231689789646623;b2.b[1]+=123456123656123; 78 | b2.b[1]^=b2.b[0]&b2.b[1]; 79 | TSCTEST(evalPtn(b2)); 80 | } 81 | inc(i,5){ 82 | TSCTEST(inc(j,10) popcnt(board.genmove())); 83 | } 84 | CHECK(testmoves(board)==x); 85 | //CHECK(testmoves(board, PWHITE)==board.genmove(PWHITE)); 86 | 87 | board.cmakemove(44); 88 | CHECK(board.b[1]==0x101810000000); 89 | CHECK(board.b[0]==0x8000000); 90 | CHECK(board.genmove()==0x280020000000); 91 | 92 | Game game; 93 | game.makemove(pos(5,4)); 94 | CHECK(game.board==board); 95 | CHECK(board.genmove()==game.genmove()); 96 | 97 | //first game 98 | while (game.hasmove()){ 99 | auto firstmove=*u64iter(game.genmove()).begin(); 100 | game.makemove(firstmove); 101 | //CHECK(testmoves(game.board, game.col)==game.genmove()); 102 | } 103 | CHECK(game.board.b[0]==0xffd7ebf3f3fbffffu && 104 | game.board.b[1]==0x28140c0c040000u); 105 | 106 | //second game 107 | while (game.step) game.unmakemove(); 108 | while (game.hasmove()){ 109 | auto firstmove=*u64iter(game.genmove()).begin(); 110 | game.makemove(firstmove); 111 | //CHECK(testmoves(game.board, game.col)==game.genmove()); 112 | } 113 | if (game.col) game.board.cswap(); 114 | CHECK(game.board.b[0]==0x3fb0888090a0c080u && 115 | game.board.b[1]==0xc04f777f6f5f3f7fu); 116 | 117 | CHECK(pow3to4(10,59048)==0xFFC00); 118 | CHECK(pow4to3_10[0xFFC00]==59048); 119 | CHECK(pow3to4(9,19682)==0x3FE00); 120 | CHECK(pow4to3_9[0x3FE00]==19682); 121 | CHECK(pow3to4(9,17222)==0x2AAAA); 122 | CHECK(pow4to3_9[0x2AAAA]==17222); 123 | CHECK(pow3to4(9,50)==0x280A0); 124 | CHECK(pow4to3_9[0x280A0]==50); 125 | CHECK(pow3to4(9,17010)==0xA0A); 126 | CHECK(pow4to3_9[0xA0A]==17010); 127 | 128 | //bitwise transform 129 | x=0x1f1f; 130 | u64 x_fv=x; flip_v(x_fv); 131 | CHECK(x_fv==0xf8f8); 132 | u64 x_r90=x; rotate_r(x_r90); 133 | CHECK(x_r90==0xc0c0c0c0c0); 134 | u64 x_l90=x; rotate_l(x_l90); 135 | CHECK(x_l90==0x303030303000000); 136 | 137 | flip_h(x);flip_h(x_fv);flip_h(x_l90);flip_h(x_r90); 138 | CHECK(x==0x1f1f000000000000); 139 | CHECK(x_fv==0xf8f8000000000000); 140 | CHECK(x_l90==0x0000000303030303); 141 | CHECK(x_r90==0xc0c0c0c0c0000000); 142 | 143 | board.b[1]=0x1f19156f4d112100; board.b[0]=0xa0e6ea90b2eede9c; 144 | 145 | Board b1=board.cmakemove_r(pos(0,5)); 146 | b1.cmakemove(pos(0,0)); 147 | b1.cmakemove(pos(0,1)); 148 | CHECK(search_end2(b1)==16); 149 | CHECK(search_end2(b1.cswap_r())==-16); 150 | CHECK(search_exact(5, board, 1, -INF, INF)==-16); 151 | 152 | board.setStart(); 153 | cout<(Board(0x3160756b5d257f01,0xa9e8a94a2da80fe), -INF, INF, false)==-6); 156 | CHECK(search_exact(6, Board(0xff120c0d9fbfcdfc,0x6d737260403000), -INF, INF)==18); 157 | 158 | CHECK(think_choice(Board(0x3160752b1d051f01,0xa9e8ad4e2fa20fe))==58); 159 | 160 | #ifdef DEBUGTREE 161 | board.b[0]=0x3160756b5d257f01; 162 | board.b[1]=0xa9e8a94a2da80fe; 163 | debug_tree=new DebugTree; 164 | cout<<"log tree "<(board, -INF, INF, false)<<'\n'; 165 | debug_tree->write_html("debugtree.html", 6); 166 | delete debug_tree; debug_tree=nullptr; 167 | #endif 168 | 169 | printf("All test passed\n"); 170 | return 0; 171 | } 172 | 173 | void global_init(){ 174 | initPtnConfig(); 175 | loadPtnData(); 176 | loadPCData(); 177 | } 178 | 179 | void runDebugMode(){ 180 | Game game; 181 | std::string cmd; 182 | cout<<"> "; 183 | bool echo=true; 184 | auto displayGame=[&](){ 185 | cout<>cmd){ 188 | if (cmd=="p" || cmd=="print") displayGame(); 189 | else if (cmd=="ph" || cmd=="printhex") cout<>x>>y; 193 | if (game.testmove(pos(x,y))){ 194 | game.makemove(pos(x,y)); 195 | if (echo) displayGame(); 196 | } 197 | else 198 | puts("invalid move"); 199 | } 200 | else if (cmd=="u" || cmd=="undo" || cmd=="unmakemove"){ 201 | if (game.step){ 202 | game.unmakemove(); 203 | if (echo) displayGame(); 204 | } 205 | else puts("invalid"); 206 | } 207 | else if (cmd=="echo"){ 208 | echo=!echo; 209 | printf("echo %s\n", echo?"on":"off"); 210 | } 211 | else if (cmd=="evalptn"){ 212 | printf("evalptn: %f\n", evalPtn(game.board)/256.0); 213 | } 214 | else if (cmd=="tree"){ 215 | #ifdef DEBUGTREE 216 | int deep; std::cin>>deep; 217 | debug_tree=new DebugTree; 218 | if (game.isend()) puts("no move"); 219 | else{ 220 | search_root(deep, game.board, -1); 221 | printf("%s\n",searchstat.str().c_str()); 222 | } 223 | debug_tree->write_html("debugtree.html", 6); 224 | delete debug_tree; debug_tree=nullptr; 225 | #else 226 | printf("macro DEBUGTREE hasn't defined\n"); 227 | #endif 228 | } 229 | else if (cmd=="t" || cmd=="think"){ 230 | if (game.isend()) puts("no move"); 231 | else{ 232 | int p=think_choice(game.board); 233 | printf("%d %d\n", p/8, p%8); 234 | printf("%s\n", debugout.str().c_str()); 235 | } 236 | } 237 | else if (cmd=="td"){ 238 | if (game.isend()) puts("no move"); 239 | else{ 240 | std::cin>>std::dec>>think_maxd; 241 | int p=think_choice(game.board); 242 | printf("%d %d\n", p/8, p%8); 243 | printf("%s\n", debugout.str().c_str()); 244 | } 245 | } 246 | else if (cmd=="fl" || cmd=="flag"){ 247 | std::cin>>std::dec>>debug_flag; 248 | } 249 | else if (cmd=="pl" || cmd=="play"){ 250 | if (game.isend()) puts("no move"); 251 | else{ 252 | int p=think_choice(game.board); 253 | game.makemove(p); 254 | if (echo) displayGame(); 255 | } 256 | } 257 | else if (cmd=="cnt" || cmd=="count"){ 258 | printf("B:%2d W:%2d E:%2d\n", 259 | game.cnt(0), game.cnt(1), 64-game.cnt(0)-game.cnt(1)); 260 | } 261 | else if (cmd=="ld" || cmd=="board"){ 262 | u64 x, y; 263 | std::cin>>std::hex>>x>>y>>std::dec; 264 | if (x&y){ 265 | puts("invalid"); 266 | } 267 | else{ 268 | game.reset(); 269 | game.board.b[0]=x; 270 | game.board.b[1]=y; 271 | if (echo) displayGame(); 272 | } 273 | } 274 | else if (cmd=="flipv"){ 275 | game.board.flip_v(); 276 | if (echo) displayGame(); 277 | } 278 | else if (cmd=="fliph"){ 279 | game.board.flip_h(); 280 | if (echo) displayGame(); 281 | } 282 | else if (cmd=="rotatel"){ 283 | game.board.rotate_l(); 284 | if (echo) displayGame(); 285 | } 286 | else if (cmd=="rotater"){ 287 | game.board.rotate_r(); 288 | if (echo) displayGame(); 289 | } 290 | else if (cmd=="cswap"){ 291 | game.board.cswap(); 292 | if (echo) displayGame(); 293 | } 294 | else if (cmd=="fcol"){ 295 | game.col=!game.col; 296 | printf("now col: %d\n",game.col); 297 | } 298 | else if (cmd=="savesgf"){ 299 | std::string filename; std::cin>>filename; 300 | game.savesgf(filename+".sgf"); 301 | } 302 | else if (cmd=="delta"){ 303 | std::cin>>search_delta; 304 | } 305 | else if (cmd=="col"){ 306 | std::cin>>game.col; 307 | } 308 | else if (cmd=="deep"){ 309 | std::cin>>think_maxd; 310 | } 311 | else if (cmd=="reset"){ 312 | game.reset(); 313 | if (echo) displayGame(); 314 | } 315 | else{ 316 | puts("unknown command"); 317 | } 318 | cout<<"> "; 319 | } 320 | } 321 | 322 | int main(int argc, char **argv){ 323 | global_init(); 324 | if (argc==2) 325 | runTests(); 326 | else 327 | runDebugMode(); 328 | return 0; 329 | } -------------------------------------------------------------------------------- /src/search.cpp: -------------------------------------------------------------------------------- 1 | #include "search.h" 2 | #include "evalptn.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | u64 debug_flag; 15 | SearchStat searchstat, searchstat_sum; 16 | std::string SearchStat::str(){ 17 | std::ostringstream o; 18 | o<<"dep:"<:"; 26 | o<(p2) || board.makemove(p2)) v1=board.cnt0()*2; 53 | else v1=board.cnt0()*2+1; 54 | board=cboard; // rewind 55 | } 56 | if (board.makemove(p2)){ 57 | if (board.makemove<1>(p1) || board.makemove(p1)) v2=board.cnt0()*2; 58 | else v2=board.cnt0()*2+1; 59 | return std::max(v1,v2)-64; 60 | } 61 | if (likely(v1!=-INF)) return v1-64; 62 | //pass 63 | v1=INF; 64 | if (board.makemove<1>(p1)){ 65 | if (board.makemove(p2) || board.makemove<1>(p2)) v1=board.cnt0()*2; 66 | else v1=board.cnt0()*2+1; 67 | board=cboard; // rewind 68 | } 69 | if (board.makemove<1>(p2)){ 70 | if (board.makemove(p1) || board.makemove<1>(p1)) v2=board.cnt0()*2; 71 | else v2=board.cnt0()*2+1; 72 | return std::min(v1,v2)-64; 73 | } 74 | if (v1!=INF) return v1-64; 75 | return board.cnt0()*2-62; 76 | #ifdef DEBUGTREE 77 | DEBUGTREE_WARPPER_END 78 | #endif 79 | } 80 | 81 | Val search_exact(int depth, CBoard cboard, Val alpha, Val beta, bool pass){ 82 | #ifdef DEBUGTREE 83 | DEBUGTREE_WARPPER_BEGIN 84 | #endif 85 | u64 move=cboard.genmove(); 86 | if (!move){ 87 | if (pass) {searchstat.leafcnt++; return eval_end(cboard);} 88 | return -search_exact(depth, cboard.cswap_r(), -beta, -alpha, 1); 89 | } 90 | if (depth==5) return search_end<5>(cboard, alpha, beta, 0); 91 | Val val=-INF; 92 | for (auto p:u64iter(move)){ 93 | Board board=cboard.cmakemove_r(p); 94 | val=max(val, -search_exact(depth-1, board, -beta, -alpha, 0)); 95 | if (val>=beta) return val; 96 | alpha=max(alpha, val); 97 | } 98 | return val; 99 | #ifdef DEBUGTREE 100 | DEBUGTREE_WARPPER_END 101 | #endif 102 | } 103 | 104 | Val evalMidGame(CBoard cboard){return evalPtn(cboard)/256.0;} 105 | 106 | // special for depth == 0 or depth == 1 107 | Val search_normal1(int depth, CBoard cboard, Val alpha, Val beta, bool pass=0){ 108 | u64 move=cboard.genmove(); 109 | if (depth==0){ 110 | searchstat.leafcnt++; 111 | if (unlikely(!move)){ 112 | if (unlikely(!cboard.genmove<1>())) return eval_end(cboard); 113 | return -evalMidGame(cboard.cswap_r()); 114 | } 115 | return evalMidGame(cboard); 116 | } 117 | if (unlikely(!move)){ 118 | if (unlikely(pass)) {searchstat.leafcnt++; return eval_end(cboard);} 119 | return -search_normal1(depth, cboard.cswap_r(), -beta, -alpha, 1); 120 | } 121 | Val val=-INF; 122 | for (auto p:u64iter(move)){ 123 | searchstat.leafcnt++; 124 | Board board=cboard.cmakemove_r(p); 125 | Val ret; 126 | if (unlikely(!board.genmove())){ 127 | if (unlikely(!board.genmove<1>())) ret=-eval_end(board); 128 | else ret=evalMidGame(board.cswap_r()); 129 | } 130 | else ret=-evalMidGame(board); 131 | val=max(val,ret); 132 | if (val>=beta) return val; 133 | } 134 | return val; 135 | } 136 | 137 | struct PC_Param{ 138 | Val w, b, sigma; 139 | }pc_param[64][MPC_MAXD+1]; 140 | constexpr int pc_depth[MPC_MAXD+1]={0,0,0,1,2,1,2,3,4,5,6,5,6,5,6}; 141 | void loadPCData(){ 142 | std::ifstream fin("data/pc_coeff.txt"); 143 | inc(i,64) inc(j,MPC_MAXD+1){ 144 | PC_Param &pa=pc_param[i][j]; 145 | fin>>pa.w>>pa.b>>pa.sigma; 146 | } 147 | } 148 | #ifdef GENDATA_PC 149 | std::ofstream pc_data("data/pc_data.txt", std::ios::app); 150 | int pc_statecnt[64][15]; 151 | #endif //GENDATA_PC 152 | // #define USE_PC 153 | Val probcut(int depth, CBoard cboard, Val alpha, Val beta){ 154 | const Val t=1.4; 155 | Val bound, ret; 156 | int cnt=popcnt(cboard.occupys()); 157 | #ifdef GENDATA_PC 158 | pc_statecnt[cnt][depth]++; 159 | if (pc_statecnt[cnt][depth]>3 && rand()%(pc_statecnt[cnt][depth]-3)) return (alpha+beta)/2; 160 | ret=search_normal(pc_depth[depth], cboard, -INF, INF); 161 | pc_data<50) return (alpha+beta)/2; // no data 166 | bound=(t*pa.sigma+beta-pa.b)/pa.w; 167 | ret=search_normal(pc_depth[depth],cboard, bound-0.01, bound); 168 | if (ret>=bound) return beta; 169 | bound=(-t*pa.sigma+alpha-pa.b)/pa.w; 170 | ret=search_normal(pc_depth[depth],cboard, bound, bound+0.01); 171 | if (ret(cboard, alpha, beta, 0); 190 | 191 | // fast leaf search 192 | if (depth==1) return search_normal1(1, cboard, alpha, beta); 193 | 194 | int firstp=ctz(move); // firstp by default 195 | #if 1 196 | auto ttnode=translation_table + (cboard.hash()+depth)%(1<<20); 197 | Val alpha0=alpha, beta0=beta; 198 | if (ttnode->board==cboard){ 199 | firstp = ttnode->pv; 200 | assertprintf(move>>firstp&1, "invalid hash pv %d", firstp); 201 | searchstat.hashhit++; 202 | if (ttnode->depth==depth){ 203 | alpha=std::max(alpha, ttnode->alpha); 204 | beta=std::min(beta, ttnode->beta); 205 | if (alpha>=beta) return beta; 206 | } 207 | } 208 | #endif //1 209 | #ifdef USE_PC 210 | if (depth>=3 && depth<=MPC_MAXD){ 211 | Val val=probcut(depth, cboard, alpha, beta); 212 | if (val>=beta || val<=alpha) return val; 213 | } 214 | #endif //USE_PC 215 | // presearch 216 | if (depth>3){ 217 | auto t_move=move; 218 | btr(t_move, firstp); 219 | Val t_alpha; 220 | if (depth>5) t_alpha=-search_normal(depth-4, cboard.cmakemove_r(firstp), -INF, INF); 221 | else t_alpha=-search_normal1(depth-4, cboard.cmakemove_r(firstp), -INF, INF); 222 | for (auto p:u64iter(t_move)){ 223 | Val ret; 224 | if (depth>5) ret=-search_normal(depth-4, cboard.cmakemove_r(p), -INF, -t_alpha); 225 | else ret=-search_normal1(depth-4, cboard.cmakemove_r(p), -INF, -t_alpha); 226 | if (ret>t_alpha) t_alpha=ret, firstp=p; 227 | } 228 | } 229 | int pv = firstp; 230 | assertprintf(move>>firstp&1, "invalid pv %d", firstp); 231 | btr(move, firstp); 232 | Val val=-search_normal(depth-1, cboard.cmakemove_r(firstp), -beta, -alpha); 233 | if (val>alpha){ 234 | if (val>=beta) goto BETA_CUT; 235 | alpha=val; 236 | } 237 | for (auto p:u64iter(move)){ 238 | Board board=cboard.cmakemove_r(p); 239 | Val ret=-search_normal(depth-1, board, -alpha-0.01, -alpha); // zwsearch 240 | if (ret>alpha+0.005 && retalpha) alpha=ret; 243 | } 244 | if (ret>val){ 245 | val=ret; pv=p; 246 | if (val>=beta) goto BETA_CUT; 247 | } 248 | } 249 | BETA_CUT: 250 | if (unlikely(btimeout)) return 0; 251 | #if 1 252 | if (!(ttnode->board==cboard) || ttnode->depthboard=cboard; 254 | ttnode->depth=depth; 255 | ttnode->alpha=-INF; 256 | ttnode->beta=INF; 257 | ttnode->pv=pv; 258 | } 259 | if (val>=alpha0+0.001) ttnode->alpha=std::max(ttnode->alpha, val); 260 | if (val<=beta0-0.001) ttnode->beta=std::min(ttnode->beta, val); 261 | #endif //1 262 | return val; 263 | #ifdef DEBUGTREE 264 | DEBUGTREE_WARPPER_END 265 | #endif 266 | } 267 | 268 | int random_choice(CBoard board){ 269 | std::vector pos; 270 | for (auto p: u64iter(board.genmove())) 271 | pos.push_back(p); 272 | assertprintf(pos.size(), "nowhere to play\n"); 273 | return pos[rand()%pos.size()]; 274 | } 275 | 276 | std::ostringstream debugout; 277 | float search_delta=1.0; 278 | 279 | void search_exact_root(CBoard cboard){ 280 | searchstat.reset(popcnt(cboard.emptys())); 281 | #ifdef DEBUGTREE 282 | if (debug_tree) 283 | debug_tree->step_in(__func__,searchstat.depth, cboard, -INF, INF); 284 | #endif 285 | u64 move=cboard.genmove(); 286 | assertprintf(move, "nowhere to play\n"); 287 | std::vector result; 288 | Val alpha=-INF; 289 | for (auto p:u64iter(move)){ 290 | Board board=cboard.cmakemove_r(p); 291 | Val ret=-search_exact(searchstat.depth-1, board, -INF, -alpha+search_delta+0.01, 0); 292 | alpha=max(alpha, ret); 293 | if (ret>=alpha-search_delta) result.emplace_back(p, ret); 294 | } 295 | #ifdef DEBUGTREE 296 | if (debug_tree) debug_tree->step_out(alpha); 297 | #endif 298 | result.erase(std::remove_if(result.begin(),result.end(), 299 | [&](const auto &x){return x.secondstep_in(__func__,depth, cboard, -INF, INF); 311 | #endif 312 | searchstat.reset(depth); 313 | u64 move=cboard.genmove(); 314 | assertprintf(move, "nowhere to play\n"); 315 | std::vector result; 316 | Val alpha=-INF; 317 | if (suggestp!=-1){ 318 | btr(move, suggestp); 319 | alpha = -search_normal(depth-1, cboard.cmakemove_r(suggestp), -INF, -alpha+search_delta+0.01, 0); 320 | result.emplace_back(suggestp, alpha); 321 | } 322 | for (auto p:u64iter(move)){ 323 | Val ret=-search_normal(depth-1, cboard.cmakemove_r(p), -INF, -alpha+search_delta+0.01, 0); 324 | if (ret>alpha) alpha=ret, suggestp=p; 325 | if (ret>=alpha-search_delta) result.emplace_back(p, ret); 326 | } 327 | if (btimeout) return 0; 328 | #ifdef DEBUGTREE 329 | if (debug_tree) debug_tree->step_out(alpha); 330 | #endif 331 | result.erase( 332 | std::remove_if(result.begin(),result.end(),[&](const auto &x){return x.second 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | using std::string, std::cin, std::cout; 15 | 16 | //#define LOGISTIC 17 | 18 | typedef std::pair DataType; 19 | std::vector data, data_test; 20 | string folder; 21 | int phase; 22 | 23 | // type of data is double when training 24 | struct CoeffPack{ 25 | double e1[pow3[10]], c52[pow3[10]], c33[pow3[9]], //by pow4to3 26 | e2[1<<16], e3[1<<16], e4[1<<16], k8[1<<16], k7[1<<14], k6[1<<12], k5[1<<10], k4[1<<8], //directly 27 | wb, wodd, wmob, wcinner[65], wcedge, wedgeodd, ccor[1<<8], cx22[1<<8]; 28 | }ptn, ptngrad; 29 | const int p_begins[] = {2,5, 8,11,14,18,22,27,32,38,44}; 30 | const int p_ends[] = {4,7,10,13,17,21,26,31,37,43,50}; 31 | 32 | void writePtnData(){ 33 | FILE *out=fopen((string("data/")+folder+"/coeff"+std::to_string(p_begins[phase]) 34 | +"_"+std::to_string(p_ends[phase])+".bin").c_str(), "wb"); 35 | auto &p=ptn; 36 | double largest=0; 37 | auto ws=[&](double s){ 38 | if (s>largest) largest=s; 39 | auto x=short(s*256+0.5); 40 | fwrite(&x, 2, 1, out); 41 | }; 42 | ws(p.wb); 43 | ws(p.wodd); 44 | ws(p.wmob); 45 | ws(p.wcedge); 46 | ws(p.wedgeodd); 47 | inc(j,65) ws(p.wcinner[j]); 48 | inc(j,pow3[10]) ws(p.e1[j]); 49 | inc(j,pow3[10]) ws(p.c52[j]); 50 | inc(j,pow3[9]) ws(p.c33[j]); 51 | inc(j,pow3[8]) ws(p.e2[pow3to4(8,j)]); 52 | inc(j,pow3[8]) ws(p.e3[pow3to4(8,j)]); 53 | inc(j,pow3[8]) ws(p.e4[pow3to4(8,j)]); 54 | inc(j,pow3[8]) ws(p.k8[pow3to4(8,j)]); 55 | inc(j,pow3[7]) ws(p.k7[pow3to4(7,j)]); 56 | inc(j,pow3[6]) ws(p.k6[pow3to4(6,j)]); 57 | inc(j,pow3[5]) ws(p.k5[pow3to4(5,j)]); 58 | inc(j,pow3[4]) ws(p.k4[pow3to4(4,j)]); 59 | inc(j,pow3[4]) ws(p.ccor[pow3to4(4,j)]); 60 | inc(j,pow3[4]) ws(p.cx22[pow3to4(4,j)]); 61 | printf("largest coeff=%f\n", largest); 62 | fclose(out); 63 | } 64 | 65 | void loadData(){ 66 | string filename="data/"+folder+"/data"+ 67 | std::to_string(p_begins[phase])+"_"+std::to_string(p_ends[phase])+".txt"; 68 | std::ifstream fin(filename); 69 | if (!fin.is_open()){ 70 | cout<<"file "<>std::hex>>b.b[0]>>b.b[1]){ 75 | fin>>val; 76 | data.emplace_back(b, val); 77 | } 78 | } 79 | double sigmoid(double x){ 80 | return 1/(1+exp(-x)); 81 | } 82 | double evalptn(const Board &board){ 83 | using namespace bitptn; 84 | int empty_cnt = popcnt(board.emptys()); 85 | auto &p=ptn; 86 | 87 | Board b_id=board; 88 | 89 | double score=p.wb; //constant 90 | int cmob = popcnt(b_id.genmove()); 91 | int codd = empty_cnt%2; 92 | score += p.wodd*codd; 93 | score += p.wmob*cmob; 94 | score += p.wcinner[popcnt(b_id.b[0]&pinner)-popcnt(b_id.b[1]&pinner)+32]; 95 | int cedge=popcnt(b_id.b[0]&pedge)-popcnt(b_id.b[1]&pedge); 96 | score += p.wcedge*cedge; 97 | score += p.wedgeodd*(cedge%2); 98 | 99 | u64 x; 100 | #define OP_EXT(brd, mask, len)\ 101 | x=(pext(brd.b[0], mask)<0.5, fx=evalptn(d.first); 271 | return y*log(fx)+(1-y)*log(1-fx); 272 | } 273 | double PredictAcc(DataType &d){ 274 | double y=d.second>0.5, fx=evalptn(d.first); 275 | return ((y>0.5)^(fx<0.5))*100; 276 | } 277 | std::mt19937 rng((std::random_device())()); 278 | auto testError(){ 279 | #ifndef LOGISTIC 280 | double sse=0,sae=0, mae=0; 281 | for (auto &d:data_test){ 282 | sse+=SE(d)/data_test.size(); 283 | sae+=AE(d)/data_test.size(); 284 | mae=std::max(mae,AE(d)); 285 | } 286 | return std::make_tuple(mae, sse, sae); 287 | #else 288 | double sce=0, acc=0; 289 | for (auto &d:data_test){ 290 | sce+=CrossEntropy(d)/data_test.size(); 291 | acc+=PredictAcc(d)/data_test.size(); 292 | } 293 | return std::make_tuple(sce, acc); 294 | #endif 295 | } 296 | void updateArgs(){ 297 | for (size_t i=0;i0.5; 304 | } 305 | void accuGrad(DataType &d, int batch_size, double lr){ 306 | auto [board, val]=d; 307 | auto fx=evalptn(board); 308 | double delta=val-fx; 309 | 310 | #ifdef LOGISTIC // logistic regression 311 | delta=(val>0.5)-evalptn(board); 312 | #endif 313 | delta/=8 * batch_size; 314 | delta*=lr; // learning rate 315 | 316 | Board b_cur=board; adjustptn(b_cur, delta); 317 | b_cur.flip_h(); adjustptn(b_cur, delta); 318 | b_cur=board; b_cur.flip_v(); adjustptn(b_cur, delta); 319 | b_cur.flip_h(); adjustptn(b_cur, delta); 320 | b_cur=board; b_cur.rotate_l(); adjustptn(b_cur, delta); 321 | b_cur.flip_h(); adjustptn(b_cur, delta); 322 | b_cur=board; b_cur.rotate_r(); adjustptn(b_cur, delta); 323 | b_cur.flip_h(); adjustptn(b_cur, delta); 324 | } 325 | 326 | FILE *flog; 327 | 328 | void train(){ 329 | int batch_size=512; 330 | int train_id=0; 331 | double lr; 332 | const int train_steps=50000; 333 | const int lr_decay_st=20000; 334 | double lr_max=0.2, lr_min=0.03; 335 | cout<<"lr_max(default 0.2): "; cin>>lr_max; 336 | fprintf(flog, "batch=%d lr_decay_st=%d lr_max=%f lr_min=%f\n", train_steps, lr_decay_st, lr_max, lr_min); 337 | inc(i, train_steps){ 338 | if (i<1000) lr=lr_min; //warming up 339 | else if (i0.5) ^ (d.second<0.5)) acc+=100; 422 | } 423 | printf("predict acc: %.2f%%\n", acc/data_test.size()); 424 | fprintf(flog, "predict acc: %.2f%%\n", acc/data_test.size()); 425 | #endif 426 | 427 | #ifndef LOGISTIC 428 | // outer point 429 | std::vector> temp; 430 | int maxf_id=0; double maxf_v=0; 431 | int _cnt=0; 432 | for (auto &d:data_test){ 433 | temp.emplace_back(AE(d), _cnt); 434 | double val=evalptn(d.first); 435 | if (fabs(val)>maxf_v) maxf_v=fabs(val), maxf_id=_cnt; 436 | _cnt++; 437 | } 438 | printf("maxv r=%.2f t=%.2f board %s\n",data_test[maxf_id].second, 439 | evalptn(data_test[maxf_id].first), data_test[maxf_id].first.repr().c_str()); 440 | std::sort(temp.begin(), temp.end(), std::greater>()); 441 | printf("outer point:\n"); 442 | for (int i=0;i<5;i++){ 443 | int id=temp[i].second; 444 | auto &[board, val]=data_test[id]; 445 | printf("r=%.2f t=%.2f board %s\n", val, evalptn(board), board.repr().c_str()); 446 | } 447 | #endif 448 | } 449 | 450 | void train_op(){ 451 | cout<< "phase: "; cin>>phase; 452 | loadData(); 453 | flog=fopen((string("data/")+folder+"/trainlog.log").c_str(),"a"); 454 | fputs("+ pcinner pcedge ccor cx22\n", flog); 455 | fprintf(flog, "phase: %d (%d_%d) ", phase, p_begins[phase], p_ends[phase]); 456 | time_t t=time(0); 457 | fprintf(flog, " time: %s", ctime(&t)); 458 | cout<>folder; 479 | 480 | int datapack_len=0; 481 | for (auto x:plen) if (x<=10) datapack_len+=pow3[x]; else datapack_len+=x; 482 | cout<<"datapack len: "<>merge_begin; 486 | cout<<"merge_end(default 10): "; cin>>merge_end; 487 | 488 | FILE *in[11], *out; 489 | for (int i=merge_begin; i<=merge_end;i++){ 490 | string filename=string("data/")+folder+"/coeff"+ 491 | std::to_string(p_begins[i])+"_"+std::to_string(p_ends[i])+".bin"; 492 | in[i]=fopen(filename.c_str(),"rb"); 493 | cout<<"open phase "< 6 | 7 | #define likely(x) __builtin_expect(!!(x), 1) 8 | #define unlikely(x) __builtin_expect(!!(x), 0) 9 | #define PBLACK 0 10 | #define PWHITE 1 11 | #define inc(i,n) for(int i=0;i> 1 | (x & 0x5555555555555555) << 1; 51 | x = (x & 0xcccccccccccccccc) >> 2 | (x & 0x3333333333333333) << 2; 52 | x = (x & 0xf0f0f0f0f0f0f0f0) >> 4 | (x & 0x0f0f0f0f0f0f0f0f) << 4; 53 | } 54 | inline void rotate_r(u64 &x){ 55 | x = (x & 0xf0f0f0f000000000) >> 4 | (x & 0x0f0f0f0f00000000) >> 32 56 | | (x & 0x00000000f0f0f0f0) << 32 | (x & 0x000000000f0f0f0f) << 4; 57 | x = (x & 0xcccc0000cccc0000) >> 2 | (x & 0x3333000033330000) >> 16 58 | | (x & 0x0000cccc0000cccc) << 16 | (x & 0x0000333300003333) << 2; 59 | x = (x & 0xaa00aa00aa00aa00) >> 1 | (x & 0x5500550055005500) >> 8 60 | | (x & 0x00aa00aa00aa00aa) << 8 | (x & 0x0055005500550055) << 1; 61 | } 62 | inline void rotate_l(u64 &x){ 63 | x = (x & 0xf0f0f0f000000000) >> 32 | (x & 0x0f0f0f0f00000000) << 4 64 | | (x & 0x00000000f0f0f0f0) >> 4 | (x & 0x000000000f0f0f0f) << 32; 65 | x = (x & 0xcccc0000cccc0000) >> 16 | (x & 0x3333000033330000) << 2 66 | | (x & 0x0000cccc0000cccc) >> 2 | (x & 0x0000333300003333) << 16; 67 | x = (x & 0xaa00aa00aa00aa00) >> 8 | (x & 0x5500550055005500) << 1 68 | | (x & 0x00aa00aa00aa00aa) >> 1 | (x & 0x0055005500550055) << 8; 69 | } 70 | struct u64iter{ 71 | u64 x; 72 | u64iter(u64 x):x(x){} 73 | struct iter; 74 | iter begin(); 75 | iter end(); 76 | }; 77 | struct u64iter::iter{ 78 | u64iter x; 79 | int operator*() const{return ctz(x.x);} 80 | bool operator!=(const iter &v) const{return x.x!=v.x.x;} 81 | iter &operator++(){ 82 | x.x=blsr(x.x); 83 | return *this; 84 | } 85 | }; 86 | inline u64iter::iter u64iter::begin(){return {*this};} 87 | inline u64iter::iter u64iter::end(){return {u64iter(0)};} 88 | namespace bitptn{ 89 | template 90 | constexpr auto make_array_helper(Function f, std::index_sequence) 91 | -> std::array::type, sizeof...(Indices)> { 92 | return {{ f(Indices)... }}; 93 | } 94 | template 95 | constexpr auto make_array(Function f) 96 | -> std::array::type, N>{ 97 | return make_array_helper(f, std::make_index_sequence{}); 98 | } 99 | #define CEXPR_ARRAY_DEF(name, N, expr) \ 100 | constexpr static auto f##name(int x){return (expr);}\ 101 | constexpr auto name = make_array(f##name) 102 | CEXPR_ARRAY_DEF(h, 8, 0xffull<<(x*8)); 103 | CEXPR_ARRAY_DEF(v, 8, 0x101010101010101ull<7?0x8040201008040201u<<((x-7)*8):0x8040201008040201u>>((7-x)*8)); 105 | CEXPR_ARRAY_DEF(d2, 15, x>7?0x0102040810204080u<<((x-7)*8):0x0102040810204080u>>((7-x)*8)); 106 | #undef CEXPR_ARRAY_DEF 107 | constexpr u64 edge2x=0x42ff, c33=0x70707, c52=0x1f1f, ccor=0x8100000000000081, 108 | cx22=0x0042000000004200, pedge=0x7e8181818181817e, pinner=0x003c7e7e7e7e3c00; 109 | } 110 | class Board{ 111 | public: 112 | u64 b[2]; 113 | Board(){} 114 | Board(u64 _0, u64 _1):b{_0,_1}{} 115 | void setStart(){b[0]=0x810000000,b[1]=0x1008000000;} 116 | bool operator==(const Board &v) const{return b[0]==v.b[0] && b[1]==v.b[1];} 117 | std::string repr() const; 118 | template u64 genmove() const{ 119 | cu64 b_cur = b[col], b_opp = b[!col]; 120 | u64 b_opp_inner = b_opp & 0x7E7E7E7E7E7E7E7Eu; 121 | u64 moves = 0; 122 | u64 b_flip, b_opp_adj; 123 | #define GENMOVE(arrow, d, opp) \ 124 | b_flip = (b_cur arrow d) & opp; \ 125 | b_flip |= (b_flip arrow d) & opp; \ 126 | b_opp_adj = opp & (opp arrow d); \ 127 | b_flip |= (b_flip arrow (d+d)) & b_opp_adj; \ 128 | b_flip |= (b_flip arrow (d+d)) & b_opp_adj; \ 129 | moves |= b_flip arrow d 130 | GENMOVE(>>, 1, b_opp_inner); GENMOVE(<<, 1, b_opp_inner); 131 | GENMOVE(>>, 8, b_opp); GENMOVE(<<, 8, b_opp); 132 | GENMOVE(>>, 7, b_opp_inner); GENMOVE(<<, 7, b_opp_inner); 133 | GENMOVE(>>, 9, b_opp_inner); GENMOVE(<<, 9, b_opp_inner); 134 | #undef GENMOVE 135 | return moves & ~(b_cur | b_opp); 136 | } 137 | bool testmove(int p) const{return bget(genmove(),p);} 138 | template bool makemove(int p){ 139 | using namespace bitptn; 140 | u64 &b_cur = b[col]; u64 &b_opp = b[!col]; 141 | u64 b_flip, b_opp_adj, flips = 0; 142 | u64 b_opp_inner = b_opp & 0x7E7E7E7E7E7E7E7Eu; 143 | #define GENMOVE(arrow, d, opp) \ 144 | b_flip = ((1ull<>, 1, b_opp_inner); GENMOVE(<<, 1, b_opp_inner); 151 | GENMOVE(>>, 8, b_opp); GENMOVE(<<, 8, b_opp); 152 | GENMOVE(>>, 7, b_opp_inner); GENMOVE(<<, 7, b_opp_inner); 153 | GENMOVE(>>, 9, b_opp_inner); GENMOVE(<<, 9, b_opp_inner); 154 | #undef GENMOVE 155 | if(flips) b_cur^=flips, b_opp^=flips, bts(b_cur, p); 156 | return flips; 157 | } 158 | bool cmakemove(int p){cswap();return makemove<1>(p);} 159 | Board makemove_r(int p) const{Board r=*this;r.makemove(p);return r;} 160 | Board cmakemove_r(int p) const{Board r(b[1],b[0]);r.makemove<1>(p);return r;} 161 | int cnt0() const{return popcnt(b[0]);} 162 | int cnt1() const{return popcnt(b[1]);} 163 | u64 hash() const{return ((b[0]*19260817)^b[1])%998244353;} 164 | Board cswap_r() const{return Board(b[1],b[0]);} 165 | void cswap(){std::swap(b[0],b[1]);} 166 | void flip_h(){::flip_h(b[0]);::flip_h(b[1]);} 167 | void flip_v(){::flip_v(b[0]);::flip_v(b[1]);} 168 | void rotate_l(){::rotate_l(b[0]);::rotate_l(b[1]);} 169 | void rotate_r(){::rotate_r(b[0]);::rotate_r(b[1]);} 170 | u64 emptys() const{return ~(b[0]|b[1]);} 171 | u64 occupys() const{return b[0]|b[1];} 172 | int operator[](int p) const{return 2-2*bget(b[0], p)-bget(b[1], p);} 173 | }; 174 | typedef const Board &CBoard; 175 | class Game{ 176 | public: 177 | std::string repr() const; 178 | Board board; 179 | Board board_before[60]; 180 | int col_before[60], move_before[60]; 181 | int step,col; 182 | Game(){ step=col=0; board.setStart(); } 183 | bool testmove(int p){return p>=0 && p<64 && board.testmove(p);} 184 | void makemove(int p, bool autopass=1){ 185 | col_before[step]=col; 186 | board_before[step]=board; 187 | move_before[step]=p; 188 | step++; 189 | board.cmakemove(p); col=!col; 190 | if (autopass && !hasmove()) col=!col, board.cswap(); 191 | } 192 | void unmakemove(){ 193 | --step; 194 | board=board_before[step]; 195 | col=col_before[step]; 196 | } 197 | void reset(){ 198 | step=0, col=0; 199 | board.setStart(); 200 | } 201 | bool isend(){return !hasmove();} 202 | int cnt(int _col) const{return popcnt(board.b[col^_col]);} 203 | int winner() const{ 204 | if (cnt(PBLACK)>cnt(PWHITE)) return PBLACK; 205 | else if (cnt(PBLACK) PosVal; 359 | Val search_end2(CBoard board); 360 | inline Val eval_end(CBoard board){return board.cnt0()-popcnt(board.b[1]);} 361 | template 362 | Val search_end(CBoard cboard, Val alpha, Val beta, bool pass){ 363 | u64 move=cboard.genmove(); 364 | if (!move){ 365 | if (pass) return eval_end(cboard); 366 | return -search_end(cboard.cswap_r(), -beta, -alpha, 1); 367 | } 368 | Val val=-INF; 369 | for (auto p:u64iter(move)){ 370 | if constexpr (depth==3) val=max(val, -search_end2(cboard.cmakemove_r(p))); 371 | else val=max(val, -search_end(cboard.cmakemove_r(p), -beta, -alpha, 0)); 372 | if (val>=beta) return val; 373 | if (val>alpha) alpha=val; 374 | } 375 | return val; 376 | } 377 | struct SearchStat{ 378 | int depth, leafcnt; 379 | int t_start, tl; 380 | int hashhit; 381 | std::vector pv; 382 | Val maxv; 383 | void timing(){ 384 | tl=clock()-t_start; 385 | t_start=clock(); 386 | } 387 | void reset(int _depth){leafcnt=hashhit=0; depth=_depth; t_start=clock(); maxv=-INF;} 388 | std::string str(); 389 | }; 390 | int search_root(int depth, CBoard cboard, int suggestp=-1); 391 | Val search_normal(int depth, CBoard cboard, Val alpha, Val beta, bool pass=0); 392 | constexpr int MPC_MAXD=14; 393 | u64 debug_flag; 394 | SearchStat searchstat, searchstat_sum; 395 | std::string SearchStat::str(){ 396 | std::ostringstream o; 397 | o<<"dep:"<:"; 405 | o<(p2) || board.makemove(p2)) v1=board.cnt0()*2; 425 | else v1=board.cnt0()*2+1; 426 | board=cboard; 427 | } 428 | if (board.makemove(p2)){ 429 | if (board.makemove<1>(p1) || board.makemove(p1)) v2=board.cnt0()*2; 430 | else v2=board.cnt0()*2+1; 431 | return std::max(v1,v2)-64; 432 | } 433 | if (likely(v1!=-INF)) return v1-64; 434 | v1=INF; 435 | if (board.makemove<1>(p1)){ 436 | if (board.makemove(p2) || board.makemove<1>(p2)) v1=board.cnt0()*2; 437 | else v1=board.cnt0()*2+1; 438 | board=cboard; 439 | } 440 | if (board.makemove<1>(p2)){ 441 | if (board.makemove(p1) || board.makemove<1>(p1)) v2=board.cnt0()*2; 442 | else v2=board.cnt0()*2+1; 443 | return std::min(v1,v2)-64; 444 | } 445 | if (v1!=INF) return v1-64; 446 | return board.cnt0()*2-62; 447 | } 448 | Val search_exact(int depth, CBoard cboard, Val alpha, Val beta, bool pass){ 449 | u64 move=cboard.genmove(); 450 | if (!move){ 451 | if (pass) {searchstat.leafcnt++; return eval_end(cboard);} 452 | return -search_exact(depth, cboard.cswap_r(), -beta, -alpha, 1); 453 | } 454 | if (depth==5) return search_end<5>(cboard, alpha, beta, 0); 455 | Val val=-INF; 456 | for (auto p:u64iter(move)){ 457 | Board board=cboard.cmakemove_r(p); 458 | val=max(val, -search_exact(depth-1, board, -beta, -alpha, 0)); 459 | if (val>=beta) return val; 460 | alpha=max(alpha, val); 461 | } 462 | return val; 463 | } 464 | Val evalMidGame(CBoard cboard){return evalPtn(cboard)/256.0;} 465 | Val search_normal1(int depth, CBoard cboard, Val alpha, Val beta, bool pass=0){ 466 | u64 move=cboard.genmove(); 467 | if (depth==0){ 468 | searchstat.leafcnt++; 469 | if (unlikely(!move)){ 470 | if (unlikely(!cboard.genmove<1>())) return eval_end(cboard); 471 | return -evalMidGame(cboard.cswap_r()); 472 | } 473 | return evalMidGame(cboard); 474 | } 475 | if (unlikely(!move)){ 476 | if (unlikely(pass)) {searchstat.leafcnt++; return eval_end(cboard);} 477 | return -search_normal1(depth, cboard.cswap_r(), -beta, -alpha, 1); 478 | } 479 | Val val=-INF; 480 | for (auto p:u64iter(move)){ 481 | searchstat.leafcnt++; 482 | Board board=cboard.cmakemove_r(p); 483 | Val ret; 484 | if (unlikely(!board.genmove())){ 485 | if (unlikely(!board.genmove<1>())) ret=-eval_end(board); 486 | else ret=evalMidGame(board.cswap_r()); 487 | } 488 | else ret=-evalMidGame(board); 489 | val=max(val,ret); 490 | if (val>=beta) return val; 491 | } 492 | return val; 493 | } 494 | struct PC_Param{ 495 | Val w, b, sigma; 496 | }pc_param[64][MPC_MAXD+1]; 497 | constexpr int pc_depth[MPC_MAXD+1]={0,0,0,1,2,1,2,3,4,5,6,5,6,5,6}; 498 | void loadPCData(){ 499 | std::ifstream fin("data/pc_coeff.txt"); 500 | inc(i,64) inc(j,MPC_MAXD+1){ 501 | PC_Param &pa=pc_param[i][j]; 502 | fin>>pa.w>>pa.b>>pa.sigma; 503 | } 504 | } 505 | Val probcut(int depth, CBoard cboard, Val alpha, Val beta){ 506 | const Val t=1.4; 507 | Val bound, ret; 508 | int cnt=popcnt(cboard.occupys()); 509 | PC_Param &pa=pc_param[cnt][depth]; 510 | if (pa.sigma>50) return (alpha+beta)/2; 511 | bound=(t*pa.sigma+beta-pa.b)/pa.w; 512 | ret=search_normal(pc_depth[depth],cboard, bound-0.01, bound); 513 | if (ret>=bound) return beta; 514 | bound=(-t*pa.sigma+alpha-pa.b)/pa.w; 515 | ret=search_normal(pc_depth[depth],cboard, bound, bound+0.01); 516 | if (ret(cboard, alpha, beta, 0); 531 | if (depth==1) return search_normal1(1, cboard, alpha, beta); 532 | int firstp=ctz(move); 533 | auto ttnode=translation_table + (cboard.hash()+depth)%(1<<20); 534 | Val alpha0=alpha, beta0=beta; 535 | if (ttnode->board==cboard){ 536 | firstp = ttnode->pv; 537 | searchstat.hashhit++; 538 | if (ttnode->depth==depth){ 539 | alpha=std::max(alpha, ttnode->alpha); 540 | beta=std::min(beta, ttnode->beta); 541 | if (alpha>=beta) return beta; 542 | } 543 | } 544 | #ifdef USE_PC 545 | if (depth>=3 && depth<=MPC_MAXD){ 546 | Val val=probcut(depth, cboard, alpha, beta); 547 | if (val>=beta || val<=alpha) return val; 548 | } 549 | #endif 550 | if (depth>3){ 551 | auto t_move=move; 552 | btr(t_move, firstp); 553 | Val t_alpha; 554 | if (depth>5) t_alpha=-search_normal(depth-4, cboard.cmakemove_r(firstp), -INF, INF); 555 | else t_alpha=-search_normal1(depth-4, cboard.cmakemove_r(firstp), -INF, INF); 556 | for (auto p:u64iter(t_move)){ 557 | Val ret; 558 | if (depth>5) ret=-search_normal(depth-4, cboard.cmakemove_r(p), -INF, -t_alpha); 559 | else ret=-search_normal1(depth-4, cboard.cmakemove_r(p), -INF, -t_alpha); 560 | if (ret>t_alpha) t_alpha=ret, firstp=p; 561 | } 562 | } 563 | int pv = firstp; 564 | btr(move, firstp); 565 | Val val=-search_normal(depth-1, cboard.cmakemove_r(firstp), -beta, -alpha); 566 | if (val>alpha){ 567 | if (val>=beta) goto BETA_CUT; 568 | alpha=val; 569 | } 570 | for (auto p:u64iter(move)){ 571 | Board board=cboard.cmakemove_r(p); 572 | Val ret=-search_normal(depth-1, board, -alpha-0.01, -alpha); 573 | if (ret>alpha+0.005 && retalpha) alpha=ret; 576 | } 577 | if (ret>val){ 578 | val=ret; pv=p; 579 | if (val>=beta) goto BETA_CUT; 580 | } 581 | } 582 | BETA_CUT: 583 | if (unlikely(btimeout)) return 0; 584 | if (!(ttnode->board==cboard) || ttnode->depthboard=cboard; 586 | ttnode->depth=depth; 587 | ttnode->alpha=-INF; 588 | ttnode->beta=INF; 589 | ttnode->pv=pv; 590 | } 591 | if (val>=alpha0+0.001) ttnode->alpha=std::max(ttnode->alpha, val); 592 | if (val<=beta0-0.001) ttnode->beta=std::min(ttnode->beta, val); 593 | return val; 594 | } 595 | int random_choice(CBoard board){ 596 | std::vector pos; 597 | for (auto p: u64iter(board.genmove())) 598 | pos.push_back(p); 599 | return pos[rand()%pos.size()]; 600 | } 601 | std::ostringstream debugout; 602 | float search_delta=1.0; 603 | void search_exact_root(CBoard cboard){ 604 | searchstat.reset(popcnt(cboard.emptys())); 605 | u64 move=cboard.genmove(); 606 | std::vector result; 607 | Val alpha=-INF; 608 | for (auto p:u64iter(move)){ 609 | Board board=cboard.cmakemove_r(p); 610 | Val ret=-search_exact(searchstat.depth-1, board, -INF, -alpha+search_delta+0.01, 0); 611 | alpha=max(alpha, ret); 612 | if (ret>=alpha-search_delta) result.emplace_back(p, ret); 613 | } 614 | result.erase(std::remove_if(result.begin(),result.end(), 615 | [&](const auto &x){return x.second result; 626 | Val alpha=-INF; 627 | if (suggestp!=-1){ 628 | btr(move, suggestp); 629 | alpha = -search_normal(depth-1, cboard.cmakemove_r(suggestp), -INF, -alpha+search_delta+0.01, 0); 630 | result.emplace_back(suggestp, alpha); 631 | } 632 | for (auto p:u64iter(move)){ 633 | Val ret=-search_normal(depth-1, cboard.cmakemove_r(p), -INF, -alpha+search_delta+0.01, 0); 634 | if (ret>alpha) alpha=ret, suggestp=p; 635 | if (ret>=alpha-search_delta) result.emplace_back(p, ret); 636 | } 637 | if (btimeout) return 0; 638 | result.erase( 639 | std::remove_if(result.begin(),result.end(),[&](const auto &x){return x.second>>BOTZONE_REQUEST_KEEP_RUNNING<<<\n"); 702 | fflush(stdout); 703 | } 704 | return 0; 705 | } 706 | -------------------------------------------------------------------------------- /tools/linreg_old.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | bwcore1.4 coeff linear regression code 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | using namespace std; 17 | 18 | typedef long long ll; 19 | typedef unsigned long long ull; 20 | typedef char Bit; 21 | 22 | ull popcount(ull x) 23 | { 24 | x = (x & 0x5555555555555555ull) + ((x >> 1) & 0x5555555555555555ull); 25 | x = (x & 0x3333333333333333ull) + ((x >> 2) & 0x3333333333333333ull); 26 | x = (x & 0x0F0F0F0F0F0F0F0Full) + ((x >> 4) & 0x0F0F0F0F0F0F0F0Full); 27 | x = (x & 0x00FF00FF00FF00FFull) + ((x >> 8) & 0x00FF00FF00FF00FFull); 28 | x = (x & 0x0000FFFF0000FFFFull) + ((x >> 16) & 0x0000FFFF0000FFFFull); 29 | x = (x & 0x00000000FFFFFFFFull) + ((x >> 32) & 0x00000000FFFFFFFFull); 30 | return x; 31 | } 32 | 33 | inline void setbit(ull &x, ull c, Bit p) 34 | { 35 | x = (x&~(1ull << p)) | (c << p); 36 | } 37 | 38 | inline ull lowbit(ull x) 39 | { 40 | return x&(-x); 41 | } 42 | 43 | /* 44 | bwcore1.3 45 | made by fffasttime 46 | start at 2016/7/7 47 | last modify to 2016/7/15 48 | a naive black_white chess AI 49 | Maxminsearch 50 | */ 51 | namespace bwcore 52 | { 53 | 54 | #define FLOAT_INF (1e10) 55 | 56 | typedef Bit Col; 57 | #define C_E 0 58 | #define C_W 1 59 | #define C_B 2 60 | 61 | inline Col col_f(Col col) { 62 | return col % 2 + 1; 63 | } 64 | 65 | /* 66 | 0 1 2 3 4 5 6 7 67 | 8 9 10 11 12 13 14 15 68 | 16 17 18 19 20 21 22 23 69 | 24 25 26 27 28 29 30 31 70 | 32 33 34 35 36 37 38 39 71 | 40 41 42 43 44 45 46 47 72 | 48 49 50 51 52 53 54 55 73 | 56 57 58 59 60 61 62 63 74 | */ 75 | typedef Bit MP; 76 | const MP MP_F = 0, MP_E = 64; 77 | const MP MP_UL = -9, MP_U = -8, MP_UR = -7, MP_L = -1, MP_R = 1, MP_DL = 7, MP_D = 8, MP_DR = 9; 78 | 79 | struct Ploc 80 | { 81 | Bit x, y; 82 | Ploc() {} 83 | Ploc(MP mp) { 84 | x = mp / 8; y = mp % 8; 85 | } 86 | Ploc(Bit _x, Bit _y) :x(_x), y(_y) {} 87 | MP toMP() { 88 | return x * 8 + y; 89 | } 90 | Ploc& operator+=(const Ploc &p1) 91 | { 92 | x += p1.x; 93 | y += p1.y; 94 | return *this; 95 | } 96 | Ploc operator+(const Ploc &p1) const 97 | { 98 | return Ploc(x + p1.x, y + p1.y); 99 | } 100 | Ploc& operator++() 101 | { 102 | if (y == 8) x++; 103 | else y++; 104 | return *this; 105 | } 106 | Ploc& operator-=(const Ploc &p1) 107 | { 108 | x -= p1.x; y -= p1.y; 109 | return *this; 110 | } 111 | Ploc operator-(const Ploc &p1) const 112 | { 113 | return Ploc(x - p1.x, y - p1.y); 114 | } 115 | bool operator==(const Ploc &p) const 116 | { 117 | return x == p.x && y == p.y; 118 | } 119 | bool operator!=(const Ploc &p) const 120 | { 121 | return x != p.x || y != p.y; 122 | } 123 | friend std::ostream& operator<<(std::ostream& i, const Ploc &v) 124 | { 125 | i << '<' << (int)v.x << ',' << (int)v.y << '>'; 126 | return i; 127 | } 128 | bool inBorder() 129 | { 130 | return x >= 0 && x < 8 && y >= 0 && y < 8; 131 | } 132 | }; 133 | 134 | #define MC_W 0 135 | #define MC_B 1 136 | typedef ull Map_0; 137 | class Map 138 | { 139 | public: 140 | //m[0] w; m[1] b 141 | Map_0 m[2]; 142 | Map() {} 143 | Map(Col _m[64]) 144 | { 145 | clear(); 146 | for (int i = 0;i<64;i++) 147 | { 148 | if (_m[i] == C_W) m[0] |= 1ull << i; 149 | else if (_m[i] == C_B) m[1] |= 1ull << i; 150 | } 151 | } 152 | void clear() { 153 | m[0] = m[1] = 0; 154 | } 155 | inline Col operator[](MP p) { 156 | return ((m[0] >> p) & 1) + ((m[1] >> p) & 1) * 2; 157 | } 158 | inline Col operator[](Ploc p) { 159 | return (*this)[p.toMP()]; 160 | } 161 | inline bool operator==(Map &op) 162 | { 163 | return m[0] == op.m[0] && m[1] == op.m[1]; 164 | } 165 | inline bool isPlaced(MP p) { 166 | return ((m[0] | m[1]) >> p) & 1; 167 | } 168 | inline void set(MP p, Col col) { 169 | setbit(m[0], col == C_W, p); 170 | setbit(m[1], col == C_B, p); 171 | } 172 | string toString() 173 | { 174 | string s; 175 | for (auto p = MP_F;p &pList); 190 | Bit getFlipCount(MP p, Col col); 191 | void toArr(Col arr[8][8]); 192 | void resetByArr(Col arr[8][8]); 193 | 194 | static const Map Map_Start; 195 | }; 196 | 197 | Col MP_SS[64] = 198 | { 199 | 0,0,0,0,0,0,0,0, 200 | 0,0,0,0,0,0,0,0, 201 | 0,0,0,0,0,0,0,0, 202 | 0,0,0,1,2,0,0,0, 203 | 0,0,0,2,1,0,0,0, 204 | 0,0,0,0,0,0,0,0, 205 | 0,0,0,0,0,0,0,0, 206 | 0,0,0,0,0,0,0,0 207 | }; 208 | const Map Map::Map_Start = Map(MP_SS); 209 | 210 | void Map::getCanDoList(Col col, vector &pList) 211 | { 212 | for (auto p = MP_F;p56 232 | 233 | //shoule keep the postion is empty 234 | //test if a postion can put piece 235 | 236 | bool Map::testPiece(MP p, Col col) 237 | { 238 | if (isPlaced(p)) return false; 239 | MP tp; auto co1 = col_f(col); 240 | //1:UL 241 | if (p % 8>1 && p >= 16 && (*this)[p + MP_UL] == co1) 242 | for (tp = p + MP_UL * 2;;tp += MP_UL) { 243 | Col cc = (*this)[tp]; 244 | if (cc == col) return true; 245 | else if (cc == C_E) break; 246 | if (tp % 8 == 0 || tp<8) break; 247 | } 248 | //2:U 249 | if (p >= 16 && (*this)[p + MP_U] == co1) 250 | for (tp = p + MP_U * 2;;tp += MP_U) { 251 | Col cc = (*this)[tp]; 252 | if (cc == col) return true; 253 | else if (cc == C_E) break; 254 | if (tp<8) break; 255 | } 256 | //3:UR 257 | if (p % 8<6 && p >= 16 && (*this)[p + MP_UR] == co1) 258 | for (tp = p + MP_UR * 2;;tp += MP_UR) { 259 | Col cc = (*this)[tp]; 260 | if (cc == col) return true; 261 | else if (cc == C_E) break; 262 | if (tp % 8 == 7 || tp<8) break; 263 | } 264 | //4:L 265 | if (p % 8>1 && (*this)[p + MP_L] == co1) 266 | for (tp = p + MP_L * 2;;tp += MP_L) { 267 | Col cc = (*this)[tp]; 268 | if (cc == col) return true; 269 | else if (cc == C_E) break; 270 | if (tp % 8 == 0) break; 271 | } 272 | //5:R 273 | if (p % 8<6 && (*this)[p + MP_R] == co1) 274 | for (tp = p + MP_R * 2;;tp += MP_R) { 275 | Col cc = (*this)[tp]; 276 | if (cc == col) return true; 277 | else if (cc == C_E) break; 278 | if (tp % 8 == 7) break; 279 | } 280 | //5:DL 281 | if (p % 8>1 && p<48 && (*this)[p + MP_DL] == co1) 282 | for (tp = p + MP_DL * 2;;tp += MP_DL) { 283 | Col cc = (*this)[tp]; 284 | if (cc == col) return true; 285 | else if (cc == C_E) break; 286 | if (tp % 8 == 0 || tp >= 56) break; 287 | } 288 | //6:D 289 | if (p<48 && (*this)[p + MP_D] == co1) 290 | for (tp = p + MP_D * 2;;tp += MP_D) { 291 | Col cc = (*this)[tp]; 292 | if (cc == col) return true; 293 | else if (cc == C_E) break; 294 | if (tp >= 56) break; 295 | } 296 | //7:DR 297 | if (p % 8<6 && p<48 && (*this)[p + MP_DR] == co1) 298 | for (tp = p + MP_DR * 2;;tp += MP_DR) { 299 | Col cc = (*this)[tp]; 300 | if (cc == col) return true; 301 | else if (cc == C_E) break; 302 | if (tp % 8 == 7 || tp >= 56) break; 303 | } 304 | return false; 305 | } 306 | 307 | Bit Map::getFlipCount(MP p, Col col) 308 | { 309 | if (isPlaced(p)) return 0; 310 | MP tp; auto co1 = col_f(col); 311 | Bit ans = 0; 312 | //1:UL 313 | if (p % 8>1 && p >= 16 && (*this)[p + MP_UL] == co1) 314 | for (tp = p + MP_UL * 2;;tp += MP_UL) { 315 | Col cc = (*this)[tp]; 316 | if (cc == col) 317 | { 318 | ans += (tp - p) / MP_UL - 1; 319 | break; 320 | } 321 | else if (cc == C_E) break; 322 | if (tp % 8 == 0 || tp<8) break; 323 | } 324 | //2:U 325 | if (p >= 16 && (*this)[p + MP_U] == co1) 326 | for (tp = p + MP_U * 2;;tp += MP_U) { 327 | Col cc = (*this)[tp]; 328 | if (cc == col) 329 | { 330 | ans += (tp - p) / MP_U - 1; 331 | break; 332 | } 333 | else if (cc == C_E) break; 334 | if (tp<8) break; 335 | } 336 | //3:UR 337 | if (p % 8<6 && p >= 16 && (*this)[p + MP_UR] == co1) 338 | for (tp = p + MP_UR * 2;;tp += MP_UR) { 339 | Col cc = (*this)[tp]; 340 | if (cc == col) 341 | { 342 | ans += (tp - p) / MP_UR - 1; 343 | break; 344 | } 345 | else if (cc == C_E) break; 346 | if (tp % 8 == 7 || tp<8) break; 347 | } 348 | //4:L 349 | if (p % 8>1 && (*this)[p + MP_L] == co1) 350 | for (tp = p + MP_L * 2;;tp += MP_L) { 351 | Col cc = (*this)[tp]; 352 | if (cc == col) 353 | { 354 | ans += (tp - p) / MP_L - 1; 355 | break; 356 | } 357 | else if (cc == C_E) break; 358 | if (tp % 8 == 0) break; 359 | } 360 | //5:R 361 | if (p % 8<6 && (*this)[p + MP_R] == co1) 362 | for (tp = p + MP_R * 2;;tp += MP_R) { 363 | Col cc = (*this)[tp]; 364 | if (cc == col) 365 | { 366 | ans += (tp - p) / MP_R - 1; 367 | break; 368 | } 369 | else if (cc == C_E) break; 370 | if (tp % 8 == 7) break; 371 | } 372 | //6:DL 373 | if (p % 8>1 && p<48 && (*this)[p + MP_DL] == co1) 374 | for (tp = p + MP_DL * 2;;tp += MP_DL) { 375 | Col cc = (*this)[tp]; 376 | if (cc == col) 377 | { 378 | ans += (tp - p) / MP_DL - 1; 379 | break; 380 | } 381 | else if (cc == C_E) break; 382 | if (tp % 8 == 0 || tp >= 56) break; 383 | } 384 | //7:D 385 | if (p<48 && (*this)[p + MP_D] == co1) 386 | for (tp = p + MP_D * 2;;tp += MP_D) { 387 | Col cc = (*this)[tp]; 388 | if (cc == col) 389 | { 390 | ans += (tp - p) / MP_D - 1; 391 | break; 392 | } 393 | else if (cc == C_E) break; 394 | if (tp >= 56) break; 395 | } 396 | //8:DR 397 | if (p % 8<6 && p<48 && (*this)[p + MP_DR] == co1) 398 | for (tp = p + MP_DR * 2;;tp += MP_DR) { 399 | Col cc = (*this)[tp]; 400 | if (cc == col) 401 | { 402 | ans += (tp - p) / MP_DR - 1; 403 | break; 404 | } 405 | else if (cc == C_E) break; 406 | if (tp % 8 == 7 || tp >= 56) break; 407 | } 408 | return ans; 409 | } 410 | 411 | void Map::setPiece(MP p, Col col) 412 | { 413 | MP tp; auto co1 = col_f(col); 414 | set(p, col); 415 | //1:UL 416 | if (p % 8>1 && p >= 16 && (*this)[p + MP_UL] == co1) 417 | for (tp = p + MP_UL * 2;;tp += MP_UL) { 418 | Col cc = (*this)[tp]; 419 | if (cc == col) 420 | { 421 | for (tp -= MP_UL;tp != p;tp -= MP_UL) (*this).set(tp, col); 422 | break; 423 | } 424 | else if (cc == C_E) break; 425 | if (tp % 8 == 0 || tp<8) break; 426 | } 427 | //2:U 428 | if (p >= 16 && (*this)[p + MP_U] == co1) 429 | for (tp = p + MP_U * 2;;tp += MP_U) { 430 | Col cc = (*this)[tp]; 431 | if (cc == col) 432 | { 433 | for (tp -= MP_U;tp != p;tp -= MP_U) (*this).set(tp, col); 434 | break; 435 | } 436 | else if (cc == C_E) break; 437 | if (tp<8) break; 438 | } 439 | //3:UR 440 | if (p % 8<6 && p >= 16 && (*this)[p + MP_UR] == co1) 441 | for (tp = p + MP_UR * 2;;tp += MP_UR) { 442 | Col cc = (*this)[tp]; 443 | if (cc == col) 444 | { 445 | for (tp -= MP_UR;tp != p;tp -= MP_UR) (*this).set(tp, col); 446 | break; 447 | } 448 | else if (cc == C_E) break; 449 | if (tp % 8 == 7 || tp<8) break; 450 | } 451 | //4:L 452 | if (p % 8>1 && (*this)[p + MP_L] == co1) 453 | for (tp = p + MP_L * 2;;tp += MP_L) { 454 | Col cc = (*this)[tp]; 455 | if (cc == col) 456 | { 457 | for (tp -= MP_L;tp != p;tp -= MP_L) (*this).set(tp, col); 458 | break; 459 | } 460 | else if (cc == C_E) break; 461 | if (tp % 8 == 0) break; 462 | } 463 | //5:R 464 | if (p % 8<6 && (*this)[p + MP_R] == co1) 465 | for (tp = p + MP_R * 2;;tp += MP_R) { 466 | Col cc = (*this)[tp]; 467 | if (cc == col) 468 | { 469 | for (tp -= MP_R;tp != p;tp -= MP_R) (*this).set(tp, col); 470 | break; 471 | } 472 | else if (cc == C_E) break; 473 | if (tp % 8 == 7) break; 474 | } 475 | //6:DL 476 | if (p % 8>1 && p<48 && (*this)[p + MP_DL] == co1) 477 | for (tp = p + MP_DL * 2;;tp += MP_DL) { 478 | Col cc = (*this)[tp]; 479 | if (cc == col) 480 | { 481 | for (tp -= MP_DL;tp != p;tp -= MP_DL) (*this).set(tp, col); 482 | break; 483 | } 484 | else if (cc == C_E) break; 485 | if (tp % 8 == 0 || tp >= 56) break; 486 | } 487 | //7:D 488 | if (p<48 && (*this)[p + MP_D] == co1) 489 | for (tp = p + MP_D * 2;;tp += MP_D) { 490 | Col cc = (*this)[tp]; 491 | if (cc == col) 492 | { 493 | for (tp -= MP_D;tp != p;tp -= MP_D) (*this).set(tp, col); 494 | break; 495 | } 496 | else if (cc == C_E) break; 497 | if (tp >= 56) break; 498 | } 499 | //8:DR 500 | if (p % 8<6 && p<48 && (*this)[p + MP_DR] == co1) 501 | for (tp = p + MP_DR * 2;;tp += MP_DR) { 502 | Col cc = (*this)[tp]; 503 | if (cc == col) 504 | { 505 | for (tp -= MP_DR;tp != p;tp -= MP_DR) (*this).set(tp, col); 506 | break; 507 | } 508 | else if (cc == C_E) break; 509 | if (tp % 8 == 7 || tp >= 56) break; 510 | } 511 | } 512 | 513 | bool Map::testAll(Col col) 514 | { 515 | for (auto p = MP_F;p>hex>> r1 >> r2) 1480 | { 1481 | mmap.m[1] = r1, mmap.m[0] = r2; 1482 | mmap.toArr(mm[ccnt]); 1483 | fin >> mval[ccnt]; 1484 | vy_ += mval[ccnt]; 1485 | ccnt++; 1486 | } 1487 | cout << ccnt << " board loaded\n" << vy_ / ccnt << '\n'; 1488 | ccnt=94000; 1489 | #define TRAIN 1490 | #ifdef TRAIN 1491 | train(); 1492 | #else 1493 | loadArgs(); 1494 | #endif 1495 | sse=0; 1496 | #if 0 1497 | valid(60001); 1498 | cout<