Skip to main content
better documentation and axplanation
Source Link
Max
  • 171
  • 7

I think the original proposal as well as the answers given so far are missing/reproducing what I'd consider as a main default: The programs are much more complicated thannot in adequacy with the problem would require. We have a specific problemsimplicity, and its specificities should be 'exploited' in a smart way to provide an eleganteven statement, lean solutionof the problem. IMHO As it is "bloatware" to have so many definitions (list of files, ranksformulated, possible moves...) or to try to make a program that can solve problems other than the given one.

The problem literally translatesmost natural answer is just to a simple increment statement within a loop over allthe 8 x 8 squares, and increment a few "one-liner" helper functions to know which counter must be incrementedfor the respective case, for each allowed square:

def amazonCheckmate(king: str, amazon: str, n_rows = 8, n_cols = None):
    """Return"""For given positions of the white king and amazon (moves like queen or
       knight), return [c0,c1,c2,c3] where the four componentsc0-c3 count the number of squares
       on which the black king would beis checkmate, in check, stalemate resp.or safe.""",
    # Convert coordinate stringrespectively, 'a1'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, consideringrank): Consider it
    # as a base 20 number (to accommodate for digitsallow '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) #  'a' = digit 10 => col.0,
    ac, ar = divmod(int(amazon, 20)-201, 20) 
 #   '1'cnt = digit[0]*4 1  =># rowcounter 0.

for each of: checkmate, defcheck, forbidden(rstalemate,c): #safe.
 Forbidden squares: at distancefor 1r fromin king.
range(n_rows):      # row returnor abs(r-kr)"rank"
 < 2 and abs(c-kc) < 2

  for c defin shieldedrange(r,cn_cols):  # iscolumn theor enemy"file"
 king between r,c and ar,ac?
       if not forbidden(r, c):  # i.e.,: itstoo coordinatesclose (rowto &enemy col)king
 are between our's and amazon's
        # and we seecnt[ the(0 kingif andthreatened(r, amazonc) inelse the2) same+ directioncan_move(r, i.e.,
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:

 directional vectors (delta row,def deltaforbidden(r, colc):
 are collinear <=>
     """True if (r,c) #is determinantat isdistance zero:<= 1 from white king."""
        return (kc-c)*abs(ar-r-kr) ==< (kr-r)*(ac-c)2 and minabs(c,ac-kc) <=< kc2

 <= max  def can_move(cr,ac c)\:
         """Is there a safe square next andto min(r,arc)?"""
 <= kr <= max    return any(rnot threatened(rr,arcc)
  and not forbidden(rr,cc)
             
     def threatened(r,c): #for isrr squarein range(max(r-1,c0), threatenedmin(r+2, byn_rows))
 the amazon?
        # <=> row or column is equal [rook move] orfor oncc diagonalin range(bishopmax(c-1,0), move:
min(c+2, n_cols),
       # absolute differences of row & col are equal) or we're not more than
        # 2 steps away (includes knight moves), and2 notif shieldedrr by== king:
r else 1))  # skip rr,cc = returnr,c

 (r==ar or c==ac ordef abs(c-ac)==absthreatened(r-ar, c):
         """Is square (r,c) "threatened"/controlled by the amazon (or (abs(r-arking)?
 < 3 and abs(c-ac) < 3)) and not shielded(r,c)

  That's the defcase can_moveif (r,c): #is ifat blackdistance is<= at2 r,cfrom the amazon, is 
 there a safe square next to it?
    or on the same returnrow any(notor threatened(rr,cc)column andor notdiagonal forbidden(rras the amazon,cc)
           and the white king is not in between the two."""
      for rr inreturn range(maxabs(r-1,0ar), min< 3 and abs(r+2,8c-ac) < 3)
  or (r==ar or c==ac or  
             for cc in range(maxabs(c-1,0ac), min==abs(c+2,8r-ar), 2 if) rrand ==not shielded(r else 1),c)
    cnt=[0]*4
    fordef shielded(r in, range(7c):
        for"""Is cthe inenemy rangeking between (7r,c):
 and (ar,ac), in diagonal or
        ifstraight notline? forbidden(r,c)Equivalently:
  the directional vectors
        (delta row, delta col) collinear #<=> 0,determinant 1,is 2,zero."""
 3 <=> checkmate or check only, stalemate orreturn safe
(kc-c)*(ar-r) == (kr-r)*(ac-c) and \
            cnt[ (0 if threatenedmin(r,c,ac) else<= 2)kc +<= can_movemax(r,c,ac) ] += 1
and min(r,ar) <= kr return<= cntmax(r,ar)

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

I think the original proposal as well as the answers so far are missing/reproducing what I'd consider as a main default: The programs are much more complicated than the problem would require. We have a specific problem, and its specificities should be 'exploited' in a smart way to provide an elegant, lean solution. IMHO it is "bloatware" to have so many definitions (list of files, ranks, possible moves...) or to try to make a program that can solve problems other than the given one.

The problem literally translates to a simple increment statement within a loop over all squares, and a few "one-liner" helper functions to know which counter must be incremented:

def amazonCheckmate(king: str, amazon: str):
    """Return [c0,c1,c2,c3] where the four components count the number of squares
    on which the black king would be checkmate, in check, stalemate resp. safe."""
    # Convert coordinate string 'a1', 'e4', etc. to  (col,row), considering it
    # as a base 20 number to accommodate for digits 'a', 'b',... = 10, 11,...
    kc,kr = divmod(int( king ,20)-201, 20) #  'a' = digit 10 => col.0,
    ac,ar = divmod(int(amazon,20)-201, 20) #   '1' = digit 1  => row 0.

    def forbidden(r,c): # Forbidden squares: at distance 1 from king.
        return abs(r-kr) < 2 and abs(c-kc) < 2

    def shielded(r,c): # is the enemy king between r,c and ar,ac?
        # i.e., its coordinates (row & col) are between our's and amazon's
        # and we see the king and amazon in the same direction, i.e.,
        # directional vectors (delta row, delta col) are 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)
                 
     def threatened(r,c): # is square (r,c) threatened by the amazon?
        # <=> row or column is equal [rook move] or on diagonal (bishop move:
        # absolute differences of row & col are equal) or we're not more than
        # 2 steps away (includes knight moves), and not shielded by king:
        return (r==ar or c==ac or abs(c-ac)==abs(r-ar)
                or (abs(r-ar) < 3 and abs(c-ac) < 3)) and not shielded(r,c)

    def can_move(r,c): # if black is at r,c, is there a safe square next to it?
        return any(not threatened(rr,cc) and not forbidden(rr,cc)
                   for rr in range(max(r-1,0), min(r+2,8))
                    for cc in range(max(c-1,0), min(c+2,8), 2 if rr == r else 1))
    cnt=[0]*4
    for r in range(7):
        for c in range(7):
            if not forbidden(r,c):
                 # 0, 1, 2, 3 <=> checkmate or check only, stalemate or safe
                cnt[ (0 if threatened(r,c) else 2) + can_move(r,c) ] += 1
    return cnt

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.

moved explanations from text directly to the relevant lines in the code
Source Link
Max
  • 171
  • 7

The amazon's definition does not require to consider knight moves, it is 100% equivalent to say: it can move like a queen, or go on any other square at distance < 3. This definitely can and should be used if it allows to make the code simple, elegant and/or efficient.

We have two more ingredients:

  • A queen moves along the row or column or diagonal <=> the absolute difference of the row coordinate equals that of the column coordinate.
  • The king "shields" us from the amazon if it is exactly in between, i.e., its coordinates are in between our's and the amazon's, and we see both in the same direction, i.e., the directional vectors (delta row, delta col) are colinear. This can be written as ad = bc <=> det [a b ; c d] = 0, the rows or columns of the matrix being the directional vectors.

With that you can translate the problem statementliterally translates to the following main program which consists in onea simple increment statement within a loop over all squares, and a few simple "one-liner" helper functions to know which counter must be incremented:

def amazonCheckmate(king: str, amazon: str):
    """Return [c0,c1,c2,c3] where the four components count the number of squares
    on which the black king would be checkmate, in check, stalemate resp. safe."""
    # Convert coordinate string 'a1', 'e4', etc. to  (col,row), considering it
    # as a base 20 number to accommodate for digits 'a', 'b',... = 10, 11,...
    kc,kr = divmod(int( king ,20)-201, 20) # use base 20, 'a' = digit 10 => col.0,
    ac,ar = divmod(int(amazon,20)-201, 20) #          '1' = digit 1 '1' => row 0.

    def forbidden(r,c): # Forbidden squares: at distance 1 from king.
        return abs(r-kr) < 2 and abs(c-kc) < 2

    def shielded(r,c): # is the enemy king between r,c and ar,ac?
        # i.e., its coordinates (row & col) are between our's and amazon's
        # and we see the king and amazon in the same direction, i.e.,
        # directional vectors (delta row, delta col) are 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)
                
    def threatened(r,c): # is square (r,c) threatened by the amazon?
        # <=> row or column is equal [rook move] or on diagonal (bishop move:
        # absolute differences of row & col are equal) or we're not more than
        # 2 steps away (includes knight moves), and not shielded by king:
        return (r==ar or c==ac or abs(c-ac)==abs(r-ar)
                or (abs(r-ar) < 3 and abs(c-ac) < 3)) and not shielded(r,c)

    def can_move(r,c): # if black is at r,c, is there a safe square next to it?
        return any(not threatened(rr,cc) and not forbidden(rr,cc)
                   for rr in range(max(r-1,0), min(r+2,8))
                   for cc in range(max(c-1,0), min(c+2,8), 2 if rr == r else 1))
    cnt=[0]*4
    for r in range(7):
        for c in range(7):
            if not forbidden(r,c):
                # 0, 1, 2, 3 <=> checkmate or check only, stalemate or safe
                cnt[ (0 if threatened(r,c) else 2) + can_move(r,c) ] += 1
    return cnt

The amazon's definition does not require to consider knight moves, it is 100% equivalent to say: it can move like a queen, or go on any other square at distance < 3. This definitely can and should be used if it allows to make the code simple, elegant and/or efficient.

We have two more ingredients:

  • A queen moves along the row or column or diagonal <=> the absolute difference of the row coordinate equals that of the column coordinate.
  • The king "shields" us from the amazon if it is exactly in between, i.e., its coordinates are in between our's and the amazon's, and we see both in the same direction, i.e., the directional vectors (delta row, delta col) are colinear. This can be written as ad = bc <=> det [a b ; c d] = 0, the rows or columns of the matrix being the directional vectors.

With that you can translate the problem statement to the following main program which consists in one simple increment statement within a loop over all squares, and a few simple "one-liner" helper functions:

def amazonCheckmate(king: str, amazon: str):
    """Return [c0,c1,c2,c3] where the four components count the number of squares
    on which the black king would be checkmate, in check, stalemate resp. safe."""
    kc,kr = divmod(int( king ,20)-201, 20) # use base 20, 'a' = digit 10 => col.0,
    ac,ar = divmod(int(amazon,20)-201, 20) #              '1' => row 0.

    def forbidden(r,c): # Forbidden squares: at distance 1 from king.
        return abs(r-kr) < 2 and abs(c-kc) < 2

    def shielded(r,c): # is the enemy king between r,c and ar,ac?
        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)
                
    def threatened(r,c): # is square (r,c) threatened by the amazon?
        return (r==ar or c==ac or abs(c-ac)==abs(r-ar)
                or (abs(r-ar) < 3 and abs(c-ac) < 3)) and not shielded(r,c)

    def can_move(r,c): # if black is at r,c, is there a safe square next to it?
        return any(not threatened(rr,cc) and not forbidden(rr,cc)
                   for rr in range(max(r-1,0), min(r+2,8))
                   for cc in range(max(c-1,0), min(c+2,8), 2 if rr == r else 1))
    cnt=[0]*4
    for r in range(7):
        for c in range(7):
            if not forbidden(r,c):
                # 0, 1, 2, 3 <=> checkmate or check only, stalemate or safe
                cnt[ (0 if threatened(r,c) else 2) + can_move(r,c) ] += 1
    return cnt

The problem literally translates to a simple increment statement within a loop over all squares, and a few "one-liner" helper functions to know which counter must be incremented:

def amazonCheckmate(king: str, amazon: str):
    """Return [c0,c1,c2,c3] where the four components count the number of squares
    on which the black king would be checkmate, in check, stalemate resp. safe."""
    # Convert coordinate string 'a1', 'e4', etc. to  (col,row), considering it
    # as a base 20 number to accommodate for digits 'a', 'b',... = 10, 11,...
    kc,kr = divmod(int( king ,20)-201, 20) #  'a' = digit 10 => col.0,
    ac,ar = divmod(int(amazon,20)-201, 20) #   '1' = digit 1  => row 0.

    def forbidden(r,c): # Forbidden squares: at distance 1 from king.
        return abs(r-kr) < 2 and abs(c-kc) < 2

    def shielded(r,c): # is the enemy king between r,c and ar,ac?
        # i.e., its coordinates (row & col) are between our's and amazon's
        # and we see the king and amazon in the same direction, i.e.,
        # directional vectors (delta row, delta col) are 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)
                
    def threatened(r,c): # is square (r,c) threatened by the amazon?
        # <=> row or column is equal [rook move] or on diagonal (bishop move:
        # absolute differences of row & col are equal) or we're not more than
        # 2 steps away (includes knight moves), and not shielded by king:
        return (r==ar or c==ac or abs(c-ac)==abs(r-ar)
                or (abs(r-ar) < 3 and abs(c-ac) < 3)) and not shielded(r,c)

    def can_move(r,c): # if black is at r,c, is there a safe square next to it?
        return any(not threatened(rr,cc) and not forbidden(rr,cc)
                   for rr in range(max(r-1,0), min(r+2,8))
                   for cc in range(max(c-1,0), min(c+2,8), 2 if rr == r else 1))
    cnt=[0]*4
    for r in range(7):
        for c in range(7):
            if not forbidden(r,c):
                # 0, 1, 2, 3 <=> checkmate or check only, stalemate or safe
                cnt[ (0 if threatened(r,c) else 2) + can_move(r,c) ] += 1
    return cnt
minor rephrasing
Source Link
Max
  • 171
  • 7

I think the original proposal as well as the answers so far are missing/reproducing what I'd consider as a main default: The proposed solution isprograms are much more complicated than the problem would require. We have a specific problem, and its specificities should be 'exploited' in a smart way to provide an elegant, lean solution. IMHO it is "bloatware" to have so many definitions (list of files, ranks, possible moves...) or to try to make a program that can solve problems other than the given one.

  • A queen moves along the row or column or diagonal <=> the absolute difference of the row coordinate equals that of the column coordinate.
  • The king "shields" us from the amazon if it is exactly in between, i.e., its coordinates are in between our's and the amazon's, and we see both in the same direction, i.e., the directional vectors (delta row, delta col) are colinear, which we. This can expressbe written as adad = bcbc <=> det [a b; c d][a b ; c d] = 0, the rows or columns of the matrix being the directional vectors (delta row, delta col.).

With that you can translate the problem statement to the following main program which consists in one simple increment statement within a loop over all squares, and a few very simple (one"one-liner)liner" helper functions:

def amazonCheckmate(king: str, amazon: str):
    """Return [c0,c1,c2,c3] where the four components count the number of squares
    on which the black king would be checkmate, in check, stalemate orresp. safe."""
    kc,kr = divmod(int( king ,20)-201, 20) # use base 20, 'a' = digit 10 => col.0,
    ac,ar = divmod(int(amazon,20)-201, 20) #              '1' => row 0.

    def forbidden(r,c): # Forbidden squares: at distance 1 from king.
        return abs(r-kr) < 2 and abs(c-kc) < 2

    def shielded(r,c): # is the enemy king between r,c and ar,ac?
        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)
                
    def threatened(r,c): # is square (r,c) threatened by the amazon?
        return (r==ar or c==ac or abs(c-ac)==abs(r-ar)
                or (abs(r-ar) < 3 and abs(c-ac) < 3)) and not shielded(r,c)

    def can_move(r,c): # if black is at r,c, is there a safe square next to it?
        return any(not threatened(rr,cc) and not forbidden(rr,cc)
                   for rr in range(max(r-1,0), min(r+2,8))
                   for cc in range(max(c-1,0), min(c+2,8), 2 if rr == r else 1))
    cnt=[0]*4
    for r in range(7):
        for c in range(7):
            if not forbidden(r,c):
                # 0, 1, 2, 3 <=> checkmate or check only, stalemate or safe
                cnt[ (0 if threatened(r,c) else 2) + can_move(r,c) ] += 1
    return cnt

I think the original proposal as well as the answers so far are missing/reproducing what I'd consider as a main default: The proposed solution is much more complicated than the problem would require. We have a specific problem, and its specificities should be 'exploited' in a smart way to provide an elegant, lean solution. IMHO it is "bloatware" to have so many definitions (list of files, ranks, possible moves...).

  • A queen moves along the row or column or diagonal <=> the absolute difference of the row coordinate equals that of the column coordinate.
  • The king "shields" us from the amazon if it is exactly in between, i.e., its coordinates are in between our's and the amazon's, and we see both in the same direction, i.e., vectors are colinear, which we can express as ad = bc <=> det [a b; c d] = 0, the rows or columns of the matrix being the directional vectors (delta row, delta col.).

With that you can translate the problem statement to the following main program which consists in one simple increment statement within a loop over all squares, and a few very simple (one-liner) helper functions:

def amazonCheckmate(king: str, amazon: str):
    """Return [c0,c1,c2,c3] where the four components count the number of squares
    on which the black king would be checkmate, in check, stalemate or safe."""
    kc,kr = divmod(int( king ,20)-201, 20) # use base 20, 'a' = digit 10 => col.0,
    ac,ar = divmod(int(amazon,20)-201, 20) #              '1' => row 0.

    def forbidden(r,c): # Forbidden squares: at distance 1 from king.
        return abs(r-kr) < 2 and abs(c-kc) < 2

    def shielded(r,c): # is the enemy king between r,c and ar,ac?
        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)
                
    def threatened(r,c): # is square (r,c) threatened by the amazon?
        return (r==ar or c==ac or abs(c-ac)==abs(r-ar)
                or (abs(r-ar) < 3 and abs(c-ac) < 3)) and not shielded(r,c)

    def can_move(r,c): # if black is at r,c, is there a safe square next to it?
        return any(not threatened(rr,cc) and not forbidden(rr,cc)
                   for rr in range(max(r-1,0), min(r+2,8))
                   for cc in range(max(c-1,0), min(c+2,8), 2 if rr == r else 1))
    cnt=[0]*4
    for r in range(7):
        for c in range(7):
            if not forbidden(r,c):
                # 0, 1, 2, 3 <=> checkmate or check only, stalemate or safe
                cnt[ (0 if threatened(r,c) else 2) + can_move(r,c) ] += 1
    return cnt

