├── .gitignore ├── Makefile ├── README.md ├── pycchess ├── BOOK.DAT ├── cchess.py ├── chessboard.py ├── chessman.py ├── chessnet.py ├── common.py ├── image │ ├── advisor.png │ ├── bishop.png │ ├── cannon.png │ ├── cchessboard.png │ ├── done.png │ ├── king.png │ ├── knight.png │ ├── over.png │ ├── pawn.png │ ├── rook.png │ └── select.png └── sounds │ ├── CAPTURE.WAV │ ├── CHECK.WAV │ └── MOVE.WAV └── src ├── Makefile ├── base.h ├── evaluate.c ├── evaluate.h ├── genmoves.c ├── genmoves.h ├── harmless.c ├── hash.c ├── hash.h ├── movesort.c ├── movesort.h ├── openbook.c ├── openbook.h ├── pipe.c ├── pipe.h ├── position.c ├── position.h ├── search.c ├── search.h ├── ucci.c └── ucci.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.pyc 3 | harmless 4 | harmless.log 5 | src/BOOK.DAT 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default all: 2 | cd src && $(MAKE) && cd .. 3 | 4 | install: all 5 | cp src/harmless pycchess 6 | 7 | .PHONY : clean 8 | clean : 9 | cd src && $(MAKE) clean && cd .. 10 | rm -f pycchess/harmless* 11 | rm -f pycchess/*.pyc 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # harmless 2 | 3 | A chinese chess engine 4 | 5 | ## INSTALL 6 | 7 | ### GNU/Linux and Mac OS X User 8 | 9 | > require 10 | 11 | * `python-2.7.x`: 12 | * `pygame-1.9.x`: 13 | 14 | Hint: install pygame on OS X Lion 15 | 16 | 17 | 18 | > run 19 | 20 | ``` 21 | $ git clone git://github.com/timebug/harmless.git 22 | $ make && make install 23 | $ cd pycchess && python cchess.py 24 | ``` 25 | 26 | ### Windows User 27 | 28 | 1. Access 29 | 2. Download `pycchess-win32-*.zip` then unzip it 30 | 3. Run `cchess.exe` 31 | 32 | ## KEYBOARD SHORTCUTS 33 | 34 | * `space`: new game 35 | -------------------------------------------------------------------------------- /pycchess/cchess.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 | from common import * 21 | from chessboard import * 22 | from chessnet import * 23 | 24 | import pygame 25 | #import pygame._view 26 | from pygame.locals import * 27 | 28 | import sys 29 | from subprocess import PIPE, Popen 30 | from threading import Thread 31 | from Queue import Queue, Empty 32 | 33 | ON_POSIX = 'posix' in sys.builtin_module_names 34 | 35 | def enqueue_output(out, queue): 36 | for line in iter(out.readline, ''): 37 | queue.put(line) 38 | out.close() 39 | 40 | pygame.init() 41 | 42 | screen = pygame.display.set_mode(size, 0, 32) 43 | chessboard = chessboard() 44 | 45 | if len(sys.argv) == 2 and sys.argv[1][:2] == '-n': 46 | chessboard.net = chessnet() 47 | 48 | if sys.argv[1][2:] == 'r': 49 | pygame.display.set_caption("red") 50 | chessboard.side = RED 51 | elif sys.argv[1][2:] == 'b': 52 | pygame.display.set_caption("black") 53 | chessboard.side = BLACK 54 | else: 55 | print '>> quit game' 56 | sys.exit() 57 | 58 | chessboard.net.NET_HOST = sys.argv[2] 59 | 60 | elif len(sys.argv) == 1: 61 | p = Popen("./harmless", stdin=PIPE, stdout=PIPE, close_fds=ON_POSIX) 62 | (chessboard.fin, chessboard.fout) = (p.stdin, p.stdout) 63 | q = Queue() 64 | t = Thread(target=enqueue_output, args=(chessboard.fout, q)) 65 | t.daemon = True 66 | t.start() 67 | 68 | chessboard.fin.write("ucci\n") 69 | chessboard.fin.flush() 70 | 71 | while True: 72 | try: 73 | output = q.get_nowait() 74 | except Empty: 75 | continue 76 | else: 77 | sys.stdout.write(output) 78 | if 'ucciok' in output: 79 | break 80 | 81 | chessboard.mode = AI 82 | pygame.display.set_caption("harmless") 83 | chessboard.side = RED 84 | else: 85 | print '>> quit game' 86 | sys.exit() 87 | 88 | chessboard.fen_parse(fen_str) 89 | init = True 90 | waiting = False 91 | moved = False 92 | 93 | def newGame(): 94 | global init 95 | global waiting 96 | global moved 97 | 98 | chessboard.fin.write("setoption newgame\n") 99 | chessboard.fin.flush() 100 | print '>> new game' 101 | 102 | chessboard.fen_parse(fen_str) 103 | init = True 104 | waiting = False 105 | moved = False 106 | 107 | def quitGame(): 108 | if chessboard.mode is NETWORK: 109 | net = chessnet() 110 | net.send_move('quit') 111 | if chessboard.mode is AI: 112 | chessboard.fin.write("quit\n") 113 | chessboard.fin.flush() 114 | p.terminate() 115 | 116 | print '>> quit game' 117 | sys.exit() 118 | 119 | def runGame(): 120 | global init 121 | global waiting 122 | global moved 123 | 124 | for event in pygame.event.get(): 125 | if event.type == QUIT: 126 | quitGame() 127 | if event.type == KEYDOWN: 128 | if event.key == K_SPACE: 129 | if chessboard.mode == AI: 130 | if not waiting or chessboard.over: 131 | newGame() 132 | return 133 | 134 | if event.type == MOUSEBUTTONDOWN: 135 | x, y = pygame.mouse.get_pos() 136 | if x < BORDER or x > (WIDTH - BORDER): 137 | break 138 | if y < BORDER or y > (HEIGHT - BORDER): 139 | break 140 | x = (x - BORDER) / SPACE 141 | y = (y - BORDER) / SPACE 142 | if not waiting and not chessboard.over: 143 | moved = chessboard.move_chessman(x, y) 144 | if chessboard.mode == NETWORK and moved: 145 | chessboard.over = chessboard.game_over(1-chessboard.side) 146 | if chessboard.over: 147 | chessboard.over_side = 1-chessboard.side 148 | 149 | chessboard.draw(screen) 150 | pygame.display.update() 151 | 152 | if moved: 153 | if chessboard.mode is NETWORK: 154 | move_str = chessboard.net.get_move() 155 | if move_str is not 'quit': 156 | # print 'recv move: %s' % move_str 157 | move_arr = str_to_move(move_str) 158 | else: 159 | quitGame() 160 | 161 | if chessboard.mode is AI: 162 | try: 163 | output = q.get_nowait() 164 | except Empty: 165 | waiting = True 166 | return 167 | else: 168 | waiting = False 169 | sys.stdout.write(output) 170 | 171 | if output[0:10] == 'nobestmove': 172 | chessboard.over = True 173 | chessboard.over_side = 1 - chessboard.side 174 | 175 | if chessboard.over_side == RED: 176 | win_side = 'BLACK' 177 | else: 178 | win_side = 'RED' 179 | print '>>', win_side, 'win' 180 | 181 | return 182 | elif output[0:8] == 'bestmove': 183 | move_str = output[9:13] 184 | move_arr = str_to_move(move_str) 185 | else: 186 | return 187 | 188 | chessboard.side = 1 - chessboard.side 189 | chessboard.move_from = OTHER 190 | chessboard.move_chessman(move_arr[0], move_arr[1]) 191 | chessboard.move_chessman(move_arr[2], move_arr[3]) 192 | chessboard.move_from = LOCAL 193 | chessboard.side = 1 - chessboard.side 194 | 195 | # if chessboard.check(chessboard.side): 196 | chessboard.over = chessboard.game_over(chessboard.side) 197 | if chessboard.over: 198 | chessboard.over_side = chessboard.side 199 | 200 | if chessboard.over_side == RED: 201 | win_side = 'BLACK' 202 | else: 203 | win_side = 'RED' 204 | print '>>', win_side, 'win' 205 | 206 | moved = False 207 | 208 | if len(sys.argv) == 2 and sys.argv[1][:2] == '-n' and init: 209 | move_str = chessboard.net.get_move() 210 | if move_str is not None: 211 | # print 'recv move: %s' % move_str 212 | move_arr = str_to_move(move_str) 213 | 214 | chessboard.side = 1 - chessboard.side 215 | chessboard.move_from = OTHER 216 | chessboard.move_chessman(move_arr[0], move_arr[1]) 217 | chessboard.move_chessman(move_arr[2], move_arr[3]) 218 | chessboard.move_from = LOCAL 219 | chessboard.side = 1 - chessboard.side 220 | init = False 221 | else: 222 | chessboard.over = True 223 | 224 | try: 225 | while True: 226 | runGame() 227 | except KeyboardInterrupt: 228 | quitGame() 229 | -------------------------------------------------------------------------------- /pycchess/chessboard.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 | from common import * 21 | from chessman import * 22 | from chessnet import * 23 | 24 | import sys 25 | import pygame 26 | 27 | class chessboard: 28 | def clearboard(self): 29 | self.board = {} 30 | self.selected = () 31 | self.done = [] 32 | self.piece = [0]*48 33 | self.over = False 34 | self.over_side = RED 35 | 36 | def __init__(self): 37 | self.clearboard() 38 | 39 | self.mode = NETWORK 40 | self.side = RED 41 | self.move_from = LOCAL 42 | self.net = None 43 | 44 | self.fin = sys.stdin; 45 | self.fout = sys.stdout; 46 | 47 | self.surface = pygame.image.load(image_path + board_image).convert() 48 | self.select_surface = pygame.image.load(image_path + select_image).convert_alpha() 49 | self.done_surface = pygame.image.load(image_path + done_image).convert_alpha() 50 | self.over_surface = pygame.image.load(image_path + over_image).convert_alpha() 51 | 52 | self.check_sound = load_sound(check_sound) 53 | self.move_sound = load_sound(move_sound) 54 | self.capture_sound = load_sound(capture_sound) 55 | 56 | def add_chessman(self, kind, color, x, y, pc): 57 | chessman_ = chessman(kind, color, x, y, pc) 58 | 59 | if kind == PAWN: 60 | if color == self.side: 61 | if y < 5: 62 | chessman_.over_river = True 63 | else: 64 | if y > 4: 65 | chessman_.over_river = True 66 | 67 | self.board[(x, y)] = chessman_ 68 | self.piece[pc] = (x, y) 69 | 70 | def get_fen(self): 71 | fen_str = '' 72 | count = 0 73 | for j in range(10): 74 | for i in range(9): 75 | if (i, j) in self.board.keys(): 76 | if count is not 0: 77 | fen_str += str(count) 78 | count = 0 79 | chessman = self.board[(i, j)] 80 | ch = get_char(chessman.kind, chessman.color) 81 | 82 | if ch is not '': 83 | fen_str += ch 84 | else: 85 | count += 1 86 | 87 | if count is not 0: 88 | fen_str += str(count) 89 | count = 0 90 | if j < 9: 91 | fen_str += '/' 92 | 93 | if self.side is BLACK: 94 | fen_str += ' w' 95 | else: 96 | fen_str += ' b' 97 | fen_str += ' - - 0 1' 98 | 99 | return fen_str 100 | 101 | def fen_parse(self, fen_str): 102 | self.clearboard() 103 | 104 | pc_code = [[16, 17, 19, 21, 23, 25, 27], [32, 33, 35, 37, 39, 41, 43]] 105 | 106 | if fen_str == '': 107 | return 108 | 109 | x = 0 110 | y = 0 111 | 112 | for i in range(0, len(fen_str)): 113 | ch = fen_str[i] 114 | if ch == ' ': 115 | break 116 | 117 | if ch == '/': 118 | x = 0 119 | y += 1 120 | 121 | if y > 9: 122 | break 123 | elif ch >= '1' and ch <= '9': 124 | x += int(ch) 125 | if x > 8: 126 | x = 8 127 | elif ch >= 'A' and ch <= 'Z': 128 | if x <= 8: 129 | kind = get_kind(ch) 130 | 131 | if kind != NONE: 132 | self.add_chessman(kind, self.side, x, y, pc_code[self.side][kind]) 133 | pc_code[self.side][kind] += 1 134 | 135 | x += 1 136 | elif ch >= 'a' and ch <= 'z': 137 | if x <= 8: 138 | kind = get_kind(ch) 139 | 140 | if kind != NONE: 141 | self.add_chessman(kind, 1-self.side, x, y, pc_code[1-self.side][kind]) 142 | pc_code[1-self.side][kind] += 1 143 | 144 | x += 1 145 | 146 | # if fen_str[i+1] == 'b': 147 | # self.side = BLACK 148 | # else: 149 | # self.side = RED 150 | 151 | def draw(self, screen): 152 | screen.fill((0,0,0)) 153 | screen.blit(self.surface, (0, 0)) 154 | # offset_ = 0 155 | for key in self.board.keys(): 156 | chessman = self.board[key] 157 | if chessman == None: 158 | continue 159 | board_x = BORDER + chessman.x * SPACE 160 | board_y = BORDER + chessman.y * SPACE 161 | if chessman.color == RED: 162 | offset = 0 163 | else: 164 | offset = 53 165 | screen.blit(chessman.surface, (board_x, board_y), (offset, 0, 52, 52)) 166 | 167 | if key == self.selected: 168 | screen.blit(self.select_surface, (board_x, board_y), (offset, 0, 52, 52)) 169 | 170 | if key in self.done: 171 | screen.blit(self.select_surface, (board_x, board_y), (offset, 0, 52, 52)) 172 | offset_ = offset 173 | 174 | for d in self.done: 175 | if d not in self.board.keys(): 176 | board_x = BORDER + d[0] * SPACE 177 | board_y = BORDER + d[1] * SPACE 178 | screen.blit(self.done_surface, (board_x, board_y), (offset_, 0, 52, 52)) 179 | 180 | if self.over: 181 | side_tag = 16 + self.over_side * 16 182 | king = self.piece[side_tag] 183 | if king: 184 | board_x = BORDER + king[0] * SPACE 185 | board_y = BORDER + king[1] * SPACE 186 | screen.blit(self.over_surface, (board_x, board_y), (self.over_side*53, 0, 52, 52)) 187 | 188 | def check(self, side): 189 | side_tag = 32 - side * 16 190 | 191 | # king 192 | w_king = self.piece[16] 193 | b_king = self.piece[32] 194 | 195 | if not w_king or not b_king: 196 | return False 197 | 198 | kill = True 199 | if w_king[0] == b_king[0]: 200 | min_y = min(w_king[1], b_king[1]) 201 | max_y = max(w_king[1], b_king[1]) 202 | for m_y in range(min_y+1, max_y): 203 | if (w_king[0] ,m_y) in self.board.keys(): 204 | kill = False 205 | break 206 | if kill: 207 | return kill 208 | 209 | # knight 210 | q = self.piece[48 - side_tag] 211 | 212 | for i in range(5, 7): 213 | p = self.piece[side_tag + i] 214 | if not p: 215 | continue 216 | for k in range(0, 8): 217 | tmp = knight_dir[k] 218 | n = (p[0]+tmp[0], p[1]+tmp[1]) 219 | if n != q: 220 | continue 221 | if self.board[p].move_check(n[0],n[1]): 222 | tmp = knight_check[k] 223 | m = (p[0]+tmp[0], p[1]+tmp[1]) 224 | if m not in self.board.keys(): 225 | return True 226 | 227 | # rook 228 | for i in range(7, 9): 229 | kill = True 230 | 231 | p = self.piece[side_tag + i] 232 | if not p: 233 | continue 234 | if p[0] == q[0]: 235 | min_y = min(p[1], q[1]) 236 | max_y = max(p[1], q[1]) 237 | 238 | for m_y in range(min_y+1, max_y): 239 | if (p[0], m_y) in self.board.keys(): 240 | kill = False 241 | break 242 | 243 | if kill: 244 | return kill 245 | elif p[1] == q[1]: 246 | min_x = min(p[0], q[0]) 247 | max_x = max(p[0], q[0]) 248 | 249 | for m_x in range(min_x+1, max_x): 250 | if (m_x, p[1]) in self.board.keys(): 251 | kill = False 252 | break 253 | 254 | if kill: 255 | return kill 256 | 257 | # cannon 258 | for i in range(9, 11): 259 | over_flag = 0 260 | p = self.piece[side_tag + i] 261 | if not p: 262 | continue 263 | if p[0] == q[0]: 264 | min_y = min(p[1], q[1]) 265 | max_y = max(p[1], q[1]) 266 | 267 | for m_y in range(min_y+1, max_y): 268 | if (p[0], m_y) in self.board.keys(): 269 | if not over_flag: 270 | over_flag = 1 271 | else: 272 | over_flag = 2 273 | break 274 | if over_flag == 1: 275 | return True 276 | elif p[1] == q[1]: 277 | min_x = min(p[0], q[0]) 278 | max_x = max(p[0], q[0]) 279 | for m_x in range(min_x+1, max_x): 280 | if (m_x, p[1]) in self.board.keys(): 281 | if not over_flag: 282 | over_flag = 1 283 | else: 284 | over_flag = 2 285 | if over_flag == 1: 286 | return True 287 | 288 | # pwan 289 | for i in range(11, 16): 290 | p = self.piece[side_tag + i] 291 | if not p: 292 | continue 293 | 294 | flag = 0 295 | if p[1] > 4: 296 | flag = 1 297 | 298 | for k in range(0, 3): 299 | tmp = pawn_dir[flag][k] 300 | n = (p[0]+tmp[0], p[1]+tmp[1]) 301 | if n != q: 302 | continue 303 | if self.board[p].move_check(n[0], n[1]): 304 | return True 305 | 306 | return False 307 | 308 | def can_move(self, chessman, x, y): 309 | ok = True 310 | if chessman.kind == BISHOP: 311 | m_x = (chessman.x + x) / 2 312 | m_y = (chessman.y + y) / 2 313 | if (m_x, m_y) in self.board.keys(): 314 | ok = False 315 | 316 | if chessman.kind == KNIGHT: 317 | if abs(chessman.x - x) == 2: 318 | m_x = (chessman.x + x) / 2 319 | m_y = chessman.y 320 | if abs(chessman.y - y) == 2: 321 | m_x = chessman.x 322 | m_y = (chessman.y + y) / 2 323 | if (m_x, m_y) in self.board.keys(): 324 | ok = False 325 | 326 | if chessman.kind == ROOK or chessman.kind == CANNON: 327 | over_flag = 0 328 | if chessman.x != x: 329 | min_x = min(chessman.x, x) 330 | max_x = max(chessman.x, x) 331 | for m_x in range(min_x+1, max_x): 332 | if (m_x, y) in self.board.keys(): 333 | over_flag += 1 334 | else: 335 | min_y = min(chessman.y, y) 336 | max_y = max(chessman.y, y) 337 | for m_y in range(min_y+1, max_y): 338 | if (x, m_y) in self.board.keys(): 339 | over_flag += 1 340 | 341 | if over_flag != 0: 342 | ok = False 343 | 344 | if chessman.kind == CANNON: 345 | if (x, y) in self.board.keys(): 346 | if over_flag == 1: 347 | ok = True 348 | else: 349 | ok = False 350 | return ok 351 | 352 | def move_chessman(self, x, y): 353 | flag = False 354 | if (x, y) in self.board.keys(): 355 | chessman = self.board[(x, y)] 356 | if chessman.color == self.side: 357 | flag = True 358 | else: 359 | if self.selected is (): 360 | return False 361 | 362 | if self.selected is (): 363 | if flag: 364 | self.selected = (x, y) 365 | 366 | else: 367 | if flag: 368 | self.selected = (x, y) 369 | else: 370 | chessman = self.board[self.selected] 371 | if chessman.move_check(x, y): 372 | ok = self.can_move(chessman, x, y) 373 | if ok: 374 | chessman_ = None 375 | if (x, y) in self.board.keys(): 376 | chessman_ = self.board[(x, y)] 377 | 378 | self.make_move(self.selected, (x, y), chessman_) 379 | 380 | if not self.check(self.side): 381 | 382 | under_attack = self.check(1 - self.side) 383 | 384 | if under_attack is True: 385 | self.check_sound.play() 386 | else: 387 | if chessman_ == None: 388 | self.move_sound.play() 389 | else: 390 | self.capture_sound.play() 391 | 392 | self.done = [self.selected, (x, y)] 393 | 394 | if self.move_from == LOCAL: 395 | if self.mode == NETWORK: 396 | move_str_ = move_to_str(self.selected[0],self.selected[1],x,y) 397 | # print 'send move: %s' % move_str_ 398 | move_str = move_to_str(8-self.selected[0],9-self.selected[1],8-x,9-y) 399 | if self.net is not None: 400 | self.net.send_move(move_str) 401 | else: 402 | print 'self.net is None' 403 | 404 | if self.mode == AI: 405 | fen_str = self.get_fen() 406 | self.fin.write('position fen ' + fen_str + '\n') 407 | # print "position fen %s" % fen_str 408 | self.fin.flush() 409 | self.fin.write('go depth ' + str(AI_SEARCH_DEPTH) + '\n') 410 | self.fin.flush() 411 | 412 | self.selected = () 413 | 414 | return True 415 | else: 416 | self.unmake_move(self.selected, (x, y), chessman_) 417 | 418 | return False 419 | 420 | def make_move(self, p, n, chessman_): 421 | chessman = self.board[p] 422 | 423 | self.piece[chessman.pc] = n 424 | if chessman_ is not None: 425 | self.piece[chessman_.pc] = 0 426 | 427 | chessman.x, chessman.y = n 428 | self.board[n] = chessman 429 | del self.board[p] 430 | 431 | def unmake_move(self, p, n, chessman_): 432 | chessman = self.board[n] 433 | 434 | chessman.x, chessman.y = p 435 | self.board[p] = chessman 436 | 437 | self.piece[chessman.pc] = p 438 | if chessman_ is not None: 439 | self.board[n] = chessman_ 440 | self.piece[chessman_.pc] = n 441 | else: 442 | del self.board[n] 443 | 444 | def save_move(self, p, n, moves, side): 445 | flag = False 446 | if n in self.board.keys(): 447 | if self.board[n].color is side: 448 | flag = True 449 | 450 | chessman = self.board[p] 451 | ok = self.can_move(chessman, n[0], n[1]) 452 | if not ok: 453 | flag = True 454 | 455 | if not flag: 456 | move_ = move(p, n) 457 | moves.append(move_) 458 | 459 | def gen_moves(self, side): 460 | moves = [] 461 | side_tag = 16 + 16 * side 462 | 463 | # king 464 | p = self.piece[side_tag] 465 | if not p: 466 | return moves 467 | for k in range(0, 4): 468 | tmp = king_dir[k] 469 | n = (p[0]+tmp[0], p[1]+tmp[1]) 470 | if self.board[p].move_check(n[0], n[1]): 471 | self.save_move(p, n, moves, side) 472 | 473 | # advisor 474 | for i in range(1,3): 475 | p = self.piece[side_tag + i] 476 | if not p: 477 | continue 478 | for k in range(0, 4): 479 | tmp = advisor_dir[k] 480 | n = (p[0]+tmp[0], p[1]+tmp[1]) 481 | if self.board[p].move_check(n[0], n[1]): 482 | self.save_move(p, n, moves, side) 483 | 484 | # bishop 485 | for i in range(3, 5): 486 | p = self.piece[side_tag + i] 487 | if not p: 488 | continue 489 | for k in range(0, 4): 490 | tmp = bishop_dir[k] 491 | n = (p[0]+tmp[0], p[1]+tmp[1]) 492 | if self.board[p].move_check(n[0], n[1]): 493 | tmp = bishop_check[k] 494 | m = (p[0]+tmp[0], p[1]+tmp[1]) 495 | if m not in self.board.keys(): 496 | self.save_move(p, n, moves, side) 497 | 498 | # knight 499 | for i in range(5, 7): 500 | p = self.piece[side_tag + i] 501 | if not p: 502 | continue 503 | for k in range(0, 8): 504 | tmp = knight_dir[k] 505 | n = (p[0]+tmp[0], p[1]+tmp[1]) 506 | if self.board[p].move_check(n[0], n[1]): 507 | tmp = knight_check[k] 508 | m = (p[0]+tmp[0], p[1]+tmp[1]) 509 | if m not in self.board.keys(): 510 | self.save_move(p, n, moves, side) 511 | 512 | # rook 513 | for i in range(7, 9): 514 | p = self.piece[side_tag + i] 515 | if not p: 516 | continue 517 | for k in range(0, 4): 518 | for j in range(1, 10): 519 | tmp = rook_dir[k] 520 | n = (p[0]+j*tmp[0],p[1]+j*tmp[1]) 521 | if not self.board[p].move_check(n[0], n[1]): 522 | break 523 | if n not in self.board.keys(): 524 | move_ = move(p, n) 525 | moves.append(move_) 526 | else: 527 | if self.board[n].color is not side: 528 | move_ = move(p, n) 529 | moves.append(move_) 530 | break 531 | 532 | # cannon 533 | for i in range(9, 11): 534 | p = self.piece[side_tag + i] 535 | if not p: 536 | continue 537 | for k in range(0, 4): 538 | over_flag = 0 539 | for j in range(1, 10): 540 | tmp = cannon_dir[k] 541 | n = (p[0]+j*tmp[0],p[1]+j*tmp[1]) 542 | if not self.board[p].move_check(n[0], n[1]): 543 | break 544 | if n not in self.board.keys(): 545 | if not over_flag: 546 | move_ = move(p, n) 547 | moves.append(move_) 548 | else: 549 | if not over_flag: 550 | over_flag = 1 551 | else: 552 | self.save_move(p, n, moves, side) 553 | break 554 | 555 | # pawn 556 | for i in range(11, 16): 557 | p = self.piece[side_tag + i] 558 | if not p: 559 | continue 560 | 561 | flag = 0 562 | if p[1] > 4: 563 | flag = 1 564 | 565 | for k in range(0, 3): 566 | tmp = pawn_dir[flag][k] 567 | n = (p[0]+tmp[0], p[1]+tmp[1]) 568 | if self.board[p].move_check(n[0], n[1]): 569 | self.save_move(p, n, moves, side) 570 | 571 | return moves 572 | 573 | def game_over(self, side): 574 | over = True 575 | moves = self.gen_moves(side) 576 | for move_ in moves: 577 | p = move_.p 578 | n = move_.n 579 | chessman_ = None 580 | if n in self.board.keys(): 581 | chessman_ = self.board[n] 582 | self.make_move(p, n, chessman_) 583 | over = self.check(side) 584 | self.unmake_move(p ,n, chessman_) 585 | if not over: 586 | # move_str = move_to_str(p[0],p[1],n[0],n[1]) 587 | # sys.stdout.write("move = " + move_str + "\n") 588 | break 589 | 590 | return over 591 | -------------------------------------------------------------------------------- /pycchess/chessman.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 | from common import * 21 | 22 | import sys 23 | import pygame 24 | 25 | class chessman: 26 | def __init__(self, kind, color, x, y, pc): 27 | self.kind = kind 28 | self.color = color 29 | self.x = x 30 | self.y = y 31 | self.pc = pc 32 | self.over_river = False 33 | self.surface = pygame.image.load(image_path + chessman_image[kind]).convert_alpha() 34 | 35 | def move_check(self, x, y): 36 | ok = True 37 | if x < 0 or x > 8: 38 | return False 39 | if y < 0 or y > 9: 40 | return False 41 | if self.kind == KING: 42 | if abs(self.x - x) + abs(self.y - y) != 1: 43 | ok = False 44 | if x < 3 or x > 5: 45 | ok = False 46 | if self.y < 3 and y >= 3: 47 | ok = False 48 | if self.y > 6 and y <= 6: 49 | ok = False 50 | elif self.kind == ADVISOR: 51 | if abs(self.x - x) != 1 or abs(self.y - y) != 1: 52 | ok = False 53 | if x < 3 or x > 5: 54 | ok = False 55 | if self.y < 3 and y >= 3: 56 | ok = False 57 | if self.y > 6 and y <= 6: 58 | ok = False 59 | elif self.kind == BISHOP: 60 | if abs(self.x - x) != 2 or abs(self.y - y) != 2: 61 | ok = False 62 | if self.y < 5 and y >= 5: 63 | ok = False 64 | if self.y > 4 and y <= 4: 65 | ok = False 66 | elif self.kind == KNIGHT: 67 | if pow((self.x - x), 2) + pow((self.y - y), 2) != 5: 68 | ok = False 69 | elif self.kind == ROOK or self.kind == CANNON: 70 | if abs(self.x - x) != 0 and abs(self.y - y) != 0: 71 | ok = False 72 | elif self.kind == PAWN: 73 | if self.over_river: 74 | if abs(self.x - x) + abs(self.y - y) != 1: 75 | ok = False 76 | if self.y < 5: 77 | if self.x == x and self.y - y != 1: 78 | ok = False 79 | else: 80 | if self.x == x and y - self.y != 1: 81 | ok = False 82 | else: 83 | if self.y < 5: 84 | if y - self.y != 1 or x != self.x: 85 | ok = False 86 | else: 87 | if self.y == 4 and y == 5: 88 | self.over_river = True 89 | else: 90 | if self.y - y != 1 or x != self.x: 91 | ok = False 92 | else: 93 | if self.y == 5 and y == 4: 94 | self.over_river = True 95 | 96 | else: 97 | ok = False 98 | return ok 99 | -------------------------------------------------------------------------------- /pycchess/chessnet.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 socket, sys, traceback 21 | 22 | class chessnet(): 23 | def __init__(self): 24 | self.host = '' 25 | self.NET_HOST = '' 26 | self.NET_PORT = 62222 27 | 28 | def send_move(self, move): 29 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 30 | 31 | try: 32 | s.connect((self.NET_HOST, self.NET_PORT)) 33 | except socket.error, e: 34 | print "Couldn't find your port: %s" % e 35 | sys.exit(1) 36 | 37 | try: 38 | s.send(move) 39 | except socket.error, e: 40 | print "Error sending data (detected by shutdown): %s" % e 41 | sys.exit(1) 42 | 43 | s.close() 44 | 45 | def get_move(self): 46 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 47 | s.setsockopt(socket.SOCK_STREAM, socket.SO_REUSEADDR, 1) 48 | s.bind((self.host, self.NET_PORT)) 49 | s.listen(1) 50 | 51 | while 1: 52 | try: 53 | clientsock, clientaddr = s.accept() 54 | # print clientaddr 55 | except KeyboardInterrupt: 56 | raise 57 | except: 58 | traceback.print_exc() 59 | continue 60 | try: 61 | move = clientsock.recv(1024) 62 | except socket.error, e: 63 | print "Error receiving data: %s" % e 64 | sys.exit(1) 65 | except: 66 | traceback.print_exc() 67 | 68 | try: 69 | clientsock.close() 70 | except KeyboardInterrupt: 71 | raise 72 | except: 73 | traceback.print_exc() 74 | 75 | return move 76 | -------------------------------------------------------------------------------- /pycchess/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 | size = (WIDTH, HEIGHT) = (530, 586) 23 | 24 | RED, BLACK = 0, 1 25 | BORDER, SPACE = 15, 56 26 | LOCAL, OTHER = 0, 1 27 | NETWORK, AI = 0, 1 28 | KING, ADVISOR, BISHOP, KNIGHT, ROOK, CANNON, PAWN, NONE = 0, 1, 2, 3, 4, 5, 6, -1 29 | 30 | AI_SEARCH_DEPTH = 5 31 | 32 | image_path = 'image/' 33 | board_image = 'cchessboard.png' 34 | select_image = 'select.png' 35 | over_image = 'over.png' 36 | done_image = 'done.png' 37 | chessman_image = ['king.png', 38 | 'advisor.png', 39 | 'bishop.png', 40 | 'knight.png', 41 | 'rook.png', 42 | 'cannon.png', 43 | 'pawn.png'] 44 | 45 | check_sound = "sounds/CHECK.WAV" 46 | move_sound = 'sounds/MOVE.WAV' 47 | capture_sound = 'sounds/CAPTURE.WAV' 48 | 49 | fen_str = 'rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1' 50 | 51 | king_dir = [(0, -1), (1, 0), (0, 1), (-1, 0)] 52 | advisor_dir = [(-1, -1), (1, -1), (-1, 1), (1, 1)] 53 | bishop_dir = [(-2, -2), (2, -2), (2, 2), (-2, 2)] 54 | knight_dir = [(-1, -2), (1, -2), (2, -1), (2, 1), (1, 2), (-1, 2), (-2, 1), (-2, -1)] 55 | rook_dir = [(0, -1), (1, 0), (0, 1), (-1, 0)] 56 | cannon_dir = [(0, -1), (1, 0), (0, 1), (-1, 0)] 57 | pawn_dir = [[(0, -1), (-1, 0), (1, 0)], [(0, 1), (-1, 0), (1, 0)]] 58 | 59 | bishop_check = [(-1, -1), (1, -1), (-1, 1), (1, 1)] 60 | knight_check = [(0, -1), (0, -1), (1, 0), (1, 0), (0, 1), (0, 1), (-1, 0), (-1, 0)] 61 | 62 | def get_kind(fen_ch): 63 | if fen_ch in ['k', 'K']: 64 | return KING 65 | elif fen_ch in ['a', 'A']: 66 | return ADVISOR 67 | elif fen_ch in ['b', 'B']: 68 | return BISHOP 69 | elif fen_ch in ['n', 'N']: 70 | return KNIGHT 71 | elif fen_ch in ['r', 'R']: 72 | return ROOK 73 | elif fen_ch in ['c', 'C']: 74 | return CANNON 75 | elif fen_ch in ['p', 'P']: 76 | return PAWN 77 | else: 78 | return NONE 79 | 80 | def get_char(kind, color): 81 | if kind is KING: 82 | return ['K', 'k'][color] 83 | elif kind is ADVISOR: 84 | return ['A', 'a'][color] 85 | elif kind is BISHOP: 86 | return ['B', 'b'][color] 87 | elif kind is KNIGHT: 88 | return ['N', 'n'][color] 89 | elif kind is ROOK: 90 | return ['R', 'r'][color] 91 | elif kind is CANNON: 92 | return ['C', 'c'][color] 93 | elif kind is PAWN: 94 | return ['P', 'p'][color] 95 | else: 96 | return '' 97 | 98 | def move_to_str(x, y, x_, y_): 99 | move_str = '' 100 | move_str += chr(ord('a') + x) 101 | move_str += str(9 - y) 102 | move_str += chr(ord('a') + x_) 103 | move_str += str(9 - y_) 104 | return move_str 105 | 106 | def str_to_move(move_str): 107 | move_arr = [0]*4 108 | move_arr[0] = ord(move_str[0]) - ord('a') 109 | move_arr[1] = ord('9') - ord(move_str[1]) 110 | move_arr[2] = ord(move_str[2]) - ord('a') 111 | move_arr[3] = ord('9') - ord(move_str[3]) 112 | return move_arr 113 | 114 | class move: 115 | def __init__(self, p, n): 116 | self.p = p 117 | self.n = n 118 | 119 | def load_sound(name): 120 | try: 121 | sound = pygame.mixer.Sound(name) 122 | except pygame.error, message: 123 | raise SystemExit, message 124 | return sound 125 | -------------------------------------------------------------------------------- /pycchess/image/advisor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/image/advisor.png -------------------------------------------------------------------------------- /pycchess/image/bishop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/image/bishop.png -------------------------------------------------------------------------------- /pycchess/image/cannon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/image/cannon.png -------------------------------------------------------------------------------- /pycchess/image/cchessboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/image/cchessboard.png -------------------------------------------------------------------------------- /pycchess/image/done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/image/done.png -------------------------------------------------------------------------------- /pycchess/image/king.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/image/king.png -------------------------------------------------------------------------------- /pycchess/image/knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/image/knight.png -------------------------------------------------------------------------------- /pycchess/image/over.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/image/over.png -------------------------------------------------------------------------------- /pycchess/image/pawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/image/pawn.png -------------------------------------------------------------------------------- /pycchess/image/rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/image/rook.png -------------------------------------------------------------------------------- /pycchess/image/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/image/select.png -------------------------------------------------------------------------------- /pycchess/sounds/CAPTURE.WAV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/sounds/CAPTURE.WAV -------------------------------------------------------------------------------- /pycchess/sounds/CHECK.WAV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/sounds/CHECK.WAV -------------------------------------------------------------------------------- /pycchess/sounds/MOVE.WAV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timebug/harmless/374df3c2df2a95a109019db94b65e55fa8d0af4b/pycchess/sounds/MOVE.WAV -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | BIN = harmless 2 | 3 | CC = gcc 4 | CFLAGS = -O3 -Wall 5 | 6 | ${BIN} : pipe.o ucci.o position.o genmoves.o movesort.o \ 7 | evaluate.o search.o hash.o openbook.o harmless.o 8 | ${CC} ${CFLAGS} $^ -o $@ 9 | 10 | position.o : base.h position.h 11 | genmoves.o : base.h position.h genmoves.h 12 | movesort.o : base.h position.h genmoves.h movesort.h 13 | evaluate.o : base.h position.h genmoves.h evaluate.h 14 | hash.o : hash.h 15 | openbook.o : base.h hash.h position.h 16 | search.o : base.h position.h genmoves.h movesort.h \ 17 | evaluate.h search.h hash.h 18 | pipe.o : pipe.h 19 | ucci.o : pipe.h ucci.h 20 | harmless.o : base.h position.h search.h ucci.h 21 | 22 | .PHONY : clean 23 | clean : 24 | ${RM} ${BIN} *.o 25 | -------------------------------------------------------------------------------- /src/base.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE_H 2 | #define BASE_H 3 | 4 | #include 5 | 6 | #define RED 0 7 | #define BLACK 1 8 | 9 | #define KING 0 10 | #define ADVISOR 1 11 | #define BISHOP 2 12 | #define KNIGHT 3 13 | #define ROOK 4 14 | #define CANNON 5 15 | #define PAWN 6 16 | 17 | #define INFINITE_ 20000 18 | /* 最大搜索棋局深度 */ 19 | #define MAX_SEARCH_DEPTH 50 20 | 21 | typedef unsigned char BYTE; 22 | typedef unsigned long INT32_; 23 | typedef unsigned long long INT64_; 24 | 25 | typedef struct { 26 | BYTE from; 27 | BYTE to; 28 | unsigned short capture; 29 | } move; 30 | 31 | extern move NULL_MOVE; 32 | 33 | #define DEBUG_LOG 34 | /* 日志文件描述符 */ 35 | extern FILE *logfile; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/evaluate.c: -------------------------------------------------------------------------------- 1 | #include "evaluate.h" 2 | 3 | /* 棋子价值数组 */ 4 | static BYTE position_values[2][7][256] = 5 | { 6 | { 7 | { /* 红帅 */ 8 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18 | 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 19 | 0, 0, 0, 0, 0, 0, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 20 | 0, 0, 0, 0, 0, 0, 15, 20, 15, 0, 0, 0, 0, 0, 0, 0, 21 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 24 | }, 25 | { /* 红仕 */ 26 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36 | 0, 0, 0, 0, 0, 0, 30, 0, 30, 0, 0, 0, 0, 0, 0, 0, 37 | 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 38 | 0, 0, 0, 0, 0, 0, 30, 0, 30, 0, 0, 0, 0, 0, 0, 0, 39 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 42 | }, 43 | { /* 红相 */ 44 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52 | 0, 0, 0, 0, 0, 25, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 53 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54 | 0, 0, 0, 20, 0, 0, 0, 35, 0, 0, 0, 20, 0, 0, 0, 0, 55 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56 | 0, 0, 0, 0, 0, 30, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 57 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 60 | }, 61 | { /* 红马 */ 62 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65 | 0, 0, 0, 70, 80, 90, 80, 70, 80, 90, 80, 70, 0, 0, 0, 0, 66 | 0, 0, 0, 80,110,125, 90, 70, 90,125,110, 80, 0, 0, 0, 0, 67 | 0, 0, 0, 90,100,120,125,120,125,120,100, 90, 0, 0, 0, 0, 68 | 0, 0, 0, 90,100,120,130,110,130,120,100, 90, 0, 0, 0, 0, 69 | 0, 0, 0, 90,110,110,120,100,120,110,110, 90, 0, 0, 0, 0, 70 | 0, 0, 0, 90,100,100,110,100,110,100,100, 90, 0, 0, 0, 0, 71 | 0, 0, 0, 80, 90,100,100, 90,100,100, 90, 80, 0, 0, 0, 0, 72 | 0, 0, 0, 80, 80, 90, 90, 80, 90, 90, 80, 80, 0, 0, 0, 0, 73 | 0, 0, 0, 70, 75, 75, 70, 50, 70, 75, 75, 70, 0, 0, 0, 0, 74 | 0, 0, 0, 60, 70, 75, 70, 60, 70, 75, 70, 60, 0, 0, 0, 0, 75 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 78 | }, 79 | { /* 红车 */ 80 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83 | 0, 0, 0,160,170,160,150,150,150,160,170,160, 0, 0, 0, 0, 84 | 0, 0, 0,170,180,170,190,250,190,170,180,170, 0, 0, 0, 0, 85 | 0, 0, 0,170,190,200,220,240,220,200,190,170, 0, 0, 0, 0, 86 | 0, 0, 0,180,220,210,240,250,240,210,220,180, 0, 0, 0, 0, 87 | 0, 0, 0,180,220,210,240,250,240,210,220,180, 0, 0, 0, 0, 88 | 0, 0, 0,180,220,210,240,250,240,210,220,180, 0, 0, 0, 0, 89 | 0, 0, 0,170,190,180,220,240,220,200,190,170, 0, 0, 0, 0, 90 | 0, 0, 0,170,180,170,170,160,170,170,180,170, 0, 0, 0, 0, 91 | 0, 0, 0,160,170,160,160,150,160,160,170,160, 0, 0, 0, 0, 92 | 0, 0, 0,150,160,150,160,150,160,150,160,150, 0, 0, 0, 0, 93 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 96 | }, 97 | { /* 红炮 */ 98 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101 | 0, 0, 0,125,130,100, 70, 60, 70,100,130,125, 0, 0, 0, 0, 102 | 0, 0, 0,110,125,100, 70, 60, 70,100,125,110, 0, 0, 0, 0, 103 | 0, 0, 0,100,120, 90, 80, 80, 80, 90,120,100, 0, 0, 0, 0, 104 | 0, 0, 0, 90,110, 90,110,130,110, 90,110, 90, 0, 0, 0, 0, 105 | 0, 0, 0, 90,110, 90,110,130,110, 90,110, 90, 0, 0, 0, 0, 106 | 0, 0, 0, 90,100, 90,110,130,110, 90,100, 90, 0, 0, 0, 0, 107 | 0, 0, 0, 90,100, 90, 90,110, 90, 90,100, 90, 0, 0, 0, 0, 108 | 0, 0, 0, 90,100, 80, 80, 70, 80, 80,100, 90, 0, 0, 0, 0, 109 | 0, 0, 0, 80, 90, 80, 70, 65, 70, 80, 90, 80, 0, 0, 0, 0, 110 | 0, 0, 0, 80, 90, 80, 70, 60, 70, 80, 90, 80, 0, 0, 0, 0, 111 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 114 | }, 115 | { /* 红兵 */ 116 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119 | 0, 0, 0, 10, 10, 10, 20, 25, 20, 10, 10, 10, 0, 0, 0, 0, 120 | 0, 0, 0, 25, 30, 40, 50, 60, 50, 40, 30, 25, 0, 0, 0, 0, 121 | 0, 0, 0, 25, 30, 30, 40, 40, 40, 30, 30, 25, 0, 0, 0, 0, 122 | 0, 0, 0, 20, 25, 25, 30, 30, 30, 25, 25, 20, 0, 0, 0, 0, 123 | 0, 0, 0, 15, 20, 20, 20, 20, 20, 20, 20, 15, 0, 0, 0, 0, 124 | 0, 0, 0, 10, 0, 15, 0, 15, 0, 15, 0, 10, 0, 0, 0, 0, 125 | 0, 0, 0, 10, 0, 10, 0, 15, 0, 10, 0, 10, 0, 0, 0, 0, 126 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 132 | } 133 | }, 134 | { 135 | { /* 黑将 */ 136 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139 | 0, 0, 0, 0, 0, 0, 15, 20, 15, 0, 0, 0, 0, 0, 0, 0, 140 | 0, 0, 0, 0, 0, 0, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 141 | 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 142 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 152 | }, 153 | { /* 黑士 */ 154 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157 | 0, 0, 0, 0, 0, 0, 30, 0, 30, 0, 0, 0, 0, 0, 0, 0, 158 | 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 159 | 0, 0, 0, 0, 0, 0, 30, 0, 30, 0, 0, 0, 0, 0, 0, 0, 160 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 162 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 170 | }, 171 | { /* 黑象 */ 172 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175 | 0, 0, 0, 0, 0, 30, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 176 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177 | 0, 0, 0, 20, 0, 0, 0, 35, 0, 0, 0, 20, 0, 0, 0, 0, 178 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179 | 0, 0, 0, 0, 0, 25, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 180 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 182 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 183 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 186 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 188 | }, 189 | { /* 黑马 */ 190 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193 | 0, 0, 0, 60, 70, 75, 70, 60, 70, 75, 70, 60, 0, 0, 0, 0, 194 | 0, 0, 0, 70, 75, 75, 70, 50, 70, 75, 75, 70, 0, 0, 0, 0, 195 | 0, 0, 0, 80, 80, 90, 90, 80, 90, 90, 80, 80, 0, 0, 0, 0, 196 | 0, 0, 0, 80, 90,100,100, 90,100,100, 90, 80, 0, 0, 0, 0, 197 | 0, 0, 0, 90,100,100,110,100,110,100,100, 90, 0, 0, 0, 0, 198 | 0, 0, 0, 90,110,110,120,100,120,110,110, 90, 0, 0, 0, 0, 199 | 0, 0, 0, 90,100,120,130,110,130,120,100, 90, 0, 0, 0, 0, 200 | 0, 0, 0, 90,100,120,125,120,125,120,100, 90, 0, 0, 0, 0, 201 | 0, 0, 0, 80,110,125, 90, 70, 90,125,110, 80, 0, 0, 0, 0, 202 | 0, 0, 0, 70, 80, 90, 80, 70, 80, 90, 80, 70, 0, 0, 0, 0, 203 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 205 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 206 | }, 207 | { /* 黑车 */ 208 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 211 | 0, 0, 0,150,160,150,160,150,160,150,160,150, 0, 0, 0, 0, 212 | 0, 0, 0,160,170,160,160,150,160,160,170,160, 0, 0, 0, 0, 213 | 0, 0, 0,170,180,170,170,160,170,170,180,170, 0, 0, 0, 0, 214 | 0, 0, 0,170,190,200,220,240,220,180,190,170, 0, 0, 0, 0, 215 | 0, 0, 0,180,220,210,240,250,240,210,220,180, 0, 0, 0, 0, 216 | 0, 0, 0,180,220,210,240,250,240,210,220,180, 0, 0, 0, 0, 217 | 0, 0, 0,180,220,210,240,250,240,210,220,180, 0, 0, 0, 0, 218 | 0, 0, 0,170,190,200,220,240,220,200,190,170, 0, 0, 0, 0, 219 | 0, 0, 0,170,180,170,190,250,190,170,180,170, 0, 0, 0, 0, 220 | 0, 0, 0,160,170,160,150,150,150,160,170,160, 0, 0, 0, 0, 221 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 224 | }, 225 | { /* 黑炮 */ 226 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 227 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229 | 0, 0, 0, 80, 90, 80, 70, 60, 70, 80, 90, 80, 0, 0, 0, 0, 230 | 0, 0, 0, 80, 90, 80, 70, 65, 70, 80, 90, 80, 0, 0, 0, 0, 231 | 0, 0, 0, 90,100, 80, 80, 70, 80, 80,100, 90, 0, 0, 0, 0, 232 | 0, 0, 0, 90,100, 90, 90,110, 90, 90,100, 90, 0, 0, 0, 0, 233 | 0, 0, 0, 90,100, 90,110,130,110, 90,100, 90, 0, 0, 0, 0, 234 | 0, 0, 0, 90,110, 90,110,130,110, 90,110, 90, 0, 0, 0, 0, 235 | 0, 0, 0, 90,110, 90,110,130,110, 90,110, 90, 0, 0, 0, 0, 236 | 0, 0, 0,100,120, 90, 80, 80, 80, 90,120,100, 0, 0, 0, 0, 237 | 0, 0, 0,110,125,100, 70, 60, 70,100,125,110, 0, 0, 0, 0, 238 | 0, 0, 0,125,130,100, 70, 60, 70,100,130,125, 0, 0, 0, 0, 239 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 242 | }, 243 | { /* 黑卒 */ 244 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250 | 0, 0, 0, 10, 0, 10, 0, 15, 0, 10, 0, 10, 0, 0, 0, 0, 251 | 0, 0, 0, 10, 0, 15, 0, 15, 0, 15, 0, 10, 0, 0, 0, 0, 252 | 0, 0, 0, 15, 20, 20, 20, 20, 20, 20, 20, 15, 0, 0, 0, 0, 253 | 0, 0, 0, 20, 25, 25, 30, 30, 30, 25, 25, 20, 0, 0, 0, 0, 254 | 0, 0, 0, 25, 30, 30, 40, 40, 40, 30, 30, 25, 0, 0, 0, 0, 255 | 0, 0, 0, 25, 30, 40, 50, 60, 50, 40, 30, 25, 0, 0, 0, 0, 256 | 0, 0, 0, 10, 10, 10, 20, 25, 20, 10, 10, 10, 0, 0, 0, 0, 257 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 259 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 260 | } 261 | } 262 | }; 263 | 264 | int evaluate() 265 | { 266 | int i,j,k,r; 267 | 268 | BYTE p, next, m; 269 | 270 | int side_tag; 271 | int over_flag; 272 | 273 | /* 红(r)、黑(b)棋子价值分量 */ 274 | short r_value, b_value; 275 | 276 | /* 红[0]、黑[1]棋子灵活度分量 */ 277 | short flexible[2] = {0, 0}; 278 | 279 | r_value = b_value = 0; 280 | 281 | for (i = 16; i < 32; i++) { 282 | if (piece[i] > 0) 283 | r_value += position_values[0][piece_type[i]][piece[i]]; 284 | } 285 | 286 | for (i = 32; i < 48; i++) { 287 | if (piece[i] > 0) 288 | b_value += position_values[1][piece_type[i]][piece[i]]; 289 | } 290 | 291 | for (r = 0; r <= 1; r++) { 292 | side_tag = 16 + 16 * r; 293 | 294 | /* 将(帅)的灵活性 */ 295 | p = piece[side_tag]; 296 | 297 | for (k = 0; k < 4; k++) { 298 | next = p + king_dir[k]; 299 | 300 | if (legal_position[r][next] & position_mask[KING]) { 301 | /* 目标位置上没有本方棋子 */ 302 | if (!(board[next] & side_tag)) 303 | flexible[r] += F_KING; 304 | } 305 | } 306 | 307 | /* 士(仕)的灵活性 */ 308 | for (i = 1; i <= 2; i++) { 309 | p = piece[side_tag + i]; 310 | 311 | if (!p) continue; 312 | 313 | for (k = 0; k < 4; k++) { 314 | next = p + advisor_dir[k]; 315 | 316 | if (legal_position[r][next] & position_mask[ADVISOR]) { 317 | 318 | if (!(board[next] & side_tag)) 319 | flexible[r] += F_ADVISOR; 320 | } 321 | } 322 | } 323 | 324 | /* 象(相)的灵活性 */ 325 | for (i = 3; i <= 4; i++) { 326 | p = piece[side_tag + i]; 327 | 328 | if (!p) continue; 329 | 330 | for (k = 0; k < 4; k++) { 331 | next = p + bishop_dir[k]; 332 | 333 | if (legal_position[r][next] & position_mask[BISHOP]) { 334 | m = p + bishop_check[k]; 335 | 336 | if (!board[m]) { 337 | if (!(board[next] & side_tag)) 338 | flexible[r] += F_BISHOP; 339 | } 340 | } 341 | } 342 | } 343 | 344 | /* 马的灵活性 */ 345 | for (i = 5; i <= 6; i++) { 346 | p = piece[side_tag + i]; 347 | 348 | if (!p) continue; 349 | 350 | for (k = 0; k < 8; k++) { 351 | next = p + knight_dir[k]; 352 | 353 | if (legal_position[r][next] & position_mask[KNIGHT]) { 354 | m = p + knight_check[k]; 355 | 356 | if (!board[m]) { 357 | if (!(board[next] & side_tag)) 358 | flexible[r] += F_KNIGHT; 359 | } 360 | } 361 | } 362 | } 363 | 364 | /* 车的灵活性 */ 365 | for (i = 7; i <= 8; i++) { 366 | p = piece[side_tag + i]; 367 | 368 | if (!p) continue; 369 | 370 | for (k = 0; k < 4; k++) { 371 | for (j = 1; j < 10; ++j) { 372 | next = p + j * rook_dir[k]; 373 | 374 | if (!(legal_position[r][next] & position_mask[ROOK])) 375 | break; 376 | 377 | if (!board[next]) { 378 | flexible[r] += F_ROOK; 379 | } else if (board[next] & side_tag) { 380 | break; 381 | } else { 382 | flexible[r] += F_ROOK; 383 | break; 384 | } 385 | } 386 | } 387 | } 388 | 389 | /* 炮的灵活性 */ 390 | for (i = 9; i <= 10; i++) { 391 | p = piece[side_tag + i]; 392 | 393 | if (!p) continue; 394 | 395 | for (k = 0; k < 4; k++) { 396 | /* 中间隔子数量 */ 397 | over_flag = 0; 398 | 399 | for (j = 1; j < 10; ++j) { 400 | next = p + j * cannon_dir[k]; 401 | 402 | if(!(legal_position[r][next] & position_mask[CANNON])) 403 | break; 404 | 405 | if (!board[next]) { 406 | if (!over_flag) 407 | flexible[r] += F_CANNON; 408 | } else { 409 | if (!over_flag) { 410 | over_flag = 1; 411 | } else { 412 | if (!(board[next] & side_tag)) 413 | flexible[r] += F_CANNON; 414 | break; 415 | } 416 | } 417 | } 418 | } 419 | } 420 | 421 | /* 兵(卒)的灵活性 */ 422 | for (i = 11; i <= 15; i++) { 423 | p = piece[side_tag + i]; 424 | 425 | if (!p) continue; 426 | 427 | for (k = 0; k < 3; k++) { 428 | next = p + pawn_dir[r][k]; 429 | 430 | if (legal_position[r][next] & position_mask[PAWN]) { 431 | if (!(board[next] & side_tag)) 432 | flexible[r] += F_PAWN; 433 | } 434 | } 435 | } 436 | } 437 | 438 | int result = (flexible[0] + r_value) - (flexible[1] + b_value); 439 | 440 | if (side == RED) 441 | return result; 442 | else 443 | return -result; 444 | } 445 | -------------------------------------------------------------------------------- /src/evaluate.h: -------------------------------------------------------------------------------- 1 | #ifndef EVALUATE_H 2 | #define EVALUATE_H 3 | 4 | #include "base.h" 5 | #include "position.h" 6 | #include "genmoves.h" 7 | 8 | #define F_KING 2 9 | #define F_ADVISOR 2 10 | #define F_BISHOP 2 11 | #define F_KNIGHT 5 12 | #define F_ROOK 4 13 | #define F_CANNON 3 14 | #define F_PAWN 2 15 | 16 | int evaluate(); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/genmoves.c: -------------------------------------------------------------------------------- 1 | #include "genmoves.h" 2 | 3 | short king_dir[8] = {-0x10, -0x01, +0x01, +0x10, 0, 0, 0, 0 }; 4 | short advisor_dir[8] = {-0x11, -0x0f, +0x0f, +0x11, 0, 0, 0, 0 }; 5 | short bishop_dir[8] = {-0x22, -0x1e, +0x1e, +0x22, 0, 0, 0, 0 }; 6 | short knight_dir[8] = {-0x21, -0x1f, -0x12, -0x0e, +0x0e, +0x12, +0x1f, +0x21}; 7 | short rook_dir[8] = {-0x01, +0x01, -0x10, +0x10, 0, 0, 0, 0 }; 8 | short cannon_dir[8] = {-0x01, +0x01, -0x10, +0x10, 0, 0, 0, 0 }; 9 | short pawn_dir[2][8] = { 10 | {-0x01, +0x01, -0x10, 0, 0, 0, 0, 0}, 11 | {-0x01, +0x01, +0x10, 0, 0, 0, 0, 0}, 12 | }; 13 | 14 | short knight_check[8] = {-0x10, -0x10, -0x01, +0x01, -0x01, +0x01, +0x10, +0x10}; 15 | short bishop_check[8] = {-0x11, -0x0f, +0x0f, +0x11, 0, 0, 0, 0 }; 16 | 17 | BYTE legal_position[2][256] = { 18 | { 19 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22 | 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 23 | 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 24 | 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 25 | 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 26 | 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 27 | 0, 0, 0, 9, 1,25, 1, 9, 1,25, 1, 9, 0, 0, 0, 0, 28 | 0, 0, 0, 9, 1, 9, 1, 9, 1, 9, 1, 9, 0, 0, 0, 0, 29 | 0, 0, 0,17, 1, 1, 7,19, 7, 1, 1,17, 0, 0, 0, 0, 30 | 0, 0, 0, 1, 1, 1, 3, 7, 3, 1, 1, 1, 0, 0, 0, 0, 31 | 0, 0, 0, 1, 1,17, 7, 3, 7,17, 1, 1, 0, 0, 0, 0, 32 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 35 | },{ 36 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39 | 0, 0, 0, 1, 1,17, 7, 3, 7,17, 1, 1, 0, 0, 0, 0, 40 | 0, 0, 0, 1, 1, 1, 3, 7, 3, 1, 1, 1, 0, 0, 0, 0, 41 | 0, 0, 0,17, 1, 1, 7,19, 7, 1, 1,17, 0, 0, 0, 0, 42 | 0, 0, 0, 9, 1, 9, 1, 9, 1, 9, 1, 9, 0, 0, 0, 0, 43 | 0, 0, 0, 9, 1,25, 1, 9, 1,25, 1, 9, 0, 0, 0, 0, 44 | 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 45 | 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 46 | 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 47 | 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 48 | 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 49 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 52 | } 53 | }; 54 | 55 | BYTE position_mask[7] = {2, 4, 16, 1, 1, 1, 8}; 56 | 57 | /* 吃子价值表 */ 58 | static BYTE cap_values[48] = { 59 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60 | 8, 2, 2, 2, 2, 4, 4, 6, 6, 4, 4, 2, 2, 2, 2, 2, 61 | 8, 2, 2, 2, 2, 4, 4, 6, 6, 4, 4, 2, 2, 2, 2, 2 62 | }; 63 | 64 | /* 检查当前局面lside方是[1]否[0]被将军 */ 65 | static int check(int lside); 66 | /* 检测lside一方能[1]否[0]保护位置dst */ 67 | static int protected(int lside, BYTE dst); 68 | 69 | static int save_move(BYTE from, BYTE to, move *move_array) 70 | { 71 | BYTE pc1, pc2; 72 | pc1 = board[from]; 73 | pc2 = board[to]; 74 | 75 | /* make move */ 76 | board[from] = 0; 77 | board[to] = pc1; 78 | piece[pc1] = to; 79 | if (pc2) { 80 | piece[pc2] = 0; 81 | } 82 | 83 | int kill = check(side); 84 | 85 | /* unmake move */ 86 | board[from] = pc1; 87 | board[to] = pc2; 88 | piece[pc1] = from; 89 | if (pc2) { 90 | piece[pc2] = to; 91 | } 92 | 93 | if (!kill) { 94 | move_array->from = from; 95 | move_array->to = to; 96 | return 1; 97 | } 98 | 99 | return 0; 100 | } 101 | 102 | int gen_all_move(move *move_array) 103 | { 104 | int i, j, k; 105 | int side_tag; /* 本方标记 */ 106 | int over_flag; /* 炮翻山标记 */ 107 | BYTE p; /* 棋子位置 */ 108 | BYTE next; /* 下一步可能走的位置 */ 109 | BYTE m; /* 象眼马腿位置 */ 110 | 111 | move *mv_array = move_array; 112 | 113 | side_tag = 16 + side * 16; 114 | 115 | p = piece[side_tag]; 116 | if (!p) 117 | return 0; 118 | 119 | /* 将(帅)的走法 */ 120 | for (k = 0; k < 4; ++k) { 121 | next = p + king_dir[k]; 122 | 123 | if (legal_position[side][next] & position_mask[KING]) { 124 | 125 | if(!(board[next] & side_tag)) { 126 | if (save_move(p, next, move_array)) { 127 | move_array++; 128 | } 129 | } 130 | } 131 | } 132 | 133 | /* 士(仕)的走法 */ 134 | for (i = 1; i <= 2; ++i) { 135 | p = piece[side_tag + i]; 136 | 137 | if (!p) continue; 138 | 139 | for (k = 0; k < 4; ++k) { 140 | next = p + advisor_dir[k]; 141 | 142 | if (legal_position[side][next] & position_mask[ADVISOR]) { 143 | 144 | if (!(board[next] & side_tag)) { 145 | if (save_move(p, next, move_array)) { 146 | move_array++; 147 | } 148 | } 149 | } 150 | } 151 | } 152 | 153 | /* 象(相)的走法 */ 154 | for (i = 3; i <= 4; ++i) { 155 | p = piece[side_tag + i]; 156 | 157 | if (!p) continue; 158 | 159 | for (k = 0; k < 4; ++k) { 160 | next = p + bishop_dir[k]; 161 | 162 | if (legal_position[side][next] & position_mask[BISHOP]) { 163 | m = p + bishop_check[k]; 164 | 165 | if (!board[m]) { 166 | if (!(board[next] & side_tag)) { 167 | if (save_move(p, next, move_array)) { 168 | move_array++; 169 | } 170 | } 171 | } 172 | } 173 | } 174 | } 175 | 176 | /* 马的走法 */ 177 | for (i = 5; i <= 6; ++i) { 178 | p = piece[side_tag + i]; 179 | 180 | if (!p) continue; 181 | 182 | for (k = 0; k < 8; ++k) { 183 | next = p + knight_dir[k]; 184 | 185 | if (legal_position[side][next] & position_mask[KNIGHT]) { 186 | m = p + knight_check[k]; 187 | 188 | if (!board[m]) { 189 | if (!(board[next] & side_tag)) { 190 | if (save_move(p, next, move_array)) { 191 | move_array++; 192 | } 193 | } 194 | } 195 | } 196 | } 197 | } 198 | 199 | /* 车的走法 */ 200 | for (i = 7; i <= 8; ++i) { 201 | p = piece[side_tag + i]; 202 | 203 | if (!p) continue; 204 | 205 | for (k = 0; k < 4; ++k) { 206 | for (j = 1; j < 10; ++j) { 207 | next = p + j * rook_dir[k]; 208 | 209 | if (!(legal_position[side][next] & 210 | position_mask[ROOK])) break; 211 | 212 | if (!board[next]) { 213 | 214 | if (save_move(p, next, move_array)) { 215 | move_array++; 216 | } 217 | 218 | } else if (board[next] & side_tag) { 219 | break; 220 | } else { 221 | 222 | if (save_move(p, next, move_array)) { 223 | move_array++; 224 | } 225 | 226 | break; 227 | } 228 | } 229 | } 230 | } 231 | 232 | /* 炮的走法 */ 233 | for (i = 9; i <= 10; ++i) { 234 | p = piece[side_tag + i]; 235 | 236 | if (!p) continue; 237 | 238 | for (k = 0; k < 4; ++k) { 239 | over_flag = 0; 240 | 241 | for (j = 1; j < 10; ++j) { 242 | next = p + j * cannon_dir[k]; 243 | 244 | if (!(legal_position[side][next] & 245 | position_mask[CANNON])) break; 246 | 247 | if (!board[next]) { 248 | 249 | if (!over_flag) { 250 | if (save_move(p, next, move_array)) { 251 | move_array++; 252 | } 253 | } 254 | 255 | } else { 256 | if (!over_flag) { 257 | over_flag = 1; 258 | } else { 259 | if (!(board[next] & side_tag)) { 260 | if (save_move(p, next, move_array)) { 261 | move_array++; 262 | } 263 | } 264 | 265 | break; 266 | } 267 | } 268 | } 269 | } 270 | } 271 | 272 | /* 兵(卒)的走法 */ 273 | for (i = 11; i <= 15; ++i) { 274 | p = piece[side_tag + i]; 275 | 276 | if (!p) continue; 277 | 278 | for (k = 0; k < 3; ++k) { 279 | next = p + pawn_dir[side][k]; 280 | 281 | if (legal_position[side][next] & position_mask[PAWN]) { 282 | 283 | if (!(board[next] & side_tag)) { 284 | if (save_move(p, next, move_array)) { 285 | move_array++; 286 | } 287 | } 288 | } 289 | } 290 | } 291 | 292 | return move_array - mv_array; 293 | } 294 | 295 | int gen_cap_move(move *move_array) 296 | { 297 | int i, j, k; 298 | int side_tag; /* 本方标记 */ 299 | int op_side_tag; /* 对方标记 */ 300 | int over_flag; /* 炮翻山标记 */ 301 | BYTE p; /* 棋子位置 */ 302 | BYTE next; /* 下一步可能走的位置 */ 303 | BYTE m; /* 象眼马腿位置 */ 304 | BYTE op_piece; /* 被吃的对方棋子 */ 305 | BYTE value; /* 价值 */ 306 | 307 | move *mv_array = move_array; 308 | 309 | side_tag = 16 + side * 16; 310 | op_side_tag = 48 - side_tag; 311 | 312 | p = piece[side_tag]; 313 | if (!p) 314 | return 0; 315 | 316 | /* 将(帅)的走法 */ 317 | for (k = 0; k < 4; ++k) { 318 | next = p + king_dir[k]; 319 | 320 | if (legal_position[side][next] & position_mask[KING]) { 321 | 322 | /* 目标位置上有对方棋子 */ 323 | if(board[next] & op_side_tag) { 324 | op_piece = board[next]; 325 | 326 | if (save_move(p, next, move_array)) { 327 | value = cap_values[op_piece] - 328 | (protected(1-side, next) ? KING_VALUE : 0); 329 | 330 | move_array->capture = value > 0 ? value : 0; 331 | move_array++; 332 | } 333 | } 334 | } 335 | } 336 | 337 | /* 士(仕)的走法 */ 338 | for (i = 1; i <= 2; ++i) { 339 | p = piece[side_tag + i]; 340 | 341 | if (!p) continue; 342 | 343 | for (k = 0; k < 4; ++k) { 344 | next = p + advisor_dir[k]; 345 | 346 | if (legal_position[side][next] & position_mask[ADVISOR]) { 347 | 348 | if (board[next] & op_side_tag) { 349 | op_piece = board[next]; 350 | 351 | if (save_move(p, next, move_array)) { 352 | value = cap_values[op_piece] - 353 | (protected(1-side, next) ? ADVISOR_VALUE : 0); 354 | 355 | move_array->capture = value > 0 ? value : 0; 356 | move_array++; 357 | } 358 | } 359 | } 360 | } 361 | } 362 | 363 | /* 象(相)的走法 */ 364 | for (i = 3; i <= 4; ++i) { 365 | p = piece[side_tag + i]; 366 | 367 | if (!p) continue; 368 | 369 | for (k = 0; k < 4; ++k) { 370 | next = p + bishop_dir[k]; 371 | 372 | if (legal_position[side][next] & position_mask[BISHOP]) { 373 | m = p + bishop_check[k]; 374 | 375 | /* 象眼位置无子 */ 376 | if (!board[m]) { 377 | if (board[next] & op_side_tag) { 378 | 379 | op_piece = board[next]; 380 | 381 | if (save_move(p, next, move_array)) { 382 | value = cap_values[op_piece] - 383 | (protected(1-side, next) ? BISHOP_VALUE : 0); 384 | 385 | move_array->capture = value > 0 ? value : 0; 386 | move_array++; 387 | } 388 | } 389 | } 390 | } 391 | } 392 | } 393 | 394 | /* 马的走法 */ 395 | for (i = 5; i <= 6; ++i) { 396 | p = piece[side_tag + i]; 397 | 398 | if (!p) continue; 399 | 400 | for (k = 0; k < 8; ++k) { 401 | next = p + knight_dir[k]; 402 | 403 | if (legal_position[side][next] & position_mask[KNIGHT]) { 404 | m = p + knight_check[k]; 405 | 406 | /* 马腿位置无子 */ 407 | if (!board[m]) { 408 | if (board[next] & op_side_tag) { 409 | 410 | op_piece = board[next]; 411 | 412 | if (save_move(p, next, move_array)) { 413 | value = cap_values[op_piece] - 414 | (protected(1-side, next) ? KNIGHT_VALUE : 0); 415 | 416 | move_array->capture = value > 0 ? value : 0; 417 | move_array++; 418 | } 419 | } 420 | } 421 | } 422 | } 423 | } 424 | 425 | /* 车的走法 */ 426 | for (i = 7; i <= 8; ++i) { 427 | p = piece[side_tag + i]; 428 | 429 | if (!p) continue; 430 | 431 | for (k = 0; k < 4; ++k) { 432 | for (j = 1; j < 10; ++j) { 433 | next = p + j * rook_dir[k]; 434 | 435 | if (!(legal_position[side][next] & 436 | position_mask[ROOK])) break; 437 | 438 | if (!board[next]) { /* 目标位置上无子 */ 439 | /* 不做处理 */ 440 | 441 | } else if (board[next] & side_tag) { /* 目标位置上有本方棋子 */ 442 | break; 443 | 444 | } else { /* 目标位置上有对方棋子 */ 445 | op_piece = board[next]; 446 | 447 | if (save_move(p, next, move_array)) { 448 | value = cap_values[op_piece] - 449 | (protected(1-side, next) ? ROOK_VALUE : 0); 450 | 451 | move_array->capture = value > 0 ? value : 0; 452 | move_array++; 453 | } 454 | 455 | break; 456 | } 457 | } 458 | } 459 | } 460 | 461 | /* 炮的走法 */ 462 | for (i = 9; i <= 10; ++i) { 463 | p = piece[side_tag + i]; 464 | 465 | if (!p) continue; 466 | 467 | for (k = 0; k < 4; ++k) { 468 | over_flag = 0; 469 | 470 | for (j = 1; j < 10; ++j) { 471 | next = p + j * cannon_dir[k]; 472 | 473 | if (!(legal_position[side][next] & 474 | position_mask[CANNON])) break; 475 | 476 | if (!board[next]) { /* 目标位置上无子 */ 477 | /* 不做处理 */ 478 | 479 | } else { 480 | if (!over_flag) { 481 | over_flag = 1; 482 | 483 | } else { /* 已翻山 */ 484 | 485 | if (board[next] & op_side_tag) { 486 | op_piece = board[next]; 487 | 488 | if (save_move(p, next, move_array)) { 489 | value = cap_values[op_piece] - 490 | (protected(1-side, next) ? CANNON_VALUE : 0); 491 | 492 | move_array->capture = value > 0 ? value : 0; 493 | move_array++; 494 | } 495 | } 496 | 497 | break; 498 | } 499 | } 500 | } 501 | } 502 | } 503 | 504 | /* 兵(卒)的走法 */ 505 | for (i = 11; i <= 15; ++i) { 506 | p = piece[side_tag + i]; 507 | 508 | if (!p) continue; 509 | 510 | for (k = 0; k < 3; ++k) { 511 | next = p + pawn_dir[side][k]; 512 | 513 | if (legal_position[side][next] & position_mask[PAWN]) { 514 | 515 | if (board[next] & op_side_tag) { 516 | op_piece = board[next]; 517 | 518 | if (save_move(p, next, move_array)) { 519 | value = cap_values[op_piece] - 520 | (protected(1-side, next) ? PAWN_VALUE : 0); 521 | 522 | move_array->capture = value > 0 ? value : 0; 523 | move_array++; 524 | } 525 | } 526 | } 527 | } 528 | } 529 | 530 | return move_array - mv_array; 531 | } 532 | 533 | int gen_non_cap_move(move *move_array) 534 | { 535 | int i, j, k; 536 | int side_tag; /* 本方标记 */ 537 | int over_flag; /* 炮翻山标记 */ 538 | BYTE p; /* 棋子位置 */ 539 | BYTE next; /* 下一步可能走的位置 */ 540 | BYTE m; /* 象眼马腿位置 */ 541 | 542 | move *mv_array = move_array; 543 | 544 | side_tag = 16 + side * 16; 545 | 546 | p = piece[side_tag]; 547 | if (!p) 548 | return 0; 549 | 550 | /* 将(帅)的走法 */ 551 | for (k = 0; k < 4; ++k) { 552 | next = p + king_dir[k]; 553 | 554 | if (legal_position[side][next] & position_mask[KING]) { 555 | 556 | /* 目标位置无子 */ 557 | if(!board[next]) { 558 | if (save_move(p, next, move_array)) { 559 | move_array++; 560 | } 561 | } 562 | } 563 | } 564 | 565 | /* 士(仕)的走法 */ 566 | for (i = 1; i <= 2; ++i) { 567 | p = piece[side_tag + i]; 568 | 569 | if (!p) continue; 570 | 571 | for (k = 0; k < 4; ++k) { 572 | next = p + advisor_dir[k]; 573 | 574 | if (legal_position[side][next] & position_mask[ADVISOR]) { 575 | 576 | if (!board[next]) { 577 | if (save_move(p, next, move_array)) { 578 | move_array++; 579 | } 580 | } 581 | } 582 | } 583 | } 584 | 585 | /* 象(相)的走法 */ 586 | for (i = 3; i <= 4; ++i) { 587 | p = piece[side_tag + i]; 588 | 589 | if (!p) continue; 590 | 591 | for (k = 0; k < 4; ++k) { 592 | next = p + bishop_dir[k]; 593 | 594 | if (legal_position[side][next] & position_mask[BISHOP]) { 595 | m = p + bishop_check[k]; 596 | 597 | if (!board[m]) { 598 | if (!board[next]) { 599 | if (save_move(p, next, move_array)) { 600 | move_array++; 601 | } 602 | } 603 | } 604 | } 605 | } 606 | } 607 | 608 | /* 马的走法 */ 609 | for (i = 5; i <= 6; ++i) { 610 | p = piece[side_tag + i]; 611 | 612 | if (!p) continue; 613 | 614 | for (k = 0; k < 8; ++k) { 615 | next = p + knight_dir[k]; 616 | 617 | if (legal_position[side][next] & position_mask[KNIGHT]) { 618 | m = p + knight_check[k]; 619 | 620 | if (!board[m]) { 621 | if (!board[next]) { 622 | if (save_move(p, next, move_array)) { 623 | move_array++; 624 | } 625 | } 626 | } 627 | } 628 | } 629 | } 630 | 631 | /* 车的走法 */ 632 | for (i = 7; i <= 8; ++i) { 633 | p = piece[side_tag + i]; 634 | 635 | if (!p) continue; 636 | 637 | for (k = 0; k < 4; ++k) { 638 | for (j = 1; j < 10; ++j) { 639 | next = p + j * rook_dir[k]; 640 | 641 | if (!(legal_position[side][next] & 642 | position_mask[ROOK])) break; 643 | 644 | if (!board[next]) { 645 | 646 | if (save_move(p, next, move_array)) { 647 | move_array++; 648 | } 649 | 650 | } else if (board[next] & side_tag) { 651 | break; 652 | } else { 653 | /* 目标位置有子 */ 654 | break; 655 | } 656 | } 657 | } 658 | } 659 | 660 | /* 炮的走法 */ 661 | for (i = 9; i <= 10; ++i) { 662 | p = piece[side_tag + i]; 663 | 664 | if (!p) continue; 665 | 666 | for (k = 0; k < 4; ++k) { 667 | over_flag = 0; 668 | 669 | for (j = 1; j < 10; ++j) { 670 | next = p + j * cannon_dir[k]; 671 | 672 | if (!(legal_position[side][next] & 673 | position_mask[CANNON])) break; 674 | 675 | if (!board[next]) { 676 | 677 | if (!over_flag) { 678 | if (save_move(p, next, move_array)) { 679 | move_array++; 680 | } 681 | } 682 | 683 | } else { 684 | /* 目标位置有子 */ 685 | break; 686 | } 687 | } 688 | } 689 | } 690 | 691 | /* 兵(卒)的走法 */ 692 | for (i = 11; i <= 15; ++i) { 693 | p = piece[side_tag + i]; 694 | 695 | if (!p) continue; 696 | 697 | for (k = 0; k < 3; ++k) { 698 | next = p + pawn_dir[side][k]; 699 | 700 | if (legal_position[side][next] & position_mask[PAWN]) { 701 | 702 | if (!board[next]) { 703 | if (save_move(p, next, move_array)) { 704 | move_array++; 705 | } 706 | } 707 | } 708 | } 709 | } 710 | 711 | return move_array - mv_array; 712 | } 713 | 714 | static int check(int lside) 715 | { 716 | BYTE w_king, b_king; 717 | BYTE p, q; 718 | int side_tag = 32 - lside * 16; 719 | int kill, i, offset; 720 | 721 | w_king = piece[16]; 722 | b_king = piece[32]; 723 | 724 | if (!w_king || !b_king) return 0; 725 | 726 | /* 检测将帅是否照面 */ 727 | kill = 1; 728 | if (w_king%16 == b_king%16) { 729 | for (w_king = w_king-16; w_king != b_king; w_king = w_king-16) { 730 | if (board[w_king]) { 731 | kill = 0; 732 | break; 733 | } 734 | } 735 | 736 | if (kill) return kill; 737 | } 738 | 739 | q = piece[48-side_tag]; 740 | 741 | /* 检测是否被马攻击 */ 742 | int k; 743 | BYTE next, m; 744 | 745 | for (i = 5; i <= 6; ++i) { 746 | p = piece[side_tag + i]; 747 | 748 | if (!p) continue; 749 | 750 | for (k = 0; k < 8; ++k) { 751 | next = p + knight_dir[k]; 752 | 753 | if (next != q) continue; 754 | if (legal_position[1-lside][next] & position_mask[KNIGHT]) { 755 | m = p + knight_check[k]; 756 | 757 | if (!board[m]) return 1; 758 | } 759 | } 760 | } 761 | 762 | /* 检测是否被车攻击 */ 763 | for (i = 7; i <= 8; ++i) { 764 | kill = 1; 765 | 766 | p = piece[side_tag + i]; 767 | 768 | if (!p) continue; 769 | 770 | if (p%16 == q%16) { 771 | offset = (p > q ? -16 : 16); 772 | 773 | for (p = p+offset; p != q; p = p+offset) { 774 | if (board[p]) { 775 | kill = 0; 776 | break; 777 | } 778 | } 779 | 780 | if (kill) return kill; 781 | 782 | } else if (p/16 == q/16) { 783 | offset = (p > q ? -1 : 1); 784 | 785 | for (p = p+offset; p != q; p = p+offset) { 786 | if (board[p]) { 787 | kill = 0; 788 | break; 789 | } 790 | } 791 | 792 | if (kill) return kill; 793 | } 794 | } 795 | 796 | /* 检测是否被炮攻击 */ 797 | for (i = 9; i <= 10; ++i) { 798 | int over_flag = 0; 799 | 800 | p = piece[side_tag + i]; 801 | 802 | if (!p) continue; 803 | 804 | if (p%16 == q%16) { 805 | offset = (p > q ? -16 : 16); 806 | 807 | for (p = p+offset; p != q; p = p+offset) { 808 | if (board[p]) { 809 | if (!over_flag) { 810 | over_flag = 1; 811 | } else { 812 | over_flag = 2; 813 | break; 814 | } 815 | } 816 | } 817 | 818 | if (over_flag == 1) return 1; 819 | 820 | } else if (p/16 == q/16) { 821 | offset = (p > q ? -1 : 1); 822 | 823 | for (p = p+offset; p != q; p = p+offset) { 824 | if (board[p]) { 825 | if (!over_flag) { 826 | over_flag = 1; 827 | } else { 828 | over_flag = 2; 829 | break; 830 | } 831 | } 832 | } 833 | 834 | if (over_flag == 1) return 1; 835 | } 836 | } 837 | 838 | /* 检测是否被兵(卒)攻击 */ 839 | for (i = 11; i <= 15; ++i) { 840 | p = piece[side_tag + i]; 841 | 842 | if (!p) continue; 843 | 844 | for (k = 0; k < 3; ++k) { 845 | next = p + pawn_dir[1-lside][k]; 846 | 847 | if ((next == q) && 848 | (legal_position[1-lside][next] & position_mask[PAWN])) { 849 | return 1; 850 | } 851 | } 852 | } 853 | 854 | return 0; 855 | } 856 | 857 | static int protected(int lside, BYTE dst) 858 | { 859 | int i, k, r, offset; 860 | int side_tag = 16 + lside * 16; /* lside方标记 */ 861 | int over_flag; 862 | BYTE p; 863 | BYTE next; 864 | BYTE m; 865 | 866 | /* 判断棋子是否过河,未过河受将、士、象保护 */ 867 | if (lside == 0 ? (dst & 0x80) != 0 : (dst & 0x80) == 0) { 868 | /* 检测是否被将保护 */ 869 | p = piece[side_tag]; 870 | 871 | if (p && p != dst) { 872 | for (k = 0; k < 4; ++k) { 873 | next = p + king_dir[k]; 874 | 875 | if (next != dst) continue; 876 | if (legal_position[lside][next] & position_mask[KING]) { 877 | return 1; 878 | } 879 | } 880 | } 881 | 882 | /* 检测是否被士保护 */ 883 | for (i = 1; i <= 2; ++i) { 884 | p = piece[side_tag + i]; 885 | 886 | if (!p || p == dst) continue; 887 | 888 | for (k = 0; k < 4; ++k) { 889 | next = p + advisor_dir[k]; 890 | 891 | if (next != dst) continue; 892 | if (legal_position[lside][next] & position_mask[ADVISOR]) { 893 | return 1; 894 | } 895 | } 896 | } 897 | 898 | /* 检测是否被象保护 */ 899 | for (i = 3; i <= 4; ++i) { 900 | p = piece[side_tag + i]; 901 | 902 | if (!p || p == dst) continue; 903 | 904 | for (k = 0; k < 4; ++k) { 905 | next = p + bishop_dir[k]; 906 | 907 | if (next != dst) continue; 908 | if (legal_position[lside][next] & position_mask[BISHOP]) { 909 | m = p + bishop_check[k]; 910 | 911 | if (!board[m]) { 912 | return 1; 913 | } 914 | } 915 | } 916 | } 917 | } 918 | 919 | /* 检测是否被马保护 */ 920 | for (i = 5; i <= 6; ++i) { 921 | p = piece[side_tag + i]; 922 | 923 | if (!p || p == dst) continue; 924 | 925 | for (k = 0; k < 8; ++k) { 926 | next = p + knight_dir[k]; 927 | 928 | if (next != dst) continue; 929 | if (legal_position[lside][next] & position_mask[KNIGHT]) { 930 | m = p + knight_check[k]; 931 | 932 | if (!board[m]) { 933 | return 1; 934 | } 935 | } 936 | } 937 | } 938 | 939 | 940 | /* 检测是否被车保护 */ 941 | for (i = 7; i <= 8; ++i) { 942 | r = 1; 943 | 944 | p = piece[side_tag + i]; 945 | 946 | if (!p || p == dst) continue; 947 | 948 | if (p%16 == dst%16) { 949 | offset = (p > dst ? -16 : 16); 950 | 951 | for (p = p+offset; p != dst; p = p+offset) { 952 | if (board[p]) { 953 | r = 0; 954 | break; 955 | } 956 | } 957 | 958 | if (r) return 1; 959 | 960 | } else if (p/16 == dst/16) { 961 | offset = (p > dst ? -1 : 1); 962 | 963 | for (p = p+offset; p != dst; p = p+offset) { 964 | if (board[p]) { 965 | r = 0; 966 | break; 967 | } 968 | } 969 | 970 | if (r) return 1; 971 | } 972 | } 973 | 974 | /* 检测是否被炮保护 */ 975 | for (i = 9; i <= 10; ++i) { 976 | over_flag = 0; 977 | 978 | p = piece[side_tag + i]; 979 | 980 | if (!p || p == dst) continue; 981 | 982 | if (p%16 == dst%16) { 983 | offset = (p > dst ? -16 : 16); 984 | 985 | for (p = p+offset; p != dst; p = p+offset) { 986 | if (board[p]) { 987 | if (!over_flag) { 988 | over_flag = 1; 989 | } else { 990 | over_flag = 2; 991 | break; 992 | } 993 | } 994 | } 995 | 996 | if (over_flag == 1) { 997 | return 1; 998 | } 999 | 1000 | } else if (p/16 == dst/16) { 1001 | offset = (p > dst ? -1 : 1); 1002 | 1003 | for (p = p+offset; p != dst; p = p+offset) { 1004 | if (board[p]) { 1005 | if (!over_flag) { 1006 | over_flag = 1; 1007 | } else { 1008 | over_flag = 2; 1009 | break; 1010 | } 1011 | } 1012 | } 1013 | 1014 | if (over_flag == 1) { 1015 | return 1; 1016 | } 1017 | } 1018 | } 1019 | 1020 | /* 检测是否被兵保护 */ 1021 | for (i = 11; i <= 15; ++i) { 1022 | p = piece[side_tag + i]; 1023 | 1024 | if (!p || p == dst) continue; 1025 | 1026 | for (k = 0; k < 3; ++k) { 1027 | next = p + pawn_dir[lside][k]; 1028 | 1029 | if (next != dst) continue; 1030 | if (legal_position[lside][next] & position_mask[PAWN]) { 1031 | return 1; 1032 | } 1033 | } 1034 | } 1035 | 1036 | return 0; 1037 | } 1038 | -------------------------------------------------------------------------------- /src/genmoves.h: -------------------------------------------------------------------------------- 1 | #ifndef GENMOVES_H 2 | #define GENMOVES_H 3 | 4 | #include "base.h" 5 | #include "position.h" 6 | 7 | /* 吃子走法价值 */ 8 | #define KING_VALUE 8 9 | #define ADVISOR_VALUE 2 10 | #define BISHOP_VALUE 2 11 | #define KNIGHT_VALUE 4 12 | #define ROOK_VALUE 6 13 | #define CANNON_VALUE 4 14 | #define PAWN_VALUE 2 15 | 16 | extern short king_dir[]; 17 | extern short advisor_dir[]; 18 | extern short bishop_dir[]; 19 | extern short knight_dir[]; 20 | extern short rook_dir[]; 21 | extern short cannon_dir[]; 22 | extern short pawn_dir[][8]; 23 | extern short knight_check[]; 24 | extern short bishop_check[]; 25 | 26 | extern BYTE legal_position[][256]; 27 | extern BYTE position_mask[]; 28 | 29 | /* 生成当前局面所有走法 */ 30 | int gen_all_move(move *move_array); 31 | /* 生成当前局面所有吃子走法 */ 32 | int gen_cap_move(move *move_array); 33 | /* 生成当前局面所有不吃子走法 */ 34 | int gen_non_cap_move(move *move_array); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/harmless.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "base.h" 5 | #include "position.h" 6 | #include "search.h" 7 | #include "ucci.h" 8 | 9 | /* 日志文件描述符 */ 10 | FILE * logfile; 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | ucci_comm_enum uce; 15 | ucci_comm_struct ucs; 16 | char bookfile[255]; 17 | 18 | strcpy(bookfile, "BOOK.DAT"); 19 | 20 | #ifdef DEBUG_LOG 21 | logfile = fopen("harmless.log", "w"); 22 | #endif 23 | 24 | if (boot_line() == UCCI_COMM_UCCI) { 25 | 26 | new_hash_table(); 27 | init_openbook(bookfile); 28 | 29 | printf("id name harmless\n"); 30 | fflush(stdout); 31 | printf("id copyright 2011\n"); 32 | fflush(stdout); 33 | printf("id anthor timebug\n"); 34 | fflush(stdout); 35 | printf("ucciok\n"); 36 | fflush(stdout); 37 | 38 | uce = UCCI_COMM_NONE; 39 | 40 | while (uce != UCCI_COMM_QUIT) { 41 | uce = idle_line(&ucs); 42 | 43 | switch (uce) { 44 | case UCCI_COMM_ISREADY: 45 | printf("readyok\n"); 46 | fflush(stdout); 47 | break; 48 | case UCCI_COMM_STOP: 49 | printf("nobestmove\n"); 50 | fflush(stdout); 51 | break; 52 | case UCCI_COMM_POSITION: 53 | fen_to_arr(ucs.position.fen_str); 54 | fflush(stdout); 55 | break; 56 | case UCCI_COMM_BANMOVES: 57 | break; 58 | case UCCI_COMM_SETOPTION: 59 | switch (ucs.option.uo_type) { 60 | case UCCI_OPTION_BATCH: 61 | break; 62 | case UCCI_OPTION_DEBUG: 63 | break; 64 | case UCCI_OPTION_NEWGAME: 65 | new_hash_table(); 66 | init_openbook(bookfile); 67 | break; 68 | default: 69 | break; 70 | } 71 | 72 | break; 73 | case UCCI_COMM_GO: 74 | case UCCI_COMM_GOPONDER: 75 | if(ucs.search.ut_mode == UCCI_TIME_DEPTH) 76 | think(ucs.search.depth_time.depth); 77 | break; 78 | default: 79 | break; 80 | } 81 | } 82 | 83 | del_hash_table(); 84 | } 85 | 86 | printf("bye\n"); 87 | fflush(stdout); 88 | 89 | #ifdef DEBUG_LOG 90 | fclose(logfile); 91 | #endif 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /src/hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "hash.h" 6 | 7 | INT32_ zobrist_player; 8 | INT64_ zobrist_player_check; 9 | INT32_ zobrist_table[14][256]; 10 | INT64_ zobrist_table_check[14][256]; 11 | 12 | INT32_ zobrist_key; 13 | INT64_ zobrist_key_check; 14 | INT32_ hash_mask; 15 | hash_node *hash_table; 16 | 17 | INT32_ rand32() 18 | { 19 | return rand() ^ ((INT32_)rand() << 15) ^ ((INT32_)rand() << 30); 20 | } 21 | 22 | static INT64_ rand64() 23 | { 24 | return rand() ^ ((INT64_)rand() << 15) ^ ((INT64_)rand() << 30) ^ 25 | ((INT64_)rand() << 45) ^ ((INT64_)rand() << 60); 26 | } 27 | 28 | static void init_zobrist() 29 | { 30 | int i, j; 31 | srand(time(NULL)); 32 | 33 | zobrist_player = rand32(); 34 | for (i = 0; i < 14; i++) { 35 | for (j = 0; j < 256; j++) { 36 | zobrist_table[i][j] = rand32(); 37 | } 38 | } 39 | 40 | zobrist_player_check = rand64(); 41 | for (i = 0; i < 14; i++) { 42 | for (j = 0; j < 256; j++) { 43 | zobrist_table_check[i][j] = rand64(); 44 | } 45 | } 46 | } 47 | 48 | void reset_hash_table() 49 | { 50 | memset(hash_table, 0, HASH_TABLE_SIZE * sizeof(hash_node)); 51 | } 52 | 53 | void new_hash_table() 54 | { 55 | init_zobrist(); 56 | 57 | hash_mask = HASH_TABLE_SIZE - 1; 58 | hash_table = (hash_node *)malloc(HASH_TABLE_SIZE * sizeof(hash_node)); 59 | reset_hash_table(); 60 | } 61 | 62 | void del_hash_table() 63 | { 64 | if (hash_table) 65 | free(hash_table); 66 | } 67 | 68 | void save_hash_table(int value, int depth, data_type type, move mv) 69 | { 70 | int add = zobrist_key & hash_mask; 71 | hash_node *pnode = &hash_table[add]; 72 | 73 | pnode->value = value; 74 | pnode->checksum = zobrist_key_check; 75 | pnode->depth = depth; 76 | pnode->type = type; 77 | pnode->goodmove = mv; 78 | } 79 | 80 | int read_hash_table(int depth, int alpha, int beta, move *mv) 81 | { 82 | int add = zobrist_key & hash_mask; 83 | hash_node *pnode = &hash_table[add]; 84 | 85 | if (pnode->depth >= depth && 86 | pnode->checksum == zobrist_key_check) { 87 | 88 | /* 确切值 */ 89 | if (pnode->type == HASH_EXACT) 90 | return pnode->value; 91 | 92 | /* 上边界 */ 93 | if (pnode->type == HASH_ALPHA && pnode->value <= alpha) 94 | return pnode->value; 95 | 96 | /* 下边界 */ 97 | if (pnode->type == HASH_BETA && pnode->value >= beta) 98 | return pnode->value; 99 | 100 | (*mv) = pnode->goodmove; 101 | } 102 | 103 | /* 没有命中,返回无效标志 */ 104 | return NOVALUE; 105 | } 106 | -------------------------------------------------------------------------------- /src/hash.h: -------------------------------------------------------------------------------- 1 | #ifndef HASH_H 2 | #define HASH_H 3 | 4 | #include "base.h" 5 | 6 | /* 设置哈希表大小,默认4MB */ 7 | #define HASH_TABLE_SIZE 1024*1024*4 8 | /* 表示当前局面在置换表中不存在 */ 9 | #define NOVALUE -66666 10 | 11 | typedef enum { 12 | HASH_EXACT, 13 | HASH_ALPHA, 14 | HASH_BETA 15 | } data_type; 16 | 17 | typedef struct { 18 | INT64_ checksum; /* 64位校验值 */ 19 | data_type type; /* 数据类型 */ 20 | int depth; /* 取得此值时的层次 */ 21 | int value; /* 节点的估值 */ 22 | move goodmove; /* 最佳走法 */ 23 | } hash_node; 24 | 25 | /* 32位的走棋方键值 */ 26 | extern INT32_ zobrist_player; 27 | /* 64位的走棋方校验值 */ 28 | extern INT64_ zobrist_player_check; 29 | /* 32位的棋子在棋盘各位置的键值 */ 30 | extern INT32_ zobrist_table[14][256]; 31 | /* 64位的棋子在棋盘各位置的校验值 */ 32 | extern INT64_ zobrist_table_check[14][256]; 33 | 34 | /* 局面的键值 */ 35 | extern INT32_ zobrist_key; 36 | /* 局面的校验值 */ 37 | extern INT64_ zobrist_key_check; 38 | extern INT32_ hash_mask; 39 | extern hash_node *hash_table; 40 | 41 | INT32_ rand32(); 42 | void reset_hash_table(); 43 | void new_hash_table(); 44 | void del_hash_table(); 45 | void save_hash_table(int value, int depth, data_type type, move mv); 46 | int read_hash_table(int depth, int alpha, int beta, move *mv); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/movesort.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "movesort.h" 4 | 5 | int history[HISTORY_SIZE]; 6 | 7 | int cmp_move(move m1, move m2) { 8 | if (m1.to == m2.to && m1.from == m2.from) return 1; 9 | else return 0; 10 | } 11 | 12 | void save_history(move mv, int depth) 13 | { 14 | int k, i; 15 | 16 | k = mv.from * 256 + mv.to; 17 | history[k] += depth; 18 | 19 | /* 防止历史表溢出 */ 20 | if (history[k] >= 240) { 21 | for (i = 0; i < HISTORY_SIZE; i++) { 22 | history[i] -= history[i] / 4; 23 | } 24 | } 25 | } 26 | 27 | void reset_history() 28 | { 29 | memset(history, 0, sizeof(history)); 30 | } 31 | 32 | static void set_non_cap_value(move *move_array, int start, int end) { 33 | 34 | int i; 35 | for (i = start; i < end; ++i) { 36 | move_array[i].capture = history[move_array[i].from * 37 | 256 + move_array[i].to]; 38 | } 39 | } 40 | 41 | static int partition(move *move_array, int p, int r) 42 | { 43 | int i, j; 44 | move x = move_array[r]; 45 | move tmp; 46 | 47 | i = p - 1; 48 | for (j = p; j < r; j++) { 49 | if (move_array[j].capture >= x.capture) { 50 | i++; 51 | tmp = move_array[i]; 52 | move_array[i] = move_array[j]; 53 | move_array[j] = tmp; 54 | } 55 | } 56 | 57 | tmp = move_array[i+1]; 58 | move_array[i+1] = move_array[r]; 59 | move_array[r] = tmp; 60 | 61 | return i+1; 62 | } 63 | 64 | static void quicksort(move *move_array, int p, int r) 65 | { 66 | if (p < r) { 67 | int q = partition(move_array, p, r); 68 | quicksort(move_array, p, q-1); 69 | quicksort(move_array, q+1, r); 70 | } 71 | } 72 | 73 | int move_array_init(move *move_array, move hash_move) 74 | { 75 | int n1, n2, count, i, j; 76 | n1 = gen_cap_move(move_array); 77 | quicksort(move_array, 0, n1-1); 78 | 79 | n2 = gen_non_cap_move(move_array + n1); 80 | count = n1 + n2; 81 | 82 | set_non_cap_value(move_array, n1, count); 83 | quicksort(move_array, n1, count-1); 84 | 85 | /* 置换表走法不为空 */ 86 | if (hash_move.from != 0 && hash_move.to != 0) 87 | { 88 | for (i = 0; i < count; i++) { 89 | if (cmp_move(move_array[i], hash_move)) { 90 | for (j = i; j > 0; j--) { 91 | move_array[j] = move_array[j-1]; 92 | } 93 | 94 | move_array[0] = hash_move; 95 | break; 96 | } 97 | } 98 | } 99 | 100 | return count; 101 | } 102 | 103 | int cap_move_array_init(move *move_array) 104 | { 105 | int n; 106 | n = gen_cap_move(move_array); 107 | quicksort(move_array, 0, n-1); 108 | return n; 109 | } 110 | -------------------------------------------------------------------------------- /src/movesort.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVESORT_H 2 | #define MOVESORT_H 3 | 4 | #include "base.h" 5 | #include "position.h" 6 | #include "genmoves.h" 7 | 8 | #define HISTORY_SIZE 65536 9 | 10 | /* 历史表 */ 11 | extern int history[]; 12 | 13 | int cmp_move(move m1, move m2); 14 | void reset_history(); 15 | void save_history(move mv, int depth); 16 | int move_array_init(move *move_array, move hash_move); 17 | int cap_move_array_init(move *move_array); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/openbook.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "openbook.h" 3 | 4 | const int BOOK_UNIQUE = 4; 5 | const int BOOK_MULTI = 8; 6 | const int BOOK_EXIST = 12; 7 | 8 | static move book_move_array[MAX_BOOK_POS][MAX_BOOK_MOVE]; 9 | static int book_num; 10 | 11 | void init_openbook(const char *bookfile) 12 | { 13 | FILE *fp; 14 | char *linechar; 15 | char linestr[256]; 16 | move book_move; 17 | hash_node hash_temp, *phash; 18 | 19 | fp = fopen(bookfile, "rt"); 20 | if (fp == NULL) 21 | return; 22 | 23 | int a = 0; 24 | linestr[254] = linestr[255] = '\0'; 25 | 26 | while (fgets(linestr, 254, fp) != NULL) { 27 | a++; 28 | linechar = linestr; 29 | str_to_move(*(long *) linechar, &book_move); 30 | if (!cmp_move(book_move, NULL_MOVE)) { 31 | linechar += 5; 32 | book_move.capture = 0; 33 | while (*linechar >= '0' && *linechar <= '9') { 34 | book_move.capture *= 10; 35 | book_move.capture += *linechar - '0'; 36 | linechar++; 37 | } 38 | 39 | linechar++; 40 | fen_to_arr(linechar); 41 | 42 | if (board[book_move.from]) { 43 | phash = &hash_table[zobrist_key & hash_mask]; 44 | hash_temp = *phash; 45 | 46 | if ((hash_temp.type & BOOK_EXIST) == 0) { 47 | hash_temp.checksum = zobrist_key_check; 48 | hash_temp.type = BOOK_UNIQUE; 49 | hash_temp.goodmove = book_move; 50 | hash_temp.value = book_move.capture; 51 | *phash = hash_temp; 52 | 53 | } else { 54 | 55 | if (hash_temp.checksum == zobrist_key_check) { 56 | if ((hash_temp.type & BOOK_UNIQUE) != 0) { 57 | if (book_num < MAX_BOOK_POS) { 58 | hash_temp.checksum = zobrist_key_check; 59 | hash_temp.type = BOOK_MULTI; 60 | book_move_array[book_num][0] = hash_temp.goodmove; 61 | book_move_array[book_num][1] = book_move; 62 | hash_temp.value = book_num; 63 | hash_temp.depth = 2; 64 | book_num++; 65 | *phash = hash_temp; 66 | } 67 | } else { 68 | if (hash_temp.depth < MAX_BOOK_MOVE) { 69 | book_move_array[hash_temp.value][hash_temp.depth] = book_move; 70 | hash_temp.depth++; 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | } 78 | 79 | fclose(fp); 80 | } 81 | 82 | move read_openbook() 83 | { 84 | int i, value; 85 | long move_str = 0; 86 | move mv; 87 | hash_node hash_temp; 88 | move *pbook_move; 89 | int temp_value[MAX_BOOK_MOVE]; 90 | 91 | if ((int)move_str) { 92 | /* do nothing */ 93 | } 94 | 95 | hash_temp = hash_table[zobrist_key & hash_mask]; 96 | 97 | if ((hash_temp.type & BOOK_EXIST) != 0 && 98 | hash_temp.checksum == zobrist_key_check) { 99 | 100 | if ((hash_temp.type & BOOK_UNIQUE) != 0) { 101 | move_str = move_to_str(&hash_temp.goodmove); 102 | return hash_temp.goodmove; 103 | 104 | } else { 105 | value = 0; 106 | pbook_move = book_move_array[hash_temp.value]; 107 | for (i = 0; i < hash_temp.depth; i++) { 108 | mv = pbook_move[i]; 109 | move_str = move_to_str(&mv); 110 | value += mv.capture; 111 | temp_value[i] = value; 112 | } 113 | 114 | if (value > 0) { 115 | value = rand32() % value; 116 | for (i = 0; i < hash_temp.depth; i++) { 117 | if (value < temp_value[i]) { 118 | break; 119 | } 120 | } 121 | return pbook_move[i]; 122 | 123 | } else { 124 | mv = NULL_MOVE; 125 | return mv; 126 | } 127 | } 128 | 129 | } else { 130 | mv = NULL_MOVE; 131 | return mv; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/openbook.h: -------------------------------------------------------------------------------- 1 | #ifndef OPENBOOK_H 2 | #define OPENBOOK_H 3 | 4 | #include "base.h" 5 | #include "hash.h" 6 | #include "position.h" 7 | #include "movesort.h" 8 | 9 | #define MAX_BOOK_POS 30000 10 | #define MAX_BOOK_MOVE 128 11 | 12 | void init_openbook(const char *bookfile); 13 | move read_openbook(); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/pipe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pipe.h" 5 | 6 | static char buffer[LINE_INPUT_MAX_CHAR]; 7 | static int read_end; 8 | static int eof; 9 | 10 | #ifdef _WIN32 11 | 12 | #include 13 | 14 | HANDLE input, output; 15 | int console; 16 | int bytes_left; 17 | 18 | void open_pipe() 19 | { 20 | DWORD dw_mode; 21 | eof = 0; 22 | input = GetStdHandle(STD_INPUT_HANDLE); 23 | output = GetStdHandle(STD_OUTPUT_HANDLE); 24 | console = GetConsoleMode(input, &dw_mode); 25 | read_end = 0; 26 | } 27 | 28 | void close() 29 | { 30 | CloseHandle(input); 31 | CloseHandle(output); 32 | } 33 | 34 | void read_input() 35 | { 36 | DWORD dw_bytes; 37 | if (ReadFile(input, buffer + read_end, LINE_INPUT_MAX_CHAR - read_end, 38 | &dw_bytes, NULL)) { 39 | read_end += dw_bytes; 40 | if (bytes_left > 0) { 41 | bytes_left -= dw_bytes; 42 | } else { 43 | eof = 1; 44 | } 45 | } 46 | } 47 | 48 | int check_input() 49 | { 50 | DWORD dw_events, dw_bytes; 51 | if (console) { 52 | GetNumberOfConsoleInputEvents(input, &dw_events); 53 | return dw_events > 1; 54 | } else if (bytes_left > 0) { 55 | return 1; 56 | } else if (PeekNamedPipe(input, NULL, 0, NULL, &dw_bytes, NULL)) { 57 | bytes_left = dw_bytes; 58 | return bytes_left > 0; 59 | } else { 60 | return 1; 61 | } 62 | } 63 | 64 | void line_output(const char *line_str) 65 | { 66 | DWORD dw_bytes; 67 | int str_len; 68 | str_len = strlen(line_str); 69 | memcpy(buffer, line_str, str_len); 70 | buffer[str_len] = 'r'; 71 | buffer[str_len + 1] = '\n'; 72 | WriteFile(output, buffer, str_len + 2, &dw_bytes, NULL); 73 | } 74 | 75 | #else 76 | 77 | #include 78 | #include 79 | #include 80 | #include 81 | 82 | static int input, output; 83 | 84 | void open_pipe() 85 | { 86 | eof = 0; 87 | input = STDIN_FILENO; 88 | output = STDOUT_FILENO; 89 | read_end = 0; 90 | } 91 | 92 | void close_pipe() 93 | { 94 | close(input); 95 | close(output); 96 | } 97 | 98 | static void read_input() 99 | { 100 | int n; 101 | n = read(input, buffer + read_end, LINE_INPUT_MAX_CHAR - read_end); 102 | 103 | if (n < 0) { 104 | eof = 1; 105 | } else { 106 | read_end += n; 107 | } 108 | } 109 | 110 | static int check_input() 111 | { 112 | fd_set set; 113 | struct timeval tv; 114 | int val; 115 | 116 | FD_ZERO(&set); 117 | FD_SET(input, &set); 118 | 119 | tv.tv_sec = 0; 120 | tv.tv_usec = 0; 121 | val = select(input + 1, &set, NULL, NULL, &tv); 122 | 123 | if (val > 0 && FD_ISSET(input, &set) > 0) 124 | return 1; 125 | else 126 | return 0; 127 | } 128 | 129 | void line_output(const char *line_str) 130 | { 131 | int str_len; 132 | 133 | char write_buffer[LINE_INPUT_MAX_CHAR]; 134 | str_len = strlen(line_str); 135 | 136 | memcpy(write_buffer, line_str, str_len); 137 | 138 | write_buffer[str_len] = '\n'; 139 | if (write(output, write_buffer, str_len + 1) != str_len + 1) { 140 | /* err_sys("write error"); */ 141 | } 142 | } 143 | 144 | #endif 145 | 146 | static int get_buffer(char *line_str) 147 | { 148 | char *pfe; 149 | int feed_end; 150 | 151 | pfe = (char *) memchr(buffer, '\n', read_end); 152 | 153 | if (pfe == NULL) { 154 | return 0; 155 | 156 | } else { 157 | feed_end = pfe - buffer; 158 | 159 | memcpy(line_str, buffer, feed_end); 160 | 161 | line_str[feed_end] = '\0'; 162 | 163 | feed_end ++; 164 | read_end -= feed_end; 165 | 166 | memcpy(buffer, buffer + feed_end, read_end); 167 | pfe = (char *) strchr(line_str, '\r'); 168 | 169 | if (pfe != NULL) { 170 | *pfe = '\0'; 171 | } 172 | 173 | return 1; 174 | } 175 | } 176 | 177 | int line_input(char *line_str) 178 | { 179 | 180 | if (get_buffer(line_str)) { 181 | return 1; 182 | 183 | } else if (check_input()) { 184 | 185 | read_input(); 186 | 187 | if (get_buffer(line_str)) { 188 | return 1; 189 | 190 | } else if (read_end == LINE_INPUT_MAX_CHAR) { 191 | 192 | memcpy(line_str, buffer, LINE_INPUT_MAX_CHAR - 1); 193 | line_str[LINE_INPUT_MAX_CHAR - 1] = '\0'; 194 | buffer[0] = buffer[LINE_INPUT_MAX_CHAR - 1]; 195 | 196 | read_end = 1; 197 | return 1; 198 | 199 | } else { 200 | return 0; 201 | } 202 | 203 | } else { 204 | return 0; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/pipe.h: -------------------------------------------------------------------------------- 1 | #ifndef PIPE_H 2 | #define PIPE_H 3 | 4 | #define LINE_INPUT_MAX_CHAR 8192 5 | #define PATH_MAX_CHAR 1024 6 | #define PATH_SEPARATOR '/' 7 | 8 | void open_pipe(); 9 | int line_input(char *line_str); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/position.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "position.h" 4 | 5 | int side; 6 | BYTE board[256]; 7 | BYTE piece[48]; 8 | 9 | BYTE piece_type[48] = { 10 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11 | 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6, 6, 12 | 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6, 6 13 | }; 14 | 15 | void change_side() 16 | { 17 | side = 1 - side; 18 | 19 | /* hash */ 20 | zobrist_key ^= zobrist_player; 21 | zobrist_key_check ^= zobrist_player_check; 22 | /* end */ 23 | } 24 | 25 | static void add_piece(BYTE pos, BYTE pc) 26 | { 27 | board[pos] = pc; 28 | piece[pc] = pos; 29 | 30 | /* hash */ 31 | BYTE pt = piece_type[pc]; 32 | if (pc >= 32) 33 | pt += 7; 34 | 35 | zobrist_key ^= zobrist_table[pt][pos]; 36 | zobrist_key_check ^= zobrist_table_check[pt][pos]; 37 | /* end */ 38 | } 39 | 40 | static char piece_to_char(BYTE pc) 41 | { 42 | if (pc < 32) { 43 | switch(pc) { 44 | case 16: return 'K'; 45 | case 17: 46 | case 18: return 'A'; 47 | case 19: 48 | case 20: return 'B'; 49 | case 21: 50 | case 22: return 'N'; 51 | case 23: 52 | case 24: return 'R'; 53 | case 25: 54 | case 26: return 'C'; 55 | case 27: 56 | case 28: 57 | case 29: 58 | case 30: 59 | case 31: return 'P'; 60 | default: return 0; 61 | } 62 | 63 | } else { 64 | pc = pc - 16; 65 | 66 | switch(pc) { 67 | case 16: return 'k'; 68 | case 17: 69 | case 18: return 'a'; 70 | case 19: 71 | case 20: return 'b'; 72 | case 21: 73 | case 22: return 'n'; 74 | case 23: 75 | case 24: return 'r'; 76 | case 25: 77 | case 26: return 'c'; 78 | case 27: 79 | case 28: 80 | case 29: 81 | case 30: 82 | case 31: return 'p'; 83 | default: return 0; 84 | 85 | } 86 | } 87 | } 88 | 89 | static int char_to_type(char ch) 90 | { 91 | switch(ch) { 92 | case 'k': 93 | case 'K': return 0; 94 | case 'a': 95 | case 'A': return 1; 96 | case 'b': 97 | case 'B': return 2; 98 | case 'n': 99 | case 'N': return 3; 100 | case 'r': 101 | case 'R': return 4; 102 | case 'c': 103 | case 'C': return 5; 104 | case 'p': 105 | case 'P': return 6; 106 | default: return 7; 107 | } 108 | } 109 | 110 | static void clear_board() 111 | { 112 | side = 0; 113 | memset(board, 0, sizeof(board)); 114 | memset(piece, 0, sizeof(piece)); 115 | 116 | zobrist_key = 0; 117 | zobrist_key_check = 0; 118 | } 119 | 120 | void arr_to_fen(char *fen_str) 121 | { 122 | int i, j, k, pc; 123 | char *str; 124 | 125 | str = fen_str; 126 | 127 | for (i = 3; i <= 12; ++i) { 128 | k = 0; 129 | 130 | for (j = 3; j <= 11; ++j) { 131 | pc = board[(i << 4) + j]; 132 | 133 | if (pc != 0) { 134 | if (k > 0) { 135 | *str = k + '0'; 136 | str ++; 137 | k = 0; 138 | } 139 | 140 | *str = piece_to_char(pc); 141 | str ++; 142 | 143 | } else { 144 | k ++; 145 | } 146 | } 147 | 148 | if (k > 0) { 149 | *str = k + '0'; 150 | str ++; 151 | } 152 | 153 | *str = '/'; 154 | str ++; 155 | } 156 | 157 | str --; 158 | *str = ' '; 159 | str ++; 160 | *str = (side == RED ? 'w' : 'b'); 161 | str ++; 162 | *str = '\0'; 163 | } 164 | 165 | void fen_to_arr(const char *fen_str) 166 | { 167 | clear_board(); 168 | 169 | int i, j, k; 170 | int r_pc[7] = {16, 17, 19, 21, 23, 25, 27}; 171 | int b_pc[7] = {32, 33, 35, 37, 39, 41, 43}; 172 | const char *str; 173 | 174 | str = fen_str; 175 | 176 | if (*str == '\0') { 177 | return; 178 | } 179 | 180 | i = 3; 181 | j = 3; 182 | 183 | while (*str != ' ') { 184 | if (*str == '/') { 185 | j = 3; 186 | i ++; 187 | 188 | if (i > 12) break; 189 | } else if (*str >= '1' && *str <= '9') { 190 | for (k = 0; k < (*str - '0'); ++k) { 191 | if (j >= 11) break; 192 | j ++; 193 | } 194 | } else if (*str >= 'A' && *str <= 'Z') { 195 | if (j <= 11) { 196 | k = char_to_type(*str); 197 | 198 | if (k < 7) { 199 | if (r_pc[k] < 32) { 200 | 201 | add_piece((i << 4) + j,r_pc[k]); 202 | r_pc[k] ++; 203 | } 204 | } 205 | 206 | j ++; 207 | } 208 | } else if (*str >= 'a' && *str <= 'z') { 209 | if (j <= 11) { 210 | 211 | k = char_to_type(*str); 212 | 213 | if (k < 7) { 214 | if (b_pc[k] < 48) { 215 | 216 | add_piece((i << 4) + j,b_pc[k]); 217 | b_pc[k] ++; 218 | } 219 | } 220 | 221 | j ++; 222 | } 223 | } 224 | 225 | str ++; 226 | 227 | if (*str == '\0') { 228 | return; 229 | } 230 | } 231 | 232 | str ++; 233 | 234 | if (side != (*str == 'b' ? 1 : 0)) { 235 | change_side(); 236 | } 237 | } 238 | 239 | inline void str_to_move(long str, move *mv) 240 | { 241 | char *ch; 242 | 243 | ch = (char *) &str; 244 | 245 | mv->from = (ch[0] - 'a' + 3) + (('9' - ch[1] + 3) << 4); 246 | mv->to = (ch[2] - 'a' + 3) + (('9' - ch[3] + 3) << 4); 247 | } 248 | 249 | inline long move_to_str(move *mv) 250 | { 251 | /* union u_str fix '-O2(3)' warning: 252 | * dereferencing type-punned pointer 253 | * will break strict-aliasing rules */ 254 | union u_str { 255 | char str[4]; 256 | long p; 257 | }; 258 | 259 | union u_str str_u; 260 | 261 | str_u.str[0] = (mv->from % 16) - 3 + 'a'; 262 | str_u.str[1] = '9' - (mv->from / 16) + 3; 263 | str_u.str[2] = (mv->to % 16) - 3 + 'a'; 264 | str_u.str[3] = '9' - (mv->to / 16) + 3; 265 | 266 | /* return *(long *)str */ 267 | return *(&str_u.p); 268 | } 269 | -------------------------------------------------------------------------------- /src/position.h: -------------------------------------------------------------------------------- 1 | #ifndef POSITION_H 2 | #define POSITION_H 3 | 4 | #include "base.h" 5 | #include "hash.h" 6 | 7 | extern int side; 8 | extern BYTE board[]; 9 | extern BYTE piece[]; 10 | extern BYTE piece_type[]; 11 | 12 | void change_side(); 13 | void arr_to_fen(char *fen_str); 14 | void fen_to_arr(const char *fen_str); 15 | void str_to_move(long str, move *mv); 16 | long move_to_str(move *mv); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/search.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef _WIN32 5 | 6 | #include "windows.h" 7 | 8 | #else 9 | 10 | #include 11 | #include 12 | 13 | #endif 14 | 15 | #include "search.h" 16 | 17 | static move move_array[MAX_SEARCH_DEPTH][128]; /* 所有走法 */ 18 | 19 | /* 记录历史哈希局面 */ 20 | static INT32_ hash_history[MAX_SEARCH_DEPTH]; 21 | 22 | /* 记录4步历史最佳走法,避免出现长将等循环走法 */ 23 | static move move_history[4]; 24 | 25 | static move best_move; 26 | static move better_move; 27 | static move good_move; 28 | 29 | static int max_depth; 30 | 31 | /* 统计相关结点 */ 32 | static int eval_node_count; 33 | static int hash_node_count; 34 | static int dead_node_count; 35 | 36 | static time_t starttime; 37 | 38 | move NULL_MOVE; 39 | 40 | /* 当前搜索步数 */ 41 | int cur_step; 42 | 43 | #ifdef _WIN32 44 | 45 | static unsigned long get_tick_count() 46 | { 47 | return GetTickCount(); 48 | } 49 | 50 | #else 51 | 52 | static unsigned long get_tick_count() 53 | { 54 | struct timeval tv; 55 | if (gettimeofday(&tv, NULL) != 0) 56 | return 0; 57 | 58 | return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); 59 | } 60 | 61 | #endif 62 | 63 | static void init_search() 64 | { 65 | cur_step = 0; 66 | 67 | reset_hash_table(); 68 | reset_history(); 69 | 70 | eval_node_count = 0; 71 | hash_node_count = 0; 72 | dead_node_count = 0; 73 | 74 | } 75 | 76 | static void make_move(move *mv) 77 | { 78 | BYTE pc1, pc2, pt; 79 | 80 | pc1 = board[mv->from]; 81 | pc2 = board[mv->to]; 82 | mv->capture = pc2; 83 | 84 | /* 目的地有其它子存在 */ 85 | if (pc2) { 86 | piece[pc2] = 0; 87 | 88 | /* hash */ 89 | pt = piece_type[pc2]; 90 | if (pc2 >= 32) 91 | pt += 7; 92 | 93 | zobrist_key ^= zobrist_table[pt][mv->to]; 94 | zobrist_key_check ^= zobrist_table_check[pt][mv->to]; 95 | /* end */ 96 | } 97 | 98 | board[mv->from] = 0; 99 | board[mv->to] = pc1; 100 | piece[pc1] = mv->to; 101 | 102 | /* hash */ 103 | pt = piece_type[pc1]; 104 | if (pc1 >= 32) 105 | pt += 7; 106 | 107 | zobrist_key ^= zobrist_table[pt][mv->to] ^ 108 | zobrist_table[pt][mv->from]; 109 | zobrist_key_check ^= zobrist_table_check[pt][mv->to] ^ 110 | zobrist_table_check[pt][mv->from]; 111 | /* end */ 112 | 113 | change_side(); 114 | 115 | cur_step++; 116 | } 117 | 118 | static void unmake_move(move *mv) 119 | { 120 | BYTE pc1, pc2, pt; 121 | 122 | pc1 = board[mv->to]; 123 | pc2 = mv->capture; 124 | 125 | if (pc2) { 126 | piece[pc2] = mv->to; 127 | 128 | /* hash */ 129 | pt = piece_type[pc2]; 130 | if (pc2 >= 32) 131 | pt += 7; 132 | 133 | zobrist_key ^= zobrist_table[pt][mv->to]; 134 | zobrist_key_check ^= zobrist_table_check[pt][mv->to]; 135 | /* end */ 136 | } 137 | 138 | board[mv->from] = pc1; 139 | board[mv->to] = pc2; 140 | piece[pc1] = mv->from; 141 | 142 | /* hash */ 143 | pt = piece_type[pc1]; 144 | if (pc1 >= 32) 145 | pt += 7; 146 | 147 | zobrist_key ^= zobrist_table[pt][mv->from] ^ 148 | zobrist_table[pt][mv->to]; 149 | zobrist_key_check ^= zobrist_table_check[pt][mv->from] ^ 150 | zobrist_table_check[pt][mv->to]; 151 | /* end */ 152 | 153 | change_side(); 154 | 155 | cur_step--; 156 | } 157 | 158 | /* 极小窗口搜索(Minimal Window Search/PVS) */ 159 | /* static int principal_variation_search(int depth, int alpha, int beta); */ 160 | 161 | /* PVS + TT(置换表) + HH(历史启发) */ 162 | static int nega_scout(int depth, int alpha, int beta); 163 | 164 | void think(int depth) 165 | { 166 | long best; 167 | max_depth = depth; 168 | best_move = NULL_MOVE; 169 | 170 | int i; 171 | int count = move_array_init(move_array[MAX_SEARCH_DEPTH - 1], NULL_MOVE); 172 | if (count != 0) { 173 | best_move = better_move = good_move = 174 | move_array[MAX_SEARCH_DEPTH - 1][0]; 175 | 176 | } else { 177 | printf("nobestmove\n"); 178 | fflush(stdout); 179 | return; 180 | } 181 | 182 | /* 搜索开局库 */ 183 | move book_move = read_openbook(); 184 | if (!cmp_move(book_move, NULL_MOVE)) { 185 | best = move_to_str(&book_move); 186 | printf("bestmove %.4s\n", (const char *)&best); 187 | fflush(stdout); 188 | move_history[0] = move_history[1]; 189 | move_history[1] = move_history[2]; 190 | move_history[2] = move_history[3]; 191 | move_history[3] = book_move; 192 | 193 | #ifdef DEBUG_LOG 194 | fprintf(logfile, ">> bestmove(openbook) = %.4s\n", (const char *)&best); 195 | fflush(logfile); 196 | #endif 197 | 198 | return; 199 | } 200 | 201 | init_search(); 202 | 203 | /* 开始迭代深化 */ 204 | starttime = get_tick_count(); 205 | move backupmove = NULL_MOVE; 206 | 207 | for (max_depth = 1; max_depth <= MAX_SEARCH_DEPTH; max_depth++) { 208 | 209 | int value = nega_scout(max_depth, -INFINITE_, INFINITE_); 210 | 211 | if (value != TIME_OVER) { 212 | backupmove = best_move; 213 | } else { 214 | break; 215 | } 216 | } 217 | 218 | best_move = backupmove; 219 | 220 | /* principal_variation_search(depth, -INFINITE_, INFINITE_); */ 221 | /* nega_scout(max_depth, -INFINITE_, INFINITE_); */ 222 | 223 | int timeuse; 224 | timeuse = get_tick_count() - starttime; 225 | 226 | int flag = 0; 227 | if (cmp_move(move_history[0] , move_history[2]) && 228 | cmp_move(move_history[2] , best_move)) { 229 | if (cmp_move(move_history[0] , move_history[2]) && 230 | cmp_move(move_history[2] , better_move)) { 231 | 232 | if (cmp_move(move_history[0], move_history[2]) && 233 | cmp_move(move_history[2], good_move)) { 234 | 235 | if (count != 1) { 236 | for (i = 0; i < count; i++) { 237 | if (!cmp_move(move_array[MAX_SEARCH_DEPTH - 1][i], good_move)) { 238 | good_move = move_array[MAX_SEARCH_DEPTH - 1][i]; 239 | } 240 | } 241 | } else { 242 | printf("nobestmove\n"); 243 | fflush(stdout); 244 | return; 245 | } 246 | } 247 | 248 | move_history[0] = move_history[1]; 249 | move_history[1] = move_history[2]; 250 | move_history[2] = move_history[3]; 251 | move_history[3] = good_move; 252 | best = move_to_str(&good_move); 253 | 254 | flag = 3; 255 | 256 | } else { 257 | move_history[0] = move_history[1]; 258 | move_history[1] = move_history[2]; 259 | move_history[2] = move_history[3]; 260 | move_history[3] = better_move; 261 | best = move_to_str(&better_move); 262 | 263 | flag = 2; 264 | } 265 | } else { 266 | move_history[0] = move_history[1]; 267 | move_history[1] = move_history[2]; 268 | move_history[2] = move_history[3]; 269 | move_history[3] = best_move; 270 | best = move_to_str(&best_move); 271 | 272 | flag = 1; 273 | } 274 | 275 | printf("bestmove %.4s\n", (const char *)&best); 276 | fflush(stdout); 277 | 278 | #ifdef DEBUG_LOG 279 | fprintf(logfile, ">> depth = %-2d bestmove(%d) = %.4s eval = %-8d " 280 | "hash = %-8d dead = %-3d time = %dms\n", 281 | max_depth-1, 282 | flag, 283 | (const char *)&best, 284 | eval_node_count, 285 | hash_node_count, 286 | dead_node_count, 287 | timeuse); 288 | fflush(logfile); 289 | #endif 290 | 291 | } 292 | 293 | /* 静态搜索函数 */ 294 | static int quiescence_search(int alpha, int beta) 295 | { 296 | int score = evaluate(); 297 | if (score >= beta) 298 | return score; 299 | 300 | move move_arr[128]; 301 | int count = cap_move_array_init(move_arr); 302 | if (count == 0) 303 | return -INFINITE_ + cur_step; 304 | 305 | int i; 306 | for (i = 0; i < count; i++) { 307 | make_move(&move_arr[i]); 308 | score = -quiescence_search(-beta, -alpha); 309 | unmake_move(&move_arr[i]); 310 | 311 | if (score >= alpha) { 312 | alpha = score; 313 | /* beta剪枝 */ 314 | if (score >= beta) 315 | break; 316 | } 317 | } 318 | 319 | return score; 320 | } 321 | 322 | static int nega_scout(int depth, int alpha, int beta) 323 | { 324 | int score, count; 325 | int a, b, t, i, j; 326 | move hash_move = NULL_MOVE; 327 | 328 | /* 用哈希历史局面防止产生循环局面 */ 329 | hash_history[depth] = zobrist_key; 330 | for (j = max_depth; j > depth; j--) { 331 | if (hash_history[j] == hash_history[depth]) { 332 | return -INFINITE_; 333 | } 334 | } 335 | 336 | /* 读取哈希表 */ 337 | score = read_hash_table(depth, alpha, beta, &hash_move); 338 | if (score != NOVALUE) { 339 | hash_node_count++; 340 | return score; 341 | } 342 | 343 | if (depth <= 0) { 344 | score = quiescence_search(alpha, beta); 345 | /* score = evaluate(); */ 346 | eval_node_count++; 347 | save_hash_table(score, depth, HASH_EXACT, NULL_MOVE); 348 | return score; 349 | } 350 | 351 | count = move_array_init(move_array[depth], hash_move); 352 | if (count == 0) { 353 | dead_node_count++; 354 | return -INFINITE_ + cur_step; 355 | } 356 | 357 | /* 将上次迭代的最佳走法设置为走法数组的第一位 */ 358 | if (depth == max_depth && max_depth > 1) { 359 | for (i = 1; i < count; i++) { 360 | if (cmp_move(move_array[depth][i], best_move)) { 361 | move_array[depth][i] = move_array[depth][0]; 362 | move_array[depth][0] = best_move; 363 | } 364 | } 365 | } 366 | 367 | int best = -1; 368 | a = alpha; 369 | b = beta; 370 | int eval_is_exact = 0; 371 | 372 | for (i = 0; i < count; i++) { 373 | 374 | if (depth == max_depth) { 375 | if (get_tick_count() - starttime >= LONGEST_SEARCH_TIME) 376 | return TIME_OVER; 377 | } 378 | 379 | make_move(&move_array[depth][i]); 380 | t = -nega_scout(depth-1, -b, -a); 381 | 382 | if (t > a && t < beta && i > 0) { 383 | 384 | a = -nega_scout(depth-1, -beta, -t); 385 | eval_is_exact = 1; 386 | 387 | if (depth == max_depth) { 388 | good_move = better_move; 389 | better_move = best_move; 390 | best_move = move_array[depth][i]; 391 | } 392 | 393 | best = i; 394 | hash_move = move_array[depth][i]; 395 | } 396 | 397 | unmake_move(&move_array[depth][i]); 398 | 399 | if (a < t) { 400 | eval_is_exact = 1; 401 | a = t; 402 | 403 | if (depth == max_depth) { 404 | good_move = better_move; 405 | better_move = best_move; 406 | best_move = move_array[depth][i]; 407 | } 408 | } 409 | 410 | if (a >= beta) { 411 | save_hash_table(a, depth, HASH_BETA, move_array[depth][i]); 412 | save_history(move_array[depth][i], depth); 413 | return a; 414 | } 415 | 416 | b = a + 1; 417 | } 418 | 419 | if (best != -1) { 420 | save_history(move_array[depth][best], depth); 421 | } 422 | 423 | if (eval_is_exact) 424 | save_hash_table(a, depth, HASH_EXACT, hash_move); 425 | else 426 | save_hash_table(a, depth, HASH_ALPHA, hash_move); 427 | 428 | return a; 429 | } 430 | 431 | #if 0 432 | static int principal_variation_search(int depth, int alpha, int beta) 433 | { 434 | int score, count, best; 435 | 436 | /* 叶子节点取估值 */ 437 | if (depth <= 0) { 438 | eval_node_count++; 439 | return evaluate(); 440 | } 441 | 442 | /* 产生下一步所有的可能的走法 */ 443 | count = gen_all_move(move_array[depth]); 444 | 445 | /* 产生第一个节点 */ 446 | make_move(&move_array[depth][0]); 447 | /* 使用全窗口搜索第一个节点 */ 448 | best = -principal_variation_search(depth-1, -beta, -alpha); 449 | unmake_move(&move_array[depth][0]); 450 | 451 | if (count != 0) { 452 | if (depth == max_depth) 453 | best_move = move_array[depth][0]; 454 | } 455 | 456 | int i; 457 | for (i = 1; i < count; i++) { 458 | /* 如果不能beta剪枝 */ 459 | if (best < beta) { 460 | if (best > alpha) 461 | alpha = best; 462 | 463 | /* 产生子节点 */ 464 | make_move(&move_array[depth][i]); 465 | 466 | /* 使用极窄窗口搜索 */ 467 | score = -principal_variation_search(depth-1, -alpha-1, -alpha); 468 | 469 | if (score > alpha && score < beta) { 470 | /* fail high. 重新搜索 */ 471 | best = -principal_variation_search(depth-1, -beta, -score); 472 | if (depth == max_depth) 473 | best_move = move_array[depth][i]; 474 | 475 | } else if (score > best) { 476 | /* 极窄窗口命中 */ 477 | best = score; 478 | if (depth == max_depth) 479 | best_move = move_array[depth][i]; 480 | } 481 | /* 撤销子节点 */ 482 | unmake_move(&move_array[depth][i]); 483 | } 484 | } 485 | 486 | return best; 487 | } 488 | #endif 489 | -------------------------------------------------------------------------------- /src/search.h: -------------------------------------------------------------------------------- 1 | #ifndef SEARCH_H 2 | #define SEARCH_H 3 | 4 | #include "base.h" 5 | #include "position.h" 6 | #include "genmoves.h" 7 | #include "movesort.h" 8 | #include "evaluate.h" 9 | #include "openbook.h" 10 | #include "hash.h" 11 | 12 | /* 每步最长时间 */ 13 | #define LONGEST_SEARCH_TIME 6000 14 | #define TIME_OVER -65432 15 | 16 | void think(int depth); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/ucci.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef __APPLE__ 4 | #include 5 | #else 6 | #include 7 | #endif 8 | #include "pipe.h" 9 | #include "ucci.h" 10 | 11 | char command_line_str[LINE_INPUT_MAX_CHAR]; 12 | 13 | static long coord_list[256]; 14 | 15 | #ifdef _WIN32 16 | 17 | #include "windows.h" 18 | 19 | static void idle() 20 | { 21 | Sleep(1); 22 | } 23 | 24 | #else 25 | 26 | #include "unistd.h" 27 | 28 | static void idle() 29 | { 30 | usleep(1000); 31 | } 32 | 33 | #endif 34 | 35 | static int read_digit(char *line_str, int max_value) 36 | { 37 | int value = 0; 38 | 39 | for ( ; ; ) { 40 | if (*line_str >= '0' && *line_str <= '9') { 41 | value *= 10; 42 | value += *line_str - '0'; 43 | line_str ++; 44 | 45 | if (value > max_value) { 46 | value = max_value; 47 | } 48 | 49 | } else { 50 | break; 51 | } 52 | } 53 | 54 | return value; 55 | } 56 | 57 | ucci_comm_enum boot_line() 58 | { 59 | char line_str[LINE_INPUT_MAX_CHAR]; 60 | 61 | open_pipe(); 62 | 63 | while (!line_input(line_str)) { 64 | idle(); 65 | } 66 | 67 | if (strcmp(line_str, "ucci") == 0) { 68 | return UCCI_COMM_UCCI; 69 | } else { 70 | return UCCI_COMM_NONE; 71 | } 72 | } 73 | 74 | ucci_comm_enum idle_line(ucci_comm_struct *ucs_command) 75 | { 76 | int i; 77 | char *line_str; 78 | ucci_comm_enum uce_return_value = UCCI_COMM_NONE; 79 | 80 | while (!line_input(command_line_str)) { 81 | idle(); 82 | } 83 | 84 | line_str = command_line_str; 85 | 86 | if (strcmp(line_str, "isready") == 0) { 87 | /* isready */ 88 | return UCCI_COMM_ISREADY; 89 | } else if (strncmp(line_str, "setoption ", 10) == 0) { 90 | /* setoption