├── .gitignore ├── LICENSE ├── README.md ├── cchess_alphazero ├── README.md ├── __init__.py ├── agent │ ├── __init__.py │ ├── api.py │ ├── model.py │ └── player.py ├── config.py ├── configs │ ├── __init__.py │ ├── distribute.py │ ├── mini.py │ └── normal.py ├── environment │ ├── README.md │ ├── __init__.py │ ├── chessboard.py │ ├── chessman.py │ ├── env.py │ ├── light_env │ │ ├── chessboard.py │ │ └── common.py │ ├── lookup_tables.py │ └── static_env.py ├── lib │ ├── data_helper.py │ ├── elo_helper.py │ ├── logger.py │ ├── model_helper.py │ ├── tf_util.py │ └── web_helper.py ├── manager.py ├── play_games │ ├── images │ │ ├── CANVAS.GIF │ │ ├── DELICATE │ │ │ ├── BA.GIF │ │ │ ├── BAS.GIF │ │ │ ├── BB.GIF │ │ │ ├── BBS.GIF │ │ │ ├── BC.GIF │ │ │ ├── BCS.GIF │ │ │ ├── BK.GIF │ │ │ ├── BKM.GIF │ │ │ ├── BKS.GIF │ │ │ ├── BN.GIF │ │ │ ├── BNS.GIF │ │ │ ├── BP.GIF │ │ │ ├── BPS.GIF │ │ │ ├── BR.GIF │ │ │ ├── BRS.GIF │ │ │ ├── OO.GIF │ │ │ ├── OOS.GIF │ │ │ ├── RA.GIF │ │ │ ├── RAS.GIF │ │ │ ├── RB.GIF │ │ │ ├── RBS.GIF │ │ │ ├── RC.GIF │ │ │ ├── RCS.GIF │ │ │ ├── RK.GIF │ │ │ ├── RKM.GIF │ │ │ ├── RKS.GIF │ │ │ ├── RN.GIF │ │ │ ├── RNS.GIF │ │ │ ├── RP.GIF │ │ │ ├── RPS.GIF │ │ │ ├── RR.GIF │ │ │ └── RRS.GIF │ │ ├── DROPS.GIF │ │ ├── GREEN.GIF │ │ ├── POLISH │ │ │ ├── BA.GIF │ │ │ ├── BAS.GIF │ │ │ ├── BB.GIF │ │ │ ├── BBS.GIF │ │ │ ├── BC.GIF │ │ │ ├── BCS.GIF │ │ │ ├── BK.GIF │ │ │ ├── BKM.GIF │ │ │ ├── BKS.GIF │ │ │ ├── BN.GIF │ │ │ ├── BNS.GIF │ │ │ ├── BP.GIF │ │ │ ├── BPS.GIF │ │ │ ├── BR.GIF │ │ │ ├── BRS.GIF │ │ │ ├── OO.GIF │ │ │ ├── OOS.GIF │ │ │ ├── RA.GIF │ │ │ ├── RAS.GIF │ │ │ ├── RB.GIF │ │ │ ├── RBS.GIF │ │ │ ├── RC.GIF │ │ │ ├── RCS.GIF │ │ │ ├── RK.GIF │ │ │ ├── RKM.GIF │ │ │ ├── RKS.GIF │ │ │ ├── RN.GIF │ │ │ ├── RNS.GIF │ │ │ ├── RP.GIF │ │ │ ├── RPS.GIF │ │ │ ├── RR.GIF │ │ │ └── RRS.GIF │ │ ├── QIANHONG.GIF │ │ ├── SHEET.GIF │ │ ├── SKELETON.GIF │ │ ├── WHITE.GIF │ │ ├── WOOD.GIF │ │ └── WOOD │ │ │ ├── BA.GIF │ │ │ ├── BAS.GIF │ │ │ ├── BB.GIF │ │ │ ├── BBS.GIF │ │ │ ├── BC.GIF │ │ │ ├── BCS.GIF │ │ │ ├── BK.GIF │ │ │ ├── BKM.GIF │ │ │ ├── BKS.GIF │ │ │ ├── BN.GIF │ │ │ ├── BNS.GIF │ │ │ ├── BP.GIF │ │ │ ├── BPS.GIF │ │ │ ├── BR.GIF │ │ │ ├── BRS.GIF │ │ │ ├── OO.GIF │ │ │ ├── OOS.GIF │ │ │ ├── RA.GIF │ │ │ ├── RAS.GIF │ │ │ ├── RB.GIF │ │ │ ├── RBS.GIF │ │ │ ├── RC.GIF │ │ │ ├── RCS.GIF │ │ │ ├── RK.GIF │ │ │ ├── RKM.GIF │ │ │ ├── RKS.GIF │ │ │ ├── RN.GIF │ │ │ ├── RNS.GIF │ │ │ ├── RP.GIF │ │ │ ├── RPS.GIF │ │ │ ├── RR.GIF │ │ │ └── RRS.GIF │ ├── ob_self_play.py │ ├── play.py │ ├── play_cli.py │ ├── test_cli_game.py │ └── test_window_game.py ├── run.py ├── test.py ├── uci.py └── worker │ ├── __init__.py │ ├── compute_elo.py │ ├── compute_elo_windows.py │ ├── evaluator.py │ ├── optimize.py │ ├── play_with_ucci_engine.py │ ├── self_play.py │ ├── self_play_windows.py │ ├── sl.py │ └── sl_onegreen.py ├── colaboratory ├── eval.py ├── run.py └── test.py ├── data └── model │ ├── model_128_l1_config.json │ ├── model_128f.json │ ├── model_192x10_config.json │ ├── model_256f.json │ ├── model_best_config.json │ └── model_best_weight.h5 ├── elo.png ├── freeze ├── evaluate.py ├── play_games.py ├── play_games.spec └── run_self_play.py ├── model.png ├── requirements.txt └── screenshots └── board.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | data/* 4 | logs/* 5 | *.ttc 6 | Icon* 7 | .vscode 8 | *.icloud -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 中国象棋Zero(CCZero) 2 | 3 | 4 | App Icon 5 | 6 | 7 | ## About 8 | 9 | Chinese Chess reinforcement learning by [AlphaZero](https://arxiv.org/abs/1712.01815) methods. 10 | 11 | This project is based on these main resources: 12 | 1. DeepMind's Oct 19th publication: [Mastering the Game of Go without Human Knowledge](https://www.nature.com/articles/nature24270.epdf?author_access_token=VJXbVjaSHxFoctQQ4p2k4tRgN0jAjWel9jnR3ZoTv0PVW4gB86EEpGqTRDtpIz-2rmo8-KG06gqVobU5NSCFeHILHcVFUeMsbvwS-lxjqQGg98faovwjxeTUgZAUMnRQ). 13 | 2. The **great** Reversi/Chess/Chinese chess development of the DeepMind ideas that @mokemokechicken/@Akababa/@TDteach did in their repo: https://github.com/mokemokechicken/reversi-alpha-zero, https://github.com/Akababa/Chess-Zero, https://github.com/TDteach/AlphaZero_ChineseChess 14 | 3. A Chinese chess engine with gui: https://github.com/mm12432/MyChess 15 | 16 | 17 | ## Help to train 18 | 19 | In order to build a strong chinese chess AI following the same type of techniques as AlphaZero, we need to do this with a distributed project, as it requires a huge amount of computations. 20 | 21 | If you want to join us to build the best chinese chess AI in the world: 22 | 23 | * For instructions, see [wiki](https://github.com/NeymarL/ChineseChess-AlphaZero/wiki) 24 | * For live status, see https://cczero.org 25 | 26 | ![elo](elo.png) 27 | 28 | 29 | ## Environment 30 | 31 | * Python 3.6.3 32 | * tensorflow-gpu: 1.3.0 33 | * Keras: 2.0.8 34 | 35 | 36 | ## Modules 37 | 38 | ### Reinforcement Learning 39 | 40 | This AlphaZero implementation consists of two workers: `self` and `opt`. 41 | 42 | * `self` is Self-Play to generate training data by self-play using BestModel. 43 | * `opt` is Trainer to train model, and generate new models. 44 | 45 | For the sake of faster training, another two workers are involved: 46 | 47 | * `sl` is Supervised learning to train data crawled from the Internet. 48 | * `eval` is Evaluator to evaluate the NextGenerationModel with the current BestModel. 49 | 50 | ### Built-in GUI 51 | 52 | Requirement: pygame 53 | 54 | ```bash 55 | python cchess_alphazero/run.py play 56 | ``` 57 | 58 | **Screenshots** 59 | 60 | ![board](screenshots/board.png) 61 | 62 | You can choose different board/piece styles and sides, see [play with human](#play-with-human). 63 | 64 | 65 | ## How to use 66 | 67 | ### Setup 68 | 69 | ### install libraries 70 | ```bash 71 | pip install -r requirements.txt 72 | ``` 73 | 74 | If you want to use CPU only, replace `tensorflow-gpu` with `tensorflow` in `requirements.txt`. 75 | 76 | Make sure Keras is using Tensorflow and you have Python 3.6.3+. 77 | 78 | ### Configuration 79 | 80 | **PlayDataConfig** 81 | 82 | * `nb_game_in_file, max_file_num`: The max game number of training data is `nb_game_in_file * max_file_num`. 83 | 84 | **PlayConfig, PlayWithHumanConfig** 85 | 86 | * `simulation_num_per_move` : MCTS number per move. 87 | * `c_puct`: balance parameter of value network and policy network in MCTS. 88 | * `search_threads`: balance parameter of speed and accuracy in MCTS. 89 | * `dirichlet_alpha`: random parameter in self-play. 90 | 91 | ### Full Usage 92 | 93 | ``` 94 | usage: run.py [-h] [--new] [--type TYPE] [--total-step TOTAL_STEP] 95 | [--ai-move-first] [--cli] [--gpu GPU] [--onegreen] [--skip SKIP] 96 | [--ucci] [--piece-style {WOOD,POLISH,DELICATE}] 97 | [--bg-style {CANVAS,DROPS,GREEN,QIANHONG,SHEET,SKELETON,WHITE,WOOD}] 98 | [--random {none,small,medium,large}] [--distributed] [--elo] 99 | {self,opt,eval,play,eval,sl,ob} 100 | 101 | positional arguments: 102 | {self,opt,eval,play,eval,sl,ob} 103 | what to do 104 | 105 | optional arguments: 106 | -h, --help show this help message and exit 107 | --new run from new best model 108 | --type TYPE use normal setting 109 | --total-step TOTAL_STEP 110 | set TrainerConfig.start_total_steps 111 | --ai-move-first set human or AI move first 112 | --cli play with AI with CLI, default with GUI 113 | --gpu GPU device list 114 | --onegreen train sl work with onegreen data 115 | --skip SKIP skip games 116 | --ucci play with ucci engine instead of self play 117 | --piece-style {WOOD,POLISH,DELICATE} 118 | choose a style of piece 119 | --bg-style {CANVAS,DROPS,GREEN,QIANHONG,SHEET,SKELETON,WHITE,WOOD} 120 | choose a style of board 121 | --random {none,small,medium,large} 122 | choose a style of randomness 123 | --distributed whether upload/download file from remote server 124 | --elo whether to compute elo score 125 | ``` 126 | 127 | ### Self-Play 128 | 129 | ``` 130 | python cchess_alphazero/run.py self 131 | ``` 132 | 133 | When executed, self-play will start using BestModel. If the BestModel does not exist, new random model will be created and become BestModel. Self-play records will store in `data/play_record` and BestMode will store in `data/model`. 134 | 135 | options 136 | 137 | * `--new`: create new BestModel 138 | * `--type mini`: use mini config, (see `cchess_alphazero/configs/mini.py`) 139 | * `--gpu '1'`: specify which gpu to use 140 | * `--ucci`: whether to play with ucci engine (rather than self play, see `cchess_alphazero/worker/play_with_ucci_engine.py`) 141 | * `--distributed`: run self play in distributed mode which means it will upload the play data to the remote server and download latest model from it 142 | 143 | **Note1**: To help training, you should run `python cchess_alphazero/run.py --type distribute --distributed self` (and do not change the configuration file `configs/distribute.py`), for more info, see [wiki](https://github.com/NeymarL/ChineseChess-AlphaZero/wiki/For-Developers). 144 | 145 | **Note2**: If you want to view the self-play records in GUI, see [wiki](https://github.com/NeymarL/ChineseChess-AlphaZero/wiki/View-self-play-games-in-GUI). 146 | 147 | ### Trainer 148 | 149 | ``` 150 | python cchess_alphazero/run.py opt 151 | ``` 152 | 153 | When executed, Training will start. The current BestModel will be loaded. Trained model will be saved every epoch as new BestModel. 154 | 155 | options 156 | 157 | * `--type mini`: use mini config, (see `cchess_alphazero/configs/mini.py`) 158 | * `--total-step TOTAL_STEP`: specify total step(mini-batch) numbers. The total step affects learning rate of training. 159 | * `--gpu '1'`: specify which gpu to use 160 | 161 | **View training log in Tensorboard** 162 | 163 | ``` 164 | tensorboard --logdir logs/ 165 | ``` 166 | 167 | And access `http://:6006/`. 168 | 169 | ### Play with human 170 | 171 | **Run with built-in GUI** 172 | 173 | ``` 174 | python cchess_alphazero/run.py play 175 | ``` 176 | 177 | When executed, the BestModel will be loaded to play against human. 178 | 179 | options 180 | 181 | * `--ai-move-first`: if set this option, AI will move first, otherwise human move first. 182 | * `--type mini`: use mini config, (see `cchess_alphazero/configs/mini.py`) 183 | * `--gpu '1'`: specify which gpu to use 184 | * `--piece-style WOOD`: choose a piece style, default is `WOOD` 185 | * `--bg-style CANVAS`: choose a board style, default is `CANVAS` 186 | * `--cli`: if set this flag, play with AI in a cli environment rather than gui 187 | 188 | **Note**: Before you start, you need to download/find a font file (`.ttc`) and rename it as `PingFang.ttc`, then put it into `cchess_alphazero/play_games`. I have removed the font file from this repo because it's too big, but you can download it from [here](http://alphazero.52coding.com.cn/PingFang.ttc). 189 | 190 | You can also download Windows executable directly from [here](https://pan.baidu.com/s/1uE_zmkn0x9Be_olRL9U9cQ). For more information, see [wiki](https://github.com/NeymarL/ChineseChess-AlphaZero/wiki/For-Non-Developers#%E4%B8%8B%E6%A3%8B). 191 | 192 | **UCI mode** 193 | 194 | ``` 195 | python cchess_alphazero/uci.py 196 | ``` 197 | 198 | If you want to play in general GUIs such as '冰河五四', you can download the Windows executable [here](https://share.weiyun.com/5cK50Z4). For more information, see [wiki](https://github.com/NeymarL/ChineseChess-AlphaZero/wiki/For-Non-Developers#%E4%B8%8B%E6%A3%8B). 199 | 200 | ### Evaluator 201 | 202 | ``` 203 | python cchess_alphazero/run.py eval 204 | ``` 205 | 206 | When executed, evaluate the NextGenerationModel with the current BestModel. If the NextGenerationModel does not exist, worker will wait until it exists and check every 5 minutes. 207 | 208 | options 209 | 210 | * `--type mini`: use mini config, (see `cchess_alphazero/configs/mini.py`) 211 | * `--gpu '1'`: specify which gpu to use 212 | 213 | ### Supervised Learning 214 | 215 | ``` 216 | python cchess_alphazero/run.py sl 217 | ``` 218 | 219 | When executed, Training will start. The current SLBestModel will be loaded. Tranined model will be saved every epoch as new SLBestModel. 220 | 221 | *About the data* 222 | 223 | I have two data sources, one is downloaded from https://wx.jcloud.com/market/packet/10479 ; the other is crawled from http://game.onegreen.net/chess/Index.html (with option --onegreen). 224 | 225 | options 226 | 227 | * `--type mini`: use mini config, (see `cchess_alphazero/configs/mini.py`) 228 | * `--gpu '1'`: specify which gpu to use 229 | * `--onegreen`: if set the flag, `sl_onegreen` worker will start to train data crawled from `game.onegreen.net` 230 | * `--skip SKIP`: if set this flag, games whoses index is less than `SKIP` would not be used to train (only valid when `onegreen` flag is set) 231 | -------------------------------------------------------------------------------- /cchess_alphazero/README.md: -------------------------------------------------------------------------------- 1 | # Code Structures 2 | 3 | ``` 4 | ├── cchess_alphazero 5 | │ ├── agent : the AI (AlphaZero) agent 6 | │ │ ├── api.py : neural networks' prediction api 7 | │ │ ├── model.py : policy & value network model 8 | │ │ └── player.py : the final agent that play with neural network and MCTS 9 | │ ├── configs : different types of configuration 10 | │ │ ├── mini.py 11 | │ │ └── normal.py 12 | │ ├── environment : a Chinese Chess engine 13 | │ │ │── light_env : a lightweight chinese chess engine (for training) 14 | │ │ │ ├── chessboard.py 15 | │ │ │ ├── common.py 16 | │ │ │ └── chessman.py.py 17 | │ │ ├── chessboard.py 18 | │ │ ├── chessman.py 19 | │ │ ├── env.py : the environment api of the engine (mainly used in play-with-human and previous MCTS player) 20 | │ │ ├── static_env.py : a static chess engine which does not store the board (used in new MCTS player) 21 | │ │ └── lookup_tables.py 22 | │ ├── lib : helper functions 23 | │ │ ├── data_helper.py : load & save data 24 | │ │ ├── logger.py : setup logger 25 | │ │ ├── model_helper.py : load & save model 26 | │ │ └── tf_util.py : setup tf session 27 | │ ├── play_games : play with human 28 | │ │ ├── images : game materials 29 | │ │ ├── play.py : AI vs human with gui 30 | │ │ ├── play_cli.py : AI vs human with cli 31 | │ │ ├── ob_self_play.py : observe AI vs AI with cli 32 | │ │ ├── test_cli_game.py : human vs human with cli 33 | │ │ └── test_window_game.py : human vs human with gui 34 | │ ├── worker 35 | │ │ ├── self_play.py : self play worker 36 | │ │ ├── self_play_windows.py : self play worker of Windows os 37 | │ │ ├── compute_elo.py : evaluate next generation model and compute it's elo 38 | │ │ ├── optimize.py : trainer 39 | │ │ ├── sl.py : supervised learning worker 40 | │ │ ├── sl_onegreen.py : supervised learning worker which train data crawled from game.onegreen.net 41 | │ │ ├── play_with_ucci_engine.py : play with an ucci engine rather than self play 42 | │ │ └── evaluator.py : evaluate next generation model with current best model 43 | │ ├── config.py : setup configuration 44 | │ ├── manager.py : manage to start which worker 45 | │ ├── run.py : start interface 46 | │ ├── uci.py : for UCI protocal 47 | ├── └── test.py : for debug and test 48 | 49 | ``` 50 | -------------------------------------------------------------------------------- /cchess_alphazero/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/__init__.py -------------------------------------------------------------------------------- /cchess_alphazero/agent/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/agent/__init__.py -------------------------------------------------------------------------------- /cchess_alphazero/agent/api.py: -------------------------------------------------------------------------------- 1 | from multiprocessing import connection, Pipe 2 | from threading import Thread 3 | 4 | import os 5 | import numpy as np 6 | import shutil 7 | 8 | from cchess_alphazero.config import Config 9 | from cchess_alphazero.lib.model_helper import load_best_model_weight, need_to_reload_best_model_weight 10 | from cchess_alphazero.lib.web_helper import http_request, download_file 11 | from time import time 12 | from logging import getLogger 13 | 14 | logger = getLogger(__name__) 15 | 16 | class CChessModelAPI: 17 | 18 | def __init__(self, config: Config, agent_model): 19 | self.agent_model = agent_model # CChessModel 20 | self.pipes = [] # use for communication between processes/threads 21 | self.config = config 22 | self.need_reload = True 23 | self.done = False 24 | 25 | def start(self, need_reload=True): 26 | self.need_reload = need_reload 27 | prediction_worker = Thread(target=self.predict_batch_worker, name="prediction_worker") 28 | prediction_worker.daemon = True 29 | prediction_worker.start() 30 | 31 | def get_pipe(self, need_reload=True): 32 | me, you = Pipe() 33 | self.pipes.append(me) 34 | self.need_reload = need_reload 35 | return you 36 | 37 | def predict_batch_worker(self): 38 | if self.config.internet.distributed and self.need_reload: 39 | self.try_reload_model_from_internet() 40 | last_model_check_time = time() 41 | while not self.done: 42 | if last_model_check_time + 600 < time() and self.need_reload: 43 | self.try_reload_model() 44 | last_model_check_time = time() 45 | ready = connection.wait(self.pipes, timeout=0.001) 46 | if not ready: 47 | continue 48 | data, result_pipes, data_len = [], [], [] 49 | for pipe in ready: 50 | while pipe.poll(): 51 | try: 52 | tmp = pipe.recv() 53 | except EOFError as e: 54 | logger.error(f"EOF error: {e}") 55 | pipe.close() 56 | else: 57 | data.extend(tmp) 58 | data_len.append(len(tmp)) 59 | result_pipes.append(pipe) 60 | if not data: 61 | continue 62 | data = np.asarray(data, dtype=np.float32) 63 | with self.agent_model.graph.as_default(): 64 | policy_ary, value_ary = self.agent_model.model.predict_on_batch(data) 65 | buf = [] 66 | k, i = 0, 0 67 | for p, v in zip(policy_ary, value_ary): 68 | buf.append((p, float(v))) 69 | k += 1 70 | if k >= data_len[i]: 71 | result_pipes[i].send(buf) 72 | buf = [] 73 | k = 0 74 | i += 1 75 | 76 | def try_reload_model(self, config_file=None): 77 | if config_file: 78 | config_path = os.path.join(self.config.resource.model_dir, config_file) 79 | shutil.copy(config_path, self.config.resource.model_best_config_path) 80 | try: 81 | if self.config.internet.distributed and not config_file: 82 | self.try_reload_model_from_internet() 83 | else: 84 | if self.need_reload and need_to_reload_best_model_weight(self.agent_model): 85 | with self.agent_model.graph.as_default(): 86 | load_best_model_weight(self.agent_model) 87 | except Exception as e: 88 | logger.error(e) 89 | 90 | def try_reload_model_from_internet(self, config_file=None): 91 | response = http_request(self.config.internet.get_latest_digest) 92 | if response is None: 93 | logger.error(f"无法连接到远程服务器!请检查网络连接,并重新打开客户端") 94 | return 95 | digest = response['data']['digest'] 96 | 97 | if digest != self.agent_model.fetch_digest(self.config.resource.model_best_weight_path): 98 | logger.info(f"正在下载最新权重,请稍后...") 99 | if download_file(self.config.internet.download_url, self.config.resource.model_best_weight_path): 100 | logger.info(f"权重下载完毕!开始训练...") 101 | try: 102 | with self.agent_model.graph.as_default(): 103 | load_best_model_weight(self.agent_model) 104 | except ValueError as e: 105 | logger.error(f"权重架构不匹配,自动重新加载 {e}") 106 | self.try_reload_model(config_file='model_192x10_config.json') 107 | except Exception as e: 108 | logger.error(f"加载权重发生错误:{e},稍后重新下载") 109 | os.remove(self.config.resource.model_best_weight_path) 110 | self.try_reload_model_from_internet() 111 | else: 112 | logger.error(f"权重下载失败!请检查网络连接,并重新打开客户端") 113 | else: 114 | logger.info(f"检查完毕,权重未更新") 115 | 116 | def close(self): 117 | self.done = True 118 | -------------------------------------------------------------------------------- /cchess_alphazero/agent/model.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | import os 4 | from logging import getLogger 5 | 6 | import tensorflow as tf 7 | 8 | from keras.engine.topology import Input 9 | from keras.engine.training import Model 10 | from keras.layers.convolutional import Conv2D 11 | from keras.layers.core import Activation, Dense, Flatten 12 | from keras.layers.merge import Add 13 | from keras.layers.normalization import BatchNormalization 14 | from keras.regularizers import l2 15 | 16 | from cchess_alphazero.agent.api import CChessModelAPI 17 | from cchess_alphazero.config import Config 18 | from cchess_alphazero.environment.lookup_tables import ActionLabelsRed, ActionLabelsBlack 19 | 20 | logger = getLogger(__name__) 21 | 22 | class CChessModel: 23 | 24 | def __init__(self, config: Config): 25 | self.config = config 26 | self.model = None # type: Model 27 | self.digest = None 28 | self.n_labels = len(ActionLabelsRed) 29 | self.graph = None 30 | self.api = None 31 | 32 | def build(self): 33 | mc = self.config.model 34 | in_x = x = Input((14, 10, 9)) # 14 x 10 x 9 35 | 36 | # (batch, channels, height, width) 37 | x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_first_filter_size, padding="same", 38 | data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), 39 | name="input_conv-"+str(mc.cnn_first_filter_size)+"-"+str(mc.cnn_filter_num))(x) 40 | x = BatchNormalization(axis=1, name="input_batchnorm")(x) 41 | x = Activation("relu", name="input_relu")(x) 42 | 43 | for i in range(mc.res_layer_num): 44 | x = self._build_residual_block(x, i + 1) 45 | 46 | res_out = x 47 | 48 | # for policy output 49 | x = Conv2D(filters=4, kernel_size=1, data_format="channels_first", use_bias=False, 50 | kernel_regularizer=l2(mc.l2_reg), name="policy_conv-1-2")(res_out) 51 | x = BatchNormalization(axis=1, name="policy_batchnorm")(x) 52 | x = Activation("relu", name="policy_relu")(x) 53 | x = Flatten(name="policy_flatten")(x) 54 | policy_out = Dense(self.n_labels, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) 55 | 56 | # for value output 57 | x = Conv2D(filters=2, kernel_size=1, data_format="channels_first", use_bias=False, 58 | kernel_regularizer=l2(mc.l2_reg), name="value_conv-1-4")(res_out) 59 | x = BatchNormalization(axis=1, name="value_batchnorm")(x) 60 | x = Activation("relu",name="value_relu")(x) 61 | x = Flatten(name="value_flatten")(x) 62 | x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu", name="value_dense")(x) 63 | value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) 64 | 65 | self.model = Model(in_x, [policy_out, value_out], name="cchess_model") 66 | self.graph = tf.get_default_graph() 67 | 68 | def _build_residual_block(self, x, index): 69 | mc = self.config.model 70 | in_x = x 71 | res_name = "res" + str(index) 72 | x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", 73 | data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), 74 | name=res_name+"_conv1-"+str(mc.cnn_filter_size)+"-"+str(mc.cnn_filter_num))(x) 75 | x = BatchNormalization(axis=1, name=res_name+"_batchnorm1")(x) 76 | x = Activation("relu",name=res_name+"_relu1")(x) 77 | x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", 78 | data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), 79 | name=res_name+"_conv2-"+str(mc.cnn_filter_size)+"-"+str(mc.cnn_filter_num))(x) 80 | x = BatchNormalization(axis=1, name="res"+str(index)+"_batchnorm2")(x) 81 | x = Add(name=res_name+"_add")([in_x, x]) 82 | x = Activation("relu", name=res_name+"_relu2")(x) 83 | return x 84 | 85 | @staticmethod 86 | def fetch_digest(weight_path): 87 | if os.path.exists(weight_path): 88 | m = hashlib.sha256() 89 | with open(weight_path, "rb") as f: 90 | m.update(f.read()) 91 | return m.hexdigest() 92 | return None 93 | 94 | 95 | def load(self, config_path, weight_path): 96 | if os.path.exists(config_path) and os.path.exists(weight_path): 97 | logger.debug(f"loading model from {config_path}") 98 | with open(config_path, "rt") as f: 99 | self.model = Model.from_config(json.load(f)) 100 | self.model.load_weights(weight_path) 101 | self.digest = self.fetch_digest(weight_path) 102 | self.graph = tf.get_default_graph() 103 | logger.debug(f"loaded model digest = {self.digest}") 104 | return True 105 | else: 106 | logger.debug(f"model files does not exist at {config_path} and {weight_path}") 107 | return False 108 | 109 | def save(self, config_path, weight_path): 110 | logger.debug(f"save model to {config_path}") 111 | with open(config_path, "wt") as f: 112 | json.dump(self.model.get_config(), f) 113 | self.model.save_weights(weight_path) 114 | self.digest = self.fetch_digest(weight_path) 115 | logger.debug(f"saved model digest {self.digest}") 116 | 117 | def get_pipes(self, num=1, api=None, need_reload=True): 118 | if self.api is None: 119 | self.api = CChessModelAPI(self.config, self) 120 | self.api.start(need_reload) 121 | return self.api.get_pipe(need_reload) 122 | 123 | def close_pipes(self): 124 | if self.api is not None: 125 | self.api.close() 126 | self.api = None 127 | 128 | -------------------------------------------------------------------------------- /cchess_alphazero/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | import getpass 3 | 4 | def _project_dir(): 5 | d = os.path.dirname 6 | return d(d(os.path.abspath(__file__))) 7 | 8 | 9 | def _data_dir(): 10 | return os.path.join(_project_dir(), "data") 11 | 12 | class Config: 13 | def __init__(self, config_type="mini"): 14 | self.opts = Options() 15 | self.resource = ResourceConfig() 16 | self.internet = InternetConfig() 17 | 18 | if config_type == "mini": 19 | import configs.mini as c 20 | elif config_type == "normal": 21 | import configs.normal as c 22 | elif config_type == 'distribute': 23 | import cchess_alphazero.configs.distribute as c 24 | else: 25 | raise RuntimeError('unknown config_type: %s' % (config_type)) 26 | self.model = c.ModelConfig() 27 | self.play = c.PlayConfig() 28 | self.play_data = c.PlayDataConfig() 29 | self.trainer = c.TrainerConfig() 30 | self.eval = c.EvaluateConfig() 31 | 32 | class ResourceConfig: 33 | def __init__(self): 34 | self.project_dir = os.environ.get("PROJECT_DIR", _project_dir()) 35 | self.data_dir = os.environ.get("DATA_DIR", _data_dir()) 36 | 37 | self.model_dir = os.environ.get("MODEL_DIR", os.path.join(self.data_dir, "model")) 38 | self.model_best_config_path = os.path.join(self.model_dir, "model_best_config.json") 39 | self.model_best_weight_path = os.path.join(self.model_dir, "model_best_weight.h5") 40 | self.sl_best_config_path = os.path.join(self.model_dir, "sl_best_config.json") 41 | self.sl_best_weight_path = os.path.join(self.model_dir, "sl_best_weight.h5") 42 | self.eleeye_path = os.path.join(self.model_dir, 'ELEEYE') 43 | 44 | self.next_generation_model_dir = os.path.join(self.model_dir, "next_generation") 45 | self.next_generation_config_path = os.path.join(self.next_generation_model_dir, "next_generation_config.json") 46 | self.next_generation_weight_path = os.path.join(self.next_generation_model_dir, "next_generation_weight.h5") 47 | self.rival_model_config_path = os.path.join(self.model_dir, "rival_config.json") 48 | self.rival_model_weight_path = os.path.join(self.model_dir, "rival_weight.h5") 49 | 50 | self.play_data_dir = os.path.join(self.data_dir, "play_data") 51 | self.play_data_filename_tmpl = "play_%s.json" 52 | self.self_play_game_idx_file = os.path.join(self.data_dir, "play_data_idx") 53 | self.play_record_filename_tmpl = "record_%s.qp" 54 | self.play_record_dir = os.path.join(self.data_dir, "play_record") 55 | 56 | self.log_dir = os.path.join(self.project_dir, "logs") 57 | self.main_log_path = os.path.join(self.log_dir, "main.log") 58 | self.opt_log_path = os.path.join(self.log_dir, "opt.log") 59 | self.play_log_path = os.path.join(self.log_dir, "play.log") 60 | self.sl_log_path = os.path.join(self.log_dir, "sl.log") 61 | self.eval_log_path = os.path.join(self.log_dir, "eval.log") 62 | 63 | self.sl_data_dir = os.path.join(self.data_dir, "sl_data") 64 | self.sl_data_gameinfo = os.path.join(self.sl_data_dir, "gameinfo.csv") 65 | self.sl_data_move = os.path.join(self.sl_data_dir, "moves.csv") 66 | self.sl_onegreen = os.path.join(self.sl_data_dir, "onegreen.json") 67 | 68 | self.font_path = os.path.join(self.project_dir, 'cchess_alphazero', 'play_games', 'PingFang.ttc') 69 | 70 | def create_directories(self): 71 | dirs = [self.project_dir, self.data_dir, self.model_dir, self.play_data_dir, self.log_dir, 72 | self.play_record_dir, self.next_generation_model_dir, self.sl_data_dir] 73 | for d in dirs: 74 | if not os.path.exists(d): 75 | os.makedirs(d) 76 | 77 | class Options: 78 | new = False 79 | light = True 80 | device_list = '0' 81 | bg_style = 'CANVAS' 82 | piece_style = 'WOOD' 83 | random = 'none' 84 | log_move = False 85 | use_multiple_gpus = False 86 | gpu_num = 1 87 | evaluate = False 88 | has_history = False 89 | 90 | class PlayWithHumanConfig: 91 | def __init__(self): 92 | self.simulation_num_per_move = 800 93 | self.c_puct = 1 94 | self.search_threads = 10 95 | self.noise_eps = 0 96 | self.tau_decay_rate = 0 97 | self.dirichlet_alpha = 0.2 98 | 99 | def update_play_config(self, pc): 100 | pc.simulation_num_per_move = self.simulation_num_per_move 101 | pc.c_puct = self.c_puct 102 | pc.noise_eps = self.noise_eps 103 | pc.tau_decay_rate = self.tau_decay_rate 104 | pc.search_threads = self.search_threads 105 | pc.dirichlet_alpha = self.dirichlet_alpha 106 | 107 | class InternetConfig: 108 | def __init__(self): 109 | self.distributed = False 110 | self.username = getpass.getuser() 111 | self.base_url = 'https://cczero.org' 112 | self.upload_url = f'{self.base_url}/api/upload_game_file/192x10' 113 | self.upload_eval_url = f'{self.base_url}/api/upload_eval_game_file' 114 | self.download_url = f'http://download.52coding.com.cn/192x10/model_best_weight.h5' 115 | # self.download_url = 'http://alphazero-1251776088.cossh.myqcloud.com/model/128x7/model_best_weight.h5' 116 | self.get_latest_digest = f'{self.base_url}/api/get_latest_digest/192x10' 117 | self.add_model_url = f'{self.base_url}/api/add_model' 118 | self.get_evaluate_model_url = f'{self.base_url}/api/query_for_evaluate' 119 | self.download_base_url = f'http://download.52coding.com.cn/' 120 | # self.download_base_url = 'http://alphazero-1251776088.cossh.myqcloud.com/model/' 121 | self.get_elo_url = f'{self.base_url}/api/get_elo/' 122 | self.update_elo_url = f'{self.base_url}/api/add_eval_result/' 123 | 124 | 125 | -------------------------------------------------------------------------------- /cchess_alphazero/configs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/configs/__init__.py -------------------------------------------------------------------------------- /cchess_alphazero/configs/distribute.py: -------------------------------------------------------------------------------- 1 | class EvaluateConfig: 2 | def __init__(self): 3 | self.vram_frac = 1.0 4 | self.game_num = 10 5 | self.simulation_num_per_move = 800 6 | self.thinking_loop = 1 7 | self.c_puct = 1 # lower = prefer mean action value 8 | self.tau_decay_rate = 0.5 9 | self.noise_eps = 0.1 10 | self.max_game_length = 200 11 | self.max_processes = 10 12 | self.search_threads = 10 13 | 14 | def update_play_config(self, pc): 15 | pc.simulation_num_per_move = self.simulation_num_per_move 16 | pc.thinking_loop = self.thinking_loop 17 | pc.c_puct = self.c_puct 18 | pc.tau_decay_rate = self.tau_decay_rate 19 | pc.noise_eps = self.noise_eps 20 | pc.max_game_length = self.max_game_length 21 | pc.max_processes = self.max_processes 22 | pc.search_threads = self.search_threads 23 | 24 | 25 | class PlayDataConfig: 26 | def __init__(self): 27 | self.sl_nb_game_in_file = 250 28 | self.nb_game_in_file = 1 # WARNING: DO NOT CHANGE THIS PARAMETER 29 | self.max_file_num = 5000 30 | self.nb_game_save_record = 1 # not supported in distributed mode 31 | 32 | 33 | class PlayConfig: 34 | def __init__(self): 35 | self.max_processes = 10 # tune this to your cpu cores 36 | self.search_threads = 10 # increase this will be faster but with weaker performance 37 | self.vram_frac = 1.0 38 | self.simulation_num_per_move = 800 39 | self.thinking_loop = 1 40 | self.logging_thinking = False 41 | self.c_puct = 5 42 | self.noise_eps = 0.2 43 | self.dirichlet_alpha = 0.2 44 | self.tau_decay_rate = 0.9 45 | self.virtual_loss = 3 46 | self.resign_threshold = -0.99 47 | self.min_resign_turn = 40 48 | self.enable_resign_rate = 0.99 49 | self.max_game_length = 200 50 | self.share_mtcs_info_in_self_play = False 51 | self.reset_mtcs_info_per_game = 5 52 | 53 | 54 | class TrainerConfig: 55 | def __init__(self): 56 | self.min_games_to_begin_learn = 5000 57 | self.min_data_size_to_learn = 0 58 | self.cleaning_processes = 20 59 | self.vram_frac = 1.0 60 | self.batch_size = 1024 61 | self.epoch_to_checkpoint = 1 62 | self.dataset_size = 90000000 63 | self.start_total_steps = 0 64 | self.save_model_steps = 25 65 | self.load_data_steps = 100 66 | self.momentum = 0.9 67 | self.loss_weights = [1.0, 1.0] 68 | self.lr_schedules = [ 69 | (0, 0.03), 70 | (100000, 0.01), 71 | (200000, 0.003), 72 | (300000, 0.001), 73 | (400000, 0.0003), 74 | (500000, 0.0001), 75 | ] 76 | self.sl_game_step = 2000 77 | self.load_step = 25000 78 | 79 | class ModelConfig: 80 | def __init__(self): 81 | ''' 82 | WARNING: DO NOT CHANGE THESE PARAMETERS 83 | ''' 84 | self.cnn_filter_num = 192 85 | self.cnn_first_filter_size = 5 86 | self.cnn_filter_size = 3 87 | self.res_layer_num = 10 88 | self.l2_reg = 1e-4 89 | self.value_fc_size = 256 90 | self.distributed = False 91 | self.input_depth = 14 92 | -------------------------------------------------------------------------------- /cchess_alphazero/configs/mini.py: -------------------------------------------------------------------------------- 1 | class EvaluateConfig: 2 | def __init__(self): 3 | self.vram_frac = 1.0 4 | self.game_num = 2 5 | self.simulation_num_per_move = 20 # before 200 6 | self.thinking_loop = 1 7 | self.c_puct = 1 # lower = prefer mean action value 8 | self.tau_decay_rate = 0 9 | self.noise_eps = 0.2 10 | self.max_game_length = 100 11 | self.max_processes = 2 12 | self.search_threads = 10 13 | 14 | def update_play_config(self, pc): 15 | pc.simulation_num_per_move = self.simulation_num_per_move 16 | pc.thinking_loop = self.thinking_loop 17 | pc.c_puct = self.c_puct 18 | pc.tau_decay_rate = self.tau_decay_rate 19 | pc.noise_eps = self.noise_eps 20 | pc.max_game_length = self.max_game_length 21 | pc.max_processes = self.max_processes 22 | pc.search_threads = self.search_threads 23 | 24 | 25 | class PlayDataConfig: 26 | def __init__(self): 27 | self.sl_nb_game_in_file = 250 28 | self.nb_game_in_file = 1 29 | self.max_file_num = 10 30 | self.nb_game_save_record = 1 31 | 32 | 33 | class PlayConfig: 34 | def __init__(self): 35 | self.max_processes = 1 36 | self.search_threads = 10 37 | self.vram_frac = 1.0 38 | self.simulation_num_per_move = 100 # just for debug 39 | self.c_puct = 1.5 40 | self.noise_eps = 0.25 41 | self.dirichlet_alpha = 0.2 42 | self.tau_decay_rate = 0.98 43 | self.virtual_loss = 3 44 | self.max_game_length = 100 45 | self.share_mtcs_info_in_self_play = False 46 | self.reset_mtcs_info_per_game = 5 47 | self.enable_resign_rate = 0.1 48 | self.resign_threshold = -0.92 49 | self.min_resign_turn = 20 50 | 51 | class TrainerConfig: 52 | def __init__(self): 53 | self.min_games_to_begin_learn = 1 54 | self.min_data_size_to_learn = 0 55 | self.cleaning_processes = 1 56 | self.vram_frac = 1.0 57 | self.batch_size = 2 58 | self.epoch_to_checkpoint = 1 59 | self.dataset_size = 100000 60 | self.start_total_steps = 0 61 | self.save_model_steps = 25 62 | self.load_data_steps = 100 63 | self.momentum = 0.9 64 | self.loss_weights = [1.25, 1.0] 65 | self.lr_schedules = [ 66 | (0, 0.01), 67 | (150000, 0.001), 68 | (300000, 0.0001), 69 | ] 70 | self.sl_game_step = 10000 71 | self.load_step = 6 72 | 73 | class ModelConfig: 74 | def __init__(self): 75 | self.cnn_filter_num = 256 76 | self.cnn_first_filter_size = 5 77 | self.cnn_filter_size = 3 78 | self.res_layer_num = 7 79 | self.l2_reg = 1e-4 80 | self.value_fc_size = 256 81 | self.distributed = False 82 | self.input_depth = 14 83 | -------------------------------------------------------------------------------- /cchess_alphazero/configs/normal.py: -------------------------------------------------------------------------------- 1 | class EvaluateConfig: 2 | def __init__(self): 3 | self.vram_frac = 1.0 4 | self.game_num = 2 5 | self.simulation_num_per_move = 800 6 | self.thinking_loop = 1 7 | self.c_puct = 1 # lower = prefer mean action value 8 | self.tau_decay_rate = 0 9 | self.noise_eps = 0.2 10 | self.max_game_length = 200 11 | self.max_processes = 10 12 | self.search_threads = 8 13 | self.next_generation_replace_rate = 0.55 14 | 15 | def update_play_config(self, pc): 16 | pc.simulation_num_per_move = self.simulation_num_per_move 17 | pc.thinking_loop = self.thinking_loop 18 | pc.c_puct = self.c_puct 19 | pc.tau_decay_rate = self.tau_decay_rate 20 | pc.noise_eps = self.noise_eps 21 | pc.max_game_length = self.max_game_length 22 | pc.max_processes = self.max_processes 23 | pc.search_threads = self.search_threads 24 | 25 | 26 | class PlayDataConfig: 27 | def __init__(self): 28 | self.sl_nb_game_in_file = 250 29 | self.nb_game_in_file = 5 30 | self.max_file_num = 300 31 | self.nb_game_save_record = 1 32 | 33 | 34 | class PlayConfig: 35 | def __init__(self): 36 | self.max_processes = 10 37 | self.search_threads = 40 38 | self.vram_frac = 1.0 39 | self.simulation_num_per_move = 800 40 | self.thinking_loop = 1 41 | self.logging_thinking = False 42 | self.c_puct = 1.5 43 | self.noise_eps = 0.15 44 | self.dirichlet_alpha = 0.2 45 | self.tau_decay_rate = 0.9 46 | self.virtual_loss = 3 47 | self.resign_threshold = -0.98 48 | self.min_resign_turn = 40 49 | self.enable_resign_rate = 0.5 50 | self.max_game_length = 100 51 | self.share_mtcs_info_in_self_play = False 52 | self.reset_mtcs_info_per_game = 5 53 | 54 | 55 | class TrainerConfig: 56 | def __init__(self): 57 | self.min_games_to_begin_learn = 200 58 | self.min_data_size_to_learn = 0 59 | self.cleaning_processes = 4 # RAM explosion... 60 | self.vram_frac = 1.0 61 | self.batch_size = 512 # tune this to your gpu memory 62 | self.epoch_to_checkpoint = 3 63 | self.dataset_size = 100000 64 | self.start_total_steps = 0 65 | self.save_model_steps = 25 66 | self.load_data_steps = 100 67 | self.momentum = 0.9 68 | self.loss_weights = [1.0, 1.0] 69 | self.lr_schedules = [ 70 | (0, 0.01), 71 | (150000, 0.003), 72 | (400000, 0.0001), 73 | ] 74 | self.sl_game_step = 2000 75 | 76 | class ModelConfig: 77 | def __init__(self): 78 | self.cnn_filter_num = 256 79 | self.cnn_first_filter_size = 5 80 | self.cnn_filter_size = 3 81 | self.res_layer_num = 7 82 | self.l2_reg = 1e-4 83 | self.value_fc_size = 256 84 | self.distributed = False 85 | self.input_depth = 14 86 | -------------------------------------------------------------------------------- /cchess_alphazero/environment/README.md: -------------------------------------------------------------------------------- 1 | # Chinese Chess Environment 2 | 3 | **象棋程序功能 (Functions of the chess program)** 4 | 5 | * 判断移动是否合法 (Detect leagal moves) 6 | * 判断胜负 (Detect winner) 7 | * 记录棋谱 (Record) 8 | * 生成训练数据 (Generate training data) 9 | * 可视化 (Visualization) 10 | 11 | **API** 12 | 13 | Class: `cchess_alphazero.environment.env.ChessEnv` 14 | * `reset()`:重制(初始化)引擎环境 15 | 16 | * `observation`:返回当前棋盘 17 | 18 | * `copy()`:(深度)复制当前环境 19 | 20 | * `input_planes(flip=False)`:返回输入给神经网络的特征平面 21 | 22 | 轮到红方时 `flip=True`;轮到黑方时`flip=False` 23 | 24 | 25 | -------------------------------------------------------------------------------- /cchess_alphazero/environment/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/environment/__init__.py -------------------------------------------------------------------------------- /cchess_alphazero/environment/env.py: -------------------------------------------------------------------------------- 1 | import enum 2 | import numpy as np 3 | import copy 4 | 5 | from cchess_alphazero.environment.chessboard import Chessboard 6 | from cchess_alphazero.environment.lookup_tables import Chessman_2_idx, Fen_2_Idx, Winner 7 | from cchess_alphazero.environment.light_env.chessboard import L_Chessboard 8 | 9 | from logging import getLogger 10 | 11 | logger = getLogger(__name__) 12 | 13 | class CChessEnv: 14 | 15 | def __init__(self, config=None): 16 | self.board = None 17 | self.winner = None 18 | self.num_halfmoves = 0 19 | self.config = config 20 | 21 | def reset(self, init=None): 22 | if self.config is None or not self.config.opts.light: 23 | # logger.info("Initialize heavy environment!") 24 | self.board = Chessboard() 25 | self.board.init_board() 26 | else: 27 | # logger.info("Initialize light environment!") 28 | self.board = L_Chessboard(init) 29 | self.winner = None 30 | self.num_halfmoves = 0 31 | return self 32 | 33 | def update(self, board): 34 | self.board = board 35 | self.winner = None 36 | return self 37 | 38 | @property 39 | def done(self): 40 | return self.winner is not None 41 | 42 | @property 43 | def red_won(self): 44 | return self.winner == Winner.red 45 | 46 | @property 47 | def red_to_move(self): 48 | return self.board.is_red_turn 49 | 50 | @property 51 | def observation(self): 52 | if self.board.is_red_turn: 53 | return self.board.FENboard() 54 | else: 55 | return self.board.fliped_FENboard() 56 | 57 | def get_state(self): 58 | fen = self.observation 59 | foo = fen.split(' ') 60 | return foo[0] 61 | 62 | def step(self, action: str, check_over = True): 63 | if check_over and action is None: 64 | return 65 | 66 | if not self.board.move_action_str(action): 67 | logger.error("Move Failed, action=%s, is_red_turn=%d, board=\n%s" % (action, 68 | self.red_to_move, self.board.screen)) 69 | moves = self.board.legal_moves() 70 | logger.error(f"Legal moves: {moves}") 71 | self.num_halfmoves += 1 72 | 73 | if check_over and self.board.is_end(): 74 | self.winner = self.board.winner 75 | 76 | self.board.clear_chessmans_moving_list() 77 | self.board.calc_chessmans_moving_list() 78 | 79 | def copy(self): 80 | env = copy.deepcopy(self) 81 | env.board = copy.deepcopy(self.board) 82 | return env 83 | 84 | def render(self, gui=False): 85 | if gui: 86 | pass 87 | else: 88 | self.board.print_to_cl() 89 | 90 | def input_planes(self): 91 | planes = self.fen_to_planes(self.observation) 92 | return planes 93 | 94 | def state_to_planes(self, state): 95 | planes = self.fen_to_planes(state) 96 | return planes 97 | 98 | def fen_to_planes(self, fen): 99 | ''' 100 | e.g. 101 | rkemsmekr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RKEMSMEKR r - - 0 1 102 | rkemsmek1/8r/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RKEMSMEKR b - - 0 1 103 | ''' 104 | planes = np.zeros(shape=(14, 10, 9), dtype=np.float32) 105 | foo = fen.split(' ') 106 | rows = foo[0].split('/') 107 | 108 | for i in range(len(rows)): 109 | row = rows[i] 110 | j = 0 111 | for letter in row: 112 | if letter.isalpha(): 113 | # 0 ~ 7 : upper, 7 ~ 14: lower 114 | planes[Fen_2_Idx[letter] + int(letter.islower()) * 7][i][j] = 1 115 | j += 1 116 | else: 117 | j += int(letter) 118 | return planes 119 | 120 | def save_records(self, filename): 121 | self.board.save_record(filename) 122 | 123 | -------------------------------------------------------------------------------- /cchess_alphazero/environment/light_env/common.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # pycchess - just another chinese chess UI 5 | # Copyright (C) 2011 - 2015 timebug 6 | 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # any later version. 11 | 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | # import pygame 21 | 22 | RED, BLACK = 1, 0 23 | BORDER, SPACE = 15, 56 24 | LOCAL, OTHER = 0, 1 25 | NETWORK, AI = 0, 1 26 | KING, ADVISOR, BISHOP, KNIGHT, ROOK, CANNON, PAWN, NONE = 0, 1, 2, 3, 4, 5, 6, -1 27 | 28 | AI_SEARCH_DEPTH = 5 29 | 30 | init_fen = 'rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR r - - 0 1' 31 | 32 | replace_dict = { 33 | 'n': 'k', 34 | 'N': 'K', 35 | 'b': 'e', 36 | 'B': 'E', 37 | 'a': 'm', 38 | 'A': 'M', 39 | 'k': 's', 40 | 'K': 'S', 41 | 'r': 'r', 42 | 'R': 'R', 43 | 'p': 'p', 44 | 'P': 'P', 45 | 'c': 'c', 46 | 'C': 'C', 47 | } 48 | 49 | state_to_board_dict = { 50 | 'k': 'n', 51 | 'K': 'N', 52 | 'e': 'b', 53 | 'E': 'B', 54 | 'm': 'a', 55 | 'M': 'A', 56 | 's': 'k', 57 | 'S': 'K', 58 | 'r': 'r', 59 | 'R': 'R', 60 | 'p': 'p', 61 | 'P': 'P', 62 | 'c': 'c', 63 | 'C': 'C', 64 | } 65 | 66 | mov_dir = { 67 | 'k': [(0, -1), (1, 0), (0, 1), (-1, 0)], 68 | 'K': [(0, -1), (1, 0), (0, 1), (-1, 0)], 69 | 'a': [(-1, -1), (1, -1), (-1, 1), (1, 1)], 70 | 'A': [(-1, -1), (1, -1), (-1, 1), (1, 1)], 71 | 'b': [(-2, -2), (2, -2), (2, 2), (-2, 2)], 72 | 'B': [(-2, -2), (2, -2), (2, 2), (-2, 2)], 73 | 'n': [(-1, -2), (1, -2), (2, -1), (2, 1), (1, 2), (-1, 2), (-2, 1), (-2, -1)], 74 | 'N': [(-1, -2), (1, -2), (2, -1), (2, 1), (1, 2), (-1, 2), (-2, 1), (-2, -1)], 75 | 'P': [(0, -1), (-1, 0), (1, 0)], 76 | 'p': [(0, 1), (-1, 0), (1, 0)]} 77 | 78 | bishop_check = [(-1, -1), (1, -1), (-1, 1), (1, 1)] 79 | knight_check = [(0, -1), (0, -1), (1, 0), (1, 0), (0, 1), (0, 1), (-1, 0), (-1, 0)] 80 | 81 | def get_kind(fen_ch): 82 | if fen_ch in ['k', 'K']: 83 | return KING 84 | elif fen_ch in ['a', 'A']: 85 | return ADVISOR 86 | elif fen_ch in ['b', 'B']: 87 | return BISHOP 88 | elif fen_ch in ['n', 'N']: 89 | return KNIGHT 90 | elif fen_ch in ['r', 'R']: 91 | return ROOK 92 | elif fen_ch in ['c', 'C']: 93 | return CANNON 94 | elif fen_ch in ['p', 'P']: 95 | return PAWN 96 | else: 97 | return NONE 98 | 99 | def get_char(kind, color): 100 | if kind is KING: 101 | return ['K', 'k'][color] 102 | elif kind is ADVISOR: 103 | return ['A', 'a'][color] 104 | elif kind is BISHOP: 105 | return ['B', 'b'][color] 106 | elif kind is KNIGHT: 107 | return ['N', 'n'][color] 108 | elif kind is ROOK: 109 | return ['R', 'r'][color] 110 | elif kind is CANNON: 111 | return ['C', 'c'][color] 112 | elif kind is PAWN: 113 | return ['P', 'p'][color] 114 | else: 115 | return '' 116 | 117 | def move_to_str(x, y, x_, y_): 118 | move_str = '' 119 | move_str += str(x) 120 | move_str += str(y) 121 | move_str += str(x_) 122 | move_str += str(y_) 123 | return move_str 124 | 125 | def str_to_move(move_str): 126 | move_arr = [0] * 4 127 | move_arr[0] = int(move_str[0]) 128 | move_arr[1] = int(move_str[1]) 129 | move_arr[2] = int(move_str[2]) 130 | move_arr[3] = int(move_str[3]) 131 | return move_arr 132 | 133 | class Move: 134 | def __init__(self, uci:str): 135 | s = str_to_move(uci) 136 | self.p = (s[0],s[1]) 137 | self.n = (s[2],s[3]) 138 | self.uci = uci 139 | @staticmethod 140 | def from_uci(uci): 141 | return Move(uci) 142 | 143 | -------------------------------------------------------------------------------- /cchess_alphazero/environment/lookup_tables.py: -------------------------------------------------------------------------------- 1 | #-*- coding:utf-8 -*- 2 | 3 | from cchess_alphazero.environment.chessman import * 4 | from enum import Enum 5 | import numpy as np 6 | 7 | Chessman_2_idx = { 8 | Pawn: 0, 9 | Cannon: 1, 10 | Rook: 2, 11 | Knight: 3, 12 | Elephant: 4, 13 | Mandarin: 5, 14 | King: 6 15 | } 16 | 17 | Idx_2_Chessman = { 18 | 0: Pawn, 19 | 1: Cannon, 20 | 2: Rook, 21 | 3: Knight, 22 | 4: Elephant, 23 | 5: Mandarin, 24 | 6: King 25 | } 26 | 27 | Fen_2_Idx = { 28 | 'p': 0, 29 | 'P': 0, 30 | 'c': 1, 31 | 'C': 1, 32 | 'r': 2, 33 | 'R': 2, 34 | 'k': 3, 35 | 'K': 3, 36 | 'e': 4, 37 | 'E': 4, 38 | 'm': 5, 39 | 'M': 5, 40 | 's': 6, 41 | 'S': 6 42 | } 43 | 44 | class Color(Enum): 45 | Black = 0 46 | Red = 1 47 | 48 | Winner = Enum("Winner", "red black draw") 49 | 50 | def flip_move(x): 51 | new = '' 52 | new = ''.join([new, str(8 - int(x[0]))]) 53 | new = ''.join([new, str(9 - int(x[1]))]) 54 | new = ''.join([new, str(8 - int(x[2]))]) 55 | new = ''.join([new, str(9 - int(x[3]))]) 56 | return new 57 | 58 | def flip_action_labels(labels): 59 | return [flip_move(x) for x in labels] 60 | 61 | 62 | def create_action_labels(): 63 | labels_array = [] # [col_src,row_src,col_dst,row_dst] 64 | numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] # row 65 | letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8'] # col 66 | 67 | for n1 in range(10): 68 | for l1 in range(9): 69 | destinations = [(n1, t) for t in range(9)] + \ 70 | [(t, l1) for t in range(10)] + \ 71 | [(n1 + a, l1 + b) for (a, b) in 72 | [(-2, -1), (-1, -2), (-2, 1), (1, -2), (2, -1), (-1, 2), (2, 1), (1, 2)]] 73 | for (n2, l2) in destinations: 74 | if (n1, l1) != (n2, l2) and n2 in range(10) and l2 in range(9): 75 | move = letters[l1] + numbers[n1] + letters[l2] + numbers[n2] 76 | labels_array.append(move) 77 | 78 | #for red mandarin 79 | labels_array.append('3041') 80 | labels_array.append('5041') 81 | labels_array.append('3241') 82 | labels_array.append('5241') 83 | labels_array.append('4130') 84 | labels_array.append('4150') 85 | labels_array.append('4132') 86 | labels_array.append('4152') 87 | # for black mandarin 88 | labels_array.append('3948') 89 | labels_array.append('5948') 90 | labels_array.append('3748') 91 | labels_array.append('5748') 92 | labels_array.append('4839') 93 | labels_array.append('4859') 94 | labels_array.append('4837') 95 | labels_array.append('4857') 96 | 97 | #for red elephant 98 | labels_array.append('2002') 99 | labels_array.append('2042') 100 | labels_array.append('6042') 101 | labels_array.append('6082') 102 | labels_array.append('2402') 103 | labels_array.append('2442') 104 | labels_array.append('6442') 105 | labels_array.append('6482') 106 | labels_array.append('0220') 107 | labels_array.append('4220') 108 | labels_array.append('4260') 109 | labels_array.append('8260') 110 | labels_array.append('0224') 111 | labels_array.append('4224') 112 | labels_array.append('4264') 113 | labels_array.append('8264') 114 | # for black elephant 115 | labels_array.append('2907') 116 | labels_array.append('2947') 117 | labels_array.append('6947') 118 | labels_array.append('6987') 119 | labels_array.append('2507') 120 | labels_array.append('2547') 121 | labels_array.append('6547') 122 | labels_array.append('6587') 123 | labels_array.append('0729') 124 | labels_array.append('4729') 125 | labels_array.append('4769') 126 | labels_array.append('8769') 127 | labels_array.append('0725') 128 | labels_array.append('4725') 129 | labels_array.append('4765') 130 | labels_array.append('8765') 131 | 132 | return labels_array 133 | 134 | ActionLabelsRed = create_action_labels() 135 | ActionLabelsBlack = flip_action_labels(ActionLabelsRed) 136 | 137 | Unflipped_index = [ActionLabelsRed.index(x) for x in ActionLabelsBlack] 138 | 139 | def flip_policy(pol): 140 | global Unflipped_index 141 | return np.asarray([pol[ind] for ind in Unflipped_index]) 142 | -------------------------------------------------------------------------------- /cchess_alphazero/lib/data_helper.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | from datetime import datetime 4 | from glob import glob 5 | from logging import getLogger 6 | 7 | from cchess_alphazero.config import ResourceConfig 8 | 9 | logger = getLogger(__name__) 10 | 11 | def get_game_data_filenames(rc: ResourceConfig): 12 | pattern = os.path.join(rc.play_data_dir, rc.play_data_filename_tmpl % "*") 13 | # files = list(sorted(glob(pattern), key=get_key)) 14 | files = list(sorted(glob(pattern))) 15 | return files 16 | 17 | def write_game_data_to_file(path, data): 18 | with open(path, "wt") as f: 19 | json.dump(data, f) 20 | 21 | 22 | def read_game_data_from_file(path): 23 | with open(path, "rt") as f: 24 | return json.load(f) 25 | 26 | def get_key(x): 27 | stat_x = os.stat(x) 28 | return stat_x.st_ctime 29 | -------------------------------------------------------------------------------- /cchess_alphazero/lib/elo_helper.py: -------------------------------------------------------------------------------- 1 | from logging import getLogger 2 | 3 | logger = getLogger(__name__) 4 | 5 | # 0 ~ 999: K = 30; 1000 ~ 1999: K = 15; 2000 ~ 2999: K = 10; 3000 ~ : K = 5 6 | K_TABLE = [30, 15, 10, 5] 7 | 8 | R_PRI = 40 9 | 10 | def compute_elo(r0, r1, w): 11 | ''' 12 | Compute the elo rating with method from http://www.xqbase.com/protocol/elostat.htm 13 | r0: red player's elo rating 14 | r1: black player's elo rating 15 | w: game result: 1 = red win, 0.5 = draw, 0 = black win 16 | ''' 17 | relative_elo = r1 - r0 - R_PRI 18 | we = 1 / (1 + 10 ** (relative_elo / 400)) 19 | k0 = K_TABLE[-1] if r0 >= 3000 else K_TABLE[r0 // 1000] 20 | k1 = K_TABLE[-1] if r1 >= 3000 else K_TABLE[r1 // 1000] 21 | rn0 = int(r0 + k0 * (w - we)) 22 | rn1 = int(r1 + k1 * (we - w)) 23 | rn0 = rn0 if rn0 > 0 else 0 24 | rn1 = rn1 if rn1 > 0 else 0 25 | return (rn0, rn1) 26 | -------------------------------------------------------------------------------- /cchess_alphazero/lib/logger.py: -------------------------------------------------------------------------------- 1 | from logging import StreamHandler, basicConfig, DEBUG, getLogger, Formatter, FileHandler 2 | 3 | 4 | def setup_logger(log_filename): 5 | format_str = '%(asctime)s@%(name)s %(levelname)s # %(message)s' 6 | basicConfig(filename=log_filename, level=DEBUG, format=format_str) 7 | stream_handler = StreamHandler() 8 | stream_handler.setFormatter(Formatter(format_str)) 9 | getLogger().addHandler(stream_handler) 10 | 11 | def setup_file_logger(log_filename): 12 | format_str = '%(asctime)s@%(name)s %(levelname)s # %(message)s' 13 | basicConfig(filename=log_filename, level=DEBUG, format=format_str) 14 | 15 | if __name__ == '__main__': 16 | setup_logger("test.log") 17 | logger = getLogger("test") 18 | logger.info("OK") 19 | -------------------------------------------------------------------------------- /cchess_alphazero/lib/model_helper.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | from logging import getLogger 4 | 5 | logger = getLogger(__name__) 6 | 7 | 8 | def load_best_model_weight(model): 9 | """ 10 | :param cchess_alphazero.agent.model.CChessModel model: 11 | :return: 12 | """ 13 | return model.load(model.config.resource.model_best_config_path, model.config.resource.model_best_weight_path) 14 | 15 | def load_best_model_weight_from_internet(model): 16 | """ 17 | :param cchess_alphazero.agent.model.CChessModel model: 18 | :return: 19 | """ 20 | from cchess_alphazero.lib.web_helper import download_file 21 | logger.info(f"download model from remote server") 22 | download_file(model.config.internet.download_url, model.config.resource.model_best_weight_path) 23 | return model.load(model.config.resource.model_best_config_path, model.config.resource.model_best_weight_path) 24 | 25 | 26 | def save_as_best_model(model): 27 | """ 28 | 29 | :param cchess_alphazero.agent.model.CChessModel model: 30 | :return: 31 | """ 32 | return model.save(model.config.resource.model_best_config_path, model.config.resource.model_best_weight_path) 33 | 34 | 35 | def need_to_reload_best_model_weight(model): 36 | """ 37 | 38 | :param cchess_alphazero.agent.model.CChessModel model: 39 | :return: 40 | """ 41 | logger.debug("start reload the best model if changed") 42 | digest = model.fetch_digest(model.config.resource.model_best_weight_path) 43 | if digest != model.digest: 44 | return True 45 | 46 | logger.debug("the best model is not changed") 47 | return False 48 | 49 | def load_model_weight(model, config_path, weight_path, name=None): 50 | if name is not None: 51 | logger.info(f"{name}: load model from {config_path}") 52 | return model.load(config_path, weight_path) 53 | 54 | def save_as_next_generation_model(model): 55 | return model.save(model.config.resource.next_generation_config_path, model.config.resource.next_generation_weight_path) 56 | 57 | 58 | def load_sl_best_model_weight(model): 59 | """ 60 | :param cchess_alphazero.agent.model.CChessModel model: 61 | :return: 62 | """ 63 | return model.load(model.config.resource.sl_best_config_path, model.config.resource.sl_best_weight_path) 64 | 65 | 66 | def save_as_sl_best_model(model): 67 | """ 68 | 69 | :param cchess_alphazero.agent.model.CChessModel model: 70 | :return: 71 | """ 72 | return model.save(model.config.resource.sl_best_config_path, model.config.resource.sl_best_weight_path) 73 | -------------------------------------------------------------------------------- /cchess_alphazero/lib/tf_util.py: -------------------------------------------------------------------------------- 1 | 2 | def set_session_config(per_process_gpu_memory_fraction=None, allow_growth=None, device_list='0'): 3 | """ 4 | 5 | :param allow_growth: When necessary, reserve memory 6 | :param float per_process_gpu_memory_fraction: specify GPU memory usage as 0 to 1 7 | 8 | :return: 9 | """ 10 | import tensorflow as tf 11 | import keras.backend as K 12 | 13 | config = tf.ConfigProto( 14 | gpu_options=tf.GPUOptions( 15 | per_process_gpu_memory_fraction=per_process_gpu_memory_fraction, 16 | allow_growth=allow_growth, 17 | visible_device_list=device_list 18 | ) 19 | ) 20 | sess = tf.Session(config=config) 21 | K.set_session(sess) 22 | -------------------------------------------------------------------------------- /cchess_alphazero/lib/web_helper.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import os 3 | from logging import getLogger 4 | from urllib.request import urlopen 5 | from tqdm import tqdm 6 | 7 | logger = getLogger(__name__) 8 | 9 | def upload_file(url, path, filename=None, data=None, rm=False): 10 | filename = filename if filename is not None else 'file' 11 | files = {'file': (filename, open(path, 'rb'), 'application/json')} 12 | success = False 13 | for i in range(3): 14 | try: 15 | r = requests.post(url, files=files, data=data) 16 | if r.status_code != 200: 17 | logger.error(f"Error occurs when upload {filename}: {r.text}") 18 | else: 19 | success = True 20 | break 21 | except Exception as e: 22 | logger.error(f"Error occurs when upload {filename}: {e}") 23 | if rm: 24 | os.remove(path) 25 | return r.json() if success else None 26 | 27 | def http_request(url, post=False, data=None): 28 | success = False 29 | for i in range(3): 30 | try: 31 | if post: 32 | r = requests.post(url, data=data) 33 | else: 34 | r = requests.get(url) 35 | if r.status_code != 200: 36 | logger.error(f"Error occurs when request {url}: {r.text}") 37 | else: 38 | success = True 39 | break 40 | except Exception as e: 41 | logger.error(f"Error occurs when request {url}: {e}") 42 | return r.json() if success else None 43 | 44 | def download_file(url, save_path): 45 | if os.path.exists(save_path): 46 | os.remove(save_path) 47 | file_size = int(urlopen(url).info().get('Content-Length', -1)) 48 | if os.path.exists(save_path): 49 | first_byte = os.path.getsize(save_path) 50 | else: 51 | first_byte = 0 52 | if first_byte >= file_size: 53 | return file_size 54 | header = {"Range": "bytes=%s-%s" % (first_byte, file_size)} 55 | pbar = tqdm( 56 | total=file_size, initial=first_byte, 57 | unit='B', unit_scale=True, desc=url.split('/')[-1]) 58 | req = requests.get(url, headers=header, stream=True) 59 | with(open(save_path, 'ab')) as f: 60 | for chunk in req.iter_content(chunk_size=1024): 61 | if chunk: 62 | f.write(chunk) 63 | pbar.update(1024) 64 | pbar.close() 65 | return True 66 | 67 | 68 | -------------------------------------------------------------------------------- /cchess_alphazero/manager.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import multiprocessing as mp 3 | 4 | from logging import getLogger 5 | 6 | from cchess_alphazero.lib.logger import setup_logger 7 | from cchess_alphazero.config import Config, PlayWithHumanConfig 8 | 9 | logger = getLogger(__name__) 10 | 11 | CMD_LIST = ['self', 'opt', 'eval', 'play', 'eval', 'sl', 'ob'] 12 | PIECE_STYLE_LIST = ['WOOD', 'POLISH', 'DELICATE'] 13 | BG_STYLE_LIST = ['CANVAS', 'DROPS', 'GREEN', 'QIANHONG', 'SHEET', 'SKELETON', 'WHITE', 'WOOD'] 14 | RANDOM_LIST = ['none', 'small', 'medium', 'large'] 15 | 16 | def create_parser(): 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument("cmd", help="what to do", choices=CMD_LIST) 19 | parser.add_argument("--new", help="run from new best model", action="store_true") 20 | parser.add_argument("--type", help="use normal setting", default="mini") 21 | parser.add_argument("--total-step", help="set TrainerConfig.start_total_steps", type=int) 22 | parser.add_argument("--ai-move-first", help="set human or AI move first", action="store_true") 23 | parser.add_argument("--cli", help="play with AI with CLI, default with GUI", action="store_true") 24 | parser.add_argument("--gpu", help="device list", default="0") 25 | parser.add_argument("--onegreen", help="train sl work with onegreen data", action="store_true") 26 | parser.add_argument("--skip", help="skip games", default=0, type=int) 27 | parser.add_argument("--ucci", help="play with ucci engine instead of self play", action="store_true") 28 | parser.add_argument("--piece-style", help="choose a style of piece", choices=PIECE_STYLE_LIST, default="WOOD") 29 | parser.add_argument("--bg-style", help="choose a style of board", choices=BG_STYLE_LIST, default="WOOD") 30 | parser.add_argument("--random", help="choose a style of randomness", choices=RANDOM_LIST, default="none") 31 | parser.add_argument("--distributed", help="whether upload/download file from remote server", action="store_true") 32 | parser.add_argument("--elo", help="whether to compute elo score", action="store_true") 33 | return parser 34 | 35 | def setup(config: Config, args): 36 | config.opts.new = args.new 37 | if args.total_step is not None: 38 | config.trainer.start_total_steps = args.total_step 39 | config.opts.device_list = args.gpu 40 | config.resource.create_directories() 41 | if args.cmd == 'self': 42 | setup_logger(config.resource.main_log_path) 43 | elif args.cmd == 'opt': 44 | setup_logger(config.resource.opt_log_path) 45 | elif args.cmd == 'play' or args.cmd == 'ob': 46 | setup_logger(config.resource.play_log_path) 47 | elif args.cmd == 'eval': 48 | setup_logger(config.resource.eval_log_path) 49 | elif args.cmd == 'sl': 50 | setup_logger(config.resource.sl_log_path) 51 | 52 | def start(): 53 | parser = create_parser() 54 | args = parser.parse_args() 55 | config_type = args.type 56 | 57 | config = Config(config_type=config_type) 58 | setup(config, args) 59 | 60 | logger.info('Config type: %s' % (config_type)) 61 | config.opts.piece_style = args.piece_style 62 | config.opts.bg_style = args.bg_style 63 | config.internet.distributed = args.distributed 64 | 65 | # use multiple GPU 66 | gpus = config.opts.device_list.split(',') 67 | if len(gpus) > 1: 68 | config.opts.use_multiple_gpus = True 69 | config.opts.gpu_num = len(gpus) 70 | logger.info(f"User GPU {config.opts.device_list}") 71 | 72 | if args.cmd == 'self': 73 | if args.ucci: 74 | import cchess_alphazero.worker.play_with_ucci_engine as self_play 75 | else: 76 | if mp.get_start_method() == 'spawn': 77 | import cchess_alphazero.worker.self_play_windows as self_play 78 | else: 79 | from cchess_alphazero.worker import self_play 80 | return self_play.start(config) 81 | elif args.cmd == 'opt': 82 | from cchess_alphazero.worker import optimize 83 | return optimize.start(config) 84 | elif args.cmd == 'play': 85 | if args.cli: 86 | import cchess_alphazero.play_games.play_cli as play 87 | else: 88 | from cchess_alphazero.play_games import play 89 | config.opts.light = False 90 | pwhc = PlayWithHumanConfig() 91 | pwhc.update_play_config(config.play) 92 | logger.info(f"AI move first : {args.ai_move_first}") 93 | play.start(config, not args.ai_move_first) 94 | elif args.cmd == 'eval': 95 | if args.elo == False: 96 | from cchess_alphazero.worker import evaluator 97 | else: 98 | if mp.get_start_method() == 'spawn': 99 | import cchess_alphazero.worker.compute_elo_windows as evaluator 100 | else: 101 | import cchess_alphazero.worker.compute_elo as evaluator 102 | config.eval.update_play_config(config.play) 103 | evaluator.start(config) 104 | elif args.cmd == 'sl': 105 | if args.onegreen: 106 | import cchess_alphazero.worker.sl_onegreen as sl 107 | sl.start(config, args.skip) 108 | else: 109 | from cchess_alphazero.worker import sl 110 | sl.start(config) 111 | 112 | elif args.cmd == 'ob': 113 | from cchess_alphazero.play_games import ob_self_play 114 | pwhc = PlayWithHumanConfig() 115 | pwhc.update_play_config(config.play) 116 | ob_self_play.start(config, args.ucci, args.ai_move_first) 117 | 118 | -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/CANVAS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/CANVAS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BA.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BA.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BAS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BAS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BB.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BB.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BBS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BBS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BC.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BC.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BCS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BCS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BK.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BK.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BKM.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BKM.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BKS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BKS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BN.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BN.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BNS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BNS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BP.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BP.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BPS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BPS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BR.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BR.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/BRS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/BRS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/OO.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/OO.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/OOS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/OOS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RA.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RA.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RAS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RAS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RB.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RB.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RBS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RBS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RC.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RC.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RCS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RCS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RK.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RK.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RKM.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RKM.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RKS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RKS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RN.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RN.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RNS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RNS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RP.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RP.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RPS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RPS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RR.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RR.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DELICATE/RRS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DELICATE/RRS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/DROPS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/DROPS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/GREEN.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/GREEN.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BA.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BA.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BAS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BAS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BB.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BB.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BBS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BBS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BC.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BC.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BCS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BCS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BK.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BK.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BKM.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BKM.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BKS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BKS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BN.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BN.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BNS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BNS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BP.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BP.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BPS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BPS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BR.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BR.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/BRS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/BRS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/OO.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/OO.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/OOS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/OOS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RA.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RA.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RAS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RAS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RB.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RB.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RBS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RBS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RC.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RC.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RCS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RCS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RK.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RK.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RKM.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RKM.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RKS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RKS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RN.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RN.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RNS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RNS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RP.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RP.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RPS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RPS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RR.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RR.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/POLISH/RRS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/POLISH/RRS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/QIANHONG.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/QIANHONG.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/SHEET.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/SHEET.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/SKELETON.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/SKELETON.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WHITE.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WHITE.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BA.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BA.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BAS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BAS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BB.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BB.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BBS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BBS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BC.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BC.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BCS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BCS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BK.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BK.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BKM.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BKM.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BKS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BKS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BN.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BN.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BNS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BNS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BP.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BP.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BPS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BPS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BR.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BR.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/BRS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/BRS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/OO.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/OO.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/OOS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/OOS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RA.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RA.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RAS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RAS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RB.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RB.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RBS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RBS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RC.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RC.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RCS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RCS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RK.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RK.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RKM.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RKM.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RKS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RKS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RN.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RN.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RNS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RNS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RP.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RP.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RPS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RPS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RR.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RR.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/images/WOOD/RRS.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/play_games/images/WOOD/RRS.GIF -------------------------------------------------------------------------------- /cchess_alphazero/play_games/ob_self_play.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import numpy as np 4 | from collections import defaultdict 5 | from logging import getLogger 6 | from time import sleep, time 7 | 8 | import cchess_alphazero.environment.static_env as senv 9 | from cchess_alphazero.environment.chessboard import Chessboard 10 | from cchess_alphazero.environment.chessman import * 11 | from cchess_alphazero.agent.model import CChessModel 12 | from cchess_alphazero.agent.player import CChessPlayer, VisitState 13 | from cchess_alphazero.agent.api import CChessModelAPI 14 | from cchess_alphazero.config import Config 15 | from cchess_alphazero.environment.env import CChessEnv 16 | from cchess_alphazero.environment.lookup_tables import Winner, ActionLabelsRed, flip_move 17 | from cchess_alphazero.lib.model_helper import load_best_model_weight 18 | from cchess_alphazero.lib.tf_util import set_session_config 19 | 20 | logger = getLogger(__name__) 21 | 22 | def start(config: Config, ucci=False, ai_move_first=True): 23 | set_session_config(per_process_gpu_memory_fraction=1, allow_growth=True, device_list=config.opts.device_list) 24 | if not ucci: 25 | play = ObSelfPlay(config) 26 | else: 27 | play = ObSelfPlayUCCI(config, ai_move_first) 28 | play.start() 29 | 30 | class ObSelfPlay: 31 | def __init__(self, config: Config): 32 | self.config = config 33 | self.env = CChessEnv() 34 | self.model = None 35 | self.pipe = None 36 | self.ai = None 37 | self.chessmans = None 38 | 39 | def load_model(self): 40 | self.model = CChessModel(self.config) 41 | if self.config.opts.new or not load_best_model_weight(self.model): 42 | self.model.build() 43 | 44 | def start(self): 45 | self.env.reset() 46 | self.load_model() 47 | self.pipe = self.model.get_pipes() 48 | self.ai = CChessPlayer(self.config, search_tree=defaultdict(VisitState), pipes=self.pipe, 49 | enable_resign=True, debugging=False) 50 | 51 | labels = ActionLabelsRed 52 | labels_n = len(ActionLabelsRed) 53 | 54 | self.env.board.print_to_cl() 55 | history = [self.env.get_state()] 56 | 57 | while not self.env.board.is_end(): 58 | no_act = None 59 | state = self.env.get_state() 60 | if state in history[:-1]: 61 | no_act = [] 62 | for i in range(len(history) - 1): 63 | if history[i] == state: 64 | no_act.append(history[i + 1]) 65 | action, _ = self.ai.action(state, self.env.num_halfmoves, no_act) 66 | history.append(action) 67 | if action is None: 68 | print("AI投降了!") 69 | break 70 | move = self.env.board.make_single_record(int(action[0]), int(action[1]), int(action[2]), int(action[3])) 71 | if not self.env.red_to_move: 72 | action = flip_move(action) 73 | self.env.step(action) 74 | history.append(self.env.get_state()) 75 | print(f"AI选择移动 {move}") 76 | self.env.board.print_to_cl() 77 | sleep(1) 78 | 79 | self.ai.close() 80 | print(f"胜者是 is {self.env.board.winner} !!!") 81 | self.env.board.print_record() 82 | 83 | class ObSelfPlayUCCI: 84 | def __init__(self, config: Config, ai_move_first=True): 85 | self.config = config 86 | self.env = CChessEnv() 87 | self.model = None 88 | self.pipe = None 89 | self.ai = None 90 | self.chessmans = None 91 | self.ai_move_first = ai_move_first 92 | 93 | def load_model(self): 94 | self.model = CChessModel(self.config) 95 | if self.config.opts.new or not load_best_model_weight(self.model): 96 | self.model.build() 97 | 98 | def start(self): 99 | self.env.reset() 100 | self.load_model() 101 | self.pipe = self.model.get_pipes() 102 | self.ai = CChessPlayer(self.config, search_tree=defaultdict(VisitState), pipes=self.pipe, 103 | enable_resign=True, debugging=False) 104 | 105 | labels = ActionLabelsRed 106 | labels_n = len(ActionLabelsRed) 107 | 108 | self.env.board.print_to_cl() 109 | history = [self.env.get_state()] 110 | turns = 0 111 | game_over = False 112 | final_move = None 113 | 114 | while not game_over: 115 | if (self.ai_move_first and turns % 2 == 0) or (not self.ai_move_first and turns % 2 == 1): 116 | start_time = time() 117 | no_act = None 118 | state = self.env.get_state() 119 | if state in history[:-1]: 120 | no_act = [] 121 | for i in range(len(history) - 1): 122 | if history[i] == state: 123 | act = history[i + 1] 124 | if not self.env.red_to_move: 125 | act = flip_move(act) 126 | no_act.append(act) 127 | action, _ = self.ai.action(state, self.env.num_halfmoves, no_act) 128 | end_time = time() 129 | if action is None: 130 | print("AlphaZero 投降了!") 131 | break 132 | move = self.env.board.make_single_record(int(action[0]), int(action[1]), int(action[2]), int(action[3])) 133 | print(f"AlphaZero 选择移动 {move}, 消耗时间 {(end_time - start_time):.2f}s") 134 | if not self.env.red_to_move: 135 | action = flip_move(action) 136 | else: 137 | state = self.env.get_state() 138 | print(state) 139 | fen = senv.state_to_fen(state, turns) 140 | action = self.get_ucci_move(fen) 141 | if action is None: 142 | print("Eleeye 投降了!") 143 | break 144 | print(action) 145 | if not self.env.red_to_move: 146 | rec_action = flip_move(action) 147 | else: 148 | rec_action = action 149 | move = self.env.board.make_single_record(int(rec_action[0]), int(rec_action[1]), int(rec_action[2]), int(rec_action[3])) 150 | print(f"Eleeye 选择移动 {move}") 151 | history.append(action) 152 | self.env.step(action) 153 | history.append(self.env.get_state()) 154 | self.env.board.print_to_cl() 155 | turns += 1 156 | sleep(1) 157 | game_over, final_move = self.env.board.is_end_final_move() 158 | print(game_over, final_move) 159 | 160 | if final_move: 161 | move = self.env.board.make_single_record(int(final_move[0]), int(final_move[1]), int(final_move[2]), int(final_move[3])) 162 | print(f"Final Move {move}") 163 | if not self.env.red_to_move: 164 | final_move = flip_move(final_move) 165 | self.env.step(final_move) 166 | self.env.board.print_to_cl() 167 | 168 | self.ai.close() 169 | print(f"胜者是 is {self.env.board.winner} !!!") 170 | self.env.board.print_record() 171 | 172 | def get_ucci_move(self, fen, time=3): 173 | p = subprocess.Popen(self.config.resource.eleeye_path, 174 | stdin=subprocess.PIPE, 175 | stdout=subprocess.PIPE, 176 | stderr=subprocess.PIPE, 177 | universal_newlines=True) 178 | setfen = f'position fen {fen}\n' 179 | setrandom = 'setoption randomness small\n' 180 | cmd = 'ucci\n' + setrandom + setfen + f'go time {time * 1000}\n' 181 | try: 182 | out, err = p.communicate(cmd, timeout=time+0.5) 183 | except: 184 | p.kill() 185 | try: 186 | out, err = p.communicate() 187 | except Exception as e: 188 | logger.error(f"{e}, cmd = {cmd}") 189 | return self.get_ucci_move(fen, time+1) 190 | print(out) 191 | lines = out.split('\n') 192 | if lines[-2] == 'nobestmove': 193 | return None 194 | move = lines[-2].split(' ')[1] 195 | if move == 'depth': 196 | move = lines[-1].split(' ')[6] 197 | return senv.parse_ucci_move(move) 198 | -------------------------------------------------------------------------------- /cchess_alphazero/play_games/play_cli.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from logging import getLogger 3 | 4 | import cchess_alphazero.environment.static_env as senv 5 | from cchess_alphazero.environment.chessboard import Chessboard 6 | from cchess_alphazero.environment.chessman import * 7 | from cchess_alphazero.agent.model import CChessModel 8 | from cchess_alphazero.agent.player import CChessPlayer, VisitState 9 | from cchess_alphazero.agent.api import CChessModelAPI 10 | from cchess_alphazero.config import Config 11 | from cchess_alphazero.environment.env import CChessEnv 12 | from cchess_alphazero.environment.lookup_tables import Winner, ActionLabelsRed, flip_move 13 | from cchess_alphazero.lib.model_helper import load_best_model_weight 14 | from cchess_alphazero.lib.tf_util import set_session_config 15 | 16 | logger = getLogger(__name__) 17 | 18 | def start(config: Config, human_move_first=True): 19 | set_session_config(per_process_gpu_memory_fraction=1, allow_growth=True, device_list=config.opts.device_list) 20 | play = PlayWithHuman(config) 21 | play.start(human_move_first) 22 | 23 | class PlayWithHuman: 24 | def __init__(self, config: Config): 25 | self.config = config 26 | self.env = CChessEnv() 27 | self.model = None 28 | self.pipe = None 29 | self.ai = None 30 | self.chessmans = None 31 | self.human_move_first = True 32 | 33 | def load_model(self): 34 | self.model = CChessModel(self.config) 35 | if self.config.opts.new or not load_best_model_weight(self.model): 36 | self.model.build() 37 | 38 | def start(self, human_first=True): 39 | self.env.reset() 40 | self.load_model() 41 | self.pipe = self.model.get_pipes() 42 | self.ai = CChessPlayer(self.config, search_tree=defaultdict(VisitState), pipes=self.pipe, 43 | enable_resign=True, debugging=False) 44 | self.human_move_first = human_first 45 | 46 | labels = ActionLabelsRed 47 | labels_n = len(ActionLabelsRed) 48 | 49 | self.env.board.print_to_cl() 50 | 51 | while not self.env.board.is_end(): 52 | if human_first == self.env.red_to_move: 53 | self.env.board.calc_chessmans_moving_list() 54 | is_correct_chessman = False 55 | is_correct_position = False 56 | chessman = None 57 | while not is_correct_chessman: 58 | title = "请输入棋子位置: " 59 | input_chessman_pos = input(title) 60 | x, y = int(input_chessman_pos[0]), int(input_chessman_pos[1]) 61 | chessman = self.env.board.chessmans[x][y] 62 | if chessman != None and chessman.is_red == self.env.board.is_red_turn: 63 | is_correct_chessman = True 64 | print(f"当前棋子为{chessman.name_cn},可以落子的位置有:") 65 | for point in chessman.moving_list: 66 | print(point.x, point.y) 67 | else: 68 | print("没有找到此名字的棋子或未轮到此方走子") 69 | while not is_correct_position: 70 | title = "请输入落子的位置: " 71 | input_chessman_pos = input(title) 72 | x, y = int(input_chessman_pos[0]), int(input_chessman_pos[1]) 73 | is_correct_position = chessman.move(x, y) 74 | if is_correct_position: 75 | self.env.board.print_to_cl() 76 | self.env.board.clear_chessmans_moving_list() 77 | else: 78 | action, policy = self.ai.action(self.env.get_state(), self.env.num_halfmoves) 79 | if not self.env.red_to_move: 80 | action = flip_move(action) 81 | if action is None: 82 | print("AI投降了!") 83 | break 84 | self.env.step(action) 85 | print(f"AI选择移动 {action}") 86 | self.env.board.print_to_cl() 87 | 88 | self.ai.close() 89 | print(f"胜者是 is {self.env.board.winner} !!!") 90 | self.env.board.print_record() 91 | -------------------------------------------------------------------------------- /cchess_alphazero/play_games/test_cli_game.py: -------------------------------------------------------------------------------- 1 | #-*- coding:utf-8 -*- 2 | 3 | from cchess_alphazero.environment.chessboard import Chessboard 4 | 5 | def print_chessman_name(chessman): 6 | if chessman: 7 | print(chessman.name) 8 | else: 9 | print("None") 10 | 11 | 12 | def main(): 13 | cbd = Chessboard('000') 14 | cbd.init_board() 15 | cbd.print_to_cl() 16 | while not cbd.is_end(): 17 | cbd.calc_chessmans_moving_list() 18 | if cbd.is_red_turn: 19 | print("is_red_turn") 20 | else: 21 | print("is_black_turn") 22 | is_correct_chessman = False 23 | is_correct_position = False 24 | chessman = None 25 | while not is_correct_chessman: 26 | title = "请输入棋子名字: " 27 | input_chessman_name = input(title) 28 | chessman = cbd.get_chessman_by_name(input_chessman_name) 29 | if chessman != None and chessman.is_red == cbd.is_red_turn: 30 | is_correct_chessman = True 31 | print("当前可以落子的位置有:") 32 | for point in chessman.moving_list: 33 | print(point.x, point.y) 34 | print(cbd.legal_moves()) 35 | else: 36 | print("没有找到此名字的棋子或未轮到此方走子") 37 | while not is_correct_position: 38 | title = "请输入落子的位置: " 39 | input_chessman_position0 = int(input(title)) 40 | input_chessman_position1 = int(input(title)) 41 | print("Inputed Position:", input_chessman_position0, input_chessman_position1) 42 | is_correct_position = chessman.move( 43 | input_chessman_position0, input_chessman_position1) 44 | if is_correct_position: 45 | cbd.print_to_cl() 46 | cbd.clear_chessmans_moving_list() 47 | 48 | 49 | if __name__ == '__main__': 50 | main() 51 | 52 | -------------------------------------------------------------------------------- /cchess_alphazero/play_games/test_window_game.py: -------------------------------------------------------------------------------- 1 | #-*- coding:utf-8 -*- 2 | 3 | import sys 4 | import pygame 5 | import random 6 | import os.path 7 | 8 | from cchess_alphazero.environment.chessboard import Chessboard 9 | from cchess_alphazero.environment.chessman import * 10 | from pygame.locals import * 11 | 12 | main_dir = os.path.split(os.path.abspath(__file__))[0] 13 | SCREENRECT = Rect(0, 0, 700, 577) 14 | PIECE_STYLE = 'POLISH' 15 | BOARD_STYLE = 'QIANHONG' 16 | 17 | 18 | def load_image(file, sub_dir=None): 19 | '''loads an image, prepares it for play''' 20 | if sub_dir: 21 | file = os.path.join(main_dir, 'images', sub_dir, file) 22 | else: 23 | file = os.path.join(main_dir, 'images', file) 24 | try: 25 | surface = pygame.image.load(file) 26 | except pygame.error: 27 | raise SystemExit('Could not load image "%s" %s' % 28 | (file, pygame.get_error())) 29 | return surface.convert() 30 | 31 | 32 | def load_images(*files): 33 | imgs = [] 34 | style = PIECE_STYLE 35 | for file in files: 36 | imgs.append(load_image(file, style)) 37 | return imgs 38 | 39 | 40 | class Chessman_Sprite(pygame.sprite.Sprite): 41 | is_selected = False 42 | images = [] 43 | is_transparent = False 44 | 45 | def __init__(self, images, chessman): 46 | pygame.sprite.Sprite.__init__(self) 47 | self.chessman = chessman 48 | self.images = images 49 | self.image = self.images[0] 50 | self.rect = Rect(chessman.col_num * 57, (9 - chessman.row_num) * 57, 57, 57) 51 | 52 | def move(self, col_num, row_num): 53 | # print self.chessman.name, col_num, row_num 54 | old_col_num = self.chessman.col_num 55 | old_row_num = self.chessman.row_num 56 | is_correct_position = self.chessman.move(col_num, row_num) 57 | if is_correct_position: 58 | self.rect.move_ip((col_num - old_col_num) 59 | * 57, (old_row_num - row_num) * 57) 60 | self.rect = self.rect.clamp(SCREENRECT) 61 | self.chessman.chessboard.clear_chessmans_moving_list() 62 | self.chessman.chessboard.calc_chessmans_moving_list() 63 | return True 64 | return False 65 | 66 | def update(self): 67 | if self.is_selected: 68 | self.image = self.images[1] 69 | else: 70 | self.image = self.images[0] 71 | 72 | 73 | def creat_sprite_group(sprite_group, chessmans_hash): 74 | for chess in chessmans_hash.values(): 75 | if chess.is_red: 76 | if isinstance(chess, Rook): 77 | images = load_images("RR.GIF", "RRS.GIF") 78 | elif isinstance(chess, Cannon): 79 | images = load_images("RC.GIF", "RCS.GIF") 80 | elif isinstance(chess, Knight): 81 | images = load_images("RN.GIF", "RNS.GIF") 82 | elif isinstance(chess, King): 83 | images = load_images("RK.GIF", "RKS.GIF") 84 | elif isinstance(chess, Elephant): 85 | images = load_images("RB.GIF", "RBS.GIF") 86 | elif isinstance(chess, Mandarin): 87 | images = load_images("RA.GIF", "RAS.GIF") 88 | else: 89 | images = load_images("RP.GIF", "RPS.GIF") 90 | else: 91 | if isinstance(chess, Rook): 92 | images = load_images("BR.GIF", "BRS.GIF") 93 | elif isinstance(chess, Cannon): 94 | images = load_images("BC.GIF", "BCS.GIF") 95 | elif isinstance(chess, Knight): 96 | images = load_images("BN.GIF", "BNS.GIF") 97 | elif isinstance(chess, King): 98 | images = load_images("BK.GIF", "BKS.GIF") 99 | elif isinstance(chess, Elephant): 100 | images = load_images("BB.GIF", "BBS.GIF") 101 | elif isinstance(chess, Mandarin): 102 | images = load_images("BA.GIF", "BAS.GIF") 103 | else: 104 | images = load_images("BP.GIF", "BPS.GIF") 105 | chessman_sprite = Chessman_Sprite(images, chess) 106 | sprite_group.add(chessman_sprite) 107 | 108 | 109 | def select_sprite_from_group(sprite_group, col_num, row_num): 110 | for sprite in sprite_group: 111 | if sprite.chessman.col_num == col_num and sprite.chessman.row_num == row_num: 112 | return sprite 113 | 114 | 115 | def translate_hit_area(screen_x, screen_y): 116 | return screen_x // 57, 9 - screen_y // 57 117 | 118 | 119 | def main(winstyle=0): 120 | 121 | pygame.init() 122 | bestdepth = pygame.display.mode_ok(SCREENRECT.size, winstyle, 32) 123 | screen = pygame.display.set_mode(SCREENRECT.size, winstyle, bestdepth) 124 | pygame.display.set_caption("中国象棋-AlphaZero") 125 | 126 | # create the background, tile the bgd image 127 | bgdtile = load_image(f'{BOARD_STYLE}.GIF') 128 | board_background = pygame.Surface([521, 577]) 129 | board_background.blit(bgdtile, (0, 0)) 130 | widget_background = pygame.Surface([700 - 521, 577]) 131 | white_rect = Rect(0, 0, 700 - 521, 577) 132 | widget_background.fill((255, 255, 255), white_rect) 133 | 134 | #create text label 135 | font_file = os.path.join(main_dir, 'PingFang.ttc') 136 | font = pygame.font.Font(font_file, 16) 137 | font_color = (0, 0, 0) 138 | font_background = (255, 255, 255) 139 | t = font.render("着法记录", True, font_color, font_background) 140 | t_rect = t.get_rect() 141 | t_rect.centerx = (700 - 521) / 2 142 | t_rect.y = 10 143 | widget_background.blit(t, t_rect) 144 | 145 | # background = pygame.Surface(SCREENRECT.size) 146 | # for x in range(0, SCREENRECT.width, bgdtile.get_width()): 147 | # background.blit(bgdtile, (x, 0)) 148 | screen.blit(board_background, (0, 0)) 149 | screen.blit(widget_background, (521, 0)) 150 | pygame.display.flip() 151 | 152 | cbd = Chessboard('000') 153 | cbd.init_board() 154 | 155 | chessmans = pygame.sprite.Group() 156 | framerate = pygame.time.Clock() 157 | 158 | creat_sprite_group(chessmans, cbd.chessmans_hash) 159 | current_chessman = None 160 | cbd.calc_chessmans_moving_list() 161 | print(cbd.legal_moves()) 162 | while not cbd.is_end(): 163 | for event in pygame.event.get(): 164 | if event.type == pygame.QUIT: 165 | cbd.print_record() 166 | sys.exit() 167 | elif event.type == MOUSEBUTTONDOWN: 168 | pressed_array = pygame.mouse.get_pressed() 169 | for index in range(len(pressed_array)): 170 | if index == 0 and pressed_array[index]: 171 | mouse_x, mouse_y = pygame.mouse.get_pos() 172 | col_num, row_num = translate_hit_area(mouse_x, mouse_y) 173 | chessman_sprite = select_sprite_from_group( 174 | chessmans, col_num, row_num) 175 | if current_chessman is None and chessman_sprite != None: 176 | if chessman_sprite.chessman.is_red == cbd.is_red_turn: 177 | current_chessman = chessman_sprite 178 | chessman_sprite.is_selected = True 179 | elif current_chessman != None and chessman_sprite != None: 180 | if chessman_sprite.chessman.is_red == cbd.is_red_turn: 181 | current_chessman.is_selected = False 182 | current_chessman = chessman_sprite 183 | chessman_sprite.is_selected = True 184 | else: 185 | success = current_chessman.move(col_num, row_num) 186 | if success: 187 | chessmans.remove(chessman_sprite) 188 | chessman_sprite.kill() 189 | current_chessman.is_selected = False 190 | current_chessman = None 191 | print(cbd.legal_moves()) 192 | elif current_chessman != None and chessman_sprite is None: 193 | success = current_chessman.move(col_num, row_num) 194 | if success: 195 | current_chessman.is_selected = False 196 | current_chessman = None 197 | print(cbd.legal_moves()) 198 | records = cbd.record.split('\n') 199 | font = pygame.font.Font(font_file, 12) 200 | i = 0 201 | for record in records[-20:]: 202 | rec_label = font.render(record, True, font_color, font_background) 203 | t_rect = rec_label.get_rect() 204 | t_rect.centerx = (700 - 521) / 2 205 | t_rect.y = 35 + i * 15 206 | widget_background.blit(rec_label, t_rect) 207 | i += 1 208 | screen.blit(widget_background, (521, 0)) 209 | framerate.tick(20) 210 | # clear/erase the last drawn sprites 211 | chessmans.clear(screen, board_background) 212 | 213 | # update all the sprites 214 | chessmans.update() 215 | chessmans.draw(screen) 216 | pygame.display.update() 217 | 218 | cbd.print_record() 219 | 220 | if __name__ == '__main__': 221 | main() 222 | -------------------------------------------------------------------------------- /cchess_alphazero/run.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import multiprocessing as mp 4 | 5 | _PATH_ = os.path.dirname(os.path.dirname(__file__)) 6 | 7 | if _PATH_ not in sys.path: 8 | sys.path.append(_PATH_) 9 | 10 | if __name__ == "__main__": 11 | # mp.set_start_method('spawn') 12 | sys.setrecursionlimit(10000) 13 | from cchess_alphazero import manager 14 | manager.start() 15 | -------------------------------------------------------------------------------- /cchess_alphazero/test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import multiprocessing as mp 4 | 5 | _PATH_ = os.path.dirname(os.path.dirname(__file__)) 6 | 7 | 8 | if _PATH_ not in sys.path: 9 | sys.path.append(_PATH_) 10 | 11 | def test_env(): 12 | from cchess_alphazero.environment.env import CChessEnv 13 | env = CChessEnv() 14 | env.reset() 15 | print(env.observation) 16 | env.step('0001') 17 | print(env.observation) 18 | env.step('7770') 19 | print(env.observation) 20 | env.render() 21 | print(env.input_planes()[0+7:3+7]) 22 | 23 | def test_player(): 24 | from cchess_alphazero.agent.player import CChessPlayer 25 | 26 | def test_config(): 27 | from cchess_alphazero.config import Config 28 | c = Config('mini') 29 | c.resource.create_directories() 30 | print(c.resource.project_dir, c.resource.data_dir) 31 | 32 | def test_self_play(): 33 | from cchess_alphazero.config import Config 34 | from cchess_alphazero.worker.self_play import start 35 | from cchess_alphazero.lib.logger import setup_logger 36 | c = Config('mini') 37 | c.resource.create_directories() 38 | setup_logger(c.resource.main_log_path) 39 | start(c) 40 | 41 | def test_cli_play(): 42 | from cchess_alphazero.play_games.test_cli_game import main 43 | main() 44 | 45 | def test_gui_play(): 46 | from cchess_alphazero.play_games.test_window_game import main 47 | main() 48 | 49 | def test_optimise(): 50 | from cchess_alphazero.worker.optimize import start 51 | from cchess_alphazero.config import Config 52 | from cchess_alphazero.lib.logger import setup_logger 53 | c = Config('mini') 54 | c.resource.create_directories() 55 | setup_logger(c.resource.main_log_path) 56 | start(c) 57 | 58 | def test_light(): 59 | from cchess_alphazero.environment.light_env.chessboard import L_Chessboard 60 | from cchess_alphazero.environment.chessboard import Chessboard 61 | lboard = L_Chessboard() 62 | while not lboard.is_end(): 63 | for i in range(lboard.height): 64 | print(lboard.screen[i]) 65 | print(f"legal_moves = {lboard.legal_moves()}") 66 | action = input(f'Enter move for {lboard.is_red_turn} r/b: ') 67 | lboard.move_action_str(action) 68 | for i in range(lboard.height): 69 | print(lboard.screen[i]) 70 | print(lboard.winner) 71 | print(f"Turns = {lboard.steps / 2}") 72 | 73 | def test_light_env(): 74 | from cchess_alphazero.environment.env import CChessEnv 75 | from cchess_alphazero.config import Config 76 | c = Config('mini') 77 | env = CChessEnv(c) 78 | env.reset() 79 | print(env.observation) 80 | env.step('0001') 81 | print(env.observation) 82 | env.step('7770') 83 | print(env.observation) 84 | env.render() 85 | print(env.input_planes()[0+7:3+7]) 86 | 87 | def test_wxf(): 88 | from cchess_alphazero.environment.light_env.chessboard import L_Chessboard 89 | lboard = L_Chessboard() 90 | while not lboard.is_end(): 91 | for i in range(lboard.height): 92 | print(lboard.screen[i]) 93 | print(f"legal_moves = {lboard.legal_moves()}") 94 | wxf = input(f'Enter WXF move for {lboard.is_red_turn} r/b: ') 95 | action = lboard.parse_WXF_move(wxf) 96 | print(action) 97 | lboard.move_action_str(action) 98 | 99 | def test_sl(): 100 | from cchess_alphazero.worker import sl 101 | from cchess_alphazero.config import Config 102 | from cchess_alphazero.environment.lookup_tables import ActionLabelsRed, flip_policy, flip_move 103 | c = Config('mini') 104 | labels_n = len(ActionLabelsRed) 105 | move_lookup = {move: i for move, i in zip(ActionLabelsRed, range(labels_n))} 106 | slworker = sl.SupervisedWorker(c) 107 | p1 = slworker.build_policy('0001', False) 108 | print(p1[move_lookup['0001']]) 109 | p2 = slworker.build_policy('0001', True) 110 | print(p2[move_lookup[flip_move('0001')]]) 111 | 112 | def test_static_env(): 113 | from cchess_alphazero.environment.env import CChessEnv 114 | import cchess_alphazero.environment.static_env as senv 115 | from cchess_alphazero.environment.static_env import INIT_STATE 116 | from cchess_alphazero.environment.lookup_tables import flip_move 117 | env = CChessEnv() 118 | env.reset() 119 | print("env: " + env.observation) 120 | print("senv: " + INIT_STATE) 121 | state = INIT_STATE 122 | env.step('0001') 123 | state = senv.step(state, '0001') 124 | print(senv.evaluate(state)) 125 | print("env: " + env.observation) 126 | print("senv: " + state) 127 | env.step('7770') 128 | state = senv.step(state, flip_move('7770')) 129 | print(senv.evaluate(state)) 130 | print("env: " + env.observation) 131 | print("senv: " + state) 132 | env.render() 133 | board = senv.state_to_board(state) 134 | for i in range(9, -1, -1): 135 | print(board[i]) 136 | print("env: ") 137 | print(env.input_planes()[0+7:3+7]) 138 | print("senv: ") 139 | print(senv.state_to_planes(state)[0+7:3+7]) 140 | print(f"env: {env.board.legal_moves()}" ) 141 | print(f"senv: {senv.get_legal_moves(state)}") 142 | print(set(env.board.legal_moves()) == set(senv.get_legal_moves(state))) 143 | 144 | def test_onegreen(): 145 | import cchess_alphazero.environment.static_env as senv 146 | from cchess_alphazero.environment.lookup_tables import flip_move 147 | init = '9999299949999999249999869999999958999999519999999999999999997699' 148 | state = senv.init(init) 149 | print(state) 150 | senv.render(state) 151 | move = senv.parse_onegreen_move('8685') 152 | state = senv.step(state, move) 153 | print(state) 154 | senv.render(state) 155 | move = senv.parse_onegreen_move('7666') 156 | state = senv.step(state, flip_move(move)) 157 | print(state) 158 | senv.render(state) 159 | 160 | def test_onegreen2(): 161 | from cchess_alphazero.environment.env import CChessEnv 162 | import cchess_alphazero.environment.static_env as senv 163 | from cchess_alphazero.config import Config 164 | c = Config('mini') 165 | init = '9999299949999999249999869999999958999999519999999999999999997699' 166 | env = CChessEnv(c) 167 | env.reset(init) 168 | print(env.observation) 169 | env.render() 170 | move = senv.parse_onegreen_move('8685') 171 | env.step(move) 172 | print(env.observation) 173 | env.render() 174 | move = senv.parse_onegreen_move('7666') 175 | env.step(move) 176 | print(env.observation) 177 | env.render() 178 | 179 | def test_ucci(): 180 | import cchess_alphazero.environment.static_env as senv 181 | from cchess_alphazero.environment.lookup_tables import flip_move 182 | state = senv.INIT_STATE 183 | state = senv.step(state, '0001') 184 | fen = senv.state_to_fen(state, 1) 185 | print(fen) 186 | senv.render(state) 187 | move = 'b7b0' 188 | move = senv.parse_ucci_move(move) 189 | print(f'Parsed move {move}') 190 | move = flip_move(move) 191 | print(f'fliped move {move}') 192 | state = senv.step(state, move) 193 | senv.render(state) 194 | fen = senv.state_to_fen(state, 2) 195 | print(fen) 196 | 197 | def test_done(): 198 | import cchess_alphazero.environment.static_env as senv 199 | state = '4s4/9/4e4/p8/2e2R2p/P5E2/8P/9/9/4S1E2' 200 | board = senv.state_to_board(state) 201 | for i in range(9, -1, -1): 202 | print(board[i]) 203 | print(senv.done(state)) 204 | 205 | def test_upload(): 206 | from cchess_alphazero.lib.web_helper import upload_file 207 | from cchess_alphazero.config import Config 208 | c = Config('mini') 209 | url = 'http://alphazero.52coding.com.cn/api/upload_game_file' 210 | path = '/Users/liuhe/Documents/Graduation Project/ChineseChess-AlphaZero/data/play_data/test.json' 211 | filename = 'test.json' 212 | data = {'digest': 'test', 'username': 'test'} 213 | res = upload_file(url, path, filename=filename, data=data) 214 | print(res) 215 | 216 | def test_download(): 217 | from cchess_alphazero.lib.web_helper import download_file 218 | from cchess_alphazero.config import Config 219 | c = Config('mini') 220 | url = 'http://alphazero.52coding.com.cn/model_best_weight.h5' 221 | path = '/Users/liuhe/Downloads/model_best_weight.h5' 222 | res = download_file(url, path) 223 | print(res) 224 | 225 | def test_request(): 226 | from cchess_alphazero.lib.web_helper import http_request 227 | from cchess_alphazero.config import Config 228 | c = Config('mini') 229 | url = 'http://alphazero.52coding.com.cn/api/add_model' 230 | digest = 'd6fce85e040a63966fa7651d4a08a7cdba2ef0e5975fc16a6d178c96345547b3' 231 | elo = 0 232 | data = {'digest': digest, 'elo': elo} 233 | res = http_request(url, post=True, data=data) 234 | print(res) 235 | 236 | def fixbug(): 237 | from cchess_alphazero.config import Config 238 | from cchess_alphazero.lib.data_helper import get_game_data_filenames, read_game_data_from_file, write_game_data_to_file 239 | import cchess_alphazero.environment.static_env as senv 240 | c = Config('distribute') 241 | files = get_game_data_filenames(c.resource) 242 | cnt = 0 243 | fix = 0 244 | draw_cnt = 0 245 | for filename in files: 246 | try: 247 | data = read_game_data_from_file(filename) 248 | except: 249 | print(f"error: {filename}") 250 | os.remove(filename) 251 | continue 252 | state = data[0] 253 | real_data = [state] 254 | need_fix = True 255 | draw = False 256 | action = None 257 | value = None 258 | is_red_turn = True 259 | for item in data[1:]: 260 | action = item[0] 261 | value = -item[1] 262 | if value == 0: 263 | need_fix = False 264 | draw = True 265 | draw_cnt += 1 266 | break 267 | state = senv.step(state, action) 268 | is_red_turn = not is_red_turn 269 | real_data.append([action, value]) 270 | if not draw: 271 | game_over, v, final_move = senv.done(state) 272 | if final_move: 273 | v = -v 274 | is_red_turn = not is_red_turn 275 | if not is_red_turn: 276 | v = -v 277 | if not game_over: 278 | v = 1 279 | # print(game_over, v, final_move, state) 280 | if v == data[1][1]: 281 | need_fix = False 282 | else: 283 | need_fix = True 284 | if need_fix: 285 | write_game_data_to_file(filename, real_data) 286 | # print(filename) 287 | fix += 1 288 | cnt += 1 289 | if cnt % 1000 == 0: 290 | print(cnt, fix, draw_cnt) 291 | print(f"all {cnt}, fix {fix}, draw {draw_cnt}") 292 | 293 | 294 | def plot_model(): 295 | from keras.utils import plot_model 296 | from cchess_alphazero.agent.model import CChessModel 297 | from cchess_alphazero.config import Config 298 | from cchess_alphazero.lib.model_helper import save_as_best_model 299 | config = Config('distribute') 300 | model = CChessModel(config) 301 | model.build() 302 | save_as_best_model(model) 303 | plot_model(model.model, to_file='model.png', show_shapes=True, show_layer_names=True) 304 | 305 | def test_check_and_catch(): 306 | import cchess_alphazero.environment.static_env as senv 307 | state = senv.fen_to_state('rnba1cbnr/1a7/1c7/p1p3p1p/2p5k/2P1R4/P1P3P1P/1C5C1/9/RNBAKABN1 r') 308 | # state = senv.fliped_state(state) 309 | ori_state = state 310 | senv.render(state) 311 | print() 312 | action = '4454' 313 | state = senv.step(state, action) 314 | senv.render(state) 315 | state = senv.fliped_state(state) 316 | print() 317 | senv.render(state) 318 | print(senv.will_check_or_catch(ori_state, action)) 319 | 320 | def test_be_catched(): 321 | import cchess_alphazero.environment.static_env as senv 322 | state = senv.fen_to_state('rnbakab1r/9/1c3c2n/p1p5p/7p1/3PR4/P1P3P1P/C7C/9/RNBAKABN1 b') 323 | # state = senv.fliped_state(state) 324 | ori_state = state 325 | senv.render(state) 326 | print() 327 | action = '4454' 328 | print(senv.be_catched(ori_state, action)) 329 | 330 | 331 | if __name__ == "__main__": 332 | test_be_catched() 333 | 334 | -------------------------------------------------------------------------------- /cchess_alphazero/worker/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/cchess_alphazero/worker/__init__.py -------------------------------------------------------------------------------- /cchess_alphazero/worker/evaluator.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | from collections import deque 4 | from concurrent.futures import ProcessPoolExecutor, wait 5 | from datetime import datetime 6 | from logging import getLogger 7 | from multiprocessing import Manager 8 | from threading import Thread 9 | from time import time, sleep 10 | from collections import defaultdict 11 | from multiprocessing import Lock 12 | from random import random, randint 13 | import numpy as np 14 | 15 | import cchess_alphazero.environment.static_env as senv 16 | from cchess_alphazero.agent.model import CChessModel 17 | from cchess_alphazero.agent.player import CChessPlayer, VisitState 18 | from cchess_alphazero.agent.api import CChessModelAPI 19 | from cchess_alphazero.config import Config 20 | from cchess_alphazero.environment.env import CChessEnv 21 | from cchess_alphazero.environment.lookup_tables import Winner, flip_move, ActionLabelsRed 22 | from cchess_alphazero.lib.data_helper import get_game_data_filenames, write_game_data_to_file 23 | from cchess_alphazero.lib.model_helper import load_model_weight 24 | from cchess_alphazero.lib.tf_util import set_session_config 25 | 26 | logger = getLogger(__name__) 27 | 28 | def start(config: Config): 29 | set_session_config(per_process_gpu_memory_fraction=1, allow_growth=True, device_list=config.opts.device_list) 30 | m = Manager() 31 | # while True: 32 | model_bt = load_model(config, config.resource.model_best_config_path, config.resource.model_best_weight_path) 33 | modelbt_pipes = m.list([model_bt.get_pipes(need_reload=False) for _ in range(config.play.max_processes)]) 34 | model_ng = load_model(config, config.resource.next_generation_config_path, config.resource.next_generation_weight_path) 35 | # while not model_ng: 36 | # logger.info(f"Next generation model is None, wait for 300s") 37 | # sleep(300) 38 | # model_ng = load_model(config, config.resource.next_generation_config_path, config.resource.next_generation_weight_path) 39 | logger.info(f"Next generation model has loaded!") 40 | modelng_pipes = m.list([model_ng.get_pipes(need_reload=False) for _ in range(config.play.max_processes)]) 41 | 42 | # play_worker = EvaluateWorker(config, model1_pipes, model2_pipes) 43 | # play_worker.start() 44 | with ProcessPoolExecutor(max_workers=config.play.max_processes) as executor: 45 | futures = [] 46 | for i in range(config.play.max_processes): 47 | eval_worker = EvaluateWorker(config, modelbt_pipes, modelng_pipes, pid=i) 48 | futures.append(executor.submit(eval_worker.start)) 49 | 50 | wait(futures) 51 | model_bt.close_pipes() 52 | model_ng.close_pipes() 53 | # compute whether to update best model 54 | # and remove next generation model 55 | total_score = 0 56 | red_new_win = 0 57 | red_new_fail = 0 58 | red_new_draw = 0 59 | black_new_win = 0 60 | black_new_fail = 0 61 | black_new_draw = 0 62 | for future in futures: 63 | data = future.result() 64 | total_score += data[0] 65 | red_new_win += data[1] 66 | red_new_draw += data[2] 67 | red_new_fail += data[3] 68 | black_new_win += data[4] 69 | black_new_draw += data[5] 70 | black_new_fail += data[6] 71 | game_num = config.eval.game_num * config.play.max_processes 72 | win_rate = total_score * 100 / game_num 73 | logger.info(f"Evaluate over, next generation win {total_score}/{game_num} = {win_rate:.2f}%") 74 | logger.info(f"红\t黑\t胜\t平\t负") 75 | logger.info(f"新\t旧\t{red_new_win}\t{red_new_draw}\t{red_new_fail}") 76 | logger.info(f"旧\t新\t{black_new_win}\t{black_new_draw}\t{black_new_fail}") 77 | # if total_score * 1.0 / game_num >= config.eval.next_generation_replace_rate: 78 | # logger.info("Best model will be replaced by next generation model") 79 | # replace_best_model(config) 80 | # else: 81 | # logger.info("Next generation fail to defeat best model and will be removed") 82 | # remove_ng_model(config) 83 | 84 | class EvaluateWorker: 85 | def __init__(self, config: Config, pipes1=None, pipes2=None, pid=None): 86 | self.config = config 87 | self.player_bt = None 88 | self.player_ng = None 89 | self.pid = pid 90 | self.pipes_bt = pipes1 91 | self.pipes_ng = pipes2 92 | 93 | def start(self): 94 | ran = self.config.play.max_processes * 2 95 | sleep((self.pid % ran) * 10) 96 | logger.debug(f"Evaluate#Start Process index = {self.pid}, pid = {os.getpid()}") 97 | score = 0 98 | total_score = 0 99 | red_new_win = 0 100 | red_new_fail = 0 101 | red_new_draw = 0 102 | black_new_win = 0 103 | black_new_fail = 0 104 | black_new_draw = 0 105 | 106 | for idx in range(self.config.eval.game_num): 107 | start_time = time() 108 | value, turns = self.start_game(idx) 109 | end_time = time() 110 | 111 | if (value == 1 and idx % 2 == 0) or (value == -1 and idx % 2 == 1): 112 | if idx % 2 == 0: 113 | black_new_fail += 1 114 | else: 115 | red_new_fail += 1 116 | result = '基准模型胜' 117 | elif (value == 1 and idx % 2 == 1) or (value == -1 and idx % 2 == 0): 118 | if idx % 2 == 0: 119 | black_new_win += 1 120 | else: 121 | red_new_win += 1 122 | result = '待评测模型胜' 123 | else: 124 | if idx % 2 == 0: 125 | black_new_draw += 1 126 | else: 127 | red_new_draw += 1 128 | result = '和棋' 129 | 130 | if value == -1: # loss 131 | score = 0 132 | elif value == 1: # win 133 | score = 1 134 | else: 135 | score = 0.5 136 | 137 | if idx % 2 == 0: 138 | score = 1 - score 139 | else: 140 | score = score 141 | 142 | logger.info(f"进程{self.pid}评测完毕 用时{(end_time - start_time):.1f}秒, " 143 | f"{turns / 2}回合, {result}, 得分:{score}, value = {value}, idx = {idx}") 144 | total_score += score 145 | return (total_score, red_new_win, red_new_draw, red_new_fail, black_new_win, black_new_draw, black_new_fail) 146 | 147 | def start_game(self, idx): 148 | pipe1 = self.pipes_bt.pop() 149 | pipe2 = self.pipes_ng.pop() 150 | search_tree1 = defaultdict(VisitState) 151 | search_tree2 = defaultdict(VisitState) 152 | 153 | playouts = randint(8, 12) * 100 154 | self.config.play.simulation_num_per_move = playouts 155 | logger.info(f"Set playouts = {self.config.play.simulation_num_per_move}") 156 | 157 | self.player1 = CChessPlayer(self.config, search_tree=search_tree1, pipes=pipe1, 158 | debugging=False, enable_resign=False) 159 | self.player2 = CChessPlayer(self.config, search_tree=search_tree2, pipes=pipe2, 160 | debugging=False, enable_resign=False) 161 | 162 | # even: bst = red, ng = black; odd: bst = black, ng = red 163 | if idx % 2 == 0: 164 | red = self.player1 165 | black = self.player2 166 | logger.debug(f"best model is red, ng is black") 167 | else: 168 | red = self.player2 169 | black = self.player1 170 | logger.debug(f"best model is black, ng is red") 171 | 172 | state = senv.INIT_STATE 173 | history = [state] 174 | value = 0 # best model's value 175 | turns = 0 # even == red; odd == black 176 | game_over = False 177 | no_eat_count = 0 178 | check = False 179 | 180 | while not game_over: 181 | start_time = time() 182 | no_act = None 183 | increase_temp = False 184 | if not check and state in history[:-1]: 185 | no_act = [] 186 | increase_temp = True 187 | free_move = defaultdict(int) 188 | for i in range(len(history) - 1): 189 | if history[i] == state: 190 | # 如果走了下一步是将军或捉:禁止走那步 191 | if senv.will_check_or_catch(state, history[i+1]): 192 | no_act.append(history[i + 1]) 193 | # 否则当作闲着处理 194 | else: 195 | free_move[state] += 1 196 | if free_move[state] >= 3: 197 | # 作和棋处理 198 | game_over = True 199 | value = 0 200 | logger.info("闲着循环三次,作和棋处理") 201 | break 202 | if game_over: 203 | break 204 | if turns % 2 == 0: 205 | action, _ = red.action(state, turns, no_act=no_act, increase_temp=increase_temp) 206 | else: 207 | action, _ = black.action(state, turns, no_act=no_act, increase_temp=increase_temp) 208 | end_time = time() 209 | if self.config.opts.log_move: 210 | logger.debug(f"进程id = {self.pid}, action = {action}, turns = {turns}, time = {(end_time-start_time):.1f}") 211 | if action is None: 212 | logger.debug(f"{turns % 2} (0 = red; 1 = black) has resigned!") 213 | value = -1 214 | break 215 | history.append(action) 216 | state, no_eat = senv.new_step(state, action) 217 | turns += 1 218 | if no_eat: 219 | no_eat_count += 1 220 | else: 221 | no_eat_count = 0 222 | history.append(state) 223 | 224 | if no_eat_count >= 120 or turns / 2 >= self.config.play.max_game_length: 225 | game_over = True 226 | value = 0 227 | else: 228 | game_over, value, final_move, check = senv.done(state, need_check=True) 229 | if not game_over: 230 | if not senv.has_attack_chessman(state): 231 | logger.info(f"双方无进攻子力,作和。state = {state}") 232 | game_over = True 233 | value = 0 234 | 235 | if final_move: 236 | history.append(final_move) 237 | state = senv.step(state, final_move) 238 | turns += 1 239 | value = - value 240 | history.append(state) 241 | 242 | self.player1.close() 243 | self.player2.close() 244 | 245 | if turns % 2 == 1: # black turn 246 | value = -value 247 | 248 | self.pipes_bt.append(pipe1) 249 | self.pipes_ng.append(pipe2) 250 | return value, turns 251 | 252 | 253 | def replace_best_model(config): 254 | rc = config.resource 255 | shutil.copyfile(rc.next_generation_config_path, rc.model_best_config_path) 256 | shutil.copyfile(rc.next_generation_weight_path, rc.model_best_weight_path) 257 | remove_ng_model(config) 258 | 259 | def remove_ng_model(config): 260 | rc = config.resource 261 | os.remove(rc.next_generation_config_path) 262 | os.remove(rc.next_generation_weight_path) 263 | 264 | def load_model(config, config_path, weight_path, name=None): 265 | model = CChessModel(config) 266 | if not load_model_weight(model, config_path, weight_path, name): 267 | return None 268 | return model 269 | 270 | -------------------------------------------------------------------------------- /cchess_alphazero/worker/optimize.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import gc 4 | import subprocess 5 | import shutil 6 | import numpy as np 7 | 8 | from collections import deque 9 | from concurrent.futures import ProcessPoolExecutor 10 | from datetime import datetime 11 | from logging import getLogger 12 | from time import sleep 13 | from random import shuffle 14 | from threading import Thread 15 | 16 | import cchess_alphazero.environment.static_env as senv 17 | from cchess_alphazero.agent.model import CChessModel 18 | from cchess_alphazero.config import Config 19 | from cchess_alphazero.lib.data_helper import get_game_data_filenames, read_game_data_from_file 20 | from cchess_alphazero.lib.model_helper import load_best_model_weight, save_as_best_model 21 | from cchess_alphazero.lib.model_helper import need_to_reload_best_model_weight, save_as_next_generation_model, save_as_best_model 22 | from cchess_alphazero.environment.env import CChessEnv 23 | from cchess_alphazero.environment.lookup_tables import Winner, ActionLabelsRed, flip_policy, flip_move 24 | from cchess_alphazero.lib.tf_util import set_session_config 25 | from cchess_alphazero.lib.web_helper import http_request 26 | 27 | from keras.optimizers import SGD 28 | from keras.callbacks import TensorBoard 29 | # from keras.utils import multi_gpu_model 30 | import keras.backend as K 31 | 32 | logger = getLogger(__name__) 33 | 34 | def start(config: Config): 35 | set_session_config(per_process_gpu_memory_fraction=1, allow_growth=True, device_list=config.opts.device_list) 36 | return OptimizeWorker(config).start() 37 | 38 | class OptimizeWorker: 39 | def __init__(self, config:Config): 40 | self.config = config 41 | self.model = None 42 | self.loaded_filenames = set() 43 | self.loaded_data = deque(maxlen=self.config.trainer.dataset_size) 44 | self.dataset = deque(), deque(), deque() 45 | self.executor = ProcessPoolExecutor(max_workers=config.trainer.cleaning_processes) 46 | self.filenames = [] 47 | self.opt = None 48 | self.count = 0 49 | self.eva = False 50 | 51 | def start(self): 52 | self.model = self.load_model() 53 | self.training() 54 | 55 | def training(self): 56 | self.compile_model() 57 | total_steps = self.config.trainer.start_total_steps 58 | bef_files = [] 59 | last_file = None 60 | 61 | while True: 62 | files = get_game_data_filenames(self.config.resource) 63 | offset = self.config.trainer.min_games_to_begin_learn 64 | if (len(files) < self.config.trainer.min_games_to_begin_learn \ 65 | or ((last_file is not None and last_file in files) and files.index(last_file) + 1 + offset > len(files))): 66 | # if last_file is not None: 67 | # logger.info('Waiting for enough data 300s, ' + str((len(files) - files.index(last_file)) * self.config.play_data.nb_game_in_file) \ 68 | # +' vs '+ str(self.config.trainer.min_games_to_begin_learn)+' games') 69 | # else: 70 | # logger.info('Waiting for enough data 300s, ' + str(len(files) * self.config.play_data.nb_game_in_file) \ 71 | # +' vs '+ str(self.config.trainer.min_games_to_begin_learn)+' games') 72 | # time.sleep(300) 73 | if last_file is not None: 74 | self.save_current_model(send=True) 75 | break 76 | else: 77 | if last_file is not None and last_file in files: 78 | idx = files.index(last_file) + 1 79 | if len(files) - idx > self.config.trainer.load_step: 80 | files = files[idx:idx + self.config.trainer.load_step] 81 | else: 82 | files = files[idx:] 83 | elif len(files) > self.config.trainer.load_step: 84 | files = files[0:self.config.trainer.load_step] 85 | last_file = files[-1] 86 | logger.info(f"Last file = {last_file}") 87 | logger.debug(f"files = {files[0:-1:2000]}") 88 | self.filenames = deque(files) 89 | logger.debug(f"Start training {len(self.filenames)} files") 90 | shuffle(self.filenames) 91 | self.fill_queue() 92 | self.update_learning_rate(total_steps) 93 | if len(self.dataset[0]) > self.config.trainer.batch_size: 94 | steps = self.train_epoch(self.config.trainer.epoch_to_checkpoint) 95 | total_steps += steps 96 | self.save_current_model(send=False) 97 | self.update_learning_rate(total_steps) 98 | self.count += 1 99 | a, b, c = self.dataset 100 | a.clear() 101 | b.clear() 102 | c.clear() 103 | del self.dataset, a, b, c 104 | gc.collect() 105 | self.dataset = deque(), deque(), deque() 106 | self.backup_play_data(files) 107 | 108 | def train_epoch(self, epochs): 109 | tc = self.config.trainer 110 | state_ary, policy_ary, value_ary = self.collect_all_loaded_data() 111 | tensorboard_cb = TensorBoard(log_dir="./logs", batch_size=tc.batch_size, histogram_freq=1) 112 | if self.config.opts.use_multiple_gpus: 113 | self.mg_model.fit(state_ary, [policy_ary, value_ary], 114 | batch_size=tc.batch_size, 115 | epochs=epochs, 116 | shuffle=True, 117 | validation_split=0.02, 118 | callbacks=[tensorboard_cb]) 119 | else: 120 | self.model.model.fit(state_ary, [policy_ary, value_ary], 121 | batch_size=tc.batch_size, 122 | epochs=epochs, 123 | shuffle=True, 124 | validation_split=0.02, 125 | callbacks=[tensorboard_cb]) 126 | steps = (state_ary.shape[0] // tc.batch_size) * epochs 127 | return steps 128 | 129 | def compile_model(self): 130 | self.opt = SGD(lr=0.02, momentum=self.config.trainer.momentum) 131 | losses = ['categorical_crossentropy', 'mean_squared_error'] 132 | if self.config.opts.use_multiple_gpus: 133 | self.mg_model = multi_gpu_model(self.model.model, gpus=self.config.opts.gpu_num) 134 | self.mg_model.compile(optimizer=self.opt, loss=losses, loss_weights=self.config.trainer.loss_weights) 135 | else: 136 | self.model.model.compile(optimizer=self.opt, loss=losses, loss_weights=self.config.trainer.loss_weights) 137 | 138 | def update_learning_rate(self, total_steps): 139 | # The deepmind paper says 140 | # ~400k: 1e-2 141 | # 400k~600k: 1e-3 142 | # 600k~: 1e-4 143 | 144 | lr = self.decide_learning_rate(total_steps) 145 | if lr: 146 | K.set_value(self.opt.lr, lr) 147 | logger.debug(f"total step={total_steps}, set learning rate to {lr}") 148 | 149 | def fill_queue(self): 150 | futures = deque() 151 | n = len(self.filenames) 152 | with ProcessPoolExecutor(max_workers=self.config.trainer.cleaning_processes) as executor: 153 | for _ in range(self.config.trainer.cleaning_processes): 154 | if len(self.filenames) == 0: 155 | break 156 | filename = self.filenames.pop() 157 | # logger.debug("loading data from %s" % (filename)) 158 | futures.append(executor.submit(load_data_from_file, filename, self.config.opts.has_history)) 159 | while futures and len(self.dataset[0]) < self.config.trainer.dataset_size: #fill tuples 160 | _tuple = futures.popleft().result() 161 | if _tuple is not None: 162 | for x, y in zip(self.dataset, _tuple): 163 | x.extend(y) 164 | m = len(self.filenames) 165 | if m > 0: 166 | if (n - m) % 1000 == 0: 167 | logger.info(f"Reading {n - m} files") 168 | filename = self.filenames.pop() 169 | # logger.debug("loading data from %s" % (filename)) 170 | futures.append(executor.submit(load_data_from_file, filename, self.config.opts.has_history)) 171 | 172 | def collect_all_loaded_data(self): 173 | state_ary, policy_ary, value_ary = self.dataset 174 | 175 | state_ary1 = np.asarray(state_ary, dtype=np.float32) 176 | policy_ary1 = np.asarray(policy_ary, dtype=np.float32) 177 | value_ary1 = np.asarray(value_ary, dtype=np.float32) 178 | return state_ary1, policy_ary1, value_ary1 179 | 180 | def load_model(self): 181 | model = CChessModel(self.config) 182 | if self.config.opts.new or not load_best_model_weight(model): 183 | model.build() 184 | save_as_best_model(model) 185 | return model 186 | 187 | def save_current_model(self, send=False): 188 | logger.info("Save as ng model") 189 | if not send: 190 | save_as_best_model(self.model) 191 | else: 192 | save_as_next_generation_model(self.model) 193 | 194 | def decide_learning_rate(self, total_steps): 195 | ret = None 196 | 197 | for step, lr in self.config.trainer.lr_schedules: 198 | if total_steps >= step: 199 | ret = lr 200 | return ret 201 | 202 | def try_reload_model(self): 203 | logger.debug("check model") 204 | if need_to_reload_best_model_weight(self.model): 205 | with self.model.graph.as_default(): 206 | load_best_model_weight(self.model) 207 | return True 208 | return False 209 | 210 | def backup_play_data(self, files): 211 | backup_folder = os.path.join(self.config.resource.data_dir, 'trained') 212 | cnt = 0 213 | if not os.path.exists(backup_folder): 214 | os.makedirs(backup_folder) 215 | for i in range(len(files)): 216 | try: 217 | shutil.move(files[i], backup_folder) 218 | except Exception as e: 219 | # logger.error(f"Backup error : {e}") 220 | cnt = cnt + 1 221 | logger.info(f"backup {len(files)} files, {cnt} empty files") 222 | 223 | def load_data_from_file(filename, use_history=False): 224 | try: 225 | data = read_game_data_from_file(filename) 226 | except Exception as e: 227 | logger.error(f"Error when loading data {e}") 228 | os.remove(filename) 229 | return None 230 | if data is None: 231 | return None 232 | return expanding_data(data, use_history) 233 | 234 | def expanding_data(data, use_history=False): 235 | state = data[0] 236 | real_data = [] 237 | action = None 238 | policy = None 239 | value = None 240 | if use_history: 241 | history = [state] 242 | else: 243 | history = None 244 | for item in data[1:]: 245 | action = item[0] 246 | value = item[1] 247 | try: 248 | policy = build_policy(action, flip=False) 249 | except Exception as e: 250 | logger.error(f"Expand data error {e}, item = {item}, data = {data}, state = {state}") 251 | return None 252 | real_data.append([state, policy, value]) 253 | state = senv.step(state, action) 254 | if use_history: 255 | history.append(action) 256 | history.append(state) 257 | 258 | return convert_to_trainging_data(real_data, history) 259 | 260 | 261 | def convert_to_trainging_data(data, history): 262 | state_list = [] 263 | policy_list = [] 264 | value_list = [] 265 | i = 0 266 | 267 | for state, policy, value in data: 268 | if history is None: 269 | state_planes = senv.state_to_planes(state) 270 | else: 271 | state_planes = senv.state_history_to_planes(state, history[0:i * 2 + 1]) 272 | sl_value = value 273 | 274 | state_list.append(state_planes) 275 | policy_list.append(policy) 276 | value_list.append(sl_value) 277 | i += 1 278 | 279 | return np.asarray(state_list, dtype=np.float32), \ 280 | np.asarray(policy_list, dtype=np.float32), \ 281 | np.asarray(value_list, dtype=np.float32) 282 | 283 | def build_policy(action, flip): 284 | labels_n = len(ActionLabelsRed) 285 | move_lookup = {move: i for move, i in zip(ActionLabelsRed, range(labels_n))} 286 | policy = np.zeros(labels_n) 287 | 288 | policy[move_lookup[action]] = 1 289 | 290 | if flip: 291 | policy = flip_policy(policy) 292 | return list(policy) 293 | 294 | 295 | 296 | -------------------------------------------------------------------------------- /cchess_alphazero/worker/play_with_ucci_engine.py: -------------------------------------------------------------------------------- 1 | import os 2 | import gc 3 | import subprocess 4 | import numpy as np 5 | from time import sleep 6 | from collections import deque 7 | from concurrent.futures import ProcessPoolExecutor 8 | from datetime import datetime 9 | from logging import getLogger 10 | from multiprocessing import Manager 11 | from time import time, sleep 12 | from collections import defaultdict 13 | from random import random 14 | 15 | import cchess_alphazero.environment.static_env as senv 16 | from cchess_alphazero.agent.model import CChessModel 17 | from cchess_alphazero.agent.player import CChessPlayer, VisitState 18 | from cchess_alphazero.agent.api import CChessModelAPI 19 | from cchess_alphazero.config import Config 20 | from cchess_alphazero.environment.env import CChessEnv 21 | from cchess_alphazero.environment.lookup_tables import ActionLabelsRed, flip_policy, flip_move 22 | from cchess_alphazero.lib.data_helper import get_game_data_filenames, write_game_data_to_file 23 | from cchess_alphazero.lib.model_helper import load_best_model_weight, save_as_best_model 24 | from cchess_alphazero.lib.tf_util import set_session_config 25 | 26 | logger = getLogger(__name__) 27 | 28 | def load_model(config): 29 | model = CChessModel(config) 30 | if config.opts.new or not load_best_model_weight(model): 31 | model.build() 32 | save_as_best_model(model) 33 | return model 34 | 35 | def start(config: Config): 36 | set_session_config(per_process_gpu_memory_fraction=1, allow_growth=True, device_list=config.opts.device_list) 37 | current_model = load_model(config) 38 | m = Manager() 39 | cur_pipes = m.list([current_model.get_pipes() for _ in range(config.play.max_processes)]) 40 | 41 | # play_worker = SelfPlayWorker(config, cur_pipes, 0) 42 | # play_worker.start() 43 | with ProcessPoolExecutor(max_workers=config.play.max_processes) as executor: 44 | futures = [] 45 | for i in range(config.play.max_processes): 46 | play_worker = SelfPlayWorker(config, cur_pipes, i) 47 | logger.debug("Initialize selfplay worker") 48 | futures.append(executor.submit(play_worker.start)) 49 | 50 | class SelfPlayWorker: 51 | def __init__(self, config: Config, pipes=None, pid=None): 52 | self.config = config 53 | self.player = None 54 | self.cur_pipes = pipes 55 | self.id = pid 56 | self.buffer = [] 57 | self.pid = os.getpid() 58 | 59 | def start(self): 60 | self.pid = os.getpid() 61 | logger.debug(f"Selfplay#Start Process index = {self.id}, pid = {self.pid}") 62 | 63 | idx = 1 64 | self.buffer = [] 65 | 66 | while True: 67 | search_tree = defaultdict(VisitState) 68 | start_time = time() 69 | value, turns, state, store = self.start_game(idx, search_tree) 70 | end_time = time() 71 | if value != 1 and value != -1: 72 | winner = 'Draw' 73 | elif idx % 2 == 0 and value == 1 or idx % 2 == 1 and value == -1: 74 | winner = 'AlphaHe' 75 | else: 76 | winner = 'Eleeye' 77 | 78 | logger.debug(f"Process {self.pid}-{self.id} play game {idx} time={(end_time - start_time):.1f} sec, " 79 | f"turn={turns / 2}, value = {value:.2f}, winner is {winner}") 80 | if turns <= 10 and store: 81 | senv.render(state) 82 | if store: 83 | idx += 1 84 | 85 | def start_game(self, idx, search_tree): 86 | pipes = self.cur_pipes.pop() 87 | 88 | if not self.config.play.share_mtcs_info_in_self_play or \ 89 | idx % self.config.play.reset_mtcs_info_per_game == 0: 90 | search_tree = defaultdict(VisitState) 91 | 92 | if random() > self.config.play.enable_resign_rate: 93 | enable_resign = True 94 | else: 95 | enable_resign = False 96 | 97 | self.player = CChessPlayer(self.config, search_tree=search_tree, pipes=pipes, enable_resign=enable_resign, debugging=False) 98 | 99 | state = senv.INIT_STATE 100 | history = [state] 101 | value = 0 102 | turns = 0 # even == red; odd == black 103 | game_over = False 104 | is_alpha_red = True if idx % 2 == 0 else False 105 | final_move = None 106 | check = False 107 | 108 | while not game_over: 109 | if (is_alpha_red and turns % 2 == 0) or (not is_alpha_red and turns % 2 == 1): 110 | no_act = None 111 | if not check and state in history[:-1]: 112 | no_act = [] 113 | for i in range(len(history) - 1): 114 | if history[i] == state: 115 | no_act.append(history[i + 1]) 116 | action, _ = self.player.action(state, turns, no_act) 117 | if action is None: 118 | logger.debug(f"{turns % 2} (0 = red; 1 = black) has resigned!") 119 | value = -1 120 | break 121 | else: 122 | fen = senv.state_to_fen(state, turns) 123 | action = self.get_ucci_move(fen) 124 | if action is None: 125 | logger.debug(f"{turns % 2} (0 = red; 1 = black) has resigned!") 126 | value = -1 127 | break 128 | if turns % 2 == 1: 129 | action = flip_move(action) 130 | history.append(action) 131 | state = senv.step(state, action) 132 | turns += 1 133 | history.append(state) 134 | 135 | if turns / 2 >= self.config.play.max_game_length: 136 | game_over = True 137 | value = 0 138 | else: 139 | game_over, value, final_move, check = senv.done(state, need_check=True) 140 | 141 | if final_move: 142 | history.append(final_move) 143 | state = senv.step(state, final_move) 144 | history.append(state) 145 | turns += 1 146 | value = -value 147 | 148 | self.player.close() 149 | del search_tree 150 | del self.player 151 | gc.collect() 152 | if turns % 2 == 1: # balck turn 153 | value = -value 154 | 155 | v = value 156 | if turns <= 10: 157 | if random() > 0.7: 158 | store = True 159 | else: 160 | store = False 161 | else: 162 | store = True 163 | 164 | if store: 165 | data = [history[0]] 166 | for i in range(turns): 167 | k = i * 2 168 | data.append([history[k + 1], value]) 169 | value = -value 170 | self.save_play_data(idx, data) 171 | 172 | self.cur_pipes.append(pipes) 173 | self.remove_play_data() 174 | return v, turns, state, store 175 | 176 | def get_ucci_move(self, fen, time=3): 177 | p = subprocess.Popen(self.config.resource.eleeye_path, 178 | stdin=subprocess.PIPE, 179 | stdout=subprocess.PIPE, 180 | stderr=subprocess.PIPE, 181 | universal_newlines=True) 182 | setfen = f'position fen {fen}\n' 183 | setrandom = f'setoption randomness {self.config.opts.random}\n' 184 | cmd = 'ucci\n' + setrandom + setfen + f'go time {time * 1000}\n' 185 | try: 186 | out, err = p.communicate(cmd, timeout=time+0.5) 187 | except subprocess.TimeoutExpired: 188 | p.kill() 189 | try: 190 | out, err = p.communicate() 191 | except Exception as e: 192 | logger.error(f"{e}, cmd = {cmd}") 193 | return self.get_ucci_move(fen, time+1) 194 | lines = out.split('\n') 195 | if lines[-2] == 'nobestmove': 196 | return None 197 | move = lines[-2].split(' ')[1] 198 | if move == 'depth': 199 | move = lines[-1].split(' ')[6] 200 | return senv.parse_ucci_move(move) 201 | 202 | def save_play_data(self, idx, data): 203 | self.buffer += data 204 | 205 | if not idx % self.config.play_data.nb_game_in_file == 0: 206 | return 207 | 208 | rc = self.config.resource 209 | game_id = datetime.now().strftime("%Y%m%d-%H%M%S.%f") 210 | path = os.path.join(rc.play_data_dir, rc.play_data_filename_tmpl % game_id) 211 | logger.info(f"Process {self.pid} save play data to {path}") 212 | write_game_data_to_file(path, self.buffer) 213 | self.buffer = [] 214 | 215 | def remove_play_data(self): 216 | files = get_game_data_filenames(self.config.resource) 217 | if len(files) < self.config.play_data.max_file_num: 218 | return 219 | try: 220 | for i in range(len(files) - self.config.play_data.max_file_num): 221 | os.remove(files[i]) 222 | except: 223 | pass 224 | 225 | def build_policy(self, action, flip): 226 | labels_n = len(ActionLabelsRed) 227 | move_lookup = {move: i for move, i in zip(ActionLabelsRed, range(labels_n))} 228 | policy = np.zeros(labels_n) 229 | 230 | policy[move_lookup[action]] = 1 231 | 232 | if flip: 233 | policy = flip_policy(policy) 234 | return list(policy) 235 | 236 | -------------------------------------------------------------------------------- /cchess_alphazero/worker/self_play.py: -------------------------------------------------------------------------------- 1 | import os 2 | import gc 3 | import numpy as np 4 | from time import sleep 5 | from collections import deque 6 | from concurrent.futures import ProcessPoolExecutor 7 | from datetime import datetime, timezone, timedelta 8 | from logging import getLogger 9 | from multiprocessing import Manager 10 | from time import time, sleep 11 | from collections import defaultdict 12 | from random import random 13 | from threading import Thread 14 | 15 | import cchess_alphazero.environment.static_env as senv 16 | from cchess_alphazero.agent.model import CChessModel 17 | from cchess_alphazero.agent.player import CChessPlayer, VisitState 18 | from cchess_alphazero.agent.api import CChessModelAPI 19 | from cchess_alphazero.config import Config 20 | from cchess_alphazero.environment.env import CChessEnv 21 | from cchess_alphazero.environment.lookup_tables import Winner, ActionLabelsRed, flip_policy, flip_move 22 | from cchess_alphazero.lib.data_helper import get_game_data_filenames, write_game_data_to_file 23 | from cchess_alphazero.lib.model_helper import load_model_weight, save_as_best_model, load_best_model_weight_from_internet 24 | from cchess_alphazero.lib.tf_util import set_session_config 25 | from cchess_alphazero.lib.web_helper import upload_file 26 | 27 | logger = getLogger(__name__) 28 | 29 | def load_model(config, config_file=None): 30 | use_history = False 31 | model = CChessModel(config) 32 | weight_path = config.resource.model_best_weight_path 33 | if not config_file: 34 | config_path = config.resource.model_best_config_path 35 | use_history = False 36 | else: 37 | config_path = os.path.join(config.resource.model_dir, config_file) 38 | try: 39 | if not load_model_weight(model, config_path, weight_path): 40 | model.build() 41 | save_as_best_model(model) 42 | use_history = True 43 | except Exception as e: 44 | logger.info(f"Exception {e}, 重新加载权重") 45 | return load_model(config, config_file='model_192x10_config.json') 46 | return model, use_history 47 | 48 | def start(config: Config): 49 | set_session_config(per_process_gpu_memory_fraction=1, allow_growth=True, device_list=config.opts.device_list) 50 | current_model, use_history = load_model(config) 51 | m = Manager() 52 | cur_pipes = m.list([current_model.get_pipes() for _ in range(config.play.max_processes)]) 53 | # play_worker = SelfPlayWorker(config, cur_pipes, 0) 54 | # play_worker.start() 55 | with ProcessPoolExecutor(max_workers=config.play.max_processes) as executor: 56 | futures = [] 57 | for i in range(config.play.max_processes): 58 | play_worker = SelfPlayWorker(config, cur_pipes, i, use_history) 59 | logger.debug("Initialize selfplay worker") 60 | futures.append(executor.submit(play_worker.start)) 61 | 62 | class SelfPlayWorker: 63 | def __init__(self, config: Config, pipes=None, pid=None, use_history=False): 64 | self.config = config 65 | self.player = None 66 | self.cur_pipes = pipes 67 | self.id = pid 68 | self.buffer = [] 69 | self.pid = os.getpid() 70 | self.use_history = use_history 71 | 72 | def start(self): 73 | self.pid = os.getpid() 74 | ran = self.config.play.max_processes if self.config.play.max_processes > 5 else self.config.play.max_processes * 2 75 | sleep((self.pid % ran) * 10) 76 | logger.debug(f"Selfplay#Start Process index = {self.id}, pid = {self.pid}") 77 | 78 | idx = 1 79 | self.buffer = [] 80 | search_tree = defaultdict(VisitState) 81 | 82 | while True: 83 | start_time = time() 84 | search_tree = defaultdict(VisitState) 85 | value, turns, state, store = self.start_game(idx, search_tree) 86 | end_time = time() 87 | logger.debug(f"Process {self.pid}-{self.id} play game {idx} time={(end_time - start_time):.1f} sec, " 88 | f"turn={turns / 2}, winner = {value:.2f} (1 = red, -1 = black, 0 draw)") 89 | if turns <= 10: 90 | senv.render(state) 91 | if store: 92 | idx += 1 93 | sleep(random()) 94 | 95 | def start_game(self, idx, search_tree): 96 | pipes = self.cur_pipes.pop() 97 | 98 | if not self.config.play.share_mtcs_info_in_self_play or \ 99 | idx % self.config.play.reset_mtcs_info_per_game == 0: 100 | search_tree = defaultdict(VisitState) 101 | 102 | if random() > self.config.play.enable_resign_rate: 103 | enable_resign = True 104 | else: 105 | enable_resign = False 106 | 107 | self.player = CChessPlayer(self.config, search_tree=search_tree, pipes=pipes, 108 | enable_resign=enable_resign, debugging=False, use_history=self.use_history) 109 | 110 | state = senv.INIT_STATE 111 | history = [state] 112 | # policys = [] 113 | value = 0 114 | turns = 0 # even == red; odd == black 115 | game_over = False 116 | final_move = None 117 | no_eat_count = 0 118 | check = False 119 | no_act = [] 120 | increase_temp = False 121 | 122 | while not game_over: 123 | start_time = time() 124 | action, policy = self.player.action(state, turns, no_act, increase_temp=increase_temp) 125 | end_time = time() 126 | if action is None: 127 | logger.debug(f"{turns % 2} (0 = red; 1 = black) has resigned!") 128 | value = -1 129 | break 130 | # if self.config.opts.log_move: 131 | # logger.info(f"Process{self.pid} Playing: {turns % 2}, action: {action}, time: {(end_time - start_time):.1f}s") 132 | # logger.info(f"Process{self.pid} Playing: {turns % 2}, action: {action}, time: {(end_time - start_time):.1f}s") 133 | history.append(action) 134 | # policys.append(policy) 135 | try: 136 | state, no_eat = senv.new_step(state, action) 137 | except Exception as e: 138 | logger.error(f"{e}, no_act = {no_act}, policy = {policy}") 139 | game_over = True 140 | value = 0 141 | break 142 | turns += 1 143 | if no_eat: 144 | no_eat_count += 1 145 | else: 146 | no_eat_count = 0 147 | history.append(state) 148 | 149 | if no_eat_count >= 120 or turns / 2 >= self.config.play.max_game_length: 150 | game_over = True 151 | value = 0 152 | else: 153 | game_over, value, final_move, check = senv.done(state, need_check=True) 154 | if not game_over: 155 | if not senv.has_attack_chessman(state): 156 | logger.info(f"双方无进攻子力,作和。state = {state}") 157 | game_over = True 158 | value = 0 159 | increase_temp = False 160 | no_act = [] 161 | if not game_over and not check and state in history[:-1]: 162 | free_move = defaultdict(int) 163 | for i in range(len(history) - 1): 164 | if history[i] == state: 165 | if senv.will_check_or_catch(state, history[i+1]): 166 | no_act.append(history[i + 1]) 167 | elif not senv.be_catched(state, history[i+1]): 168 | increase_temp = True 169 | free_move[state] += 1 170 | if free_move[state] >= 3: 171 | # 作和棋处理 172 | game_over = True 173 | value = 0 174 | logger.info("闲着循环三次,作和棋处理") 175 | break 176 | 177 | if final_move: 178 | # policy = self.build_policy(final_move, False) 179 | history.append(final_move) 180 | # policys.append(policy) 181 | state = senv.step(state, final_move) 182 | turns += 1 183 | value = -value 184 | history.append(state) 185 | 186 | self.player.close() 187 | del search_tree 188 | del self.player 189 | gc.collect() 190 | if turns % 2 == 1: # balck turn 191 | value = -value 192 | 193 | v = value 194 | if turns < 10: 195 | if random() > 0.9: 196 | store = True 197 | else: 198 | store = False 199 | else: 200 | store = True 201 | 202 | if store: 203 | data = [history[0]] 204 | for i in range(turns): 205 | k = i * 2 206 | data.append([history[k + 1], value]) 207 | value = -value 208 | self.save_play_data(idx, data) 209 | 210 | self.cur_pipes.append(pipes) 211 | self.remove_play_data() 212 | return v, turns, state, store 213 | 214 | def save_play_data(self, idx, data): 215 | self.buffer += data 216 | 217 | if not idx % self.config.play_data.nb_game_in_file == 0: 218 | return 219 | 220 | rc = self.config.resource 221 | utc_dt = datetime.utcnow().replace(tzinfo=timezone.utc) 222 | bj_dt = utc_dt.astimezone(timezone(timedelta(hours=8))) 223 | game_id = bj_dt.strftime("%Y%m%d-%H%M%S.%f") 224 | filename = rc.play_data_filename_tmpl % game_id 225 | path = os.path.join(rc.play_data_dir, filename) 226 | logger.info(f"Process {self.pid} save play data to {path}") 227 | write_game_data_to_file(path, self.buffer) 228 | if self.config.internet.distributed: 229 | upload_worker = Thread(target=self.upload_play_data, args=(path, filename), name="upload_worker") 230 | upload_worker.daemon = True 231 | upload_worker.start() 232 | self.buffer = [] 233 | 234 | def upload_play_data(self, path, filename): 235 | digest = CChessModel.fetch_digest(self.config.resource.model_best_weight_path) 236 | data = {'digest': digest, 'username': self.config.internet.username, 'version': '2.4'} 237 | response = upload_file(self.config.internet.upload_url, path, filename, data, rm=False) 238 | if response is not None and response['status'] == 0: 239 | logger.info(f"Upload play data {filename} finished.") 240 | else: 241 | logger.error(f'Upload play data {filename} failed. {response.msg if response is not None else None}') 242 | 243 | def remove_play_data(self): 244 | files = get_game_data_filenames(self.config.resource) 245 | if len(files) < self.config.play_data.max_file_num: 246 | return 247 | try: 248 | for i in range(len(files) - self.config.play_data.max_file_num): 249 | os.remove(files[i]) 250 | except: 251 | pass 252 | 253 | def build_policy(self, action, flip): 254 | labels_n = len(ActionLabelsRed) 255 | move_lookup = {move: i for move, i in zip(ActionLabelsRed, range(labels_n))} 256 | policy = np.zeros(labels_n) 257 | 258 | policy[move_lookup[action]] = 1 259 | 260 | if flip: 261 | policy = flip_policy(policy) 262 | return list(policy) 263 | 264 | -------------------------------------------------------------------------------- /cchess_alphazero/worker/self_play_windows.py: -------------------------------------------------------------------------------- 1 | import os 2 | import gc 3 | import numpy as np 4 | from collections import deque 5 | from concurrent.futures import ProcessPoolExecutor 6 | from datetime import datetime 7 | from logging import getLogger 8 | from multiprocessing import Manager 9 | from threading import Thread 10 | from time import time 11 | from collections import defaultdict 12 | from threading import Lock 13 | from time import sleep 14 | from random import random 15 | 16 | import cchess_alphazero.environment.static_env as senv 17 | from cchess_alphazero.agent.model import CChessModel 18 | from cchess_alphazero.agent.player import CChessPlayer, VisitState 19 | from cchess_alphazero.agent.api import CChessModelAPI 20 | from cchess_alphazero.config import Config 21 | from cchess_alphazero.environment.env import CChessEnv 22 | from cchess_alphazero.environment.lookup_tables import Winner, ActionLabelsRed, flip_policy, flip_move 23 | from cchess_alphazero.lib.data_helper import get_game_data_filenames, write_game_data_to_file 24 | from cchess_alphazero.lib.model_helper import load_model_weight, save_as_best_model, load_best_model_weight_from_internet 25 | from cchess_alphazero.lib.tf_util import set_session_config 26 | from cchess_alphazero.lib.web_helper import upload_file 27 | 28 | logger = getLogger(__name__) 29 | 30 | job_done = Lock() 31 | thr_free = Lock() 32 | rst = None 33 | data = None 34 | futures =[] 35 | 36 | def start(config: Config): 37 | set_session_config(per_process_gpu_memory_fraction=1, allow_growth=True, device_list=config.opts.device_list) 38 | return SelfPlayWorker(config).start() 39 | 40 | class SelfPlayWorker: 41 | def __init__(self, config: Config): 42 | """ 43 | :param config: 44 | """ 45 | self.config = config 46 | self.current_model, self.use_history = self.load_model() 47 | self.m = Manager() 48 | self.cur_pipes = self.m.list([self.current_model.get_pipes() for _ in range(self.config.play.max_processes)]) 49 | 50 | def start(self): 51 | global job_done 52 | global thr_free 53 | global rst 54 | global data 55 | global futures 56 | 57 | self.buffer = [] 58 | need_to_renew_model = True 59 | job_done.acquire(True) 60 | logger.info(f"自我博弈开始,请耐心等待....") 61 | 62 | with ProcessPoolExecutor(max_workers=self.config.play.max_processes) as executor: 63 | game_idx = 0 64 | while True: 65 | game_idx += 1 66 | start_time = time() 67 | 68 | if len(futures) == 0: 69 | for i in range(self.config.play.max_processes): 70 | ff = executor.submit(self_play_buffer, self.config, self.cur_pipes, self.use_history) 71 | ff.add_done_callback(recall_fn) 72 | futures.append(ff) 73 | 74 | job_done.acquire(True) 75 | 76 | end_time = time() 77 | 78 | turns = rst[0] 79 | value = rst[1] 80 | logger.debug(f"对局完成:对局ID {game_idx} 耗时{(end_time - start_time):.1f} 秒, " 81 | f"{turns / 2}回合, 胜者 = {value:.2f} (1 = 红, -1 = 黑, 0 = 和)") 82 | self.buffer += data 83 | 84 | if (game_idx % self.config.play_data.nb_game_in_file) == 0: 85 | self.flush_buffer() 86 | self.remove_play_data(all=False) # remove old data 87 | ff = executor.submit(self_play_buffer, self.config, self.cur_pipes, self.use_history) 88 | ff.add_done_callback(recall_fn) 89 | futures.append(ff) # Keep it going 90 | thr_free.release() 91 | 92 | if len(data) > 0: 93 | self.flush_buffer() 94 | 95 | def load_model(self, config_file=None): 96 | use_history = False 97 | model = CChessModel(self.config) 98 | weight_path = self.config.resource.model_best_weight_path 99 | if not config_file: 100 | config_path = self.config.resource.model_best_config_path 101 | use_history = False 102 | else: 103 | config_path = os.path.join(self.config.resource.model_dir, config_file) 104 | try: 105 | if not load_model_weight(model, config_path, weight_path): 106 | model.build() 107 | save_as_best_model(model) 108 | use_history = True 109 | except Exception as e: 110 | logger.info(f"Exception {e}, 重新加载权重") 111 | return self.load_model(config_file='model_192x10_config.json') 112 | return model, use_history 113 | 114 | def flush_buffer(self): 115 | rc = self.config.resource 116 | game_id = datetime.now().strftime("%Y%m%d-%H%M%S.%f") 117 | filename = rc.play_data_filename_tmpl % game_id 118 | path = os.path.join(rc.play_data_dir, filename) 119 | logger.info("保存博弈数据到 %s" % (path)) 120 | write_game_data_to_file(path, self.buffer) 121 | if self.config.internet.distributed: 122 | upload_worker = Thread(target=self.upload_play_data, args=(path, filename)) 123 | upload_worker.start() 124 | self.buffer = [] 125 | 126 | def remove_play_data(self,all=False): 127 | files = get_game_data_filenames(self.config.resource) 128 | if (all): 129 | for path in files: 130 | os.remove(path) 131 | else: 132 | while len(files) > self.config.play_data.max_file_num: 133 | os.remove(files[0]) 134 | del files[0] 135 | 136 | def upload_play_data(self, path, filename): 137 | digest = CChessModel.fetch_digest(self.config.resource.model_best_weight_path) 138 | data = {'digest': digest, 'username': self.config.internet.username, 'version': '2.4'} 139 | response = upload_file(self.config.internet.upload_url, path, filename, data, rm=False) 140 | if response is not None and response['status'] == 0: 141 | logger.info(f"上传博弈数据 {filename} 成功.") 142 | else: 143 | logger.error(f'上传博弈数据 {filename} 失败. {response.msg if response is not None else None}') 144 | 145 | def recall_fn(future): 146 | global thr_free 147 | global job_done 148 | global rst 149 | global data 150 | global futures 151 | 152 | thr_free.acquire(True) 153 | rst, data = future.result() 154 | futures.remove(future) 155 | job_done.release() 156 | 157 | def self_play_buffer(config, cur, use_history=False) -> (tuple, list): 158 | pipe = cur.pop() # borrow 159 | 160 | if random() > config.play.enable_resign_rate: 161 | enable_resign = True 162 | else: 163 | enable_resign = False 164 | 165 | player = CChessPlayer(config, search_tree=defaultdict(VisitState), pipes=pipe, 166 | enable_resign=enable_resign, debugging=False, use_history=use_history) 167 | 168 | state = senv.INIT_STATE 169 | history = [state] 170 | # policys = [] 171 | value = 0 172 | turns = 0 173 | game_over = False 174 | final_move = None 175 | no_eat_count = 0 176 | check = False 177 | no_act = None 178 | increase_temp = False 179 | 180 | while not game_over: 181 | start_time = time() 182 | action, policy = player.action(state, turns, no_act, increase_temp=increase_temp) 183 | end_time = time() 184 | if action is None: 185 | print(f"{turns % 2} (0 = 红; 1 = 黑) 投降了!") 186 | value = -1 187 | break 188 | print(f"博弈中: 回合{turns / 2 + 1} {'红方走棋' if turns % 2 == 0 else '黑方走棋'}, 着法: {action}, 用时: {(end_time - start_time):.1f}s") 189 | # policys.append(policy) 190 | history.append(action) 191 | try: 192 | state, no_eat = senv.new_step(state, action) 193 | except Exception as e: 194 | logger.error(f"{e}, no_act = {no_act}, policy = {policy}") 195 | game_over = True 196 | value = 0 197 | break 198 | turns += 1 199 | if no_eat: 200 | no_eat_count += 1 201 | else: 202 | no_eat_count = 0 203 | history.append(state) 204 | 205 | if no_eat_count >= 120 or turns / 2 >= config.play.max_game_length: 206 | game_over = True 207 | value = 0 208 | else: 209 | game_over, value, final_move, check = senv.done(state, need_check=True) 210 | no_act = [] 211 | increase_temp = False 212 | if not game_over: 213 | if not senv.has_attack_chessman(state): 214 | logger.info(f"双方无进攻子力,作和。state = {state}") 215 | game_over = True 216 | value = 0 217 | if not game_over and not check and state in history[:-1]: 218 | free_move = defaultdict(int) 219 | for i in range(len(history) - 1): 220 | if history[i] == state: 221 | if senv.will_check_or_catch(state, history[i+1]): 222 | no_act.append(history[i + 1]) 223 | elif not senv.be_catched(state, history[i+1]): 224 | increase_temp = True 225 | free_move[state] += 1 226 | if free_move[state] >= 3: 227 | # 作和棋处理 228 | game_over = True 229 | value = 0 230 | logger.info("闲着循环三次,作和棋处理") 231 | break 232 | 233 | if final_move: 234 | # policy = build_policy(final_move, False) 235 | history.append(final_move) 236 | # policys.append(policy) 237 | state = senv.step(state, final_move) 238 | turns += 1 239 | value = -value 240 | history.append(state) 241 | 242 | player.close() 243 | del player 244 | gc.collect() 245 | 246 | if turns % 2 == 1: # balck turn 247 | value = -value 248 | 249 | v = value 250 | data = [history[0]] 251 | for i in range(turns): 252 | k = i * 2 253 | data.append([history[k + 1], value]) 254 | value = -value 255 | 256 | cur.append(pipe) 257 | return (turns, v), data 258 | 259 | def build_policy(action, flip): 260 | labels_n = len(ActionLabelsRed) 261 | move_lookup = {move: i for move, i in zip(ActionLabelsRed, range(labels_n))} 262 | policy = np.zeros(labels_n) 263 | 264 | policy[move_lookup[action]] = 1 265 | 266 | if flip: 267 | policy = flip_policy(policy) 268 | return list(policy) 269 | -------------------------------------------------------------------------------- /cchess_alphazero/worker/sl.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | 5 | from collections import deque 6 | from concurrent.futures import ProcessPoolExecutor 7 | from datetime import datetime 8 | from logging import getLogger 9 | from time import sleep 10 | from random import shuffle 11 | from time import time 12 | 13 | from cchess_alphazero.agent.model import CChessModel 14 | from cchess_alphazero.config import Config 15 | from cchess_alphazero.lib.data_helper import get_game_data_filenames, read_game_data_from_file 16 | from cchess_alphazero.lib.model_helper import load_sl_best_model_weight, save_as_sl_best_model 17 | from cchess_alphazero.environment.env import CChessEnv 18 | from cchess_alphazero.environment.lookup_tables import ActionLabelsRed, flip_policy, flip_move 19 | from cchess_alphazero.lib.tf_util import set_session_config 20 | 21 | from keras.optimizers import Adam 22 | from keras.callbacks import TensorBoard 23 | import keras.backend as K 24 | 25 | logger = getLogger(__name__) 26 | 27 | def start(config: Config): 28 | set_session_config(per_process_gpu_memory_fraction=1, allow_growth=True, device_list='0,1') 29 | return SupervisedWorker(config).start() 30 | 31 | class SupervisedWorker: 32 | def __init__(self, config:Config): 33 | self.config = config 34 | self.model = None 35 | self.loaded_data = deque(maxlen=self.config.trainer.dataset_size) 36 | self.dataset = deque(), deque(), deque() 37 | self.filenames = [] 38 | self.opt = None 39 | self.buffer = [] 40 | self.gameinfo = None 41 | self.moves = None 42 | self.config.opts.light = True 43 | 44 | def start(self): 45 | self.model = self.load_model() 46 | self.gameinfo = pd.read_csv(self.config.resource.sl_data_gameinfo) 47 | self.moves = pd.read_csv(self.config.resource.sl_data_move) 48 | self.training() 49 | 50 | def training(self): 51 | self.compile_model() 52 | total_steps = self.config.trainer.start_total_steps 53 | logger.info(f"Start training, game count = {len(self.gameinfo)}, step = {self.config.trainer.sl_game_step} games") 54 | 55 | for i in range(0, len(self.gameinfo), self.config.trainer.sl_game_step): 56 | games = self.gameinfo[i:i+self.config.trainer.sl_game_step] 57 | self.fill_queue(games) 58 | if len(self.dataset[0]) > self.config.trainer.batch_size: 59 | steps = self.train_epoch(self.config.trainer.epoch_to_checkpoint) 60 | total_steps += steps 61 | self.save_current_model() 62 | a, b, c = self.dataset 63 | a.clear() 64 | b.clear() 65 | c.clear() 66 | 67 | def train_epoch(self, epochs): 68 | tc = self.config.trainer 69 | state_ary, policy_ary, value_ary = self.collect_all_loaded_data() 70 | tensorboard_cb = TensorBoard(log_dir="./logs/tensorboard_sl/", batch_size=tc.batch_size, histogram_freq=1) 71 | self.model.model.fit(state_ary, [policy_ary, value_ary], 72 | batch_size=tc.batch_size, 73 | epochs=epochs, 74 | shuffle=True, 75 | validation_split=0.02, 76 | callbacks=[tensorboard_cb]) 77 | steps = (state_ary.shape[0] // tc.batch_size) * epochs 78 | return steps 79 | 80 | def compile_model(self): 81 | self.opt = Adam(lr=1e-2) 82 | losses = ['categorical_crossentropy', 'mean_squared_error'] # avoid overfit for supervised 83 | self.model.model.compile(optimizer=self.opt, loss=losses, loss_weights=self.config.trainer.loss_weights) 84 | 85 | def fill_queue(self, games): 86 | _tuple = self.generate_game_data(games) 87 | if _tuple is not None: 88 | for x, y in zip(self.dataset, _tuple): 89 | x.extend(y) 90 | 91 | def collect_all_loaded_data(self): 92 | state_ary, policy_ary, value_ary = self.dataset 93 | 94 | state_ary1 = np.asarray(state_ary, dtype=np.float32) 95 | policy_ary1 = np.asarray(policy_ary, dtype=np.float32) 96 | value_ary1 = np.asarray(value_ary, dtype=np.float32) 97 | return state_ary1, policy_ary1, value_ary1 98 | 99 | def load_model(self): 100 | model = CChessModel(self.config) 101 | if self.config.opts.new or not load_sl_best_model_weight(model): 102 | model.build() 103 | save_as_sl_best_model(model) 104 | return model 105 | 106 | def save_current_model(self): 107 | logger.debug("Save best sl model") 108 | save_as_sl_best_model(self.model) 109 | 110 | def generate_game_data(self, games): 111 | self.buffer = [] 112 | start_time = time() 113 | for idx, game in games.iterrows(): 114 | gid = game['gameID'] 115 | winner = game['winner'] 116 | move = self.moves[self.moves.gameID == gid] 117 | red = move[move.side == 'red'] 118 | black = move[move.side == 'black'] 119 | self.load_game(red, black, winner, idx) 120 | end_time = time() 121 | logger.debug(f"Loading {len(games)} games, time: {end_time - start_time}s") 122 | return self.convert_to_trainging_data() 123 | 124 | def load_game(self, red, black, winner, idx): 125 | env = CChessEnv(self.config).reset() 126 | red_moves = [] 127 | black_moves = [] 128 | turns = 1 129 | black_max_turn = black['turn'].max() 130 | red_max_turn = red['turn'].max() 131 | 132 | while turns < black_max_turn or turns < red_max_turn: 133 | if turns < red_max_turn: 134 | wxf_move = red[red.turn == turns]['move'].item() 135 | action = env.board.parse_WXF_move(wxf_move) 136 | try: 137 | red_moves.append([env.observation, self.build_policy(action, flip=False)]) 138 | except Exception as e: 139 | for i in range(10): 140 | logger.debug(f"{env.board.screen[i]}") 141 | logger.debug(f"{turns} {wxf_move} {action}") 142 | 143 | env.step(action) 144 | if turns < black_max_turn: 145 | wxf_move = black[black.turn == turns]['move'].item() 146 | action = env.board.parse_WXF_move(wxf_move) 147 | try: 148 | black_moves.append([env.observation, self.build_policy(action, flip=True)]) 149 | except Exception as e: 150 | for i in range(10): 151 | logger.debug(f"{env.board.screen[i]}") 152 | logger.debug(f"{turns} {wxf_move} {action}") 153 | 154 | env.step(action) 155 | turns += 1 156 | 157 | if winner == 'red': 158 | red_win = 1 159 | elif winner == 'black': 160 | red_win = -1 161 | else: 162 | red_win = 0 163 | 164 | for move in red_moves: 165 | move += [red_win] 166 | for move in black_moves: 167 | move += [-red_win] 168 | 169 | data = [] 170 | for i in range(len(red_moves)): 171 | data.append(red_moves[i]) 172 | if i < len(black_moves): 173 | data.append(black_moves[i]) 174 | self.buffer += data 175 | 176 | def build_policy(self, action, flip): 177 | labels_n = len(ActionLabelsRed) 178 | move_lookup = {move: i for move, i in zip(ActionLabelsRed, range(labels_n))} 179 | policy = np.zeros(labels_n) 180 | 181 | policy[move_lookup[action]] = 1 182 | 183 | if flip: 184 | policy = flip_policy(policy) 185 | return policy 186 | 187 | def convert_to_trainging_data(self): 188 | data = self.buffer 189 | state_list = [] 190 | policy_list = [] 191 | value_list = [] 192 | env = CChessEnv() 193 | 194 | for state_fen, policy, value in data: 195 | state_planes = env.fen_to_planes(state_fen) 196 | sl_value = value 197 | 198 | state_list.append(state_planes) 199 | policy_list.append(policy) 200 | value_list.append(sl_value) 201 | 202 | return np.asarray(state_list, dtype=np.float32), \ 203 | np.asarray(policy_list, dtype=np.float32), \ 204 | np.asarray(value_list, dtype=np.float32) 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /cchess_alphazero/worker/sl_onegreen.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import json 4 | 5 | from collections import deque 6 | from concurrent.futures import ProcessPoolExecutor 7 | from datetime import datetime 8 | from logging import getLogger 9 | from time import sleep 10 | from random import shuffle 11 | from time import time 12 | 13 | import cchess_alphazero.environment.static_env as senv 14 | from cchess_alphazero.agent.model import CChessModel 15 | from cchess_alphazero.config import Config 16 | from cchess_alphazero.lib.data_helper import get_game_data_filenames, read_game_data_from_file 17 | from cchess_alphazero.lib.model_helper import load_sl_best_model_weight, save_as_sl_best_model 18 | from cchess_alphazero.environment.env import CChessEnv 19 | from cchess_alphazero.environment.lookup_tables import ActionLabelsRed, flip_policy, flip_move 20 | from cchess_alphazero.lib.tf_util import set_session_config 21 | from cchess_alphazero.environment.lookup_tables import Winner 22 | 23 | from keras.optimizers import Adam 24 | from keras.callbacks import TensorBoard 25 | import keras.backend as K 26 | 27 | logger = getLogger(__name__) 28 | 29 | def start(config: Config, skip): 30 | set_session_config(per_process_gpu_memory_fraction=1, allow_growth=True, device_list=config.opts.device_list) 31 | return SupervisedWorker(config).start(skip) 32 | 33 | class SupervisedWorker: 34 | def __init__(self, config:Config): 35 | self.config = config 36 | self.model = None 37 | self.loaded_data = deque(maxlen=self.config.trainer.dataset_size) 38 | self.dataset = deque(), deque(), deque() 39 | self.filenames = [] 40 | self.opt = None 41 | self.buffer = [] 42 | self.games = None 43 | 44 | def start(self, skip=0): 45 | self.model = self.load_model() 46 | with open(self.config.resource.sl_onegreen, 'r') as f: 47 | self.games = json.load(f) 48 | self.training(skip) 49 | 50 | def training(self, skip=0): 51 | self.compile_model() 52 | total_steps = self.config.trainer.start_total_steps 53 | logger.info(f"Start training, game count = {len(self.games)}, step = {self.config.trainer.sl_game_step} games, skip = {skip}") 54 | 55 | for i in range(skip, len(self.games), self.config.trainer.sl_game_step): 56 | games = self.games[i:i+self.config.trainer.sl_game_step] 57 | self.fill_queue(games) 58 | if len(self.dataset[0]) > self.config.trainer.batch_size: 59 | steps = self.train_epoch(self.config.trainer.epoch_to_checkpoint) 60 | total_steps += steps 61 | self.save_current_model() 62 | a, b, c = self.dataset 63 | a.clear() 64 | b.clear() 65 | c.clear() 66 | logger.debug(f"total steps = {total_steps}") 67 | 68 | def train_epoch(self, epochs): 69 | tc = self.config.trainer 70 | state_ary, policy_ary, value_ary = self.collect_all_loaded_data() 71 | tensorboard_cb = TensorBoard(log_dir="./logs/tensorboard_sl/", batch_size=tc.batch_size, histogram_freq=1) 72 | self.model.model.fit(state_ary, [policy_ary, value_ary], 73 | batch_size=tc.batch_size, 74 | epochs=epochs, 75 | shuffle=True, 76 | validation_split=0.02, 77 | callbacks=[tensorboard_cb]) 78 | steps = (state_ary.shape[0] // tc.batch_size) * epochs 79 | return steps 80 | 81 | def compile_model(self): 82 | self.opt = Adam(lr=0.003) 83 | losses = ['categorical_crossentropy', 'mean_squared_error'] 84 | self.model.model.compile(optimizer=self.opt, loss=losses, loss_weights=self.config.trainer.loss_weights) 85 | 86 | def fill_queue(self, games): 87 | _tuple = self.generate_game_data(games) 88 | if _tuple is not None: 89 | for x, y in zip(self.dataset, _tuple): 90 | x.extend(y) 91 | 92 | def collect_all_loaded_data(self): 93 | state_ary, policy_ary, value_ary = self.dataset 94 | 95 | state_ary1 = np.asarray(state_ary, dtype=np.float32) 96 | policy_ary1 = np.asarray(policy_ary, dtype=np.float32) 97 | value_ary1 = np.asarray(value_ary, dtype=np.float32) 98 | return state_ary1, policy_ary1, value_ary1 99 | 100 | def load_model(self): 101 | model = CChessModel(self.config) 102 | if self.config.opts.new or not load_sl_best_model_weight(model): 103 | model.build() 104 | save_as_sl_best_model(model) 105 | return model 106 | 107 | def save_current_model(self): 108 | logger.debug("Save best sl model") 109 | save_as_sl_best_model(self.model) 110 | 111 | def generate_game_data(self, games): 112 | self.buffer = [] 113 | start_time = time() 114 | idx = 0 115 | cnt = 0 116 | for game in games: 117 | init = game['init'] 118 | move_list = game['move_list'] 119 | winner = Winner.draw 120 | if game['result'] == '红胜' or '胜' in game['title']: 121 | winner = Winner.red 122 | elif game['result'] == '黑胜' or '负' in game['title']: 123 | winner = Winner.black 124 | else: 125 | winner = Winner.draw 126 | v = self.load_game(init, move_list, winner, idx, game['title'], game['url']) 127 | if v == 1 or v == -1: 128 | cnt += 1 129 | idx += 1 130 | end_time = time() 131 | logger.debug(f"Loading {len(games)} games, time: {end_time - start_time}s, end games = {cnt}") 132 | return self.convert_to_trainging_data() 133 | 134 | def load_game(self, init, move_list, winner, idx, title, url): 135 | turns = 0 136 | env = CChessEnv(self.config).reset(init) 137 | red_moves = [] 138 | black_moves = [] 139 | moves = [move_list[i:i+4] for i in range(len(move_list)) if i % 4 == 0] 140 | 141 | for move in moves: 142 | action = senv.parse_onegreen_move(move) 143 | try: 144 | if turns % 2 == 0: 145 | red_moves.append([env.observation, self.build_policy(action, flip=False)]) 146 | else: 147 | black_moves.append([env.observation, self.build_policy(action, flip=True)]) 148 | env.step(action) 149 | except: 150 | logger.error(f"Invalid Action: idx = {idx}, action = {action}, turns = {turns}, moves = {moves}, " 151 | f"winner = {winner}, init = {init}, title: {title}, url: {url}") 152 | return 153 | turns += 1 154 | 155 | if winner == Winner.red: 156 | red_win = 1 157 | elif winner == Winner.black: 158 | red_win = -1 159 | else: 160 | red_win = senv.evaluate(env.get_state()) 161 | if not env.red_to_move: 162 | red_win = -red_win 163 | 164 | for move in red_moves: 165 | move += [red_win] 166 | for move in black_moves: 167 | move += [-red_win] 168 | 169 | data = [] 170 | for i in range(len(red_moves)): 171 | data.append(red_moves[i]) 172 | if i < len(black_moves): 173 | data.append(black_moves[i]) 174 | self.buffer += data 175 | return red_win 176 | 177 | def build_policy(self, action, flip): 178 | labels_n = len(ActionLabelsRed) 179 | move_lookup = {move: i for move, i in zip(ActionLabelsRed, range(labels_n))} 180 | policy = np.zeros(labels_n) 181 | 182 | policy[move_lookup[action]] = 1 183 | 184 | if flip: 185 | policy = flip_policy(policy) 186 | return policy 187 | 188 | def convert_to_trainging_data(self): 189 | data = self.buffer 190 | state_list = [] 191 | policy_list = [] 192 | value_list = [] 193 | env = CChessEnv() 194 | 195 | for state_fen, policy, value in data: 196 | state_planes = env.fen_to_planes(state_fen) 197 | sl_value = value 198 | 199 | state_list.append(state_planes) 200 | policy_list.append(policy) 201 | value_list.append(sl_value) 202 | 203 | return np.asarray(state_list, dtype=np.float32), \ 204 | np.asarray(policy_list, dtype=np.float32), \ 205 | np.asarray(value_list, dtype=np.float32) 206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /colaboratory/eval.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import multiprocessing as mp 4 | 5 | from logging import getLogger 6 | 7 | _PATH_ = os.path.dirname(os.path.dirname(__file__)) 8 | 9 | if _PATH_ not in sys.path: 10 | sys.path.append(_PATH_) 11 | 12 | from cchess_alphazero.lib.logger import setup_logger 13 | from cchess_alphazero.config import Config 14 | import cchess_alphazero.worker.compute_elo as evaluator 15 | 16 | def setup_parameters(config): 17 | num_cores = mp.cpu_count() 18 | max_processes = 2 19 | if len(sys.argv) > 1: 20 | max_processes = int(sys.argv[1]) 21 | if len(sys.argv) > 2: 22 | flag = sys.argv[2] 23 | if flag == 'new': 24 | config.internet.base_url = 'http://temp.52coding.com.cn' 25 | elif flag == 'new2': 26 | config.internet.base_url = 'http://temp3.52coding.com.cn' 27 | config.internet.upload_url = f'{config.internet.base_url}/api/upload_game_file/192x10' 28 | config.internet.upload_eval_url = f'{config.internet.base_url}/api/upload_eval_game_file' 29 | config.internet.get_latest_digest = f'{config.internet.base_url}/api/get_latest_digest/192x10' 30 | config.internet.get_evaluate_model_url = f'{config.internet.base_url}/api/query_for_evaluate' 31 | config.internet.update_elo_url = f'{config.internet.base_url}/api/add_eval_result/' 32 | search_threads = 20 33 | print(f"max_processes = {max_processes}, search_threads = {search_threads}") 34 | config.play.max_processes = max_processes 35 | config.play.search_threads = search_threads 36 | 37 | if __name__ == "__main__": 38 | sys.setrecursionlimit(10000) 39 | config_type = 'distribute' 40 | config = Config(config_type=config_type) 41 | config.opts.device_list = '0' 42 | config.opts.log_move = True 43 | config.resource.create_directories() 44 | setup_logger(config.resource.eval_log_path) 45 | config.eval.update_play_config(config.play) 46 | setup_parameters(config) 47 | # config.internet.download_base_url = 'http://alphazero-1251776088.cossh.myqcloud.com/model/' 48 | evaluator.start(config) 49 | -------------------------------------------------------------------------------- /colaboratory/run.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import multiprocessing as mp 4 | 5 | from logging import getLogger 6 | 7 | _PATH_ = os.path.dirname(os.path.dirname(__file__)) 8 | 9 | if _PATH_ not in sys.path: 10 | sys.path.append(_PATH_) 11 | 12 | from cchess_alphazero.lib.logger import setup_logger 13 | from cchess_alphazero.config import Config, PlayWithHumanConfig 14 | from cchess_alphazero.worker import self_play 15 | 16 | def setup_parameters(config): 17 | if len(sys.argv) > 1: 18 | config.internet.username = sys.argv[1] 19 | print(f'用户名设置为:{config.internet.username}') 20 | num_cores = mp.cpu_count() 21 | max_processes = 2 22 | if len(sys.argv) > 2: 23 | max_processes = int(sys.argv[2]) 24 | if len(sys.argv) > 3: 25 | config.internet.base_url = sys.argv[3] 26 | config.internet.upload_url = f'{config.internet.base_url}/api/upload_game_file/192x10' 27 | config.internet.upload_eval_url = f'{config.internet.base_url}/api/upload_eval_game_file' 28 | config.internet.get_latest_digest = f'{config.internet.base_url}/api/get_latest_digest/192x10' 29 | # config.internet.get_evaluate_model_url = f'{config.internet.base_url}/api/query_for_evaluate' 30 | # config.internet.update_elo_url = f'{config.internet.base_url}/api/add_eval_result/' 31 | search_threads = 10 32 | print(f"max_processes = {max_processes}, search_threads = {search_threads}") 33 | config.play.max_processes = max_processes 34 | config.play.search_threads = search_threads 35 | 36 | if __name__ == "__main__": 37 | sys.setrecursionlimit(10000) 38 | config_type = 'distribute' 39 | config = Config(config_type=config_type) 40 | config.opts.device_list = '0' 41 | config.resource.create_directories() 42 | setup_logger(config.resource.main_log_path) 43 | config.internet.distributed = True 44 | config.opts.log_move = True 45 | setup_parameters(config) 46 | # config.internet.download_url = 'http://alphazero-1251776088.cossh.myqcloud.com/model/128x7/model_best_weight.h5' 47 | self_play.start(config) 48 | -------------------------------------------------------------------------------- /colaboratory/test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import multiprocessing as mp 4 | 5 | from logging import getLogger 6 | 7 | _PATH_ = os.path.dirname(os.path.dirname(__file__)) 8 | 9 | if _PATH_ not in sys.path: 10 | sys.path.append(_PATH_) 11 | 12 | from cchess_alphazero.lib.logger import setup_logger 13 | from cchess_alphazero.config import Config, PlayWithHumanConfig 14 | from cchess_alphazero.worker import self_play 15 | 16 | def setup_parameters(config): 17 | if len(sys.argv) > 1: 18 | config.internet.username = sys.argv[1] 19 | print(f'用户名设置为:{config.internet.username}') 20 | num_cores = mp.cpu_count() 21 | max_processes = 2 22 | if len(sys.argv) > 2: 23 | max_processes = int(sys.argv[2]) 24 | search_threads = 20 25 | print(f"max_processes = {max_processes}, search_threads = {search_threads}") 26 | config.play.max_processes = max_processes 27 | config.play.search_threads = search_threads 28 | 29 | if __name__ == "__main__": 30 | sys.setrecursionlimit(10000) 31 | config_type = 'distribute' 32 | config = Config(config_type=config_type) 33 | config.opts.device_list = '0' 34 | config.resource.create_directories() 35 | setup_logger(config.resource.main_log_path) 36 | config.internet.distributed = False 37 | config.opts.log_move = True 38 | config.opts.has_history = True 39 | setup_parameters(config) 40 | # config.internet.download_url = 'http://alphazero-1251776088.cossh.myqcloud.com/model/128x7/model_best_weight.h5' 41 | self_play.start(config) 42 | -------------------------------------------------------------------------------- /data/model/model_best_weight.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/data/model/model_best_weight.h5 -------------------------------------------------------------------------------- /elo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/elo.png -------------------------------------------------------------------------------- /freeze/evaluate.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import multiprocessing as mp 4 | 5 | from logging import getLogger 6 | 7 | _PATH_ = os.path.dirname(os.path.dirname(__file__)) 8 | 9 | if _PATH_ not in sys.path: 10 | sys.path.append(_PATH_) 11 | 12 | from cchess_alphazero.lib.logger import setup_logger 13 | from cchess_alphazero.config import Config, PlayWithHumanConfig 14 | import cchess_alphazero.worker.compute_elo_windows as evaluate 15 | 16 | def setup_parameters(config): 17 | gpu = input(f"请输入GPU编号(0代表第一块,1代表第二块,以此类推...):") 18 | config.opts.device_list = gpu 19 | num_cores = mp.cpu_count() 20 | max_processes = num_cores // 2 if num_cores < 20 else 10 21 | search_threads = 10 22 | max_processes = input(f"请输入运行进程数(推荐{max_processes}):") 23 | max_processes = int(max_processes) 24 | print(f"max_processes = {max_processes}, search_threads = {search_threads}") 25 | config.play.max_processes = max_processes 26 | config.play.search_threads = search_threads 27 | 28 | 29 | if __name__ == "__main__": 30 | mp.freeze_support() 31 | sys.setrecursionlimit(10000) 32 | config_type = 'distribute' 33 | config = Config(config_type=config_type) 34 | config.resource.create_directories() 35 | setup_logger(config.resource.main_log_path) 36 | config.internet.distributed = True 37 | setup_parameters(config) 38 | evaluate.start(config) 39 | -------------------------------------------------------------------------------- /freeze/play_games.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import multiprocessing as mp 4 | 5 | from logging import getLogger 6 | 7 | _PATH_ = os.path.dirname(os.path.dirname(__file__)) 8 | 9 | if _PATH_ not in sys.path: 10 | sys.path.append(_PATH_) 11 | 12 | 13 | from cchess_alphazero.lib.logger import setup_logger 14 | from cchess_alphazero.config import Config, PlayWithHumanConfig 15 | from cchess_alphazero.play_games import play 16 | 17 | 18 | def setup_parameters(config): 19 | num_cores = mp.cpu_count() 20 | search_threads = 10 if num_cores < 10 else 20 21 | print(f"search_threads = {search_threads}") 22 | config.play.search_threads = search_threads 23 | 24 | if __name__ == "__main__": 25 | mp.freeze_support() 26 | sys.setrecursionlimit(10000) 27 | config_type = 'distribute' 28 | 29 | config = Config(config_type=config_type) 30 | config.opts.device_list = '0' 31 | config.resource.create_directories() 32 | setup_logger(config.resource.play_log_path) 33 | config.opts.new = False 34 | config.opts.light = False 35 | pwhc = PlayWithHumanConfig() 36 | pwhc.update_play_config(config.play) 37 | config.opts.bg_style = 'WOOD' 38 | setup_parameters(config) 39 | simulation_num = input('请输入AI搜索次数(必须为整数):') 40 | ai_move_first = input('AI执红?(Y/N)') 41 | ai_move_first = True if ai_move_first == 'Y' or ai_move_first == 'y' else False 42 | config.play.simulation_num_per_move = int(simulation_num) 43 | play.start(config, not ai_move_first) 44 | input('按任意键退出...') 45 | -------------------------------------------------------------------------------- /freeze/play_games.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | 3 | block_cipher = None 4 | 5 | a = Analysis(['play_games.py'], 6 | pathex=['C:\\Users\\niuhe\\Desktop\\ChineseChess-AlphaZero-master'], 7 | binaries=[], 8 | datas=[ 9 | ('C:\\Users\\niuhe\\Desktop\\ChineseChess-AlphaZero-master\\cchess_alphazero\\play_games\\images\\WOOD.GIF', 'cchess_alphazero\\play_games\\images'), 10 | ('C:\\Users\\niuhe\\Desktop\\ChineseChess-AlphaZero-master\\cchess_alphazero\\play_games\\PingFang.ttc', 'cchess_alphazero\\play_games'), 11 | ('C:\\Users\\niuhe\\Desktop\\ChineseChess-AlphaZero-master\\cchess_alphazero\\play_games\\images\\WOOD\\*.GIF', 'cchess_alphazero\\play_games\\images\\WOOD') 12 | ], 13 | hiddenimports=[], 14 | hookspath=[], 15 | runtime_hooks=[], 16 | excludes=[], 17 | win_no_prefer_redirects=False, 18 | win_private_assemblies=False, 19 | cipher=block_cipher) 20 | pyz = PYZ(a.pure, a.zipped_data, 21 | cipher=block_cipher) 22 | exe = EXE(pyz, 23 | a.scripts, 24 | exclude_binaries=True, 25 | name='play_games', 26 | debug=False, 27 | strip=False, 28 | upx=True, 29 | console=True , resources=['cchess_alphazero\\\\play_games\\\\images']) 30 | coll = COLLECT(exe, 31 | a.binaries, 32 | a.zipfiles, 33 | a.datas, 34 | strip=False, 35 | upx=True, 36 | name='play_games') 37 | -------------------------------------------------------------------------------- /freeze/run_self_play.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import multiprocessing as mp 4 | 5 | from logging import getLogger 6 | 7 | _PATH_ = os.path.dirname(os.path.dirname(__file__)) 8 | 9 | if _PATH_ not in sys.path: 10 | sys.path.append(_PATH_) 11 | 12 | from cchess_alphazero.lib.logger import setup_logger 13 | from cchess_alphazero.config import Config, PlayWithHumanConfig 14 | import cchess_alphazero.worker.self_play_windows as self_play 15 | 16 | def setup_parameters(config): 17 | username = input(f"请输入用户名:") 18 | config.internet.username = username 19 | gpu = input(f"请输入GPU编号(0代表第一块,1代表第二块,以此类推...):") 20 | config.opts.device_list = gpu 21 | num_cores = mp.cpu_count() 22 | max_processes = num_cores // 2 if num_cores < 20 else 10 23 | search_threads = 10 24 | max_processes = input(f"请输入运行进程数(推荐{max_processes}):") 25 | max_processes = int(max_processes) 26 | print(f"max_processes = {max_processes}, search_threads = {search_threads}") 27 | config.play.max_processes = max_processes 28 | config.play.search_threads = search_threads 29 | 30 | 31 | if __name__ == "__main__": 32 | mp.freeze_support() 33 | sys.setrecursionlimit(10000) 34 | config_type = 'distribute' 35 | config = Config(config_type=config_type) 36 | config.resource.create_directories() 37 | setup_logger(config.resource.main_log_path) 38 | config.internet.distributed = True 39 | setup_parameters(config) 40 | self_play.start(config) 41 | -------------------------------------------------------------------------------- /model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/model.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | h5py==2.7.1 2 | tensorflow-gpu==1.3.0 3 | tensorflow-tensorboard==0.1.8 4 | Keras==2.0.8 5 | numpy 6 | pandas 7 | pygame 8 | requests 9 | tqdm -------------------------------------------------------------------------------- /screenshots/board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeymarL/ChineseChess-AlphaZero/7f45b0cd7470f7f8e1da7331fc1a31374d6cc10f/screenshots/board.png --------------------------------------------------------------------------------