I think the original proposal as well as the answers so far are missing/reproducing what I'd consider as a main default: The programs are much more complicated than the problem would require. We have a specific problem, and its specificities should be 'exploited' in a smart way to provide an elegant, lean solution. IMHO it is "bloatware" to have so many definitions (list of files, ranks, possible moves...) or to try to make a program that can solve problems other than the given one.

  • A queen moves along the row or column or diagonal <=> the absolute difference of the row coordinate equals that of the column coordinate.
  • The king "shields" us from the amazon if it is exactly in between, i.e., its coordinates are in between our's and the amazon's, and we see both in the same direction, i.e., the directional vectors (delta row, delta col) are colinear. This can be written as ad = bc <=> det [a b ; c d] = 0, the rows or columns of the matrix being the directional vectors.

With that you can translate the problem statement to the following main program which consists in one simple increment statement within a loop over all squares, and a few simple "one-liner" helper functions:

def amazonCheckmate(king: str, amazon: str):
    """Return [c0,c1,c2,c3] where the four components count the number of squares
    on which the black king would be checkmate, in check, stalemate resp. safe."""
    kc,kr = divmod(int( king ,20)-201, 20) # use base 20, 'a' = digit 10 => col.0,
    ac,ar = divmod(int(amazon,20)-201, 20) #              '1' => row 0.

    def forbidden(r,c): # Forbidden squares: at distance 1 from king.
        return abs(r-kr) < 2 and abs(c-kc) < 2

    def shielded(r,c): # is the enemy king between r,c and ar,ac?
        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)
                
    def threatened(r,c): # is square (r,c) threatened by the amazon?
        return (r==ar or c==ac or abs(c-ac)==abs(r-ar)
                or (abs(r-ar) < 3 and abs(c-ac) < 3)) and not shielded(r,c)

    def can_move(r,c): # if black is at r,c, is there a safe square next to it?
        return any(not threatened(rr,cc) and not forbidden(rr,cc)
                   for rr in range(max(r-1,0), min(r+2,8))
                   for cc in range(max(c-1,0), min(c+2,8), 2 if rr == r else 1))
    cnt=[0]*4
    for r in range(7):
        for c in range(7):
            if not forbidden(r,c):
                # 0, 1, 2, 3 <=> checkmate or check only, stalemate or safe
                cnt[ (0 if threatened(r,c) else 2) + can_move(r,c) ] += 1
    return cnt
Source Link
Max
  • 171
  • 7
Loading