Exemple #1
0
    def _search(self, pos):
        """ Iterative deepening MTD-bi search """
        self.nodes = 0

        # In finished games, we could potentially go far enough to cause a recursion
        # limit exception. Hence we bound the ply.
        for depth in range(1, 1000):
            self.depth = depth
            # The inner loop is a binary search on the score of the position.
            # Inv: lower <= score <= upper
            # 'while lower != upper' would work, but play tests show a margin of 20 plays better.
            lower, upper = -MATE_UPPER, MATE_UPPER
            while lower < upper - EVAL_ROUGHNESS:
                gamma = (lower + upper + 1) // 2
                score = self.bound(pos, gamma, depth)
                # Test for debugging search instability
                if not lower <= score <= upper:
                    import tools
                    print(__file__, 'search instability?', lower, upper,
                          'gamma score', gamma, score, 'depth', depth, 'pos',
                          tools.renderFEN(pos))
                if score >= gamma:
                    lower = score
                if score < gamma:
                    upper = score
            # We want to make sure the move to play hasn't been kicked out of the table,
            # So we make another call that must always fail high and thus produce a move.
            score = self.bound(pos, lower, depth)

            # Test for debugging tp_score
            assert score >= lower
            if self.tp_score.get((pos, depth, True)) is None:
                print("No score stored?", score)
                self.tp_score[(pos, depth, True)] = Entry(score, score)
            assert score == self.tp_score.get((pos, depth, True)).lower

            # Test for debugging tp_move
            arb_legal_move = lambda: next(
                (m for m in pos.gen_moves() if not any(
                    pos.move(m).value(m1) >= MATE_LOWER
                    for m1 in pos.move(m).gen_moves())), None)
            if self.tp_move.get(pos) is None:
                print('No move stored? Score: {}'.format(score))
                self.tp_move[pos] = arb_legal_move()
            else:
                move = self.tp_move.get(pos)
                pos1 = pos.move(move)
                if any(pos1.value(m) >= MATE_LOWER for m in pos1.gen_moves()):
                    import tools
                    print('Returned illegal move? Score: {}'.format(score),
                          'move', tools.mrender(pos, move), 'pos',
                          tools.renderFEN(pos))
                    self.tp_move[pos] = arb_legal_move()

            # Yield so the user may inspect the search
            yield
Exemple #2
0
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
Exemple #3
0
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
Exemple #4
0
 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))
Exemple #5
0
 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))
Exemple #6
0
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)
Exemple #7
0
 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)
Exemple #8
0
 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 make_request(self, url, lastmove='', nextmove=''):
        fen = renderFEN(self.pos).split()[0]
        if lastmove is None:
            lastmove = ''
        if nextmove is None:
            nextmove = ''
        if self.pc == "black":
            fen = fen[::-1]

        try:
            response = requests.post(url,
                                     json={
                                         'fen': fen,
                                         'lastmove': lastmove,
                                         'arrows': nextmove
                                     })
        except:
            response = None
        return response
Exemple #10
0
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
Exemple #11
0
 def svg(self):
     board = chess.Board(renderFEN(self.hist[-1]))
     lastmove = chess.Move.from_uci(
         self.lastmove) if self.lastmove else self.lastmove
     svg = chess.svg.board(board=board, lastmove=lastmove)
     return svg
Exemple #12
0
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
Exemple #13
0
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