def findbest(f, times): pos = tools.parseFEN(tools.FEN_INITIAL) searcher = sunfish.Searcher() print('Printing best move after seconds', times) print('-'*60) totalpoints = 0 totaltests = 0 for line in f: fen, opts = tools.parseEPD(line, opt_dict=True) if type(opts) != dict or ('am' not in opts and 'bm' not in opts): print("Line didn't have am/bm in opts", line, opts) continue pos = tools.parseFEN(fen) # am -> avoid move; bm -> best move am = tools.parseSAN(pos,opts['am']) if 'am' in opts else None bm = tools.parseSAN(pos,opts['bm']) if 'bm' in opts else None print('Looking for am/bm', opts.get('am'), opts.get('bm')) points = 0 print(opts.get('id','unnamed'), end=' ', flush=True) for t in times: move, _ = searcher.search(pos, t) mark = tools.renderSAN(pos,move) if am and move != am or bm and move == bm: mark += '(1)' points += 1 else: mark += '(0)' print(mark, end=' ', flush=True) totaltests += 1 print(points) totalpoints += points print('-'*60) print('Total Points: %d/%d', totalpoints, totaltests)
def findbest(f, times): pos = tools.parseFEN(tools.FEN_INITIAL) searcher = sunfish.Searcher() print('Printing best move after seconds', times) print('-' * 60) totalpoints = 0 totaltests = 0 for line in f: fen, opts = tools.parseEPD(line, opt_dict=True) if type(opts) != dict or ('am' not in opts and 'bm' not in opts): print("Line didn't have am/bm in opts", line, opts) continue pos = tools.parseFEN(fen) # am -> avoid move; bm -> best move am = tools.parseSAN(pos, opts['am']) if 'am' in opts else None bm = tools.parseSAN(pos, opts['bm']) if 'bm' in opts else None print('Looking for am/bm', opts.get('am'), opts.get('bm')) points = 0 print(opts.get('id', 'unnamed'), end=' ', flush=True) for t in times: move, _, _ = tools.search(searcher, pos, t) mark = tools.renderSAN(pos, move) if am and move != am or bm and move == bm: mark += '(1)' points += 1 else: mark += '(0)' print(mark, end=' ', flush=True) totaltests += 1 print(points) totalpoints += points print('-' * 60) print('Total Points: %d/%d', totalpoints, totaltests)
def allperft(f, depth=4, verbose=True): import gc lines = f.readlines() for d in range(1, depth+1): if verbose: print("Going to depth {}/{}".format(d, depth)) for line in lines: parts = line.split(';') if verbose: print(parts[0]) pos, score = tools.parseFEN(parts[0]), int(parts[d]) res = sum(1 for _ in tools.collect_tree_depth(tools.expand_position(pos), d)) if res != score: print('=========================================') print('ERROR at depth %d. Gave %d rather than %d' % (d, res, score)) print('=========================================') print(tools.renderFEN(pos,0)) for move in pos.gen_moves(): split = sum(1 for _ in tools.collect_tree_depth(tools.expand_position(pos.move(move)),1)) print('{}: {}'.format(tools.mrender(pos, move), split)) return False if verbose: print('') return True
def allperft(f, depth=4, verbose=True): import gc lines = f.readlines() for d in range(1, depth+1): if verbose: print("Going to depth {}/{}".format(d, depth)) for line in lines: parts = line.split(';') if len(parts) <= d: continue if verbose: print(parts[0]) pos, score = tools.parseFEN(parts[0]), int(parts[d]) res = sum(1 for _ in tools.collect_tree_depth(tools.expand_position(pos), d)) if res != score: print('=========================================') print('ERROR at depth %d. Gave %d rather than %d' % (d, res, score)) print('=========================================') print(tools.renderFEN(pos,0)) for move in pos.gen_moves(): split = sum(1 for _ in tools.collect_tree_depth(tools.expand_position(pos.move(move)),1)) print('{}: {}'.format(tools.mrender(pos, move), split)) return False if verbose: print('') return True
def test_selfplay(self): pos = tools.parseFEN(tools.FEN_INITIAL) for d in range(200): m, score = sunfish.Searcher().search(pos, .1) if m is None: self.assertTrue(score == 0 or abs(score) >= sunfish.MATE_LOWER) break pos = pos.move(m)
def __init__(self): # print name of chess engine self.pos = tools.parseFEN('2ba3r1/5k3/b8/1n3N2C/4p4/3R2R2/9/5p1r1/4p4/5K3 w moves d4d8 d9e8 f6d7 b6d7 g4f4 d7f6 f4f6')#tools.FEN_INITIAL) self.searcher = Searcher() self.forced = False self.our_time, self.opp_time = 1000, 1000 # time in centi-seconds self.stack = [] print('Moonfish')
def test_fen(self): fen_file = os.path.join(os.path.dirname(__file__), 'tests/chessathome_openings.fen') for fen in open(fen_file): fen = fen.strip() pos = tools.parseFEN(fen) fen1 = tools.renderFEN(pos) self.assertEqual(fen, fen1, "Sunfish didn't correctly reproduce the FEN." + repr(pos))
def test_selfplay(self): pos = tools.parseFEN(tools.FEN_INITIAL) for d in range(200): m, score, _ = tools.search(sunfish.Searcher(), pos, .1) if m is None: self.assertTrue(score == 0 or abs(score) >= sunfish.MATE_LOWER) break pos = pos.move(m)
def test_fen(self): fen_file = os.path.join(os.path.dirname(__file__), 'tests/chessathome_openings.fen') for fen in open(fen_file): fen = fen.strip() pos = tools.parseFEN(fen) fen1 = tools.renderFEN(pos) self.assertEqual( fen, fen1, "Sunfish didn't correctly reproduce the FEN." + repr(pos))
def main(): # test fen pos = tools.parseFEN(tools.FEN_INITIAL) assert (pos.board == elephantfish.initial) fen = tools.renderFEN(pos) assert (fen == tools.FEN_INITIAL) benchmark(5, 6) self_arena("elephantfish", "algorithms.elephantfish_improve", 100, 20, .1)
def allmate(path): with open(path) as f: for line in f: line = line.strip() print(line) pos = tools.parseFEN(line) _, score, _ = tools.search(sunfish.Searcher(), pos, secs=3600) if score < sunfish.MATE_LOWER: print("Unable to find mate. Only got score = %d" % score) break
def allmate(path): with open(path) as f: for line in f: line = line.strip() print(line) pos = tools.parseFEN(line) _, score = sunfish.Searcher().search(pos, secs=3600) if score < sunfish.MATE_LOWER: print("Unable to find mate. Only got score = %d" % score) break
def setUp(self): # We don't bother about closing files, since they are just part of the test warnings.simplefilter("ignore", ResourceWarning) self.perft_file = os.path.join(os.path.dirname(__file__), 'tests/queen.fen') test_trees = [ tools.expand_position(tools.parseFEN(tools.parseEPD(line)[0])) for line in open(self.perft_file) ] self.positions = list( itertools.chain( *[tools.flatten_tree(tree, depth=2) for tree in test_trees]))
def selfplay(secs=1): """ Start a game amwafish vs. amwafish """ pos = tools.parseFEN(tools.FEN_INITIAL) for d in range(200): # Always print the board from the same direction board = pos.board if d % 2 == 0 else pos.rotate().board print(' '.join(board)) m, _, _ = tools.search(amwafish.Searcher(), pos, secs) if m is None: print("Game over") break print("\nmove", tools.mrender(pos, m)) pos = pos.move(m)
def selfplay(secs=1): """ Start a game sunfish vs. sunfish """ pos = tools.parseFEN(tools.FEN_INITIAL) for d in range(200): # Always print the board from the same direction board = pos.board if d % 2 == 0 else pos.rotate().board print(' '.join(board)) m, _ = sunfish.Searcher().search(pos, secs) if m is None: print("Game over") break print("\nmove", tools.mrender(pos, m)) pos = pos.move(m)
def test_san(self): pgn_file = os.path.join(os.path.dirname(__file__), 'tests/pgns.pgn') for line in open(pgn_file): msans = [msan for i, msan in enumerate(line.split()[:-1]) if i%3] pos = tools.parseFEN(tools.FEN_INITIAL) for i, msan in enumerate(msans): move = tools.parseSAN(pos, msan) if re.search('=[BNR]', msan): # Sunfish doesn't support underpromotion break msan_back = tools.renderSAN(pos, move) self.assertEqual(msan_back, msan, "Sunfish didn't correctly reproduce the SAN move") pos = pos.move(move)
def test_fen2(self): initial = sunfish.Position(sunfish.initial, 0, (True,True), (True,True), 0, 0) for pos in tools.flatten_tree(tools.expand_position(initial),3): fen = tools.renderFEN(pos) self.assertEqual(fen.split()[1], 'wb'[tools.get_color(pos)], "Didn't read color correctly") pos1 = tools.parseFEN(fen) self.assertEqual(pos.board, pos1.board, "Sunfish didn't correctly reproduce the board") self.assertEqual(pos.wc, pos1.wc) self.assertEqual(pos.bc, pos1.bc) ep = pos.ep if not pos.board[pos.ep].isspace() else 0 ep1 = pos1.ep if not pos1.board[pos1.ep].isspace() else 0 kp = pos.kp if not pos.board[pos.kp].isspace() else 0 kp1 = pos1.kp if not pos1.board[pos1.kp].isspace() else 0 self.assertEqual(ep, ep1) self.assertEqual(kp, kp1)
def test_san(self): pgn_file = os.path.join(os.path.dirname(__file__), 'tests/pgns.pgn') for line in open(pgn_file): msans = [msan for i, msan in enumerate(line.split()[:-1]) if i % 3] pos = tools.parseFEN(tools.FEN_INITIAL) for i, msan in enumerate(msans): if re.search('=[BNR]', msan): # Sunfish doesn't support underpromotion break move = tools.parseSAN(pos, msan) msan_back = tools.renderSAN(pos, move) self.assertEqual( msan_back, msan, "Sunfish didn't correctly reproduce the SAN move") pos = pos.move(move)
def unstable(): secs = 1 unstables, total = 0, 0 path = os.path.join(os.path.dirname(__file__), 'tests/unstable_positions2') for line in open(path): pos = tools.parseFEN(line) searcher = sunfish.Searcher() start = time.time() for _ in searcher._search(pos): if searcher.was_unstable or time.time() - start > secs: break #list(zip(range(depth), searcher._search(pos))) total += 1 if searcher.was_unstable: #print('got one at depth', searcher.depth) unstables += 1 print('{} / {}, at depth {}'.format(unstables, total, searcher.depth))
def unstable(): secs = 1 unstables, total = 0, 0 path = os.path.join(os.path.dirname(__file__), 'tests/unstable_positions2') for line in open(path): pos = tools.parseFEN(line) searcher = sunfish.Searcher() start = time.time() for depth, _, _ in searcher.search(pos): if searcher.was_unstable or time.time() - start > secs: break #list(zip(range(depth), searcher._search(pos))) total += 1 if searcher.was_unstable: #print('got one at depth', searcher.depth) unstables += 1 print('{} / {}, at depth {}'.format(unstables, total, depth))
def test_fen2(self): initial = sunfish.Position(sunfish.initial, 0, (True, True), (True, True), 0, 0) for pos in tools.flatten_tree(tools.expand_position(initial), 3): fen = tools.renderFEN(pos) self.assertEqual(fen.split()[1], 'wb'[tools.get_color(pos)], "Didn't read color correctly") pos1 = tools.parseFEN(fen) self.assertEqual(pos.board, pos1.board, "Sunfish didn't correctly reproduce the board") self.assertEqual(pos.wc, pos1.wc) self.assertEqual(pos.bc, pos1.bc) ep = pos.ep if not pos.board[pos.ep].isspace() else 0 ep1 = pos1.ep if not pos1.board[pos1.ep].isspace() else 0 kp = pos.kp if not pos.board[pos.kp].isspace() else 0 kp1 = pos1.kp if not pos1.board[pos1.kp].isspace() else 0 self.assertEqual(ep, ep1) self.assertEqual(kp, kp1)
def quickmate(f, min_depth=1): """ Similar to allmate, but uses the `bound` function directly to only search for moves that will win us the game """ for line in f: line = line.strip() print(line) pos = tools.parseFEN(line) searcher = sunfish.Searcher() for d in range(min_depth, 99): score = searcher.bound(pos, sunfish.MATE_LOWER, d, root=True) if score >= sunfish.MATE_LOWER: #print(tools.pv(searcher, 0, pos)) break print('Score at depth {}: {}'.format(d, score)) else: print("Unable to find mate. Only got score = %d" % score) return print(tools.pv(searcher, pos, include_scores=False))
def benchmark(cnt=20, depth=3): path = os.path.join(os.path.dirname(__file__), 'tests/chessathome_openings.fen') random.seed(0) start = time.time() nodes = 0 for i, line in enumerate(random.sample(list(open(path)), cnt)): pos = tools.parseFEN(line) searcher = sunfish.Searcher() start1 = time.time() for _ in searcher._search(pos): speed = int(round(searcher.nodes/(time.time()-start1))) print('Benchmark: {}/{}, Depth: {}, Speed: {:,}N/s'.format( i+1, cnt, searcher.depth, speed), end='\r') sys.stdout.flush() if searcher.depth == depth: nodes += searcher.nodes break print() total_time = time.time() - start speed = int(round(nodes/total_time)) print('Total time: {}, Total nodes: {}, Average speed: {:,}N/s'.format( total_time, nodes, speed))
def quickdraw(f, depth): k, n = 0, 0 for line in f: line = line.strip() print(line) n += 1 pos = tools.parseFEN(line) searcher = sunfish.Searcher() for d in range(depth, 10): s0 = searcher.bound(pos, 0, d, root=True) s1 = searcher.bound(pos, 1, d, root=True) if s0 >= 0 and s1 < 1: k += 1 break else: print('depth {}, s0 {}, s1 {}'.format(d, s0, s1)) #print(d, s0, s1, tools.pv(0, pos)) else: print("Fail: Unable to find draw!") #return print(tools.pv(searcher, pos, False)) print('Found {}/{} draws'.format(k,n))
def quickdraw(f, depth): k, n = 0, 0 for line in f: line = line.strip() print(line) n += 1 pos = tools.parseFEN(line) searcher = sunfish.Searcher() for d in range(depth, 10): s0 = searcher.bound(pos, 0, d, root=True) s1 = searcher.bound(pos, 1, d, root=True) if s0 >= 0 and s1 < 1: k += 1 break else: print('depth {}, s0 {}, s1 {}'.format(d, s0, s1)) #print(d, s0, s1, tools.pv(0, pos)) else: print("Fail: Unable to find draw!") #return print(tools.pv(searcher, pos, False)) print('Found {}/{} draws'.format(k, n))
def benchmark(cnt=20, depth=3): path = os.path.join(os.path.dirname(__file__), 'tests/chessathome_openings.fen') random.seed(0) start = time.time() nodes = 0 for i, line in enumerate(random.sample(list(open(path)), cnt)): pos = tools.parseFEN(line) searcher = sunfish.Searcher() start1 = time.time() for search_depth, _, _ in searcher.search(pos): speed = int(round(searcher.nodes / (time.time() - start1))) print('Benchmark: {}/{}, Depth: {}, Speed: {:,}N/s'.format( i + 1, cnt, search_depth, speed), end='\r') sys.stdout.flush() if search_depth == depth: nodes += searcher.nodes break print() total_time = time.time() - start speed = int(round(nodes / total_time)) print('Total time: {}, Total nodes: {}, Average speed: {:,}N/s'.format( total_time, nodes, speed))
def generate_random_opening(): pos = tools.parseFEN(tools.FEN_INITIAL) steps = 6 for i in range(steps): pos = pos.move(random_move(pos)) return pos
def get_move(self, position = "rCbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/7C1/9/RNBAKABNR", player = "b", times = 1000, depth = 6, show_thinking = 1): smove = "position fen " + position + " " + player + " - - 0 1" params = smove.split(' ', 2) if params[1] == 'fen': fen = params[2] pos = tools.parseFEN(fen) color = RED if fen.split()[1] == 'w' else BLACK smove = "go time " + str(times) + " depth " + str(depth) if smove.startswith('go'): # default options # depth = depth movetime = -1 # parse parameters params = smove.split(' ') if len(params) == 1: return i = 0 while i < len(params): param = params[i] if param == 'depth': i += 1 depth = int(params[i]) if param == 'time': i += 1 movetime = int(params[i]) i += 1 forced = False moves_remain = 40 start = time.time() ponder = None for s_score in self.searcher._search(pos): moves = tools.pv(self.searcher, pos, include_scores=False) if show_thinking: entry = self.searcher.tp_score.get((pos, self.searcher.depth, True)) score = int(round((entry.lower + entry.upper)/2)) usedtime = int((time.time() - start) * 1000) moves_str = moves #if len(moves) < 15 else '' print('info depth {} score {} time {} nodes {} pv {}'.format(self.searcher.depth, score, usedtime, self.searcher.nodes, moves_str)) if len(moves) > 5: ponder = moves[1] #将军死和被将军死 才会出现MATE_UPPER的值 if (s_score >= MATE_UPPER) or (s_score <= -MATE_UPPER): break if movetime > 0 and (time.time() - start) * 1000 > movetime: break if self.searcher.depth >= depth: break entry = self.searcher.tp_score.get((pos, self.searcher.depth, True)) m, s = self.searcher.tp_move.get(pos), entry.lower # We only resign once we are mated.. That's never? if s == -MATE_UPPER: print('resign') else: moves = moves.split(' ') if show_thinking: if len(moves) > 1: print('bestmove ' + moves[0] + ' ponder ' + moves[1]) else: print('bestmove ' + moves[0]) return moves[0]
def main(): # parser = argparse.ArgumentParser() # parser.add_argument('module', help='play.py', type=str, default='play', nargs='?') # parser.add_argument('--tables', metavar='pst', help='alternative pst table', type=str, default=None) # args = parser.parse_args() # # engine = importlib.import_module(args.module) # if args.tables is not None: # pst_module = importlib.import_module(args.tables) # sunfish.pst = pst_module.pst # logging.basicConfig(filename='sunfish.log', level=logging.DEBUG) out = Unbuffered(sys.stdout) def output(line): print(line, file=out) logging.debug(line) board = tools.parseFEN(tools.FEN_INITIAL) e = engine.Engine() color = WHITE our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = True stack = [] while True: if stack: smove = stack.pop() else: smove = input() #logging.debug(f'>>> {smove} ') if smove == 'quit': break elif smove == 'uci': output('id name fisherman') output('id author Tyler Shen') output('uciok') elif smove == 'isready': output('readyok') elif smove == 'ucinewgame': stack.append('position fen ' + tools.FEN_INITIAL) # syntax specified in UCI # position [fen | startpos ] moves .... elif smove.startswith('position'): params = smove.split(' ') idx = smove.find('moves') if idx >= 0: moveslist = smove[idx:].split()[1:] else: moveslist = [] if params[1] == 'fen': if idx >= 0: fenpart = smove[:idx] else: fenpart = smove _, _, fen = fenpart.split(' ', 2) elif params[1] == 'startpos': fen = tools.FEN_INITIAL else: pass board = tools.parseFEN(fen) play.display_pos(board) color = WHITE if fen.split()[1] == 'w' else BLACK for move in moveslist: if color == WHITE: # WARNING! to_move Does not work properly board = board.move(tools.to_move(move)) else: board = board.move(tools.to_move(tools.flip_move(move))) color = 1 - color elif smove.startswith('go'): # default options depth = 1000 movetime = -1 _, *params = smove.split(' ') for param, val in zip(*2 * (iter(params), )): if param == 'depth': depth = int(val) if param == 'movetime': movetime = int(val) if param == 'wtime': our_time = int(val) if param == 'btime': opp_time = int(val) # moves_remain = 40 # # start = time.time() # ponder = None # for _move, _score in engine.search(board): # moves = tools.pv(searcher, pos, include_scores=False) # # if show_thinking: # node = engine.tp_score.get((pos, sdepth, True)) # score = int(round((node.lower + node.upper)/2)) # usedtime = int((time.time() - start) * 1000) # moves_str = moves if len(moves) < 15 else '' # output('info depth {} score cp {} time {} nodes {} pv {}'.format(sdepth, score, usedtime, searcher.nodes, moves_str)) # # if len(moves) > 5: # ponder = moves[1] # # if movetime > 0 and (time.time() - start) * 1000 > movetime: # break # # if (time.time() - start) * 1000 > our_time/moves_remain: # break # # if sdepth >= depth: # break move, score = e.search(board) # We only resign once we are mated.. That's never? if score == -eval.MATE_UPPER: output('resign') else: output('bestmove ' + move) # else: # moves = moves.split(' ') # if len(moves) > 1: # output(f'bestmove {moves[0]} ponder {moves[1]}') # else: # output('bestmove ' + moves[0]) elif smove.startswith('time'): our_time = int(smove.split()[1]) elif smove.startswith('otim'): opp_time = int(smove.split()[1]) else: pass
def main(): parser = argparse.ArgumentParser() parser.add_argument('module', help='sunfish.py file (without .py)', type=str, default='sunfish_nnue', nargs='?') parser.add_argument('--tables', metavar='pst', help='alternative pst table', type=str, default=None) args = parser.parse_args() sunfish = importlib.import_module(args.module) if args.tables is not None: pst_module = importlib.import_module(args.tables) sunfish.pst = pst_module.pst logging.basicConfig(filename='sunfish.log', level=logging.DEBUG) out = Unbuffered(sys.stdout) def output(line): print(line, file=out) logging.debug(line) pos = tools.parseFEN(tools.FEN_INITIAL) searcher = sunfish.Searcher() color = WHITE our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = True stack = [] while True: if stack: smove = stack.pop() else: smove = input() logging.debug(f'>>> {smove} ') if smove == 'quit': break elif smove == 'uci': output('id name Sunfish') output('id author Thomas Ahle & Contributors') output('uciok') elif smove == 'isready': output('readyok') elif smove == 'ucinewgame': stack.append('position fen ' + tools.FEN_INITIAL) # syntax specified in UCI # position [fen | startpos ] moves .... elif smove.startswith('position'): params = smove.split(' ') idx = smove.find('moves') if idx >= 0: moveslist = smove[idx:].split()[1:] else: moveslist = [] if params[1] == 'fen': if idx >= 0: fenpart = smove[:idx] else: fenpart = smove _, _, fen = fenpart.split(' ', 2) elif params[1] == 'startpos': fen = tools.FEN_INITIAL else: pass pos = tools.parseFEN(fen) color = WHITE if fen.split()[1] == 'w' else BLACK for move in moveslist: pos = pos.move(tools.mparse(color, move)) color = 1 - color elif smove.startswith('go'): # default options depth = 1000 movetime = -1 _, *params = smove.split(' ') for param, val in zip(*2 * (iter(params), )): if param == 'depth': depth = int(val) if param == 'movetime': movetime = int(val) if param == 'wtime': our_time = int(val) if param == 'btime': opp_time = int(val) moves_remain = 40 start = time.time() ponder = None for sdepth, _move, _score in searcher.search(pos): moves = tools.pv(searcher, pos, include_scores=False) if show_thinking: entry = searcher.tp_score.get((pos, sdepth, True)) score = int(round((entry.lower + entry.upper) / 2)) usedtime = int((time.time() - start) * 1000) moves_str = moves if len(moves) < 15 else '' output('info depth {} score cp {} time {} nodes {} pv {}'. format(sdepth, score, usedtime, searcher.nodes, moves_str)) if len(moves) > 5: ponder = moves[1] if movetime > 0 and (time.time() - start) * 1000 > movetime: break if (time.time() - start) * 1000 > our_time / moves_remain: break if sdepth >= depth: break entry = searcher.tp_score.get((pos, sdepth, True)) m, s = searcher.tp_move.get(pos), entry.lower # We only resign once we are mated.. That's never? # if s == -sunfish.MATE_UPPER: # output('resign') # else: moves = moves.split(' ') if len(moves) > 1: output(f'bestmove {moves[0]} ponder {moves[1]}') else: output('bestmove ' + moves[0]) elif smove.startswith('time'): our_time = int(smove.split()[1]) elif smove.startswith('otim'): opp_time = int(smove.split()[1]) else: pass
def setUp(self): # We don't bother about closing files, since they are just part of the test warnings.simplefilter("ignore", ResourceWarning) self.perft_file = os.path.join(os.path.dirname(__file__), 'tests/queen.fen') test_trees = [tools.expand_position(tools.parseFEN(tools.parseEPD(line)[0])) for line in open(self.perft_file)] self.positions = list(itertools.chain(*[tools.flatten_tree(tree, depth=2) for tree in test_trees]))
def main(): pos = tools.parseFEN(tools.FEN_INITIAL) searcher = sunfish.Searcher() forced = False color = WHITE our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = False stack = [] while True: if stack: smove = stack.pop() else: smove = input() if smove == 'quit': break elif smove == 'protover 2': print('feature done=0') print('feature myname="Sunfish"') print('feature usermove=1') print('feature setboard=1') print('feature ping=1') print('feature sigint=0') print('feature variants="normal"') print('feature done=1') elif smove == 'new': stack.append('setboard ' + tools.FEN_INITIAL) elif smove.startswith('setboard'): _, fen = smove.split(' ', 1) pos = tools.parseFEN(fen) color = WHITE if fen.split()[1] == 'w' else BLACK elif smove == 'force': forced = True elif smove == 'go': forced = False moves_remain = 40 use = our_time / moves_remain # Let's follow the clock of our opponent if our_time >= 100 and opp_time >= 100: use *= our_time / opp_time start = time.time() for _ in searcher._search(pos): if show_thinking: ply = searcher.depth entry = searcher.tp_score.get((pos, ply, True)) score = int(round((entry.lower + entry.upper) / 2)) dual_score = '{}:{}'.format(entry.lower, entry.upper) used = int((time.time() - start) * 100 + .5) moves = tools.pv(searcher, pos, include_scores=False) print('{:>3} {:>8} {:>8} {:>13} \t{}'.format( ply, score, used, searcher.nodes, moves)) if time.time() - start > use / 100: break entry = searcher.tp_score.get((pos, searcher.depth, True)) m, s = searcher.tp_move.get(pos), entry.lower # We only resign once we are mated.. That's never? if s == -sunfish.MATE_UPPER: print('resign') else: print('move', tools.mrender(pos, m)) pos = pos.move(m) color = 1 - color elif smove.startswith('ping'): _, N = smove.split() print('pong', N) elif smove.startswith('usermove'): _, smove = smove.split() m = tools.mparse(color, smove) pos = pos.move(m) color = 1 - color if not forced: stack.append('go') elif smove.startswith('time'): our_time = int(smove.split()[1]) elif smove.startswith('otim'): opp_time = int(smove.split()[1]) elif smove.startswith('perft'): start = time.time() for d in range(1, 10): res = sum(1 for _ in tools.collect_tree_depth( tools.expand_position(pos), d)) print('{:>8} {:>8}'.format(res, time.time() - start)) elif smove.startswith('post'): show_thinking = True elif smove.startswith('nopost'): show_thinking = False elif any( smove.startswith(x) for x in ('xboard', 'random', 'hard', 'accepted', 'level')): pass else: print("Error (unkown command):", smove)
def main(): parser = argparse.ArgumentParser() parser.add_argument('module', help='sunfish.py file (without .py)', type=str, default='sunfish', nargs='?') parser.add_argument('--tables', metavar='pst', help='alternative pst table', type=str, default=None) args = parser.parse_args() sunfish = importlib.import_module(args.module) if args.tables is not None: pst_module = importlib.import_module(args.tables) sunfish.pst = pst_module.pst sunfish.QS_LIMIT = pst_module.QS_LIMIT sunfish.EVAL_ROUGHNESS = pst_module.EVAL_ROUGHNESS sys.stdout = tools.Unbuffered(sys.stdout) now = datetime.now() path = 'sunfish-' + now.strftime("%d:%m:%Y-%H:%M:%S:%f") + '.log' sys.stderr = open(path, 'a') pos = tools.parseFEN(tools.FEN_INITIAL) searcher = sunfish.Searcher() forced = False color = WHITE our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = False options = {} history = [] stack = [] while True: if stack: smove = stack.pop() else: smove = input() print('>>>', smove, file=sys.stderr) sys.stderr.flush() # For Python 2.7 support if smove == 'quit': break elif smove == 'protover 2': print('feature done=0') print('feature myname="Sunfish"') print('feature usermove=1') print('feature setboard=0' ) # Removing setboard because of lichess bug print('feature ping=1') print('feature sigint=0') print('feature nps=0') print('feature variants="normal"') print('feature option="qs_limit -spin {} -100 1000"'.format( sunfish.QS_LIMIT)) print('feature option="eval_roughness -spin {} 1 1000"'.format( sunfish.EVAL_ROUGHNESS)) print('feature option="draw_test -spin {} 0 1"'.format( int(sunfish.DRAW_TEST))) print('feature done=1') elif smove == 'new': stack.append('setboard ' + tools.FEN_INITIAL) # Clear out the old searcher, including the tables searcher = sunfish.Searcher() del history[:] elif smove.startswith('setboard'): _, fen = smove.split(' ', 1) pos = tools.parseFEN(fen) color = WHITE if fen.split()[1] == 'w' else BLACK del history[:] elif smove == 'force': forced = True elif smove.startswith('option'): _, aeqb = smove.split(maxsplit=1) if '=' in aeqb: name, val = aeqb.split('=') else: name, val = aeqb, True if name == 'qs_limit': sunfish.QS_LIMIT = int(val) if name == 'eval_roughness': sunfish.EVAL_ROUGHNESS = int(val) if name == 'draw_test': sunfish.DRAW_TEST = bool(int(val)) options[name] = val elif smove == 'go': forced = False moves_remain = 40 use = our_time / moves_remain # Let's follow the clock of our opponent if our_time >= 100 and opp_time >= 100: use *= our_time / opp_time start = time.time() for ply, move, score in searcher.search(pos, history): entry = searcher.tp_score.get((pos, ply, True)) score = int(round((entry.lower + entry.upper) / 2)) if show_thinking: seconds = time.time() - start used_ms = int(seconds * 100 + .5) moves = tools.pv(searcher, pos, include_scores=False) print('{:>3} {:>8} {:>8} {:>13} \t{}'.format( ply, score, used_ms, searcher.nodes, moves)) print('# {} n/s'.format(round(searcher.nodes / seconds))) print('# Hashfull: {:.3f}%; {} <= score < {}'.format( len(searcher.tp_score) / sunfish.TABLE_SIZE * 100, entry.lower, entry.upper)) # If found mate, just stop if entry.lower >= sunfish.MATE_UPPER: break if time.time() - start > use / 100: break # We sometimes make illegal moves when we're losing, # so it's safer to just resign. if score == -sunfish.MATE_UPPER: print('resign') else: print('move', tools.mrender(pos, move)) pos = pos.move(move) history.append(pos) color = 1 - color elif smove.startswith('ping'): _, N = smove.split() print('pong', N) elif smove.startswith('usermove'): _, smove = smove.split() m = tools.mparse(color, smove) pos = pos.move(m) history.append(pos) color = 1 - color if not forced: stack.append('go') elif smove.startswith('time'): our_time = int(smove.split()[1]) elif smove.startswith('otim'): opp_time = int(smove.split()[1]) elif smove.startswith('perft'): start = time.time() for d in range(1, 10): res = sum(1 for _ in tools.collect_tree_depth( tools.expand_position(pos), d)) print('{:>8} {:>8}'.format(res, time.time() - start)) elif smove.startswith('post'): show_thinking = True elif smove.startswith('nopost'): show_thinking = False elif any( smove.startswith(x) for x in ('xboard', 'random', 'hard', 'accepted', 'level', 'easy', 'st', 'result', '?', 'name', 'rating')): print('# Ignoring command {}.'.format(smove)) elif smove.startswith('reject'): _, feature = smove.split( )[:2] # split(maxsplit=2) doesnt work in python2.7 if feature == 'sigint': signal.signal(signal.SIGINT, signal.SIG_IGN) print( '# Warning ({} rejected): Might not work as expected.'.format( feature)) else: print('# Warning (unkown command): {}. Treating as move.'.format( smove)) stack.append('usermove {}'.format(smove))
def main(eng): parser = argparse.ArgumentParser() parser.add_argument('module', help='chessEngine.py file (without .py)', type=str, default='crapbox', nargs='?') args = parser.parse_args() ''' look at this later sunfish = importlib.import_module(args.module) if args.tables is not None: pst_module = importlib.import_module(args.tables) sunfish.pst = pst_module.pst logging.basicConfig(filename='sunfish.log', level=logging.DEBUG) ''' out = Unbuffered(sys.stdout) def output(line): print(line) #print(line, file=out) #logging.debug(line) pos = tools.parseFEN(tools.FEN_INITIAL) ''' searcher = sunfish.Searcher() ''' color = WHITE our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = False stack = [] while True: if stack: smove = stack.pop() else: smove = input() logging.debug(f'>>> {smove} ') if smove == 'quit': break elif smove == 'uci': output('id name Cristiceps') output('id author Benjamin Christie') output('uciok') elif smove == 'isready': output('readyok') elif smove == 'ucinewgame': stack.append('position fen ' + tools.FEN_INITIAL) # syntax specified in UCI # position [fen | startpos ] moves .... elif smove.startswith('position'): params = smove.split(' ') idx = smove.find('moves') if idx >= 0: moveslist = smove[idx:].split()[1:] else: moveslist = [] if params[1] == 'fen': if idx >= 0: fenpart = smove[:idx] else: fenpart = smove _, _, fen = fenpart.split(' ', 2) elif params[1] == 'startpos': fen = tools.FEN_INITIAL else: pass pos = tools.parseFEN(fen) color = WHITE if fen.split()[1] == 'w' else BLACK for move in moveslist: pos = pos.move(tools.mparse(color, move)) color = 1 - color elif smove.startswith('go'): # default options depth = 1000 movetime = -1 _, *params = smove.split(' ') for param, val in zip(*2 * (iter(params), )): if param == 'depth': depth = int(val) if param == 'movetime': movetime = int(val) if param == 'wtime': our_time = int(val) if param == 'btime': opp_time = int(val) moves_remain = 40 start = time.time() ponder = None renderedFen = tools.renderFEN(pos) ''' eng = customEngine.Engine() currMove = str(eng.getRandomMove(renderedFen)) ''' currMove = str(eng.getMove(renderedFen)) output("info currmove " + currMove) time.sleep(0.1) output("bestmove " + currMove) ''' if sdepth >= depth: break ''' elif smove.startswith('stop'): output("bestmove " + currMove) elif smove.startswith('time'): our_time = int(smove.split()[1]) elif smove.startswith('otim'): opp_time = int(smove.split()[1]) else: pass
def main(): logging.basicConfig(filename='sunfish.log', level=logging.DEBUG) pos = tools.parseFEN(tools.FEN_INITIAL) searcher = sunfish.Searcher() forced = False color = WHITE our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = True # print name of chess engine print('Sunfish') stack = [] while True: if stack: smove = stack.pop() else: smove = input() logging.debug(smove) if smove == 'quit': break elif smove == 'uci': print('id name Sunfish') print('id author Thomas Ahle') print('uciok') elif smove == 'isready': print('readyok') elif smove == 'ucinewgame': stack.append('position fen ' + tools.FEN_INITIAL) elif smove.startswith('position fen'): params = smove.split(' ', 2) if params[1] == 'fen': fen = params[2] pos = tools.parseFEN(fen) color = WHITE if fen.split()[1] == 'w' else BLACK elif smove.startswith('position startpos'): params = smove.split(' ') #startpos pos = tools.parseFEN(tools.FEN_INITIAL) color = WHITE i = 0 while i < len(params): param = params[i] if param == 'moves': i += 1 while i < len(params): mymove = params[i] logging.debug(mymove) m = tools.mparse(color, mymove) logging.debug(m) pos = pos.move(m) color = 1 - color i += 1 i += 1 elif smove.startswith('go'): # default options depth = 1000 movetime = -1 # parse parameters params = smove.split(' ') if len(params) == 1: continue i = 0 while i < len(params): param = params[i] if param == 'depth': i += 1 depth = int(params[i]) if param == 'movetime': i += 1 movetime = int(params[i]) if param == 'wtime': i += 1 our_time = int(params[i]) if param == 'btime': i += 1 opp_time = int(params[i]) i += 1 forced = False moves_remain = 40 start = time.time() ponder = None for _ in searcher._search(pos): moves = tools.pv(searcher, pos, include_scores=False) if show_thinking: entry = searcher.tp_score.get((pos, searcher.depth, True)) score = int(round((entry.lower + entry.upper) / 2)) usedtime = int((time.time() - start) * 1000) moves_str = moves if len(moves) < 15 else '' print('info depth {} score cp {} time {} nodes {} pv {}'. format(searcher.depth, score, usedtime, searcher.nodes, moves_str)) logging.debug( 'info depth {} score cp {} time {} nodes {} pv {}'. format(searcher.depth, score, usedtime, searcher.nodes, moves_str)) if len(moves) > 5: ponder = moves[1] if movetime > 0 and (time.time() - start) * 1000 > movetime: break if (time.time() - start) * 1000 > our_time / moves_remain: break if searcher.depth >= depth: break entry = searcher.tp_score.get((pos, searcher.depth, True)) m, s = searcher.tp_move.get(pos), entry.lower # We only resign once we are mated.. That's never? if s == -sunfish.MATE_UPPER: print('resign') logging.debug('resign') else: moves = moves.split(' ') if len(moves) > 1: #print('bestmove ' + moves[0] + ' ponder ' + moves[1]) print('bestmove ' + moves[0]) logging.debug('bestmove ' + moves[0]) else: print('bestmove ' + moves[0]) logging.debug('bestmove ' + moves[0]) elif smove.startswith('time'): our_time = int(smove.split()[1]) elif smove.startswith('otim'): opp_time = int(smove.split()[1]) else: pass
def main(): pos = tools.parseFEN(tools.FEN_INITIAL) searcher = sunfish.Searcher() forced = False color = WHITE our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = False stack = [] while True: if stack: smove = stack.pop() else: smove = input() if smove == 'quit': break elif smove == 'protover 2': print('feature done=0') print('feature myname="Sunfish"') print('feature usermove=1') print('feature setboard=1') print('feature ping=1') print('feature sigint=0') print('feature variants="normal"') print('feature done=1') elif smove == 'new': stack.append('setboard ' + tools.FEN_INITIAL) elif smove.startswith('setboard'): _, fen = smove.split(' ', 1) pos = tools.parseFEN(fen) color = WHITE if fen.split()[1] == 'w' else BLACK elif smove == 'force': forced = True elif smove == 'go': forced = False moves_remain = 40 use = our_time/moves_remain # Let's follow the clock of our opponent if our_time >= 100 and opp_time >= 100: use *= our_time/opp_time start = time.time() for _ in searcher._search(pos): if show_thinking: ply = searcher.depth entry = searcher.tp_score.get((pos, ply, True)) score = int(round((entry.lower + entry.upper)/2)) dual_score = '{}:{}'.format(entry.lower, entry.upper) used = int((time.time() - start)*100 + .5) moves = tools.pv(searcher, pos, include_scores=False) print('{:>3} {:>8} {:>8} {:>13} \t{}'.format( ply, score, used, searcher.nodes, moves)) if time.time() - start > use/100: break entry = searcher.tp_score.get((pos, searcher.depth, True)) m, s = searcher.tp_move.get(pos), entry.lower # We only resign once we are mated.. That's never? if s == -sunfish.MATE_UPPER: print('resign') else: print('move', tools.mrender(pos, m)) pos = pos.move(m) color = 1-color elif smove.startswith('ping'): _, N = smove.split() print('pong', N) elif smove.startswith('usermove'): _, smove = smove.split() m = tools.mparse(color, smove) pos = pos.move(m) color = 1-color if not forced: stack.append('go') elif smove.startswith('time'): our_time = int(smove.split()[1]) elif smove.startswith('otim'): opp_time = int(smove.split()[1]) elif smove.startswith('perft'): start = time.time() for d in range(1,10): res = sum(1 for _ in tools.collect_tree_depth(tools.expand_position(pos), d)) print('{:>8} {:>8}'.format(res, time.time()-start)) elif smove.startswith('post'): show_thinking = True elif smove.startswith('nopost'): show_thinking = False elif any(smove.startswith(x) for x in ('xboard','random','hard','accepted','level')): pass else: print("Error (unkown command):", smove)
def play(version1_version2_secs_plus_fen): ''' returns 1 if fish1 won, 0 for draw and -1 otherwise ''' version1, version2, secs, plus, fen = version1_version2_secs_plus_fen modules = [importlib.import_module(version1), importlib.import_module(version2)] searchers = [] for module in modules: if hasattr(module, 'Searcher'): searchers.append(module.Searcher()) else: searchers.append(module) times = [secs, secs] efactor = [1, 1] pos = tools.parseFEN(fen) seen = set() for d in range(200): moves_remain = 30 use = times[d%2]/moves_remain + plus # Use a bit more time, if we have more on the clock than our opponent use += (times[d%2] - times[(d+1)%2])/10 use = max(use, plus) t = time.time() m, score, depth = tools.search(searchers[d%2], pos, use*efactor[d%2]) efactor[d%2] *= (use/(time.time() - t))**.5 times[d%2] -= time.time() - t times[d%2] += plus #print('Used {:.2} rather than {:.2}. Off by {:.2}. Remaining: {}' #.format(time.time()-t, use, (time.time()-t)/use, times[d%2])) if times[d%2] < 0: print('{} ran out of time'.format(version2 if d%2 == 1 else version1)) return version1 if d%2 == 1 else version2 pass if m is None: print('Game not done, but no move? Score', score) name = version1 if d%2 == 0 else version2 print(version1, tools.renderFEN(pos)) assert False # Test move is_dead = lambda pos: any(pos.value(m) >= amwafish.MATE_LOWER for m in pos.gen_moves()) if is_dead(pos.move(m)): name = version1 if d%2 == 0 else version2 print('{} made an illegal move {} in position {}. Depth {}, Score {}'. format(name, tools.mrender(pos,m), tools.renderFEN(pos), depth, score)) return version2 if d%2 == 0 else version1 #assert False # Make the move pos = pos.move(m) # Test repetition draws # This is by far the most common type of draw if pos in seen: #print('Rep time at end', times) return None seen.add(pos) any_moves = not all(is_dead(pos.move(m)) for m in pos.gen_moves()) in_check = is_dead(pos.nullmove()) if not any_moves: if not in_check: # This is actually a bit interesting. Why would we ever throw away a win like this? name = version1 if d%2 == 0 else version2 print('{} stalemated? depth {} {}'.format( name, depth, tools.renderFEN(pos))) if score != 0: print('it got the wrong score: {} != 0'.format(score)) return None else: name = version1 if d%2 == 0 else version2 if score < amwafish.MATE_LOWER: print('{} mated, but did not realize. Only scored {} in position {}, depth {}'.format(name, score, tools.renderFEN(pos), depth)) return name print('Game too long', tools.renderFEN(pos)) return None
def parseFen(fen): return tools.parseFEN(fen)
def main(): pos = tools.parseFEN(tools.FEN_INITIAL) searcher = sunfish.Searcher() forced = False color = WHITE our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = True # print name of chess engine print('Sunfish') while True: smove = input() if smove == 'quit' or smove == 'stop': break elif smove == 'uci': print('id name Sunfish') print('uciok') elif smove == 'isready': print('readyok') elif smove == 'ucinewgame': pos = tools.parseFEN(tools.FEN_INITIAL) searcher = sunfish.Searcher() forced = False color = WHITE our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = True elif smove.startswith('position'): params = smove.split(' ', 2) if params[1] == 'fen': fen = params[2] pos = tools.parseFEN(fen) color = WHITE if fen.split()[1] == 'w' else BLACK else: params = smove.split(' ') if len(params) == 1: continue i = 1 while i < len(params): param = params[i] if param == 'startpos': pos = tools.parseFEN(tools.FEN_INITIAL) color = WHITE elif param == 'moves': i += 1 # rest are moves while i < len(params): m0 = params[i] m = tools.mparse(color, m0) pos = pos.move(m) color = 1 - color i += 1 i += 1 elif smove.startswith('go'): # default options depth = 1000 movetime = -1 moves_remain = 40 tc = 0 # parse parameters params = smove.split(' ') if len(params) == 1: continue i = 0 while i < len(params): param = params[i] if param == 'depth': i += 1 depth = int(params[i]) elif param == 'movetime': i += 1 movetime = int(params[i]) elif param == 'wtime': i += 1 if color == WHITE: movetime = int(params[i]) tc = 1 elif param == 'btime': i += 1 if color == BLACK: movetime = int(params[i]) tc = 1 elif param == 'movestogo': i += 1 moves_remain = int(params[i]) i += 1 if tc == 1 and moves_remain > 0: movetime = movetime / moves_remain forced = False start = time.time() ponder = None for _ in searcher._search(pos): moves = tools.pv(searcher, pos, include_scores=False) if show_thinking: entry = searcher.tp_score.get((pos, searcher.depth, True)) score = int(round((entry.lower + entry.upper) / 2)) usedtime = int((time.time() - start) * 1000) moves_str = moves if len(moves) < 15 else '' print('info depth {} score {} time {} nodes {} {}'.format( searcher.depth, score, usedtime, searcher.nodes, moves_str)) if len(moves) > 5: ponder = moves[1] if movetime > 0 and (time.time() - start) * 1000 > movetime: break if searcher.depth >= depth: break entry = searcher.tp_score.get((pos, searcher.depth, True)) m, s = searcher.tp_move.get(pos), entry.lower moves = moves.split(' ') if len(moves) > 1: print('bestmove ' + moves[0] + ' ponder ' + moves[1]) else: print('bestmove ' + moves[0]) elif smove.startswith('time'): our_time = int(smove.split()[1]) elif smove.startswith('otim'): opp_time = int(smove.split()[1]) else: pass
def play(version1_version2_secs_plus_fen): ''' returns 1 if fish1 won, 0 for draw and -1 otherwise ''' version1, version2, secs, plus, fen = version1_version2_secs_plus_fen modules = [importlib.import_module(version1), importlib.import_module(version2)] searchers = [] for module in modules: if hasattr(module, 'Searcher'): searchers.append(module.Searcher()) else: searchers.append(module) times = [secs, secs] efactor = [1, 1] pos = tools.parseFEN(fen) seen = set() for d in range(200): moves_remain = 30 use = times[d%2]/moves_remain + plus # Use a bit more time, if we have more on the clock than our opponent use += (times[d%2] - times[(d+1)%2])/10 use = max(use, plus) t = time.time() m, score = searchers[d%2].search(pos, use*efactor[d%2]) efactor[d%2] *= (use/(time.time() - t))**.5 times[d%2] -= time.time() - t times[d%2] += plus #print('Used {:.2} rather than {:.2}. Off by {:.2}. Remaining: {}' #.format(time.time()-t, use, (time.time()-t)/use, times[d%2])) if times[d%2] < 0: print('{} ran out of time'.format(version2 if d%2 == 1 else version1)) return version1 if d%2 == 1 else version2 pass if m is None: print('Game not done, but no move? Score', score) name = version1 if d%2 == 0 else version2 print(version1, tools.renderFEN(pos)) assert False # Test move is_dead = lambda pos: any(pos.value(m) >= sunfish.MATE_LOWER for m in pos.gen_moves()) if is_dead(pos.move(m)): name = version1 if d%2 == 0 else version2 print('{} made an illegal move {} in position {}. Depth {}, Score {}'. format(name, tools.mrender(pos,m), tools.renderFEN(pos), searchers[d%2].depth, score)) assert False # Make the move pos = pos.move(m) # Test repetition draws # This is by far the most common type of draw if pos in seen: #print('Rep time at end', times) return None seen.add(pos) any_moves = not all(is_dead(pos.move(m)) for m in pos.gen_moves()) in_check = is_dead(pos.nullmove()) if not any_moves: if not in_check: # This is actually a bit interesting. Why would we ever throw away a win like this? name = version1 if d%2 == 0 else version2 print('{} stalemated? depth {} {}'.format( name, searchers[d%2].depth, tools.renderFEN(pos))) if score != 0: print('it got the wrong score: {} != 0'.format(score)) return None else: name = version1 if d%2 == 0 else version2 if score < sunfish.MATE_LOWER: print('{} mated, but did not realize. Only scored {} in position {}, depth {}'.format(name, score, tools.renderFEN(pos), searchers[d%2].depth)) return name print('Game too long', tools.renderFEN(pos)) return None
def positionInitial(): return tools.parseFEN(tools.FEN_INITIAL)
def main(): logging.basicConfig(filename='sunfish.log', level=logging.DEBUG) out = Unbuffered(sys.stdout) def output(line): print(line, file=out) logging.debug(line) pos = tools.parseFEN(tools.FEN_INITIAL) searcher = sunfish.Searcher() color = WHITE our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = True stack = [] while True: if stack: smove = stack.pop() else: smove = input() logging.debug(f'>>> {smove} ') if smove == 'quit': break elif smove == 'uci': output('id name Sunfish') output('id author Thomas Ahle & Contributors') output('uciok') elif smove == 'isready': output('readyok') elif smove == 'ucinewgame': stack.append('position fen ' + tools.FEN_INITIAL) elif smove.startswith('position fen'): _, _, fen = smove.split(' ', 2) pos = tools.parseFEN(fen) color = WHITE if fen.split()[1] == 'w' else BLACK elif smove.startswith('position startpos'): params = smove.split(' ') pos = tools.parseFEN(tools.FEN_INITIAL) color = WHITE if len(params) > 2 and params[2] == 'moves': for move in params[3:]: pos = pos.move(tools.mparse(color, move)) color = 1 - color elif smove.startswith('go'): # default options depth = 1000 movetime = -1 _, *params = smove.split(' ') for param, val in zip(*2*(iter(params),)): if param == 'depth': depth = int(val) if param == 'movetime': movetime = int(val) if param == 'wtime': our_time = int(val) if param == 'btime': opp_time = int(val) moves_remain = 40 start = time.time() ponder = None for sdepth, _move, _score in searcher.search(pos): moves = tools.pv(searcher, pos, include_scores=False) if show_thinking: entry = searcher.tp_score.get((pos, sdepth, True)) score = int(round((entry.lower + entry.upper)/2)) usedtime = int((time.time() - start) * 1000) moves_str = moves if len(moves) < 15 else '' output('info depth {} score cp {} time {} nodes {} pv {}'.format(sdepth, score, usedtime, searcher.nodes, moves_str)) if len(moves) > 5: ponder = moves[1] if movetime > 0 and (time.time() - start) * 1000 > movetime: break if (time.time() - start) * 1000 > our_time/moves_remain: break if sdepth >= depth: break entry = searcher.tp_score.get((pos, sdepth, True)) m, s = searcher.tp_move.get(pos), entry.lower # We only resign once we are mated.. That's never? if s == -sunfish.MATE_UPPER: output('resign') else: moves = moves.split(' ') if len(moves) > 1: output(f'bestmove {moves[0]} ponder {moves[1]}') else: output('bestmove ' + moves[0]) elif smove.startswith('time'): our_time = int(smove.split()[1]) elif smove.startswith('otim'): opp_time = int(smove.split()[1]) else: pass
def main(): pos = tools.parseFEN(tools.FEN_INITIAL) searcher = sunfish.Searcher() forced = False color = WHITE our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = False options = {} history = [] stack = [] while True: if stack: smove = stack.pop() else: smove = input() print('>>>', smove, file=sys.stderr) sys.stderr.flush() # For Python 2.7 support if smove == 'quit': break elif smove == 'protover 2': print('feature done=0') print('feature myname="Sunfish"') print('feature usermove=1') print('feature setboard=1') print('feature ping=1') print('feature sigint=0') print('feature variants="normal"') print('feature option="qs_limit -spin {} -100 1000"'.format( sunfish.QS_LIMIT)) print('feature option="eval_roughness -spin {} 1 1000"'.format( sunfish.EVAL_ROUGHNESS)) print('feature option="draw_test -spin {} 0 1"'.format( int(sunfish.DRAW_TEST))) print('feature done=1') elif smove == 'new': stack.append('setboard ' + tools.FEN_INITIAL) # Clear out the old searcher, including the tables searcher = sunfish.Searcher() del history[:] elif smove.startswith('setboard'): _, fen = smove.split(' ', 1) pos = tools.parseFEN(fen) color = WHITE if fen.split()[1] == 'w' else BLACK del history[:] elif smove == 'force': forced = True elif smove.startswith('option'): _, aeqb = smove.split(maxsplit=1) if '=' in aeqb: name, val = aeqb.split('=') else: name, val = aeqb, True if name == 'qs_limit': sunfish.QS_LIMIT = int(val) if name == 'eval_roughness': sunfish.EVAL_ROUGHNESS = int(val) if name == 'draw_test': sunfish.DRAW_TEST = bool(int(val)) options[name] = val elif smove == 'go': forced = False moves_remain = 40 use = our_time / moves_remain # Let's follow the clock of our opponent if our_time >= 100 and opp_time >= 100: use *= our_time / opp_time start = time.time() for ply, move, score in searcher.search(pos, history): entry = searcher.tp_score.get((pos, ply, True)) score = int(round((entry.lower + entry.upper) / 2)) if show_thinking: used = int((time.time() - start) * 100 + .5) moves = tools.pv(searcher, pos, include_scores=False) seldepth = 0 nps = int(searcher.nodes / (time.time() - start) + .5) print('{:>3} {:>8} {:>8} {:>13} {:>1} {:>4} \t{}'.format( ply, score, used, searcher.nodes, seldepth, nps, moves)) print('# Hashfull: {:.3f}%; {} <= score < {}'.format( len(searcher.tp_score) / sunfish.TABLE_SIZE * 100, entry.lower, entry.upper)) # If found mate, just stop if entry.lower >= sunfish.MATE_UPPER: break if time.time() - start > use / 100: break # We sometimes make illegal moves when we're losing, # so it's safer to just resign. if score == -sunfish.MATE_UPPER: print('resign') else: print('move', tools.mrender(pos, move)) pos = pos.move(move) history.append(pos) color = 1 - color elif smove.startswith('ping'): _, N = smove.split() print('pong', N) elif smove.startswith('usermove'): _, smove = smove.split() m = tools.mparse(color, smove) pos = pos.move(m) history.append(pos) color = 1 - color if not forced: stack.append('go') elif smove.startswith('time'): our_time = int(smove.split()[1]) elif smove.startswith('otim'): opp_time = int(smove.split()[1]) elif smove.startswith('perft'): start = time.time() for d in range(1, 10): res = sum(1 for _ in tools.collect_tree_depth( tools.expand_position(pos), d)) print('{:>8} {:>8}'.format(res, time.time() - start)) elif smove.startswith('post'): show_thinking = True elif smove.startswith('nopost'): show_thinking = False elif any( smove.startswith(x) for x in ('xboard', 'random', 'hard', 'accepted', 'level', 'easy', 'st', 'result', '?')): print('# Ignoring command {}.'.format(smove)) elif smove.startswith('reject'): _, feature = smove.split( )[:2] # split(maxsplit=2) doesnt work in python2.7 print( '# Warning ({} rejected): Might not work as expected.'.format( feature)) else: print('# Warning (unkown command): {}. Treating as move.'.format( smove)) stack.append('usermove {}'.format(smove))
def main(): pos = tools.parseFEN( '2ba3r1/5k3/b8/1n3N2C/4p4/3R2R2/9/5p1r1/4p4/5K3 w moves d4d8 d9e8 f6d7 b6d7 g4f4 d7f6 f4f6' ) #tools.FEN_INITIAL) searcher = Searcher() forced = False color = RED our_time, opp_time = 1000, 1000 # time in centi-seconds show_thinking = True # print name of chess engine print('Moonfish') stack = [] while True: if stack: smove = stack.pop() else: smove = input() if smove == 'quit': break elif smove == 'ucci': print('ucciok') elif smove == 'isready': print('readyok') elif smove == 'newgame': stack.append('position fen ' + tools.FEN_INITIAL) elif smove.startswith('position'): params = smove.split(' ', 2) if params[1] == 'fen': fen = params[2] pos = tools.parseFEN(fen) color = RED if fen.split()[1] == 'w' else BLACK elif smove.startswith('go'): # default options depth = 1000 movetime = -1 # parse parameters params = smove.split(' ') if len(params) == 1: continue i = 0 while i < len(params): param = params[i] if param == 'depth': i += 1 depth = int(params[i]) if param == 'movetime': i += 1 movetime = int(params[i]) i += 1 forced = False moves_remain = 40 start = time.time() ponder = None for s_score in searcher._search(pos): moves = tools.pv(searcher, pos, include_scores=False) if show_thinking: entry = searcher.tp_score.get((pos, searcher.depth, True)) score = int(round((entry.lower + entry.upper) / 2)) usedtime = int((time.time() - start) * 1000) moves_str = moves #if len(moves) < 15 else '' print( 'info depth {} score {} time {} nodes {} pv {}'.format( searcher.depth, score, usedtime, searcher.nodes, moves_str)) if len(moves) > 5: ponder = moves[1] #将军死和被将军死 才会出现MATE_UPPER的值 if (s_score >= MATE_UPPER) or (s_score <= -MATE_UPPER): break if movetime > 0 and (time.time() - start) * 1000 > movetime: break if searcher.depth >= depth: break entry = searcher.tp_score.get((pos, searcher.depth, True)) m, s = searcher.tp_move.get(pos), entry.lower # We only resign once we are mated.. That's never? if s == -MATE_UPPER: print('resign') else: moves = moves.split(' ') if len(moves) > 1: print('bestmove ' + moves[0] + ' ponder ' + moves[1]) else: print('bestmove ' + moves[0]) elif smove.startswith('time'): our_time = int(smove.split()[1]) elif smove.startswith('otim'): opp_time = int(smove.split()[1]) else: pass