def validate (self, cord0, cord1): if cord0 is None or cord1 is None: return False # prevent accidental NULL_MOVE creation if cord0 == cord1: return False if self.getBoard()[cord0] == None: return False if self.parent.setup_position: to_piece = self.getBoard()[cord1] # prevent moving pieces inside holding if (cord0.x < 0 or cord0.x > self.FILES-1) and \ (cord1.x < 0 or cord1.x > self.FILES-1): return False # prevent moving kings off board elif self.getBoard()[cord0].piece == KING and \ (cord1.x < 0 or cord1.x > self.FILES-1): return False # prevent taking enemy king elif to_piece is not None and to_piece.piece == KING: return False else: return True if cord1.x < 0 or cord1.x > self.FILES-1: return False if cord0.x < 0 or cord0.x > self.FILES-1: # drop return validate(self.getBoard(), Move(lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def validate(self, cord0, cord1): if cord0 is None or cord1 is None: return False # prevent accidental NULL_MOVE creation if cord0 == cord1 and self.parent.variant.variant != SITTUYINCHESS: return False if self.getBoard()[cord0] is None: return False if self.parent.setup_position: # prevent moving pieces inside holding if (cord0.x < 0 or cord0.x > self.FILES - 1) and \ (cord1.x < 0 or cord1.x > self.FILES - 1): return False else: return True if cord1.x < 0 or cord1.x > self.FILES - 1: return False if cord0.x < 0 or cord0.x > self.FILES - 1: # drop return validate( self.getBoard(), Move( lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def validate(self, cord0, cord1): if cord0 is None or cord1 is None: return False # prevent accidental NULL_MOVE creation if cord0 == cord1: return False if self.getBoard()[cord0] is None: return False if self.parent.setup_position: # prevent moving pieces inside holding if (cord0.x < 0 or cord0.x > self.FILES - 1) and \ (cord1.x < 0 or cord1.x > self.FILES - 1): return False else: return True if cord1.x < 0 or cord1.x > self.FILES - 1: return False if cord0.x < 0 or cord0.x > self.FILES - 1: # drop return validate(self.getBoard(), Move(lmovegen.newMove( self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def test_getstatus2(self): """Testing bare white king is not draw in Atomic variant""" board = AtomicBoard(setup=FEN3) self.assertTrue(not validate(board, parseSAN(board, 'Kxg7'))) self.assertTrue(not validate(board, parseSAN(board, 'Kg8'))) self.assertTrue(not validate(board, parseSAN(board, 'Kh7'))) print(board) self.assertEqual(getStatus(board), (DRAW, DRAW_STALEMATE))
def test_validate1(self): """Testing castling rights lose in explosion in Atomic variant""" board = AtomicBoard(setup=FEN1) board = board.move(parseSAN(board, 'Nxa7')) print(board) # Rook exploded, no O-O-O anymore! self.assertTrue(validate(board, parseSAN(board, 'b6'))) self.assertTrue(not validate(board, parseSAN(board, 'a6'))) self.assertTrue(not validate(board, parseSAN(board, 'Rb8'))) self.assertTrue(not validate(board, parseSAN(board, 'O-O-O')))
def test_validate2(self): """Testing explode king vs mate in Atomic variant""" board = AtomicBoard(setup=FEN1) board = board.move(parseSAN(board, 'Nc7+')) print(board) # King explosion takes precedence over mate! self.assertTrue(validate(board, parseSAN(board, 'Qxd2'))) self.assertTrue(validate(board, parseSAN(board, 'Qxf2'))) self.assertTrue(not validate(board, parseSAN(board, 'Qxb2'))) self.assertTrue(not validate(board, parseSAN(board, 'Qe4+')))
def test_validate(self): """Testing validate move in Crazyhouse variant""" board = CrazyhouseBoard(setup=FEN0) print(board) # Drop can save mate self.assertTrue(validate(board, parseSAN(board, 'R@b8'))) self.assertTrue(validate(board, parseSAN(board, 'Ka7'))) self.assertTrue(validate(board, parseSAN(board, 'Kb7'))) self.assertTrue(not validate(board, parseSAN(board, 'P@b8'))) self.assertTrue(not validate(board, parseSAN(board, 'Kb8')))
def validate (self, cord0, cord1): assert cord0 != None and cord1 != None, "cord0: " + str(cord0) + ", cord1: " + str(cord1) if self.getBoard()[cord0] == None: return False if cord1.x < 0 or cord1.x > self.FILES-1: return False if cord0.x < 0 or cord0.x > self.FILES-1: # drop return validate(self.getBoard(), Move(lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def validate (self, cord0, cord1): if cord0 is None or cord1 is None: return False if self.getBoard()[cord0] == None: return False if cord1.x < 0 or cord1.x > self.FILES-1: return False if cord0.x < 0 or cord0.x > self.FILES-1: # drop return validate(self.getBoard(), Move(lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def validate (self, cord0, cord1): if cord0 is None or cord1 is None: return False # prevent accidental NULL_MOVE creation if cord0 == cord1: return False if self.getBoard()[cord0] == None: return False if cord1.x < 0 or cord1.x > self.FILES-1: return False if cord0.x < 0 or cord0.x > self.FILES-1: # drop return validate(self.getBoard(), Move(lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def test1(self): """Testing both king goes to 8.row draw in racingkings variant""" board = RacingKingsBoard(setup=FEN) board = board.move(parseSAN(board, 'Kh8')) print(board) # White king reached 8th row, but this is not a win # because black can reach 8th row also with hes next move self.assertEqual(getStatus(board), (RUNNING, UNKNOWN_REASON)) self.assertTrue(validate(board, parseSAN(board, 'Kb8'))) self.assertTrue(not validate(board, parseSAN(board, 'Kd8'))) board = board.move(parseSAN(board, 'Kb8')) print(board) self.assertEqual(getStatus(board), (DRAW, DRAW_KINGSINEIGHTROW))
def makeMove(self, board1, move, board2): log.debug("Human.makeMove: move=%s, board1=%s board2=%s" % ( move, board1, board2)) if self.board.view.premove_piece and self.board.view.premove0 and \ self.board.view.premove1 and \ self.color == self.board.view.premove_piece.color: if validate(board1, Move(self.board.view.premove0, self.board.view.premove1, board1, promotion=self.board.view.premove_promotion)): log.debug("Human.makeMove: Setting move to premove %s %s" % ( self.board.view.premove0, self.board.view.premove1)) self.board.emit_move_signal( self.board.view.premove0, self.board.view.premove1, promotion=self.board.view.premove_promotion) # reset premove self.board.view.setPremove(None, None, None, None) self.gmwidg.setLocked(False) item = yield from self.move_queue.get() self.gmwidg.setLocked(True) if item == "del": log.debug("Human.makeMove got: del") raise PlayerIsDead elif item == "int": log.debug("Human.makeMove got: int") raise TurnInterrupt elif item == "pass": log.debug("Human.makeMove got: pass") raise PassInterrupt return item
def makeMove(self, board1, move, board2): log.debug("Human.makeMove: move=%s, board1=%s board2=%s" % ( move, board1, board2)) if self.board.view.premove_piece and self.board.view.premove0 and \ self.board.view.premove1 and \ self.color == self.board.view.premove_piece.color: if validate(board1, Move(self.board.view.premove0, self.board.view.premove1, board1, promotion=self.board.view.premove_promotion)): log.debug("Human.makeMove: Setting move to premove %s %s" % ( self.board.view.premove0, self.board.view.premove1)) self.board.emit_move_signal( self.board.view.premove0, self.board.view.premove1, promotion=self.board.view.premove_promotion) # reset premove self.board.view.setPremove(None, None, None, None) self.gmwidg.setLocked(False) item = self.queue.get(block=True) self.gmwidg.setLocked(True) if item == "del": raise PlayerIsDead("Killed by foreign forces") if item == "int": log.debug("Human.makeMove: %s: raise TurnInterrupt" % self) raise TurnInterrupt return item
def makeMove(self, board1, move, board2): log.debug("Human.makeMove: move=%s, board1=%s board2=%s" % (move, board1, board2)) if self.board.view.premove_piece and self.board.view.premove0 and \ self.board.view.premove1 and \ self.color == self.board.view.premove_piece.color: if validate( board1, Move(self.board.view.premove0, self.board.view.premove1, board1, promotion=self.board.view.premove_promotion)): log.debug("Human.makeMove: Setting move to premove %s %s" % (self.board.view.premove0, self.board.view.premove1)) self.board.emit_move_signal( self.board.view.premove0, self.board.view.premove1, promotion=self.board.view.premove_promotion) # reset premove self.board.view.setPremove(None, None, None, None) self.gmwidg.setLocked(False) item = yield from self.move_queue.get() self.gmwidg.setLocked(True) if item == "del": log.debug("Human.makeMove got: del") raise PlayerIsDead elif item == "int": log.debug("Human.makeMove got: int") raise TurnInterrupt elif item == "pass": log.debug("Human.makeMove got: pass") raise PassInterrupt return item
def makeMove(self, board1, move, board2): log.debug("Human.makeMove: move=%s, board1=%s board2=%s" % (move, board1, board2)) if self.board.view.premove_piece and self.board.view.premove0 and \ self.board.view.premove1 and \ self.color == self.board.view.premove_piece.color: if validate( board1, Move(self.board.view.premove0, self.board.view.premove1, board1, promotion=self.board.view.premove_promotion)): log.debug("Human.makeMove: Setting move to premove %s %s" % (self.board.view.premove0, self.board.view.premove1)) self.board.emit_move_signal( self.board.view.premove0, self.board.view.premove1, promotion=self.board.view.premove_promotion) # reset premove self.board.view.setPremove(None, None, None, None) self.gmwidg.setLocked(False) item = self.queue.get(block=True) self.gmwidg.setLocked(True) if item == "del": raise PlayerIsDead("Killed by foreign forces") if item == "int": log.debug("Human.makeMove: %s: raise TurnInterrupt" % self) raise TurnInterrupt return item
def key_pressed (self, keyname): if keyname in "PNBRQKMFSOox12345678abcdefgh": self.keybuffer += keyname elif keyname == "minus": self.keybuffer += "-" elif keyname == "at": self.keybuffer += "@" elif keyname == "equal": self.keybuffer += "=" elif keyname == "Return": color = self.view.model.boards[-1].color board = self.view.model.getBoardAtPly(self.view.shown, self.view.shownVariationIdx) try: move = parseAny(board, self.keybuffer) except: self.keybuffer = "" return if validate(board, move): if self.view.shownIsMainLine() and self.view.model.boards[-1] == board: self.emit("piece_moved", move, color) else: if board.board.next is None and not self.view.shownIsMainLine(): self.view.model.add_move2variation(board, move, self.view.shownVariationIdx) self.view.shown += 1 else: new_vari = self.view.model.add_variation(board, (move,)) self.view.setShownBoard(new_vari[-1]) self.keybuffer = "" elif keyname == "BackSpace": self.keybuffer = self.keybuffer[:-1] if self.keybuffer else ""
def validate(self, cord0, cord1): if cord0 is None or cord1 is None: return False if self.getBoard()[cord0] == None: return False if cord1.x < 0 or cord1.x > self.FILES - 1: return False if cord0.x < 0 or cord0.x > self.FILES - 1: # drop return validate( self.getBoard(), Move( lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def test_check_evasions(self): """Testing check evasions in Crazyhouse variant""" board = CrazyhouseBoard(setup=FEN1) print(board) # invalid drop self.assertTrue(validate(board, parseSAN(board, 'Q@b1'))) self.assertTrue(validate(board, parseSAN(board, 'Q@c1'))) self.assertTrue(validate(board, parseSAN(board, 'Kxb2'))) self.assertTrue(not validate(board, parseSAN(board, 'P@b1'))) self.assertTrue(not validate(board, parseSAN(board, 'P@c1'))) evasions = [move for move in genCheckEvasions(board.board)] self.assertTrue(parseSAN(board, 'Q@b1').move in evasions) self.assertTrue(parseSAN(board, 'Q@c1').move in evasions) self.assertTrue(parseSAN(board, 'Kxb2').move in evasions) self.assertTrue(parseSAN(board, 'P@b1').move not in evasions) self.assertTrue(parseSAN(board, 'P@c1').move not in evasions)
def validate(self, cord0, cord1): if cord0 is None or cord1 is None: return False # prevent accidental NULL_MOVE creation if cord0 == cord1: return False if self.getBoard()[cord0] == None: return False if cord1.x < 0 or cord1.x > self.FILES - 1: return False if cord0.x < 0 or cord0.x > self.FILES - 1: # drop return validate( self.getBoard(), Move( lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def test_validate(self): """Testing validate move in Losers variant""" board = LosersBoard(setup=FEN0) print(board) self.assertTrue(validate(board, parseSAN(board, 'Kxa7'))) self.assertTrue(not validate(board, parseSAN(board, 'Kb8'))) self.assertTrue(not validate(board, parseSAN(board, 'Kb7'))) board = LosersBoard(setup=FEN1) print(board) self.assertTrue(not validate(board, parseSAN(board, 'Kxa7'))) self.assertTrue(not validate(board, parseSAN(board, 'Kb8'))) self.assertTrue(validate(board, parseSAN(board, 'Kb7'))) board = LosersBoard(setup=FEN2) print(board) self.assertTrue(not validate(board, parseSAN(board, 'Kxb7'))) self.assertTrue(not validate(board, parseSAN(board, 'Kb8'))) self.assertTrue(validate(board, parseSAN(board, 'Rxb7')))
def test_validate(self): """Testing validate move in Suicide variant""" board = SuicideBoard(setup=FEN0) print(board) self.assertTrue(validate(board, parseSAN(board, 'Kxa7'))) self.assertTrue(not validate(board, parseSAN(board, 'Kb8'))) self.assertTrue(not validate(board, parseSAN(board, 'Kb7'))) board = SuicideBoard(setup=FEN1) print(board) self.assertTrue(validate(board, parseSAN(board, 'Kxa7'))) self.assertTrue(validate(board, parseSAN(board, 'Kxb8'))) self.assertTrue(not validate(board, parseSAN(board, 'Kb7'))) board = SuicideBoard(setup=FEN2) print(board) self.assertTrue(not validate(board, parseSAN(board, 'Ka7'))) self.assertTrue(not validate(board, parseSAN(board, 'Kb8'))) self.assertTrue(validate(board, parseSAN(board, 'Kxb7'))) board = SuicideBoard(setup=FEN3) print(board) self.assertTrue(validate(board, parseSAN(board, 'Ked2')))
def isAPotentiallyLegalNextMove(self, cord0, cord1): """ Determines whether the given move is at all legally possible as the next move after the player who's turn it is makes their move Note: This doesn't always return the correct value, such as when BoardControl.setLocked() has been called and we've begun a drag, but view.shown and BoardControl.lockedPly haven't been updated yet """ if cord0 == None or cord1 == None: return False if not self.parent.lockedPly in self.parent.possibleBoards: return False for board in self.parent.possibleBoards[self.parent.lockedPly]: if not board[cord0]: return False if validate(board, Move(cord0, cord1, board)): return True return False
def isAPotentiallyLegalNextMove (self, cord0, cord1): """ Determines whether the given move is at all legally possible as the next move after the player who's turn it is makes their move Note: This doesn't always return the correct value, such as when BoardControl.setLocked() has been called and we've begun a drag, but view.shown and BoardControl.lockedPly haven't been updated yet """ if cord0 == None or cord1 == None: return False if not self.parent.lockedPly in self.parent.possibleBoards: return False for board in self.parent.possibleBoards[self.parent.lockedPly]: if not board[cord0]: return False if validate(board, Move(cord0, cord1, board)): return True return False
def key_pressed(self, keyname): if keyname in "PNBRQKMFSOox12345678abcdefgh": self.keybuffer += keyname elif keyname == "minus": self.keybuffer += "-" elif keyname == "at": self.keybuffer += "@" elif keyname == "equal": self.keybuffer += "=" elif keyname == "Return": color = self.view.model.boards[-1].color board = self.view.model.getBoardAtPly( self.view.shown, self.view.shown_variation_idx) try: move = parseAny(board, self.keybuffer) except ParsingError: self.keybuffer = "" return if validate(board, move): if ((self.view.model.curplayer.__type__ == LOCAL or self.view.model.examined) and self.view.shownIsMainLine() and self.view.model.boards[-1] == board and self.view.model.status == RUNNING): # emit move self.emit("piece_moved", move, color) if self.view.model.examined: self.view.model.connection.bm.sendMove( toAN(board, move)) else: self.play_or_add_move(board, move) self.keybuffer = "" elif keyname == "BackSpace": self.keybuffer = self.keybuffer[:-1] if self.keybuffer else ""
def key_pressed(self, keyname): if keyname in "PNBRQKMFSOox12345678abcdefgh": self.keybuffer += keyname elif keyname == "minus": self.keybuffer += "-" elif keyname == "at": self.keybuffer += "@" elif keyname == "equal": self.keybuffer += "=" elif keyname == "Return": color = self.view.model.boards[-1].color board = self.view.model.getBoardAtPly( self.view.shown, self.view.shown_variation_idx) try: move = parseAny(board, self.keybuffer) except: self.keybuffer = "" return if validate(board, move): if (self.view.model.curplayer.__type__ == LOCAL or self.view.model.examined) and \ self.view.shownIsMainLine() and \ self.view.model.boards[-1] == board and \ self.view.model.status == RUNNING: # emit move self.emit("piece_moved", move, color) if self.view.model.examined: self.view.model.connection.bm.sendMove(toAN(board, move)) else: play_or_add_move(self.view, board, move) self.keybuffer = "" elif keyname == "BackSpace": self.keybuffer = self.keybuffer[:-1] if self.keybuffer else ""
def test_validate(self): """Testing validate move in Sittuyin variant""" board = SittuyinBoard(setup=FEN0) print(board) # no promotion if we have Met (queen) self.assertTrue(validate(board, parseAN(board, 'f4f3'))) self.assertTrue(validate(board, parseAN(board, 'b2b1'))) self.assertTrue(validate(board, parseAN(board, 'b2c1'))) self.assertFalse(validate(board, parseAN(board, 'b2b2f'))) self.assertFalse(validate(board, parseAN(board, 'b2a1f'))) self.assertFalse(validate(board, parseAN(board, 'f4f3f'))) self.assertFalse(validate(board, parseAN(board, 'b2b1f'))) self.assertFalse(validate(board, parseAN(board, 'b2c1f'))) board = SittuyinBoard(setup=FEN1) print(board) # but (optional) promotion if we don't have Met (queen) self.assertTrue(validate(board, parseAN(board, 'b2b2f'))) self.assertTrue(validate(board, parseSAN(board, 'b2=f'))) self.assertEqual(parseAN(board, 'b2b2f'), parseSAN(board, 'b2=f')) self.assertTrue(validate(board, parseAN(board, 'b2a1f'))) self.assertTrue(validate(board, parseSAN(board, 'a1=f'))) self.assertEqual(parseAN(board, 'b2a1f'), parseSAN(board, 'a1=f')) self.assertTrue(validate(board, parseAN(board, 'b2c1'))) self.assertTrue(validate(board, parseAN(board, 'f4f3'))) self.assertFalse(validate(board, parseAN(board, 'f4f3f'))) self.assertFalse(validate(board, parseAN(board, 'b2b2'))) self.assertFalse(validate(board, parseAN(board, 'b2b1f'))) self.assertFalse(validate(board, parseAN(board, 'b2c1f')))
def parseLine(self, proc): while True: line = yield from wait_signal(proc, 'line') if not line: break else: line = line[1] if line[0:1] == "#": # Debug line which we shall ignore as specified in CECPv2 specs continue # log.debug("__parseLine: line=\"%s\"" % line.strip(), extra={"task":self.defname}) parts = whitespaces.split(line.strip()) if parts[0] == "pong": self.lastpong = int(parts[1]) continue # Illegal Move if parts[0].lower().find("illegal") >= 0: log.warning("__parseLine: illegal move: line=\"%s\", board=%s" % ( line.strip(), self.board), extra={"task": self.defname}) if parts[-2] == "sd" and parts[-1].isdigit(): print("depth", parts[-1], file=self.engine) continue # A Move (Perhaps) if self.board: if parts[0] == "move": movestr = parts[1] # Old Variation elif d_plus_dot_expr.match(parts[0]) and parts[1] == "...": movestr = parts[2] else: movestr = False if movestr: self.waitingForMove = False self.readyForMoveNowCommand = False if self.engineIsInNotPlaying: # If engine was set in pause just before the engine sent its # move, we ignore it. However the engine has to know that we # ignored it, and thus we step it one back log.info("__parseLine: Discarding engine's move: %s" % movestr, extra={"task": self.defname}) print("undo", file=self.engine) continue else: try: move = parseAny(self.board, movestr) except ParsingError: self.invalid_move = movestr log.info( "__parseLine: ParsingError engine move: %s %s" % (movestr, self.board), extra={"task": self.defname}) self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue if validate(self.board, move): self.board = None self.queue.put_nowait(move) continue else: self.invalid_move = movestr log.info( "__parseLine: can't validate engine move: %s %s" % (movestr, self.board), extra={"task": self.defname}) self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue # Analyzing if self.engineIsInNotPlaying: if parts[:4] == ["0", "0", "0", "0"]: # Crafty doesn't analyze until it is out of book print("book off", file=self.engine) continue match = anare.match(line) if match: depth, score, moves = match.groups() if "mat" in score.lower() or "#" in moves: # Will look either like -Mat 3 or Mat3 scoreval = MATE_VALUE if score.startswith('-'): scoreval = -scoreval else: scoreval = int(score) mvstrs = movere.findall(moves) if mvstrs: self.emit("analyze", [(mvstrs, scoreval, depth.strip())]) continue # Offers draw if parts[0:2] == ["offer", "draw"]: self.emit("accept", Offer(DRAW_OFFER)) continue # Resigns if parts[0] == "resign" or \ (parts[0] == "tellics" and parts[1] == "resign"): # buggy crafty # Previously: if "resign" in parts, # however, this is too generic, since "hint", "bk", # "feature option=.." and possibly other, future CECPv2 # commands can validly contain the word "resign" without this # being an intentional resign offer. self.emit("offer", Offer(RESIGNATION)) continue # if parts[0].lower() == "error": # continue # Tell User Error if parts[0] == "tellusererror": # We don't want to see our stop analyzer hack as an error message if "8/8/8/8/8/8/8/8" in "".join(parts[1:]): continue # Create a non-modal non-blocking message dialog with the error: dlg = Gtk.MessageDialog(parent=None, flags=0, type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.CLOSE, message_format=None) # Use the engine name if already known, otherwise the defname: displayname = self.name if not displayname: displayname = self.defname # Compose the dialog text: dlg.set_markup(GObject.markup_escape_text(_( "The engine %s reports an error:") % displayname) + "\n\n" + GObject.markup_escape_text(" ".join(parts[1:]))) # handle response signal so the "Close" button works: dlg.connect("response", lambda dlg, x: dlg.destroy()) dlg.show_all() continue # Tell Somebody if parts[0][:4] == "tell" and \ parts[0][4:] in ("others", "all", "ics", "icsnoalias"): log.info("Ignoring tell %s: %s" % (parts[0][4:], " ".join(parts[1:]))) continue if "feature" in parts: # Some engines send features after done=1, so we will iterate after done=1 too done1 = False # We skip parts before 'feature', as some engines give us lines like # White (1) : feature setboard=1 analyze...e="GNU Chess 5.07" done=1 parts = parts[parts.index("feature"):] for i, pair in enumerate(parts[1:]): # As "parts" is split with no thoughs on quotes or double quotes # we need to do some extra handling. if pair.find("=") < 0: continue key, value = pair.split("=", 1) if key not in self.features: continue if value.startswith('"') and value.endswith('"'): value = value[1:-1] # If our pair was unfinished, like myname="GNU, we search the # rest of the pairs for a quotating mark. elif value[0] == '"': rest = value[1:] + " " + " ".join(parts[2 + i:]) j = rest.find('"') if j == -1: log.warning("Missing endquotation in %s feature", extra={"task": self.defname}) value = rest else: value = rest[:j] elif value.isdigit(): value = int(value) if key in self.supported_features: print("accepted %s" % key, file=self.engine) else: print("rejected %s" % key, file=self.engine) if key == "done": if value == 1: done1 = True continue elif value == 0: log.info("Adds %d seconds timeout" % TIME_OUT_SECOND, extra={"task": self.defname}) # This'll buy you some more time self.queue.put_nowait("not ready") break if key == "smp" and value == 1: self.options["cores"] = {"name": "cores", "type": "spin", "default": 1, "min": 1, "max": 64} elif key == "memory" and value == 1: self.options["memory"] = {"name": "memory", "type": "spin", "default": 32, "min": 1, "max": 4096} elif key == "option" and key != "done": option = self.__parse_option(value) self.options[option["name"]] = option else: self.features[key] = value if key == "myname" and not self.name: self.setName(value) if done1: # Start a new game before using the engine: # (CECPv2 engines) print("new", file=self.engine) # We are now ready for play: self.emit("readyForOptions") self.emit("readyForMoves") self.queue.put_nowait("ready") # A hack to get better names in protover 1. # Unfortunately it wont work for now, as we don't read any lines from # protover 1 engines. When should we stop? if self.protover == 1: if self.defname[0] in ''.join(parts): basis = self.defname[0] name = ' '.join(itertools.dropwhile( lambda part: basis not in part, parts)) self.features['myname'] = name if not self.name: self.setName(name)
def __parseLine (self, line): if not self.connected: return parts = line.split() if not parts: return #---------------------------------------------------------- Initializing if parts[0] == "id": self.ids[parts[1]] = " ".join(parts[2:]) return if parts[0] == "uciok": self.emit("readyForOptions") return if parts[0] == "readyok": self.emit("readyForMoves") return #------------------------------------------------------- Options parsing if parts[0] == "option": dic = {} last = 1 varlist = [] for i in xrange (2, len(parts)+1): if i == len(parts) or parts[i] in OPTKEYS: key = parts[last] value = " ".join(parts[last+1:i]) if "type" in dic and dic["type"] in TYPEDIC: value = TYPEDIC[dic["type"]](value) if key == "var": varlist.append(value) else: dic[key] = value last = i if varlist: dic["vars"] = varlist name = dic["name"] del dic["name"] self.options[name] = dic return #---------------------------------------------------------------- A Move if self.mode == NORMAL and parts[0] == "bestmove": with self.moveLock: self.needBestmove = False self.__sendQueuedGo() if self.ignoreNext: log.debug("__parseLine: line='%s' self.ignoreNext==True, returning\n" % \ line.strip(), self.defname) self.ignoreNext = False self.readyForStop = True return if not self.waitingForMove: log.warn("__parseLine: self.waitingForMove==False, ignoring move=%s\n" % \ parts[1], self.defname) self.pondermove = None return self.waitingForMove = False move = parseAN(self.board, parts[1]) if not validate(self.board, move): # This is critical. To avoid game stalls, we need to resign on # behalf of the engine. log.error("__parseLine: move=%s didn't validate, putting 'del' in returnQueue. self.board=%s\n" % \ (repr(move), self.board), self.defname) self.returnQueue.put('del') return self.board = self.board.move(move) log.debug("__parseLine: applied move=%s to self.board=%s\n" % \ (move, self.board), self.defname) if self.getOption('Ponder'): self.pondermove = None # An engine may send an empty ponder line, simply to clear. if len(parts) == 4 and self.board: # Engines don't always check for everything in their # ponders. Hence we need to validate. # But in some cases, what they send may not even be # correct AN - specially in the case of promotion. try: pondermove = parseAN(self.board, parts[3]) except ParsingError: pass else: if validate(self.board, pondermove): self.pondermove = pondermove self._startPonder() self.returnQueue.put(move) log.debug("__parseLine: put move=%s into self.returnQueue=%s\n" % \ (move, self.returnQueue.queue), self.defname) return #----------------------------------------------------------- An Analysis if self.mode != NORMAL and parts[0] == "info" and "pv" in parts: scoretype = parts[parts.index("score")+1] if scoretype in ('lowerbound', 'upperbound'): score = None else: score = int(parts[parts.index("score")+2]) if scoretype == 'mate': # print >> self.engine, "stop" sign = score/abs(score) score = sign * (MATE_VALUE-abs(score)) movstrs = parts[parts.index("pv")+1:] try: moves = listToMoves (self.board, movstrs, AN, validate=True, ignoreErrors=False) except ParsingError, e: # ParsingErrors may happen when parsing "old" lines from # analyzing engines, which haven't yet noticed their new tasks log.debug("__parseLine: Ignored (%s) from analyzer: ParsingError%s\n" % \ (' '.join(movstrs),e), self.defname) return self.emit("analyze", moves, score) return
def parseLine(self, proc): while True: line = yield from wait_signal(proc, 'line') if not line: break else: line = line[1] parts = line.split() if not parts: continue # Initializing if parts[0] == "id": if parts[1] == "name": self.ids[parts[1]] = " ".join(parts[2:]) self.setName(self.ids["name"]) continue if parts[0] == "uciok": self.emit("readyForOptions") continue if parts[0] == "readyok": self.emit("readyForMoves") continue # Options parsing if parts[0] == "option": dic = {} last = 1 varlist = [] for i in range(2, len(parts) + 1): if i == len(parts) or parts[i] in OPTKEYS: key = parts[last] value = " ".join(parts[last + 1:i]) if "type" in dic and dic["type"] in TYPEDIC: value = TYPEDIC[dic["type"]](value) if key == "var": varlist.append(value) elif key == "type" and value == "string": dic[key] = "text" else: dic[key] = value last = i if varlist: dic["choices"] = varlist if "name" in dic: self.options[dic["name"]] = dic continue # A Move if self.mode == NORMAL and parts[0] == "bestmove": self.needBestmove = False self.bestmove_event.set() self.__sendQueuedGo() if self.ignoreNext: log.debug( "__parseLine: line='%s' self.ignoreNext==True, returning" % line.strip(), extra={"task": self.defname}) self.ignoreNext = False self.readyForStop = True continue movestr = parts[1] if not self.waitingForMove: log.warning( "__parseLine: self.waitingForMove==False, ignoring move=%s" % movestr, extra={"task": self.defname}) self.pondermove = None continue self.waitingForMove = False try: move = parseAny(self.board, movestr) except ParsingError: self.invalid_move = movestr log.info( "__parseLine: ParsingError engine move: %s %s" % (movestr, self.board), extra={"task": self.defname}) self.end( WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue if not validate(self.board, move): # This is critical. To avoid game stalls, we need to resign on # behalf of the engine. log.error( "__parseLine: move=%s didn't validate, putting 'del' \ in returnQueue. self.board=%s" % (repr(move), self.board), extra={"task": self.defname}) self.invalid_move = movestr self.end( WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue self._recordMove(self.board.move(move), move, self.board) log.debug("__parseLine: applied move=%s to self.board=%s" % (move, self.board), extra={"task": self.defname}) if self.ponderOn: self.pondermove = None # An engine may send an empty ponder line, simply to clear. if len(parts) == 4: # Engines don't always check for everything in their # ponders. Hence we need to validate. # But in some cases, what they send may not even be # correct AN - specially in the case of promotion. try: pondermove = parseAny(self.board, parts[3]) except ParsingError: pass else: if validate(self.board, pondermove): self.pondermove = pondermove self._startPonder() self.queue.put_nowait(move) log.debug("__parseLine: put move=%s into self.queue=%s" % (move, self.queue), extra={"task": self.defname}) continue # An Analysis if self.mode != NORMAL and parts[0] == "info" and "pv" in parts: multipv = 1 if "multipv" in parts: multipv = int(parts[parts.index("multipv") + 1]) scoretype = parts[parts.index("score") + 1] if scoretype in ('lowerbound', 'upperbound'): score = None else: score = int(parts[parts.index("score") + 2]) if scoretype == 'mate': # print >> self.engine, "stop" if score != 0: sign = score / abs(score) score = sign * (MATE_VALUE - abs(score)) movstrs = parts[parts.index("pv") + 1:] if "depth" in parts: depth = parts[parts.index("depth") + 1] else: depth = "" if "nps" in parts: nps = parts[parts.index("nps") + 1] else: nps = "" if multipv <= len(self.analysis): self.analysis[multipv - 1] = (self.board.ply, movstrs, score, depth, nps) self.emit("analyze", self.analysis) continue # An Analyzer bestmove if self.mode != NORMAL and parts[0] == "bestmove": log.debug( "__parseLine: processing analyzer bestmove='%s'" % line.strip(), extra={"task": self.defname}) self.needBestmove = False self.bestmove_event.set() if parts[1] == "(none)": self.emit("analyze", []) else: self.__sendQueuedGo(sendlast=True) continue # Stockfish complaining it received a 'stop' without a corresponding 'position..go' if line.strip() == "Unknown command: stop": log.debug("__parseLine: processing '%s'" % line.strip(), extra={"task": self.defname}) self.ignoreNext = False self.needBestmove = False self.readyForStop = False self.__sendQueuedGo() continue
def validate (self, cord0, cord1): assert cord0 != None and cord1 != None, "cord0: " + str(cord0) + ", cord1: " + str(cord1) if self.getBoard()[cord0] == None: return False return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def test_getstatus4(self): """Testing possible move into check when king touch saves the king""" board = AtomicBoard(setup=FEN5) print(board) self.assertTrue(validate(board, parseSAN(board, 'Kg5')))
def __parseLine(self, line): if not self.connected: return parts = line.split() if not parts: return #---------------------------------------------------------- Initializing if parts[0] == "id": self.ids[parts[1]] = " ".join(parts[2:]) return if parts[0] == "uciok": self.emit("readyForOptions") return if parts[0] == "readyok": self.emit("readyForMoves") return #------------------------------------------------------- Options parsing if parts[0] == "option": dic = {} last = 1 varlist = [] for i in xrange(2, len(parts) + 1): if i == len(parts) or parts[i] in OPTKEYS: key = parts[last] value = " ".join(parts[last + 1:i]) if "type" in dic and dic["type"] in TYPEDIC: value = TYPEDIC[dic["type"]](value) if key == "var": varlist.append(value) else: dic[key] = value last = i if varlist: dic["vars"] = varlist name = dic["name"] del dic["name"] self.options[name] = dic return #---------------------------------------------------------------- A Move if self.mode == NORMAL and parts[0] == "bestmove": with self.moveLock: self.needBestmove = False self.__sendQueuedGo() if self.ignoreNext: log.debug("__parseLine: line='%s' self.ignoreNext==True, returning\n" % \ line.strip(), self.defname) self.ignoreNext = False self.readyForStop = True return if not self.waitingForMove: log.warn("__parseLine: self.waitingForMove==False, ignoring move=%s\n" % \ parts[1], self.defname) self.pondermove = None return self.waitingForMove = False move = parseAN(self.board, parts[1]) if not validate(self.board, move): # This is critical. To avoid game stalls, we need to resign on # behalf of the engine. log.error("__parseLine: move=%s didn't validate, putting 'del' in returnQueue. self.board=%s\n" % \ (repr(move), self.board), self.defname) self.returnQueue.put('del') return self.board = self.board.move(move) log.debug("__parseLine: applied move=%s to self.board=%s\n" % \ (move, self.board), self.defname) if self.getOption('Ponder'): self.pondermove = None # An engine may send an empty ponder line, simply to clear. if len(parts) == 4 and self.board: # Engines don't always check for everything in their # ponders. Hence we need to validate. # But in some cases, what they send may not even be # correct AN - specially in the case of promotion. try: pondermove = parseAN(self.board, parts[3]) except ParsingError: pass else: if validate(self.board, pondermove): self.pondermove = pondermove self._startPonder() self.returnQueue.put(move) log.debug("__parseLine: put move=%s into self.returnQueue=%s\n" % \ (move, self.returnQueue.queue), self.defname) return #----------------------------------------------------------- An Analysis if self.mode != NORMAL and parts[0] == "info" and "pv" in parts: scoretype = parts[parts.index("score") + 1] if scoretype in ('lowerbound', 'upperbound'): score = None else: score = int(parts[parts.index("score") + 2]) if scoretype == 'mate': # print >> self.engine, "stop" sign = score / abs(score) score = sign * (MATE_VALUE - abs(score)) movstrs = parts[parts.index("pv") + 1:] try: moves = listToMoves(self.board, movstrs, AN, validate=True, ignoreErrors=False) except ParsingError, e: # ParsingErrors may happen when parsing "old" lines from # analyzing engines, which haven't yet noticed their new tasks log.debug("__parseLine: Ignored (%s) from analyzer: ParsingError%s\n" % \ (' '.join(movstrs),e), self.defname) return self.emit("analyze", moves, score) return
def __parseLine (self, line): # log.debug("__parseLine: line=\"%s\"\n" % line.strip(), self.defname) parts = whitespaces.split(line.strip()) if parts[0] == "pong": self.lastpong = int(parts[1]) return # Illegal Move if parts[0].lower().find("illegal") >= 0: log.warn("__parseLine: illegal move: line=\"%s\", board=%s" \ % (line.strip(), self.board), self.defname) if parts[-2] == "sd" and parts[-1].isdigit(): print >> self.engine, "depth", parts[-1] return # A Move (Perhaps) if self.board: if parts[0] == "move": movestr = parts[1] # Old Variation elif d_plus_dot_expr.match(parts[0]) and parts[1] == "...": movestr = parts[2] else: movestr = False if movestr: log.debug("__parseLine: acquiring self.boardLock\n", self.defname) self.waitingForMove = False self.readyForMoveNowCommand = False self.boardLock.acquire() try: if self.engineIsInNotPlaying: # If engine was set in pause just before the engine sent its # move, we ignore it. However the engine has to know that we # ignored it, and thus we step it one back log.log("__parseLine: Discarding engine's move: %s\n" % movestr, self.defname) print >> self.engine, "undo" return else: try: move = parseAny(self.board, movestr) except ParsingError, e: raise PlayerIsDead, e if validate(self.board, move): self.board = None self.returnQueue.put(move) return raise PlayerIsDead, "Board didn't validate after move" finally: log.debug("__parseLine(): releasing self.boardLock\n", self.defname) self.boardLock.release() self.movecon.acquire() self.movecon.notifyAll() self.movecon.release() # Analyzing if self.engineIsInNotPlaying: if parts[:4] == ["0","0","0","0"]: # Crafty doesn't analyze until it is out of book print >> self.engine, "book off" return match = anare.match(line) if match: score, moves = match.groups() if "mat" in score.lower(): # Will look either like -Mat 3 or Mat3 scoreval = MATE_VALUE - int("".join(c for c in score if c.isdigit())) if score.startswith('-'): scoreval = -scoreval else: scoreval = int(score) mvstrs = movere.findall(moves) try: moves = listToMoves (self.board, mvstrs, type=None, validate=True, ignoreErrors=False) except ParsingError, e: # ParsingErrors may happen when parsing "old" lines from # analyzing engines, which haven't yet noticed their new tasks log.debug("Ignored a line from analyzer: ParsingError%s\n" % e, self.defname) return # Don't emit if we weren't able to parse moves, or if we have a move # to kill the opponent king - as it confuses many engines if moves and not self.board.board.opIsChecked(): self.emit("analyze", moves, scoreval) return
def parseLine(self, proc): while True: line = yield from wait_signal(proc, 'line') if not line: break else: line = line[1] parts = line.split() if not parts: continue # Initializing if parts[0] == "id": if parts[1] == "name": self.ids[parts[1]] = " ".join(parts[2:]) self.setName(self.ids["name"]) continue if parts[0] == "uciok": self.emit("readyForOptions") continue if parts[0] == "readyok": self.emit("readyForMoves") continue # Options parsing if parts[0] == "option": dic = {} last = 1 varlist = [] for i in range(2, len(parts) + 1): if i == len(parts) or parts[i] in OPTKEYS: key = parts[last] value = " ".join(parts[last + 1:i]) if "type" in dic and dic["type"] in TYPEDIC: value = TYPEDIC[dic["type"]](value) if key == "var": varlist.append(value) elif key == "type" and value == "string": dic[key] = "text" else: dic[key] = value last = i if varlist: dic["choices"] = varlist if "name" in dic: self.options[dic["name"]] = dic continue # A Move if self.mode == NORMAL and parts[0] == "bestmove": self.needBestmove = False self.bestmove_event.set() self.__sendQueuedGo() if self.ignoreNext: log.debug( "__parseLine: line='%s' self.ignoreNext==True, returning" % line.strip(), extra={"task": self.defname}) self.ignoreNext = False self.readyForStop = True continue movestr = parts[1] if not self.waitingForMove: log.warning("__parseLine: self.waitingForMove==False, ignoring move=%s" % movestr, extra={"task": self.defname}) self.pondermove = None continue self.waitingForMove = False try: move = parseAny(self.board, movestr) except ParsingError: self.invalid_move = movestr log.info( "__parseLine: ParsingError engine move: %s %s" % (movestr, self.board), extra={"task": self.defname}) self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue if not validate(self.board, move): # This is critical. To avoid game stalls, we need to resign on # behalf of the engine. log.error("__parseLine: move=%s didn't validate, putting 'del' \ in returnQueue. self.board=%s" % ( repr(move), self.board), extra={"task": self.defname}) self.invalid_move = movestr self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue self._recordMove(self.board.move(move), move, self.board) log.debug("__parseLine: applied move=%s to self.board=%s" % ( move, self.board), extra={"task": self.defname}) if self.ponderOn: self.pondermove = None # An engine may send an empty ponder line, simply to clear. if len(parts) == 4: # Engines don't always check for everything in their # ponders. Hence we need to validate. # But in some cases, what they send may not even be # correct AN - specially in the case of promotion. try: pondermove = parseAny(self.board, parts[3]) except ParsingError: pass else: if validate(self.board, pondermove): self.pondermove = pondermove self._startPonder() self.queue.put_nowait(move) log.debug("__parseLine: put move=%s into self.queue=%s" % ( move, self.queue), extra={"task": self.defname}) continue # An Analysis if self.mode != NORMAL and parts[0] == "info" and "pv" in parts: multipv = 1 if "multipv" in parts: multipv = int(parts[parts.index("multipv") + 1]) scoretype = parts[parts.index("score") + 1] if scoretype in ('lowerbound', 'upperbound'): score = None else: score = int(parts[parts.index("score") + 2]) if scoretype == 'mate': # print >> self.engine, "stop" if score != 0: sign = score / abs(score) score = sign * (MATE_VALUE - abs(score)) movstrs = parts[parts.index("pv") + 1:] if "depth" in parts: depth = parts[parts.index("depth") + 1] else: depth = "" if "nps" in parts: nps = parts[parts.index("nps") + 1] else: nps = "" if multipv <= len(self.analysis): self.analysis[multipv - 1] = (self.board.ply, movstrs, score, depth, nps) self.emit("analyze", self.analysis) continue # An Analyzer bestmove if self.mode != NORMAL and parts[0] == "bestmove": log.debug("__parseLine: processing analyzer bestmove='%s'" % line.strip(), extra={"task": self.defname}) self.needBestmove = False self.bestmove_event.set() if parts[1] == "(none)": self.emit("analyze", []) else: self.__sendQueuedGo(sendlast=True) continue # Stockfish complaining it received a 'stop' without a corresponding 'position..go' if line.strip() == "Unknown command: stop": log.debug("__parseLine: processing '%s'" % line.strip(), extra={"task": self.defname}) self.ignoreNext = False self.needBestmove = False self.readyForStop = False self.__sendQueuedGo() continue
def parseLine(self, engine, line): if line[0:1] == "#": # Debug line which we shall ignore as specified in CECPv2 specs return # log.debug("__parseLine: line=\"%s\"" % line.strip(), extra={"task":self.defname}) parts = whitespaces.split(line.strip()) if parts[0] == "pong": self.lastpong = int(parts[1]) return # Illegal Move if parts[0].lower().find("illegal") >= 0: log.warning("__parseLine: illegal move: line=\"%s\", board=%s" \ % (line.strip(), self.board), extra={"task":self.defname}) if parts[-2] == "sd" and parts[-1].isdigit(): print("depth", parts[-1], file=self.engine) return # A Move (Perhaps) if self.board: if parts[0] == "move": movestr = parts[1] # Old Variation elif d_plus_dot_expr.match(parts[0]) and parts[1] == "...": movestr = parts[2] else: movestr = False if movestr: log.debug("__parseLine: acquiring self.boardLock", extra={"task": self.defname}) self.waitingForMove = False self.readyForMoveNowCommand = False self.boardLock.acquire() try: if self.engineIsInNotPlaying: # If engine was set in pause just before the engine sent its # move, we ignore it. However the engine has to know that we # ignored it, and thus we step it one back log.info("__parseLine: Discarding engine's move: %s" % movestr, extra={"task": self.defname}) print("undo", file=self.engine) return else: try: move = parseAny(self.board, movestr) except ParsingError as e: self.invalid_move = movestr log.info( "__parseLine: ParsingError engine move: %s %s" % (movestr, self.board), extra={"task": self.defname}) self.end( WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return if validate(self.board, move): self.board = None self.returnQueue.put(move) return else: self.invalid_move = movestr log.info( "__parseLine: can't validate engine move: %s %s" % (movestr, self.board), extra={"task": self.defname}) self.end( WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return finally: log.debug("__parseLine(): releasing self.boardLock", extra={"task": self.defname}) self.boardLock.release() self.movecon.acquire() self.movecon.notifyAll() self.movecon.release() # Analyzing if self.engineIsInNotPlaying: if parts[:4] == ["0", "0", "0", "0"]: # Crafty doesn't analyze until it is out of book print("book off", file=self.engine) return match = anare.match(line) if match: depth, score, moves = match.groups() if "mat" in score.lower() or "#" in moves: # Will look either like -Mat 3 or Mat3 scoreval = MATE_VALUE if score.startswith('-'): scoreval = -scoreval else: scoreval = int(score) mvstrs = movere.findall(moves) if mvstrs: self.emit("analyze", [(mvstrs, scoreval, depth.strip())]) return # Offers draw if parts[0:2] == ["offer", "draw"]: self.emit("accept", Offer(DRAW_OFFER)) return # Resigns if parts[0] == "resign" or \ (parts[0] == "tellics" and parts[1] == "resign"): # buggy crafty # Previously: if "resign" in parts, # however, this is too generic, since "hint", "bk", # "feature option=.." and possibly other, future CECPv2 # commands can validly contain the word "resign" without this # being an intentional resign offer. self.emit("offer", Offer(RESIGNATION)) return #if parts[0].lower() == "error": # return #Tell User Error if parts[0] == "tellusererror": # We don't want to see our stop analyzer hack as an error message if "8/8/8/8/8/8/8/8" in "".join(parts[1:]): return # Create a non-modal non-blocking message dialog with the error: dlg = Gtk.MessageDialog(parent=None, flags=0, type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.CLOSE, message_format=None) # Use the engine name if already known, otherwise the defname: displayname = self.name if not displayname: displayname = self.defname # Compose the dialog text: dlg.set_markup( GObject.markup_escape_text( _("The engine %s reports an error:") % displayname) + "\n\n" + GObject.markup_escape_text(" ".join(parts[1:]))) # handle response signal so the "Close" button works: dlg.connect("response", lambda dlg, x: dlg.destroy()) dlg.show_all() return # Tell Somebody if parts[0][:4] == "tell" and \ parts[0][4:] in ("others", "all", "ics", "icsnoalias"): log.info("Ignoring tell %s: %s" % (parts[0][4:], " ".join(parts[1:]))) return if "feature" in parts: # Some engines send features after done=1, so we will iterate after done=1 too done1 = False # We skip parts before 'feature', as some engines give us lines like # White (1) : feature setboard=1 analyze...e="GNU Chess 5.07" done=1 parts = parts[parts.index("feature"):] for i, pair in enumerate(parts[1:]): # As "parts" is split with no thoughs on quotes or double quotes # we need to do some extra handling. if pair.find("=") < 0: continue key, value = pair.split("=", 1) if not key in self.features: continue if value.startswith('"') and value.endswith('"'): value = value[1:-1] # If our pair was unfinished, like myname="GNU, we search the # rest of the pairs for a quotating mark. elif value[0] == '"': rest = value[1:] + " " + " ".join(parts[2 + i:]) j = rest.find('"') if j == -1: log.warning("Missing endquotation in %s feature", extra={"task": self.defname}) value = rest else: value = rest[:j] elif value.isdigit(): value = int(value) if key in self.supported_features: print("accepted %s" % key, file=self.engine) else: print("rejected %s" % key, file=self.engine) if key == "done": if value == 1: done1 = True continue elif value == 0: log.info("Adds %d seconds timeout" % TIME_OUT_SECOND, extra={"task": self.defname}) # This'll buy you some more time self.timeout = time.time() + TIME_OUT_SECOND self.returnQueue.put("not ready") return if key == "smp" and value == 1: self.options["cores"] = { "name": "cores", "type": "spin", "default": 1, "min": 1, "max": 64 } elif key == "memory" and value == 1: self.options["memory"] = { "name": "memory", "type": "spin", "default": 32, "min": 1, "max": 4096 } elif key == "option" and key != "done": option = self.__parse_option(value) self.options[option["name"]] = option else: self.features[key] = value if key == "myname" and not self.name: self.setName(value) if done1: # Start a new game before using the engine: # (CECPv2 engines) print("new", file=self.engine) # We are now ready for play: self.emit("readyForOptions") self.emit("readyForMoves") self.returnQueue.put("ready") # A hack to get better names in protover 1. # Unfortunately it wont work for now, as we don't read any lines from # protover 1 engines. When should we stop? if self.protover == 1: if self.defname[0] in ''.join(parts): basis = self.defname[0] name = ' '.join( itertools.dropwhile(lambda part: basis not in part, parts)) self.features['myname'] = name if not self.name: self.setName(name)
def __parseLine (self, line): if not self.connected: return parts = line.split() if not parts: return #---------------------------------------------------------- Initializing if parts[0] == "id": self.ids[parts[1]] = " ".join(parts[2:]) if parts[1] == "name": self.setName(self.ids["name"]) return if parts[0] == "uciok": self.emit("readyForOptions") return if parts[0] == "readyok": self.emit("readyForMoves") return #------------------------------------------------------- Options parsing if parts[0] == "option": dic = {} last = 1 varlist = [] for i in range (2, len(parts)+1): if i == len(parts) or parts[i] in OPTKEYS: key = parts[last] value = " ".join(parts[last+1:i]) if "type" in dic and dic["type"] in TYPEDIC: value = TYPEDIC[dic["type"]](value) if key == "var": varlist.append(value) elif key == "type" and value == "string": dic[key] = "text" else: dic[key] = value last = i if varlist: dic["choices"] = varlist self.options[dic["name"]] = dic return #---------------------------------------------------------------- A Move if self.mode == NORMAL and parts[0] == "bestmove": with self.moveLock: self.needBestmove = False self.__sendQueuedGo() if self.ignoreNext: log.debug("__parseLine: line='%s' self.ignoreNext==True, returning" % \ line.strip(), extra={"task":self.defname}) self.ignoreNext = False self.readyForStop = True return if not self.waitingForMove: log.warning("__parseLine: self.waitingForMove==False, ignoring move=%s" % \ parts[1], extra={"task":self.defname}) self.pondermove = None return self.waitingForMove = False try: move = parseAny(self.board, parts[1]) except ParsingError as e: self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return if not validate(self.board, move): # This is critical. To avoid game stalls, we need to resign on # behalf of the engine. log.error("__parseLine: move=%s didn't validate, putting 'del' in returnQueue. self.board=%s" % \ (repr(move), self.board), extra={"task":self.defname}) self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return self._recordMove(self.board.move(move), move, self.board) log.debug("__parseLine: applied move=%s to self.board=%s" % \ (move, self.board), extra={"task":self.defname}) if self.ponderOn: self.pondermove = None # An engine may send an empty ponder line, simply to clear. if len(parts) == 4: # Engines don't always check for everything in their # ponders. Hence we need to validate. # But in some cases, what they send may not even be # correct AN - specially in the case of promotion. try: pondermove = parseAny(self.board, parts[3]) except ParsingError: pass else: if validate(self.board, pondermove): self.pondermove = pondermove self._startPonder() self.returnQueue.put(move) log.debug("__parseLine: put move=%s into self.returnQueue=%s" % \ (move, self.returnQueue.queue), extra={"task":self.defname}) return #----------------------------------------------------------- An Analysis if self.mode != NORMAL and parts[0] == "info" and "pv" in parts: multipv = 1 if "multipv" in parts: multipv = int(parts[parts.index("multipv")+1]) scoretype = parts[parts.index("score")+1] if scoretype in ('lowerbound', 'upperbound'): score = None else: score = int(parts[parts.index("score")+2]) if scoretype == 'mate': # print >> self.engine, "stop" if score != 0: sign = score/abs(score) score = sign*MATE_VALUE movstrs = parts[parts.index("pv")+1:] try: moves = listToMoves (self.board, movstrs, AN, validate=True, ignoreErrors=False) except ParsingError as e: # ParsingErrors may happen when parsing "old" lines from # analyzing engines, which haven't yet noticed their new tasks log.debug("__parseLine: Ignored (%s) from analyzer: ParsingError%s" % \ (' '.join(movstrs),e), extra={"task":self.defname}) return if "depth" in parts: depth = parts[parts.index("depth")+1] else: depth = "" if multipv <= len(self.analysis): self.analysis[multipv - 1] = (moves, score, depth) self.emit("analyze", self.analysis) return #----------------------------------------------- An Analyzer bestmove if self.mode != NORMAL and parts[0] == "bestmove": with self.moveLock: log.debug("__parseLine: processing analyzer bestmove='%s'" % \ line.strip(), extra={"task":self.defname}) self.needBestmove = False self.__sendQueuedGo(sendlast=True) return # Stockfish complaining it received a 'stop' without a corresponding 'position..go' if line.strip() == "Unknown command: stop": with self.moveLock: log.debug("__parseLine: processing '%s'" % line.strip(), extra={"task":self.defname}) self.ignoreNext = False self.needBestmove = False self.readyForStop = False self.__sendQueuedGo() return
def test_validate(self): """Testing validate move in Sittuyin variant""" board = SittuyinBoard(setup=FEN0) print(board) self.assertTrue(validate(board, parseAN(board, 'f4f3'))) self.assertTrue(validate(board, parseAN(board, 'b2b1'))) self.assertTrue(validate(board, parseAN(board, 'b2c1'))) # no promotion if we have Met (queen) self.assertFalse(validate(board, parseAN(board, 'b2b2f'))) self.assertFalse(validate(board, parseAN(board, 'b2a1f'))) self.assertFalse(validate(board, parseAN(board, 'f4f3f'))) self.assertFalse(validate(board, parseAN(board, 'b2b1f'))) self.assertFalse(validate(board, parseAN(board, 'b2c1f'))) board = SittuyinBoard(setup=FEN1) print(board) # but (optional) promotion if we don't have Met (queen) self.assertFalse(validate(board, parseAN(board, 'b2b2f'))) self.assertFalse(validate(board, parseSAN(board, 'b2=f'))) self.assertEqual(parseAN(board, 'b2b2f'), parseSAN(board, 'b2=f')) self.assertTrue(validate(board, parseAN(board, 'b2a1f'))) self.assertTrue(validate(board, parseSAN(board, 'a1=f'))) self.assertEqual(parseAN(board, 'b2a1f'), parseSAN(board, 'a1=f')) self.assertTrue(validate(board, parseAN(board, 'b2c1'))) self.assertTrue(validate(board, parseAN(board, 'f4f3'))) self.assertFalse(validate(board, parseAN(board, 'f4f3f'))) self.assertFalse(validate(board, parseAN(board, 'b2b2'))) self.assertFalse(validate(board, parseAN(board, 'b2b1f'))) self.assertFalse(validate(board, parseAN(board, 'b2c1f'))) board = SittuyinBoard(setup=FEN2) print(board) # simple pawn move can give check self.assertTrue(validate(board, parseAN(board, 'd6d7'))) # pawn can promote in place self.assertTrue(validate(board, parseAN(board, 'd6d6f'))) # pawn promotion move can't give check self.assertFalse(validate(board, parseAN(board, 'd6c7f'))) self.assertFalse(validate(board, parseAN(board, 'd6e7f'))) board = SittuyinBoard(setup=FEN3) print(board) self.assertTrue(validate(board, parseAN(board, 'd6d7'))) # last pawn being enywhere can promote self.assertTrue(validate(board, parseAN(board, 'd6d6f'))) self.assertTrue(validate(board, parseAN(board, 'd6c7f'))) self.assertTrue(validate(board, parseAN(board, 'd6c5f'))) self.assertTrue(validate(board, parseAN(board, 'd6e5f'))) # pawn promotion move can't give check self.assertFalse(validate(board, parseAN(board, 'd6e7f')))
def __parseLine (self, line): if line[0:1] == "#": # Debug line which we shall ignore as specified in CECPv2 specs return # log.debug("__parseLine: line=\"%s\"\n" % line.strip(), self.defname) parts = whitespaces.split(line.strip()) if parts[0] == "pong": self.lastpong = int(parts[1]) return # Illegal Move if parts[0].lower().find("illegal") >= 0: log.warn("__parseLine: illegal move: line=\"%s\", board=%s" \ % (line.strip(), self.board), self.defname) if parts[-2] == "sd" and parts[-1].isdigit(): print >> self.engine, "depth", parts[-1] return # A Move (Perhaps) if self.board: if parts[0] == "move": movestr = parts[1] # Old Variation elif d_plus_dot_expr.match(parts[0]) and parts[1] == "...": movestr = parts[2] else: movestr = False if movestr: log.debug("__parseLine: acquiring self.boardLock\n", self.defname) self.waitingForMove = False self.readyForMoveNowCommand = False self.boardLock.acquire() try: if self.engineIsInNotPlaying: # If engine was set in pause just before the engine sent its # move, we ignore it. However the engine has to know that we # ignored it, and thus we step it one back log.info("__parseLine: Discarding engine's move: %s\n" % movestr, self.defname) print >> self.engine, "undo" return else: try: move = parseAny(self.board, movestr) except ParsingError, e: self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return if validate(self.board, move): self.board = None self.returnQueue.put(move) return self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return finally: log.debug("__parseLine(): releasing self.boardLock\n", self.defname) self.boardLock.release() self.movecon.acquire() self.movecon.notifyAll() self.movecon.release() # Analyzing if self.engineIsInNotPlaying: if parts[:4] == ["0","0","0","0"]: # Crafty doesn't analyze until it is out of book print >> self.engine, "book off" return match = anare.match(line) if match: score, moves = match.groups() if "mat" in score.lower() or "#" in moves: # Will look either like -Mat 3 or Mat3 scoreval = MATE_VALUE if score.startswith('-'): scoreval = -scoreval else: scoreval = int(score) mvstrs = movere.findall(moves) try: moves = listToMoves (self.board, mvstrs, type=None, validate=True, ignoreErrors=False) except: # Errors may happen when parsing "old" lines from # analyzing engines, which haven't yet noticed their new tasks log.debug('Ignored an "old" line from analyzer: %s\n' % mvstrs, self.defname) return # Don't emit if we weren't able to parse moves, or if we have a move # to kill the opponent king - as it confuses many engines if moves and not self.board.board.opIsChecked(): #self.emit("analyze", [(moves, scoreval)]) return # Offers draw if parts[0:2] == ["offer", "draw"]: #self.emit("accept", Offer(DRAW_OFFER)) return # Resigns if parts[0] == "resign" or \ (parts[0] == "tellics" and parts[1] == "resign"): # buggy crafty # Previously: if "resign" in parts, # however, this is too generic, since "hint", "bk", # "feature option=.." and possibly other, future CECPv2 # commands can validly contain the word "resign" without this # being an intentional resign offer. #self.emit("offer", Offer(RESIGNATION)) return #if parts[0].lower() == "error": # return #Tell User Error if parts[0] == "tellusererror": # Create a non-modal non-blocking message dialog with the error: dlg = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE, message_format=None) # Use the engine name if already known, otherwise the defname: displayname = self.name if not displayname: displayname = self.defname # Compose the dialog text: dlg.set_markup(gobject.markup_escape_text(_("The engine %s reports an error:") % displayname) + "\n\n" + gobject.markup_escape_text(" ".join(parts[1:]))) # handle response signal so the "Close" button works: dlg.connect("response", lambda dlg, x: dlg.destroy()) dlg.show_all() return # Tell Somebody if parts[0][:4] == "tell" and \ parts[0][4:] in ("others", "all", "ics", "icsnoalias"): log.info("Ignoring tell %s: %s\n" % (parts[0][4:], " ".join(parts[1:]))) return if "feature" in parts: # Some engines send features after done=1, so we will iterate after done=1 too done1 = False # We skip parts before 'feature', as some engines give us lines like # White (1) : feature setboard=1 analyze...e="GNU Chess 5.07" done=1 parts = parts[parts.index("feature"):] for i, pair in enumerate(parts[1:]): # As "parts" is split with no thoughs on quotes or double quotes # we need to do some extra handling. if pair.find("=") < 0: continue key, value = pair.split("=",1) if value[0] in ('"',"'") and value[-1] in ('"',"'"): value = value[1:-1] # If our pair was unfinished, like myname="GNU, we search the # rest of the pairs for a quotating mark. elif value[0] in ('"',"'"): rest = value[1:] + " " + " ".join(parts[2+i:]) i = rest.find('"') j = rest.find("'") if i + j == -2: log.warn("Missing endquotation in %s feature", self.defname) value = rest elif min(i, j) != -1: value = rest[:min(i, j)] else: l = max(i, j) value = rest[:l] elif value.isdigit(): value = int(value) if key in self.supported_features: print >> self.engine, "accepted %s" % key else: print >> self.engine, "rejected %s" % key if key == "done": if value == 1: done1 = True continue elif value == 0: log.info("Adds %d seconds timeout\n" % TIME_OUT_SECOND, self.defname) # This'll buy you some more time self.timeout = time.time()+TIME_OUT_SECOND self.returnQueue.put("not ready") return if key == "smp" and value == 1: self.options["cores"] = {"name": "cores", "type": "spin", "default": 1, "min": 1, "max": 64} elif key == "memory" and value == 1: self.options["memory"] = {"name": "memory", "type": "spin", "default": 32, "min": 1, "max": 4096} elif key == "option" and key != "done": option = self.__parse_option(value) self.options[option["name"]] = option else: self.features[key] = value if key == "myname" and not self.name: self.setName(value) if done1: # Start a new game before using the engine: # (CECPv2 engines) print >> self.engine, "new" # We are now ready for play: #self.emit("readyForOptions") #self.emit("readyForMoves") self.returnQueue.put("ready") # A hack to get better names in protover 1. # Unfortunately it wont work for now, as we don't read any lines from # protover 1 engines. When should we stop? if self.protover == 1: if self.defname[0] in ''.join(parts): basis = self.defname[0] name = ' '.join(itertools.dropwhile(lambda part: basis not in part, parts)) self.features['myname'] = name if not self.name: self.setName(name) def __parse_option(self, option): if " -check " in option: name, value = option.split(" -check ") return {"type": "check", "name": name, "default": bool(int(value))} elif " -spin " in option: name, value = option.split(" -spin ") defv, minv, maxv = value.split() return {"type": "spin", "name": name, "default": int(defv), "min": int(minv), "max": int(maxv)} elif " -slider " in option: name, value = option.split(" -slider ") defv, minv, maxv = value.split() return {"type": "spin", "name": name, "default": int(defv), "min": int(minv), "max": int(maxv)} elif " -string " in option: name, value = option.split(" -string ") return {"type": "text", "name": name, "default": value} elif " -file " in option: name, value = option.split(" -file ") return {"type": "text", "name": name, "default": value} elif " -path " in option: name, value = option.split(" -path ") return {"type": "text", "name": name, "default": value} elif " -combo " in option: name, value = option.split(" -combo ") return {"type": "combo", "name": name, "default": value} elif " -button" in option: pos = option.find(" -button") return {"type": "button", "name": option[:pos]} elif " -save" in option: pos = option.find(" -save") return {"type": "button", "name": option[:pos]} elif " -reset" in option: pos = option.find(" -reset") return {"type": "button", "name": option[:pos]} #=========================================================================== # Info #=========================================================================== def canAnalyze (self): assert self.ready, "Still waiting for done=1" return self.features["analyze"] def maxAnalysisLines (self): return 1 def requestMultiPV (self, setting): return 1 def isAnalyzing (self): return self.mode in (ANALYZING, INVERSE_ANALYZING) def __repr__ (self): if self.name: return self.name return self.features["myname"]
def test_validate(self): """Testing validate move in Sittuyin variant""" board = SittuyinBoard(setup=FEN0) print(board) # no promotion if we have Met (queen) self.assertTrue(validate(board, parseSAN(board, 'f4f3'))) self.assertTrue(validate(board, parseSAN(board, 'b2b1'))) self.assertTrue(validate(board, parseSAN(board, 'bxc1'))) self.assertTrue(not validate(board, parseSAN(board, 'f4f3f'))) self.assertTrue(not validate(board, parseSAN(board, 'b2b2f'))) self.assertTrue(not validate(board, parseSAN(board, 'b2b1f'))) self.assertTrue(not validate(board, parseSAN(board, 'bxc1f'))) board = SittuyinBoard(setup=FEN1) print(board) # but (optional) promotion if we don't have Met (queen) self.assertTrue(validate(board, parseSAN(board, 'f4f3f'))) self.assertTrue(validate(board, parseSAN(board, 'b2b2f'))) self.assertTrue(validate(board, parseSAN(board, 'b2b1'))) self.assertTrue(validate(board, parseSAN(board, 'bxc1'))) self.assertTrue(validate(board, parseSAN(board, 'f4f3'))) self.assertTrue(not validate(board, parseSAN(board, 'b2b2'))) self.assertTrue(not validate(board, parseSAN(board, 'b2b1f'))) self.assertTrue(not validate(board, parseSAN(board, 'bxc1f')))
def validate(self, cord0, cord1): assert cord0 != None and cord1 != None, "cord0: " + str( cord0) + ", cord1: " + str(cord1) if self.getBoard()[cord0] == None: return False return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def __parseLine(self, line): if not self.connected: return parts = line.split() if not parts: return #---------------------------------------------------------- Initializing if parts[0] == "id": self.ids[parts[1]] = " ".join(parts[2:]) if parts[1] == "name": self.setName(self.ids["name"]) return if parts[0] == "uciok": self.emit("readyForOptions") return if parts[0] == "readyok": self.emit("readyForMoves") return #------------------------------------------------------- Options parsing if parts[0] == "option": dic = {} last = 1 varlist = [] for i in range(2, len(parts) + 1): if i == len(parts) or parts[i] in OPTKEYS: key = parts[last] value = " ".join(parts[last + 1:i]) if "type" in dic and dic["type"] in TYPEDIC: value = TYPEDIC[dic["type"]](value) if key == "var": varlist.append(value) elif key == "type" and value == "string": dic[key] = "text" else: dic[key] = value last = i if varlist: dic["choices"] = varlist self.options[dic["name"]] = dic return #---------------------------------------------------------------- A Move if self.mode == NORMAL and parts[0] == "bestmove": with self.moveLock: self.needBestmove = False self.__sendQueuedGo() if self.ignoreNext: log.debug("__parseLine: line='%s' self.ignoreNext==True, returning" % \ line.strip(), extra={"task":self.defname}) self.ignoreNext = False self.readyForStop = True return if not self.waitingForMove: log.warning("__parseLine: self.waitingForMove==False, ignoring move=%s" % \ parts[1], extra={"task":self.defname}) self.pondermove = None return self.waitingForMove = False try: move = parseAny(self.board, parts[1]) except ParsingError as e: self.end( WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return if not validate(self.board, move): # This is critical. To avoid game stalls, we need to resign on # behalf of the engine. log.error("__parseLine: move=%s didn't validate, putting 'del' in returnQueue. self.board=%s" % \ (repr(move), self.board), extra={"task":self.defname}) self.end( WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return self._recordMove(self.board.move(move), move, self.board) log.debug("__parseLine: applied move=%s to self.board=%s" % \ (move, self.board), extra={"task":self.defname}) if self.ponderOn: self.pondermove = None # An engine may send an empty ponder line, simply to clear. if len(parts) == 4: # Engines don't always check for everything in their # ponders. Hence we need to validate. # But in some cases, what they send may not even be # correct AN - specially in the case of promotion. try: pondermove = parseAny(self.board, parts[3]) except ParsingError: pass else: if validate(self.board, pondermove): self.pondermove = pondermove self._startPonder() self.returnQueue.put(move) log.debug("__parseLine: put move=%s into self.returnQueue=%s" % \ (move, self.returnQueue.queue), extra={"task":self.defname}) return #----------------------------------------------------------- An Analysis if self.mode != NORMAL and parts[0] == "info" and "pv" in parts: multipv = 1 if "multipv" in parts: multipv = int(parts[parts.index("multipv") + 1]) scoretype = parts[parts.index("score") + 1] if scoretype in ('lowerbound', 'upperbound'): score = None else: score = int(parts[parts.index("score") + 2]) if scoretype == 'mate': # print >> self.engine, "stop" if score != 0: sign = score / abs(score) score = sign * MATE_VALUE movstrs = parts[parts.index("pv") + 1:] try: moves = listToMoves(self.board, movstrs, AN, validate=True, ignoreErrors=False) except ParsingError as e: # ParsingErrors may happen when parsing "old" lines from # analyzing engines, which haven't yet noticed their new tasks log.debug("__parseLine: Ignored (%s) from analyzer: ParsingError%s" % \ (' '.join(movstrs),e), extra={"task":self.defname}) return if "depth" in parts: depth = parts[parts.index("depth") + 1] else: depth = "" if multipv <= len(self.analysis): self.analysis[multipv - 1] = (moves, score, depth) self.emit("analyze", self.analysis) return #----------------------------------------------- An Analyzer bestmove if self.mode != NORMAL and parts[0] == "bestmove": with self.moveLock: log.debug("__parseLine: processing analyzer bestmove='%s'" % \ line.strip(), extra={"task":self.defname}) self.needBestmove = False self.__sendQueuedGo(sendlast=True) return # Stockfish complaining it received a 'stop' without a corresponding 'position..go' if line.strip() == "Unknown command: stop": with self.moveLock: log.debug("__parseLine: processing '%s'" % line.strip(), extra={"task": self.defname}) self.ignoreNext = False self.needBestmove = False self.readyForStop = False self.__sendQueuedGo() return
def test_validate(self): """Testing validate move in Placement variant""" board = PlacementBoard(setup=FEN0) print(board) # only drop moves to base line allowed self.assertTrue(validate(board, parseSAN(board, 'K@a1'))) self.assertTrue(validate(board, parseSAN(board, 'K@b1'))) self.assertTrue(validate(board, parseSAN(board, 'K@h1'))) self.assertTrue(not validate(board, parseSAN(board, 'K@a2'))) self.assertTrue(not validate(board, parseSAN(board, 'K@a3'))) self.assertTrue(not validate(board, parseSAN(board, 'K@a8'))) self.assertTrue(not validate(board, parseSAN(board, 'b4'))) board = PlacementBoard(setup=FEN1) print(board) # only drop moves to base line allowed self.assertTrue(validate(board, parseSAN(board, 'K@a8'))) self.assertTrue(validate(board, parseSAN(board, 'K@b8'))) self.assertTrue(validate(board, parseSAN(board, 'K@h8'))) self.assertTrue(not validate(board, parseSAN(board, 'K@a7'))) self.assertTrue(not validate(board, parseSAN(board, 'K@a6'))) self.assertTrue(not validate(board, parseSAN(board, 'K@a1'))) self.assertTrue(not validate(board, parseSAN(board, 'b5'))) board = PlacementBoard(setup=FEN2) print(board) # bishops have to be placed on opposite colored fields self.assertTrue(validate(board, parseSAN(board, 'B@a1'))) self.assertTrue(validate(board, parseSAN(board, 'B@b1'))) self.assertTrue(validate(board, parseSAN(board, 'B@c1'))) self.assertTrue(validate(board, parseSAN(board, 'B@g1'))) self.assertTrue(validate(board, parseSAN(board, 'N@a1'))) self.assertTrue(validate(board, parseSAN(board, 'N@c1'))) self.assertTrue(validate(board, parseSAN(board, 'N@g1'))) self.assertTrue(not validate(board, parseSAN(board, 'N@b1'))) board = PlacementBoard(setup=FEN3) print(board) # bishops have to be placed on opposite colored fields self.assertTrue(validate(board, parseSAN(board, 'B@b1'))) self.assertTrue(validate(board, parseSAN(board, 'N@c1'))) self.assertTrue(validate(board, parseSAN(board, 'N@g1'))) self.assertTrue(not validate(board, parseSAN(board, 'B@c1'))) self.assertTrue(not validate(board, parseSAN(board, 'B@g1'))) self.assertTrue(not validate(board, parseSAN(board, 'N@b1')))
def __parseLine (self, line): if not self.connected: return parts = line.split() if not parts: return #---------------------------------------------------------- Initializing if parts[0] == "id": self.ids[parts[1]] = " ".join(parts[2:]) if parts[1] == "name": self.setName(self.ids["name"]) return if parts[0] == "uciok": #self.emit("readyForOptions") return if parts[0] == "readyok": #self.emit("readyForMoves") return #------------------------------------------------------- Options parsing if parts[0] == "option": dic = {} last = 1 varlist = [] for i in xrange (2, len(parts)+1): if i == len(parts) or parts[i] in OPTKEYS: key = parts[last] value = " ".join(parts[last+1:i]) if "type" in dic and dic["type"] in TYPEDIC: value = TYPEDIC[dic["type"]](value) if key == "var": varlist.append(value) elif key == "type" and value == "string": dic[key] = "text" else: dic[key] = value last = i if varlist: dic["choices"] = varlist self.options[dic["name"]] = dic return #---------------------------------------------------------------- A Move if self.mode == NORMAL and parts[0] == "bestmove": with self.moveLock: self.needBestmove = False self.__sendQueuedGo() if self.ignoreNext: log.debug("__parseLine: line='%s' self.ignoreNext==True, returning\n" % \ line.strip(), self.defname) self.ignoreNext = False self.readyForStop = True return if not self.waitingForMove: log.warn("__parseLine: self.waitingForMove==False, ignoring move=%s\n" % \ parts[1], self.defname) self.pondermove = None return self.waitingForMove = False try: move = parseAN(self.board, parts[1]) except ParsingError, e: self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return if not validate(self.board, move): # This is critical. To avoid game stalls, we need to resign on # behalf of the engine. log.error("__parseLine: move=%s didn't validate, putting 'del' in returnQueue. self.board=%s\n" % \ (repr(move), self.board), self.defname) self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return self._recordMove(self.board.move(move), move, self.board) log.debug("__parseLine: applied move=%s to self.board=%s\n" % \ (move, self.board), self.defname) if self.ponderOn: self.pondermove = None # An engine may send an empty ponder line, simply to clear. if len(parts) == 4: # Engines don't always check for everything in their # ponders. Hence we need to validate. # But in some cases, what they send may not even be # correct AN - specially in the case of promotion. try: pondermove = parseAN(self.board, parts[3]) except ParsingError: pass else: if validate(self.board, pondermove): self.pondermove = pondermove self._startPonder() self.returnQueue.put(move) log.debug("__parseLine: put move=%s into self.returnQueue=%s\n" % \ (move, self.returnQueue.queue), self.defname) return