Skip to main content
4 of 4
better documentation and axplanation
Max
  • 171
  • 7

I think the original proposal as well as answers given so far are not in adequacy with the simplicity, and even statement, of the problem. As it is formulated, the most natural answer is just to loop over the 8 x 8 squares and increment a counter for the respective case, for each allowed square:

def amazonCheckmate(king: str, amazon: str, n_rows = 8, n_cols = None):
    """For given positions of the white king and amazon (moves like queen or
       knight), return [c0,c1,c2,c3] where c0-c3 count the number of squares
       on which the black king is checkmate, in check, stalemate or safe,
       respectively, when the white king and amazon are at the positions
       given by the arguments in usual notation, e.g., 'a1' or 'e4', etc.
       Number of columns (files) defaults to number of rows of the board."""
    # Convert coordinate string to (col, row) (= file, rank): Consider it
    # as a base 20 number (to allow 'a', 'b',... as digits 10, 11,...);
    # subtract 10*20 + 1 so that 'a' and '1' correspond to col. and row = 0:
    if n_cols == None:
       n_cols = n_rows
    kc, kr = divmod(int( king , 20)-201, 20) 
    ac, ar = divmod(int(amazon, 20)-201, 20)
    cnt = [0]*4   # counter for each of: checkmate, check, stalemate, safe.
    for r in range(n_rows):      # row or "rank"
        for c in range(n_cols):  # column or "file"
            if not forbidden(r, c):  # i.e.: too close to enemy king
                cnt[ (0 if threatened(r, c) else 2) + can_move(r, c) ] += 1
    return cnt

Now we just need the simple if not trivial functions forbidden() (too close to the enemy king), threatened() and can_move(). If we insert them somewhere before the above loop, they can access the coordinates (kr,kc) and (ar,rc) of the white king and amazon as variables defined within their environment:

    def forbidden(r, c):
        """True if (r,c) is at distance <= 1 from white king."""
        return abs(r-kr) < 2 and abs(c-kc) < 2

    def can_move(r, c):
        """Is there a safe square next to (r,c)?"""
        return any(not threatened(rr,cc) and not forbidden(rr,cc)
                   for rr in range(max(r-1,0), min(r+2, n_rows))
                   for cc in range(max(c-1,0), min(c+2, n_cols),
                                   2 if rr == r else 1))  # skip rr,cc = r,c

    def threatened(r, c):
        """Is square (r,c) "threatened"/controlled by the amazon (or king)?
           That's the case if (r,c) is at distance <= 2 from the amazon, 
           or on the same row or column or diagonal as the amazon,
           and the white king is not in between the two."""
        return (abs(r-ar) < 3 and abs(c-ac) < 3) or (r==ar or c==ac or 
                abs(c-ac)==abs(r-ar)) and not shielded(r,c)

    def shielded(r, c):
        """Is the enemy king between (r,c) and (ar,ac), in diagonal or
        straight line? Equivalently: the directional vectors
        (delta row, delta col) collinear <=> determinant is zero."""
        return (kc-c)*(ar-r) == (kr-r)*(ac-c) and \
               min(c,ac) <= kc <= max(c,ac) and min(r,ar) <= kr <= max(r,ar)

That's all that is needed to solve the problem in a simple, self-explaining way.

Max
  • 171
  • 7