def tip_mobility(model, ply, phase): board = model.getBoardAtPly(ply).board colorBackup = board.color # People need a chance to get developed # if model.ply < 16: # return board.setColor(WHITE) wmoves = len([ move for move in genAllMoves(board) if KNIGHT <= board.arBoard[FCORD(move)] <= QUEEN and bitPosArray[TCORD(move)] & brank48[WHITE] and staticExchangeEvaluate(board, move) >= 0 ]) board.setColor(BLACK) bmoves = len([ move for move in genAllMoves(board) if KNIGHT <= board.arBoard[FCORD(move)] <= QUEEN and bitPosArray[TCORD(move)] & brank48[BLACK] and staticExchangeEvaluate(board, move) >= 0 ]) board.setColor(colorBackup) if wmoves - phase >= (bmoves + 1) * 7: yield wmoves - bmoves, _("Black has a rather cramped position") elif wmoves - phase >= (bmoves + 1) * 3: yield wmoves - bmoves, _("Black has a slightly cramped position") elif bmoves - phase >= (wmoves + 1) * 7: yield wmoves - bmoves, _("White has a rather cramped position") elif bmoves - phase >= (wmoves + 1) * 3: yield wmoves - bmoves, _("White has a slightly cramped position")
def attack_type(model, ply, phase): # We set bishop value down to knight value, as it is what most people expect bishopBackup = PIECE_VALUES[BISHOP] PIECE_VALUES[BISHOP] = PIECE_VALUES[KNIGHT] board = model.getBoardAtPly(ply).board oldboard = model.getBoardAtPly(ply - 1).board if ply - model.lowply >= 2: oldmove = model.getMoveAtPly(ply - 2).move oldboard3 = model.getBoardAtPly(ply - 2).board else: oldmove = None move = model.getMoveAtPly(ply - 1).move tcord = TCORD(move) if oldboard.arBoard[tcord] != EMPTY: if not (board.variant == FISCHERRANDOMCHESS and \ FLAG(move) in (KING_CASTLE, QUEEN_CASTLE)): if oldmove and oldboard3.arBoard[TCORD(oldmove)] != EMPTY and \ TCORD(oldmove) == tcord: yield _("takes back material") else: see = staticExchangeEvaluate(oldboard, move) if see < 0: yield _("sacrifies material") elif see == 0: yield _("exchanges material") elif see > 0: yield _("captures material") PIECE_VALUES[BISHOP] = bishopBackup
def offencive_moves_rook(model, ply, phase): move = model.getMoveAtPly(ply - 1).move tcord = TCORD(move) board = model.getBoardAtPly(ply).board color = 1 - board.color opcolor = 1 - color # We also detect rook-to-open castlings if board.arBoard[tcord] == KING: if FLAG(move) == QUEEN_CASTLE: tcord = tcord + 1 elif FLAG(move) == KING_CASTLE: tcord = tcord - 1 if board.arBoard[tcord] != ROOK: return color = 1 - board.color opcolor = 1 - color pawns = board.boards[color][PAWN] oppawns = board.boards[opcolor][PAWN] ffile = fileBits[FILE(FCORD(move))] tfile = fileBits[FILE(tcord)] if ffile & pawns and not tfile & pawns and bin(pawns).count("1") >= 3: if not tfile & oppawns: yield _("moves a rook to an open file") else: yield _("moves an rook to a half-open file")
def defencive_moves_safety(model, ply, phase): board = model.getBoardAtPly(ply).board oldboard = model.getBoardAtPly(ply - 1).board if board.arBoard[TCORD(model.getMoveAtPly(ply - 1).move)] != KING: return color = oldboard.color opcolor = 1 - color delta_eval_king = leval.evalKing(board, color, phase) - \ leval.evalKing(oldboard, color, phase) # PyChess points tropism to queen for phase <= 3. Thus we set a high phase delta_eval_tropism = leval.evalKingTropism(board, opcolor, 10) - \ leval.evalKingTropism(oldboard, opcolor, 10) # Notice, that tropism was negative delta_score = delta_eval_king - delta_eval_tropism / 2 if delta_score > 35: yield _("improves king safety") elif delta_score > 15: yield _("slightly improves king safety")
def state_trappedBishops(model, ply, phase): """ Check for bishops trapped at A2/H2/A7/H7 """ board = model.getBoardAtPly(ply).board oldboard = model.getBoardAtPly(ply - 1).board opcolor = board.color move = model.getMoveAtPly(ply - 1).move tcord = TCORD(move) # Only a pawn is able to trap a bishop if board.arBoard[tcord] != PAWN: return if tcord == B3: cord = A2 elif tcord == G3: cord = H2 elif tcord == B6: cord = A7 elif tcord == G6: cord = H7 else: return s = leval.evalTrappedBishops(board, opcolor) olds = leval.evalTrappedBishops(oldboard, opcolor) # We have got more points -> We have trapped a bishop if s > olds: yield 300 / phase, _( "%(opcolor)s has a new trapped bishop on %(cord)s") % { 'opcolor': reprColor[opcolor], 'cord': reprCord[cord]}
def simple_tropism(model, ply, phase): board = model.getBoardAtPly(ply).board oldboard = model.getBoardAtPly(ply - 1).board color = oldboard.color move = model.getMoveAtPly(ply - 1).move fcord = FCORD(move) tcord = TCORD(move) arBoard = board.arBoard if arBoard[tcord] != PAWN: score = leval.evalKingTropism(board, color, phase) oldscore = leval.evalKingTropism(oldboard, color, phase) else: if color == WHITE: rank23 = brank67[BLACK] else: rank23 = brank67[WHITE] if bitPosArray[fcord] & rank23: yield 2, _("develops a pawn: %s") % reprCord[tcord] else: yield 1, _( "brings a pawn closer to the backrow: %s") % reprCord[tcord] return king = board.kings[color] opking = board.kings[1 - color] if score > oldscore: # in FISCHERRANDOMCHESS unusual casting case the tcord is # the involved rook's position, not the king's destination! flag = move >> 12 if flag in (KING_CASTLE, QUEEN_CASTLE): piece = KING else: piece = arBoard[tcord] if phase >= 5 or distance[piece][fcord][opking] < \ distance[piece][fcord][king]: yield score - oldscore, _( "brings a %(piece)s closer to enemy king: %(cord)s") % { 'piece': reprPiece[piece], 'cord': reprCord[tcord] } else: yield (score - oldscore) * 2, _("develops a %(piece)s: %(cord)s") % { 'piece': reprPiece[piece].lower(), 'cord': reprCord[tcord] }
def offencive_moves_fianchetto(model, ply, phase): board = model.getBoardAtPly(ply).board tcord = TCORD(model.getMoveAtPly(ply - 1).move) movingcolor = 1 - board.color if movingcolor == WHITE: if board.castling & W_OO and tcord == G2: yield _("moves bishop into fianchetto: %s") % "g2" elif board.castling & W_OOO and tcord == B2: yield _("moves bishop into fianchetto: %s") % "b2" else: if board.castling & B_OO and tcord == G7: yield _("moves bishop into fianchetto: %s") % "g7" elif board.castling & B_OOO and tcord == B7: yield _("moves bishop into fianchetto: %s") % "b7"
def simple_activity(model, ply, phase): board = model.getBoardAtPly(ply).board oldboard = model.getBoardAtPly(ply - 1).board move = model.getMoveAtPly(ply - 1).move fcord = FCORD(move) tcord = TCORD(move) board.setColor(1 - board.color) moves = len([m for m in genAllMoves(board) if FCORD(m) == tcord]) board.setColor(1 - board.color) oldmoves = len([m for m in genAllMoves(oldboard) if FCORD(m) == fcord]) if moves > oldmoves: yield (moves - oldmoves) / 2, _("places a %(piece)s more active: %(cord)s") % { 'piece': reprPiece[board.arBoard[tcord]].lower(), 'cord': reprCord[tcord]}
def offencive_moves_pin(model, ply, phase): board = model.getBoardAtPly(ply).board move = model.getMoveAtPly(ply - 1).move fcord = FCORD(move) tcord = TCORD(move) piece = board.arBoard[tcord] ray = 0 if piece in (BISHOP, QUEEN): ray |= (ray45[tcord] | ray135[tcord]) & ~(ray45[fcord] | ray135[fcord]) if piece in (ROOK, QUEEN): ray |= (ray00[tcord] | ray90[tcord]) & ~(ray00[fcord] | ray90[fcord]) if ray: for c in iterBits(ray & board.friends[board.color]): # We don't pin on pieces that are less worth than us if not PIECE_VALUES[piece] < PIECE_VALUES[board.arBoard[c]]: continue # There should be zero friendly pieces in between ray = fromToRay[tcord][c] if ray & board.friends[1 - board.color]: continue # There should be exactly one opponent piece in between op = clearBit(ray & board.friends[board.color], c) if bin(op).count("1") != 1: continue # The king can't be pinned pinned = lastBit(op) oppiece = board.arBoard[pinned] if oppiece == KING: continue # Yield yield _( "pins an enemy %(oppiece)s on the %(piece)s at %(cord)s") % { 'oppiece': reprPiece[oppiece].lower(), 'piece': reprPiece[board.arBoard[c]].lower(), 'cord': reprCord[c] }
def __chooseComment(self, model, ply): if ply == model.lowply: return _("Initial position") ######################################################################## # Set up variables ######################################################################## color = model.getBoardAtPly(ply - 1).board.color s, phase = evalMaterial( model.getBoardAtPly(ply).board, model.getBoardAtPly(ply - 1).color) # * Final: Will be shown alone: "mates", "draws" # * Prefix: Will always be shown: "castles", "promotes" # * Attack: Will always be shown: "threaten", "preassures", "defendes" # * Moves (s): Will always be shown: "put into *" # * State: (s) Will always be shown: "new *" # * Simple: (s) Max one will be shown: "develops", "activity" # * Tip: (s) Will sometimes be shown: "pawn storm", "cramped position" ######################################################################## # Call strategic evaluation functions ######################################################################## def getMessages(prefix): messages = [] for functionName in dir(strateval): if not functionName.startswith(prefix + "_"): continue function = getattr(strateval, functionName) messages.extend(function(model, ply, phase)) return messages # move = model.moves[-1].move # print "----- %d - %s -----" % (model.ply/2, toSAN(oldboard, move)) # ---------------------------------------------------------------------- # Final # ---------------------------------------------------------------------- messages = getMessages("final") if messages: return "%s %s" % (reprColor[color], messages[0]) # --- strings = [] # ---------------------------------------------------------------------- # Attacks # ---------------------------------------------------------------------- messages = getMessages("attack") for message in messages: strings.append("%s %s" % (reprColor[color], message)) # ---------------------------------------------------------------------- # Check for prefixes # ---------------------------------------------------------------------- messages = getMessages("prefix") if messages: prefix = messages[0] else: prefix = "" # ---------------------------------------------------------------------- # Check for special move stuff. All of which accept prefixes # ---------------------------------------------------------------------- for message in getMessages("offencive_moves") + getMessages("defencive_moves"): if prefix: strings.append( "%s %s %s %s" % (reprColor[color], prefix, _("and") + "\n", message)) prefix = "" else: strings.append("%s %s" % (reprColor[color], message)) # ---------------------------------------------------------------------- # Simple # ---------------------------------------------------------------------- # We only add simples if there hasn't been too much stuff to say if not strings: messages = getMessages("simple") if messages: messages.sort(reverse=True) score, message = messages[0] if prefix: strings.append( "%s %s %s %s" % (reprColor[color], prefix, _("and") + "\n", message)) prefix = "" # ---------------------------------------------------------------------- # Prefix fallback # ---------------------------------------------------------------------- # There was nothing to apply the prefix to, so we just post it here # before the states and tips if prefix: strings.append("%s %s" % (reprColor[color], prefix)) prefix = "" # ---------------------------------------------------------------------- # State # ---------------------------------------------------------------------- messages = getMessages("state") messages.sort(reverse=True) for score, message in messages: strings.append(message) # ---------------------------------------------------------------------- # Tips # ---------------------------------------------------------------------- tips = getMessages("tip") tips.sort(reverse=True) for (score, tip) in tips: if tip in self.givenTips: oldscore, oldply = self.givenTips[tip] if score < oldscore * 1.3 or model.ply < oldply + 10: continue self.givenTips[tip] = (score, model.ply) strings.append(tip) break # ---------------------------------------------------------------------- # Last solution # ---------------------------------------------------------------------- if not strings: tcord = TCORD(model.getMoveAtPly(ply - 1).move) piece = model.getBoardAtPly(ply).board.arBoard[tcord] strings.append(_("%(color)s moves a %(piece)s to %(cord)s") % { 'color': reprColor[color], 'piece': reprPiece[piece], 'cord': reprCord[tcord] }) return ";\n".join(strings)
def state_pawn(model, ply, phase): board = model.getBoardAtPly(ply).board oldboard = model.getBoardAtPly(ply - 1).board color = 1 - board.color opcolor = 1 - color move = model.getMoveAtPly(ply - 1).move pawns = board.boards[color][PAWN] oppawns = board.boards[opcolor][PAWN] oldpawns = oldboard.boards[color][PAWN] oldoppawns = oldboard.boards[opcolor][PAWN] # Passed pawns for cord in iterBits(pawns): if not oppawns & passedPawnMask[color][cord]: if color == WHITE: frontCords = fromToRay[cord][cord | 56] else: frontCords = fromToRay[cord][cord & 7] if frontCords & pawns: continue # Was this a passed pawn before? if oldpawns & bitPosArray[cord] and \ not oldoppawns & passedPawnMask[color][cord] and \ not frontCords & oldpawns: continue # Is this just a passed pawn that has been moved? if TCORD(move) == cord: frontCords |= bitPosArray[cord] if not frontCords & oldpawns and \ not oldoppawns & passedPawnMask[color][FCORD(move)]: continue score = (passedScores[color][cord >> 3] * phase) yield score, _("%(color)s has a new passed pawn on %(cord)s") % { 'color': reprColor[color], 'cord': reprCord[cord] } # Double pawns found_doubles = [] found_halfopen_doubles = [] found_white_isolates = [] found_black_isolates = [] for file in range(8): bits = fileBits[file] count = bin(pawns & bits).count("1") oldcount = bin(oldpawns & bits).count("1") opcount = bin(oppawns & bits).count("1") oldopcount = bin(oldoppawns & bits).count("1") # Single pawn -> double pawns if count > oldcount >= 1: if not opcount: found_halfopen_doubles.append(reprFile[file]) else: found_doubles.append(reprFile[file]) # Closed file double pawn -> half-open file double pawn elif count > 1 and opcount == 0 and oldopcount > 0: found_halfopen_doubles.append(reprFile[file]) # Isolated pawns if color == WHITE: wpawns = pawns oldwpawns = oldpawns bpawns = oppawns oldbpawns = oldoppawns else: bpawns = pawns oldbpawns = oldpawns wpawns = oppawns oldwpawns = oldoppawns if wpawns & bits and not wpawns & isolaniMask[file] and \ (not oldwpawns & bits or oldwpawns & isolaniMask[file]): found_white_isolates.append(reprFile[file]) if bpawns & bits and not bpawns & isolaniMask[file] and \ (not oldbpawns & bits or oldbpawns & isolaniMask[file]): found_black_isolates.append(reprFile[file]) # We need to take care of 'worstcases' like: "got new double pawns in the a # file, in the half-open b, c and d files and in the open e and f files" doubles_count = len(found_doubles) + len(found_halfopen_doubles) if doubles_count > 0: parts = [] for type_, list_ in (("", found_doubles), (_("half-open") + " ", found_halfopen_doubles)): if len(list_) == 1: parts.append( _("in the %(x)s%(y)s file") % { 'x': type_, 'y': list_[0] }) elif len(list_) >= 2: parts.append( _("in the %(x)s%(y)s files") % { 'x': type_, 'y': join(list_) }) if doubles_count == 1: s = _("%(color)s got a double pawn %(place)s") else: s = _("%(color)s got new double pawns %(place)s") yield (8 + phase) * 2 * doubles_count, s % { 'color': reprColor[color], 'place': join(parts) } for (color_, list_) in ((WHITE, found_white_isolates), (BLACK, found_black_isolates)): if list_: yield 20 * len(list_), ngettext( "%(color)s got an isolated pawn in the %(x)s file", "%(color)s got isolated pawns in the %(x)s files", len(list_)) % { 'color': reprColor[color_], 'x': join(list_) } # Stone wall if stonewall[color] & pawns == stonewall[color] and \ stonewall[color] & oldpawns != stonewall[color]: yield 10, _( "%s moves pawns into stonewall formation") % reprColor[color]
def defencive_moves_tactic(model, ply, phase): # ------------------------------------------------------------------------ # # Test if we threat something, or at least put more pressure on it # # ------------------------------------------------------------------------ # # We set bishop value down to knight value, as it is what most people expect bishopBackup = PIECE_VALUES[BISHOP] PIECE_VALUES[BISHOP] = PIECE_VALUES[KNIGHT] board = model.getBoardAtPly(ply).board oldboard = model.getBoardAtPly(ply - 1).board move = model.getMoveAtPly(ply - 1).move fcord = FCORD(move) tcord = TCORD(move) found_threatens = [] found_increases = [] # What do we attack now? board.setColor(1 - board.color) for ncap in genCaptures(board): # getCaptures also generate promotions if FLAG(ncap) in PROMOTIONS: continue # We are only interested in the attacks of the piece we just moved if FCORD(ncap) != TCORD(move): continue # We don't want to move back if TCORD(ncap) == FCORD(move): continue # We don't thread the king. We check him! (in another function) if board.arBoard[TCORD(ncap)] == KING: continue # If we also was able to attack that cord last time, we don't care if validateMove(oldboard, newMove(FCORD(move), TCORD(ncap))): continue # Test if we threats our enemy, at least more than before see0 = staticExchangeEvaluate(oldboard, TCORD(ncap), 1 - oldboard.color) see1 = staticExchangeEvaluate(board, TCORD(ncap), 1 - oldboard.color) if see1 > see0: # If a new winning capture has been created if see1 > 0: # Find the easiest attack attacks = getAttacks(board, TCORD(ncap), board.color) v, cord = min((PIECE_VALUES[board.arBoard[fc]], fc) for fc in iterBits(attacks)) easiestAttack = newMove(cord, TCORD(ncap)) found_threatens.append(toSAN(board, easiestAttack, True)) # Even though we might not yet be strong enough, we might still # have strengthened another friendly attack else: found_increases.append(reprCord[TCORD(ncap)]) board.setColor(1 - board.color) # -------------------------------------------------------------------- # # Test if we defend a one of our pieces # # -------------------------------------------------------------------- # found_defends = [] # Test which pieces were under attack used = [] for ncap in genCaptures(board): # getCaptures also generate promotions if FLAG(ncap) in PROMOTIONS: continue # We don't want to know about the same cord more than once if TCORD(ncap) in used: continue used.append(TCORD(ncap)) # If the attack was poining on the piece we just moved, we ignore it if TCORD(ncap) == FCORD(move) or TCORD(ncap) == TCORD(move): continue # If we were already defending the piece, we don't send a new # message if defends(oldboard, FCORD(move), TCORD(ncap)): continue # If the attack was not strong, we ignore it see = staticExchangeEvaluate(oldboard, ncap) if see < 0: continue v = defends(board, TCORD(move), TCORD(ncap)) # If the defend didn't help, it doesn't matter. Like defending a # bishop, threatened by a pawn, with a queen. # But on the other hand - it might still be a defend... # newsee = staticExchangeEvaluate(board, ncap) # if newsee <= see: continue if v: found_defends.append(reprCord[TCORD(ncap)]) # ------------------------------------------------------------------------ # # Test if we are rescuing an otherwise exposed piece # # ------------------------------------------------------------------------ # # Rescuing is only an option, if our own move wasn't an attack if oldboard.arBoard[tcord] == EMPTY: see0 = staticExchangeEvaluate(oldboard, fcord, oldboard.color) see1 = staticExchangeEvaluate(board, tcord, oldboard.color) if see1 > see0 and see1 > 0: yield _("rescues a %s") % reprPiece[board.arBoard[tcord]].lower() if found_threatens: yield _("threatens to win material by %s") % join(found_threatens) if found_increases: yield _("increases the pressure on %s") % join(found_increases) if found_defends: yield _("defends %s") % join(found_defends) PIECE_VALUES[BISHOP] = bishopBackup