# Task
 An `amazon` (also known as a queen+knight compound) is an imaginary chess piece that can move like a `queen` or a `knight` (or, equivalently, like a `rook`, `bishop`, or `knight`). The diagram below shows all squares which the amazon attacks from e4 (circles represent knight-like moves while crosses correspond to queen-like moves).

 ![](https://codefightsuserpics.s3.amazonaws.com/tasks/amazonCheckmate/img/amazon.png?_tm=1473934566013)

 Recently you've come across a diagram with only three pieces left on the board: a `white amazon`, `white king` and `black king`. 
 
 It's black's move. You don't have time to determine whether the game is over or not, but you'd like to figure it out in your head. 
 
 Unfortunately, the diagram is smudged and you can't see the position of the `black king`, so it looks like you'll have to check them all.

 Given the positions of white pieces on a standard chessboard, determine the number of possible black king's positions such that: 

* It's a checkmate (i.e. black's king is under amazon's 
 attack and it cannot make a valid move);

* It's a check (i.e. black's king is under amazon's attack 
 but it can reach a safe square in one move);

* It's a stalemate (i.e. black's king is on a safe square 
 but it cannot make a valid move);

* Black's king is on a safe square and it can make a valid move.

Note that two kings cannot be placed on two adjacent squares (including two diagonally adjacent ones).

# Example

 For `king = "d3" and amazon = "e4"`, the output should be `[5, 21, 0, 29]`.

 ![](https://codefightsuserpics.s3.amazonaws.com/tasks/amazonCheckmate/img/example1.png?_tm=1473934566299)

 `Red crosses` correspond to the `checkmate` positions, `orange pluses` refer to `checks` and `green circles` denote `safe squares`.

 For `king = "a1" and amazon = "g5"`, the output should be `[0, 29, 1, 29]`.

 ![](https://codefightsuserpics.s3.amazonaws.com/tasks/amazonCheckmate/img/example2.png?_tm=1473934566670)

 `Stalemate` position is marked by a `blue square`.

# Input


 - String `king`

Position of white's king in chess notation.


 - String `amazon`

Position of white's amazon in the same notation.

Constraints: `amazon ≠ king.`

# Output

An array of four integers, each equal to the number of black's king positions corresponding to a specific situation. The integers should be presented in the same order as the situations were described, `i.e. 0 for checkmates, 1 for checks, etc`.

Difficulty: MEDIUM

Starter code:
def amazon_check_mate(king, amazon):
	

Solutions:
from itertools import count
ALL_MOVES = [(1, 1), (0, 1), (1, 0), (-1, 0), (0, -1), (-1, 1), (1, -1), (-1, -1)]
AMA_MOVES = [(1, 2), (2, 1), (-1, 2), (2, -1), (1, -2), (-2, 1), (-1, -2), (-2, -1)]

def amazon_check_mate(*args):

	def posInBoard(x, y):
		return 0 <= x < 8 and 0 <= y < 8

	def getCoveredPos(start, king=None):
		covered = {start}
		for m in AMA_MOVES if king else ALL_MOVES:
			pos = tuple((z + dz for (z, dz) in zip(start, m)))
			if posInBoard(*pos):
				covered.add(pos)
		if king:
			for (dx, dy) in ALL_MOVES:
				for n in count(1):
					pos = (start[0] + dx * n, start[1] + dy * n)
					if not posInBoard(*pos) or pos == king:
						break
					covered.add(pos)
		return covered
	(K, Q) = [(ord(s[0]) - 97, ord(s[1]) - 49) for s in args]
	kCover = getCoveredPos(K)
	fullCover = getCoveredPos(Q, K) | kCover
	freeQueen = Q not in kCover
	counts = [0] * 4
	for x in range(8):
		for y in range(8):
			black = (x, y)
			if black in kCover or black == Q:
				continue
			safePosAround = any((posInBoard(*neigh) and (neigh not in fullCover or (neigh == Q and freeQueen)) for neigh in ((x + dx, y + dy) for (dx, dy) in ALL_MOVES)))
			counts[2 * (black not in fullCover) + safePosAround] += 1
	return counts


from collections import namedtuple
from functools import reduce
import enum
import sys

class Outcomes:

	def __init__(self):
		self.checkmates = 0
		self.checks = 0
		self.stalemates = 0
		self.alives = 0

	def as_array(self):
		return [self.checkmates, self.checks, self.stalemates, self.alives]

class GameSquareConditions:

	def __init__(self, is_threatened=False, is_occupied=False, is_inhibited=False):
		self.is_threatened = is_threatened
		self.is_occupied = is_occupied
		self.is_inhibited = is_inhibited

	@property
	def is_safe(self):
		return not self.is_threatened

class GameSquareOutcome(enum.Enum):
	CHECKMATE = enum.auto()
	CHECK = enum.auto()
	STALEMATE = enum.auto()
	ALIVE = enum.auto()
	UNKNOWN = enum.auto()

@enum.unique
class MovementDirection(enum.Enum):
	NORTH = (0, 1)
	NORTH_EAST = (1, 1)
	EAST = (1, 0)
	SOUTH_EAST = (1, -1)
	SOUTH = (0, -1)
	SOUTH_WEST = (-1, -1)
	WEST = (-1, 0)
	NORTH_WEST = (-1, 1)

	def __init__(self, x_offset, y_offset):
		self.x_offset = x_offset
		self.y_offset = y_offset

	@property
	def cartesian_offset(self):
		return (self.x_offset, self.y_offset)

	@property
	def reciprical(self):
		if self == self.NORTH:
			return self.SOUTH
		elif self == self.NORTH_EAST:
			return self.SOUTH_WEST
		elif self == self.EAST:
			return self.WEST
		elif self == self.SOUTH_EAST:
			return self.NORTH_WEST
		elif self == self.SOUTH:
			return self.NORTH
		elif self == self.SOUTH_WEST:
			return self.NORTH_EAST
		elif self == self.WEST:
			return self.EAST
		elif self == self.NORTH_WEST:
			return self.SOUTH_EAST
		raise ValueError('Unknown direction to recipricate!')

	def move_from(self, x, y):
		offset = self.cartesian_offset
		return (x + offset[0], y + offset[1])

class GameSquare:

	def __init__(self):
		self.neighbors = {}
		self.condition = GameSquareConditions()
		self.outcome = GameSquareOutcome.UNKNOWN

	def has_neighbor(self, direction):
		return self.neighbor(direction) is not None

	def neighbor(self, direction):
		return self.neighbors.get(direction, None)

	def set_neighbor(self, neighbor, direction):
		if neighbor is self:
			raise ValueError('Cannot set yourself as a neighbor!')
		existing_neighbor = self.neighbor(direction)
		if existing_neighbor is not None and existing_neighbor is not neighbor:
			raise ValueError('Another neighbor is already in that direction!')
		recip_direction = direction.reciprical
		existing_recip_neighbor = neighbor.neighbor(recip_direction)
		if existing_recip_neighbor is not None and existing_recip_neighbor is not self:
			raise ValueError('Input neighbor already has neighbor in opposite direction!')
		self.neighbors[direction] = neighbor
		neighbor.neighbors[recip_direction] = self

	def neighbor_conditions(self):
		return [n.condition for n in list(self.neighbors.values())]

	def render(self, out_dest):
		marker = ' '
		if self.condition.is_threatened:
			marker = 'v'
		elif self.condition.is_occupied:
			marker = '*'
		elif self.condition.is_inhibited:
			marker = '-'
		out_dest.write(marker)

	def render_outcome(self, out_dest):
		marker = '?'
		if self.outcome == GameSquareOutcome.CHECKMATE:
			marker = '!'
		elif self.outcome == GameSquareOutcome.CHECK:
			marker = ':'
		elif self.outcome == GameSquareOutcome.STALEMATE:
			marker = 'o'
		elif self.outcome == GameSquareOutcome.ALIVE:
			marker = '.'
		out_dest.write(marker)

class Gameboard:

	def __init__(self, num_rows, num_cols):
		if num_rows < 1:
			fmt = 'Gameboard initialized with number of rows < 1 ({})!'
			msg = fmt.format(num_rows)
			raise ValueError()
		if num_cols < 1:
			fmt = 'Gameboard initialized with number of columns < 1 ({})!'
			msg = fmt.format(num_cols)
			raise ValueError(msg)
		self.num_rows = num_rows
		self.num_cols = num_cols
		self.board = {}
		for row in range(num_rows):
			for col in range(num_cols):
				key = (row, col)
				self.board[key] = GameSquare()
		for row in range(num_rows):
			for col in range(num_cols):
				current = self.square_at(row, col)
				for direction in MovementDirection:
					try:
						(neighbor_x, neighbor_y) = direction.move_from(row, col)
						neighbor = self.square_at(neighbor_x, neighbor_y)
						current.set_neighbor(neighbor, direction)
					except KeyError:
						pass

	def square_at(self, row, col):
		key = (row, col)
		return self.board[key]

	def render(self, out_dest):
		out_dest.write('\n\nGameboard\n')
		for y in range(self.num_cols - 1, -1, -1):
			for x in range(self.num_rows):
				out_dest.write('|')
				self.square_at(x, y).render(out_dest)
			out_dest.write('|\n')

	def render_outcome(self, out_dest):
		out_dest.write('\n\nOutcomes\n')
		for y in range(self.num_cols - 1, -1, -1):
			for x in range(self.num_rows):
				out_dest.write('|')
				self.square_at(x, y).render_outcome(out_dest)
			out_dest.write('|\n')

class DestinationMover:

	def __init__(self, *args):
		self.movement_path = list(args)

	def append_path(self, *args):
		self.movement_path = self.movement_path + list(args)

	def execute(self, origin):
		current_location = origin
		for move in self.movement_path:
			next_location = current_location.neighbor(move)
			if next_location == None:
				return []
			current_location = next_location
		return [current_location]

	def __str__(self):
		path = '-'.join((p.name for p in self.movement_path))
		return 'DestinationMover: ' + path

class VectorMover:

	def __init__(self, direction=None):
		self.direction = direction

	def execute(self, origin):
		if self.direction is None:
			return []
		visited = []
		neighbor = origin.neighbor(self.direction)
		while neighbor is not None and (not neighbor.condition.is_occupied):
			visited.append(neighbor)
			neighbor = neighbor.neighbor(self.direction)
		return visited

	def __str__(self):
		return 'VectorMover: ' + self.direction.name

class GamePiece:

	def __init__(self, inhibit_as_well_as_threaten=False):
		self.inhibit_as_well_as_threaten = inhibit_as_well_as_threaten
		self.location = None
		self.movers = []

	def place_on_board(self, gamesquare):
		self.location = gamesquare
		self.location.condition.is_occupied = True

	def impart_force(self):
		if self.location is None:
			return []
		result = []
		for mover in self.movers:
			result = result + mover.execute(self.location)
		for square in result:
			square.condition.is_threatened = True
			if self.inhibit_as_well_as_threaten:
				square.condition.is_inhibited = True
		return result

	def __str__(self):
		mover_content = '\n  '.join([str(x) for x in self.movers])
		r = '\n  '.join(['GamePiece', mover_content])
		return r

def create_knight_movers():
	knight_paths = []
	knight_paths.append((MovementDirection.NORTH, MovementDirection.NORTH, MovementDirection.WEST))
	knight_paths.append((MovementDirection.NORTH, MovementDirection.NORTH, MovementDirection.EAST))
	knight_paths.append((MovementDirection.NORTH, MovementDirection.WEST, MovementDirection.WEST))
	knight_paths.append((MovementDirection.NORTH, MovementDirection.EAST, MovementDirection.EAST))
	knight_paths.append((MovementDirection.SOUTH, MovementDirection.SOUTH, MovementDirection.WEST))
	knight_paths.append((MovementDirection.SOUTH, MovementDirection.SOUTH, MovementDirection.EAST))
	knight_paths.append((MovementDirection.SOUTH, MovementDirection.WEST, MovementDirection.WEST))
	knight_paths.append((MovementDirection.SOUTH, MovementDirection.EAST, MovementDirection.EAST))
	return [DestinationMover(*x) for x in knight_paths]

def create_rook_movers():
	directions = (MovementDirection.NORTH, MovementDirection.SOUTH, MovementDirection.EAST, MovementDirection.WEST)
	return [VectorMover(direction) for direction in directions]

def create_bishop_movers():
	directions = (MovementDirection.NORTH_EAST, MovementDirection.NORTH_WEST, MovementDirection.SOUTH_EAST, MovementDirection.SOUTH_WEST)
	return [VectorMover(direction) for direction in directions]

def create_amazon():
	piece = GamePiece()
	for mover in create_knight_movers():
		piece.movers.append(mover)
	for mover in create_rook_movers():
		piece.movers.append(mover)
	for mover in create_bishop_movers():
		piece.movers.append(mover)
	return piece

def create_king():
	piece = GamePiece(inhibit_as_well_as_threaten=True)
	for direction in MovementDirection:
		mover = DestinationMover()
		mover.append_path(direction)
		piece.movers.append(mover)
	return piece

def chess_location_to_game_indicies(pos_string):
	first = ord(pos_string[0].upper()) - ord('A')
	second = int(pos_string[1]) - 1
	return (first, second)

def determine_square_status_and_update_outcome(my_condition, neighbor_conditions, outcome):
	outcome_type = GameSquareOutcome.UNKNOWN
	if my_condition.is_occupied or my_condition.is_inhibited:
		return outcome_type
	can_move_to_safety = any([x.is_safe for x in neighbor_conditions])
	if my_condition.is_threatened:
		if can_move_to_safety:
			outcome.checks += 1
			outcome_type = GameSquareOutcome.CHECK
		else:
			outcome.checkmates += 1
			outcome_type = GameSquareOutcome.CHECKMATE
	elif can_move_to_safety:
		outcome.alives += 1
		outcome_type = GameSquareOutcome.ALIVE
	else:
		outcome.stalemates += 1
		outcome_type = GameSquareOutcome.STALEMATE
	return outcome_type

def amazon_check_mate(king, amazon):
	outcomes = Outcomes()
	king_coords = chess_location_to_game_indicies(king)
	amazon_coords = chess_location_to_game_indicies(amazon)
	board = Gameboard(8, 8)
	king_piece = create_king()
	amazon_piece = create_amazon()
	king_piece.place_on_board(board.square_at(*king_coords))
	amazon_piece.place_on_board(board.square_at(*amazon_coords))
	king_piece.impart_force()
	amazon_piece.impart_force()
	for square in list(board.board.values()):
		determine_square_status_and_update_outcome(square.condition, square.neighbor_conditions(), outcomes)
	return outcomes.as_array()


def amazon_check_mate(k, q):
	import numpy
	board = numpy.array([[0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0], [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1], [1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1], [1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1], [1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, -1, -2, -1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1], [1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1], [1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1], [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1], [0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0]])
	(qx, qy) = (ord(q[0]) - 97, int(q[1]) - 1)
	(kx, ky) = (ord(k[0]) - 97, int(k[1]) - 1)
	board[max(0, 6 + qy - ky):9 + qy - ky, max(0, 6 - qx + kx):9 - qx + kx] = -2
	board = board[qy:qy + 8, 7 - qx:15 - qx]
	if abs(qx - kx) > 1 or abs(qy - ky) > 1:
		board[board == -1] = 0
		board[7 - qy, qx] = 2
	if qy == ky:
		if kx + 2 - (qx > kx) * 4 >= 0:
			board[7 - ky, kx + 2 - (qx > kx) * 4::1 - (qx > kx) * 2] = 1
	elif qx == kx:
		if 7 - ky - 2 + (qy > ky) * 4 >= 0:
			board[7 - ky - 2 + (qy > ky) * 4::-1 + (qy > ky) * 2, kx] = 1
	elif kx + ky == qx + qy:
		newb = board[7 - ky::1 - (qx > kx) * 2, kx::1 - (qx > kx) * 2]
		newb[newb == 0] = 1
		board[7 - ky::1 - (qx > kx) * 2, kx::1 - (qx > kx) * 2] = newb
	elif kx - ky == qx - qy:
		newb = board[7 - ky::-1 + (qx > kx) * 2, kx::1 - (qx > kx) * 2]
		newb[newb == 0] = 1
		board[7 - ky::-1 + (qx > kx) * 2, kx::1 - (qx > kx) * 2] = newb
	ans = [0, 0, 0, 0]
	ansb = numpy.empty(shape=(8, 8), dtype=object)
	for nx in range(8):
		for ny in range(8):
			c = board[7 - ny, nx]
			if c == 1:
				pos = numpy.count_nonzero(board[max(0, 6 - ny):9 - ny, max(0, nx - 1):nx + 2] > 0) - 1
				if pos:
					ans[3] += 1
					ansb[7 - ny, nx] = 'o'
				else:
					ans[2] += 1
					ansb[7 - ny, nx] = '□'
			elif c == 0:
				pos = numpy.count_nonzero(board[max(0, 6 - ny):9 - ny, max(0, nx - 1):nx + 2] > 0)
				if pos:
					ans[1] += 1
					ansb[7 - ny, nx] = '+'
				else:
					ans[0] += 1
					ansb[7 - ny, nx] = 'x'
			elif c == -1:
				ans[0] += 1
				ansb[7 - ny, nx] = 'x'
			else:
				ansb[7 - ny, nx] = ' '
	print(k, q)
	print(board)
	print(ans)
	print(ansb)
	return ans


def amazon_check_mate(king, amazon):
	if king == amazon:
		return []
	king_pos = [ord(king[1]) - ord('1'), ord(king[0]) - ord('a')]
	amazon_pos = [ord(amazon[1]) - ord('1'), ord(amazon[0]) - ord('a')]
	board = [[' '] * 8, [' '] * 8, [' '] * 8, [' '] * 8, [' '] * 8, [' '] * 8, [' '] * 8, [' '] * 8]
	board[king_pos[0]][king_pos[1]] = 'K'
	board[amazon_pos[0]][amazon_pos[1]] = 'A'
	dirs = [[1, 0], [0, 1], [-1, 0], [0, -1], [1, -1], [-1, 1], [1, 1], [-1, -1], [1, 2], [2, 1], [1, -2], [2, -1], [-1, 2], [-2, 1], [-2, -1], [-1, -2]]
	for i in range(8):
		(row, col) = (king_pos[0] + dirs[i][0], king_pos[1] + dirs[i][1])
		if 0 <= row < 8 and 0 <= col < 8:
			board[row][col] = 'B' if board[row][col] == ' ' else 'C'
	for i in range(16):
		(row, col) = (amazon_pos[0] + dirs[i][0], amazon_pos[1] + dirs[i][1])
		while 0 <= row < 8 and 0 <= col < 8 and (board[row][col] in [' ', 'B']):
			if board[row][col] == ' ':
				board[row][col] = 'X'
			if 8 <= i:
				break
			(row, col) = (row + dirs[i][0], col + dirs[i][1])
	res = [0, 0, 0, 0]
	for row in range(8):
		for col in range(8):
			if board[row][col] not in ['K', 'B', 'A', 'C']:
				check = board[row][col] == 'X'
				valid_around = 0
				for i in range(8):
					(row_, col_) = (row + dirs[i][0], col + dirs[i][1])
					if 0 <= row_ < 8 and 0 <= col_ < 8 and (board[row_][col_] in [' ', 'A']):
						valid_around += 1
				if check:
					res[0 if valid_around == 0 else 1] += 1
				elif valid_around == 0:
					res[2] += 1
				else:
					res[3] += 1
	return res


from itertools import product

def get_pos(coord):
	x = ord(coord[0]) - ord('a')
	y = int(coord[1]) - 1
	return [y, x]

def place_pieces(board, king, amazon, connected):
	coord_king = get_pos(king)
	board[coord_king[0]][coord_king[1]] = 8
	coord_amazon = get_pos(amazon)
	board[coord_amazon[0]][coord_amazon[1]] = 9
	if coord_king[0] - 1 <= coord_amazon[0] <= coord_king[0] + 1 and coord_king[1] - 1 <= coord_amazon[1] <= coord_king[1] + 1:
		connected[0] = 1
	mark_attacked_squares(board, coord_king, coord_amazon)

def mark_attacked_squares(board, coord_king, coord_amazon):
	mark_queen(board, coord_amazon)
	mark_knight(board, coord_amazon)
	mark_king(board, coord_king)
	board[coord_amazon[0]][coord_amazon[1]] = 9

def mark_king(board, coord_king):
	y = coord_king[0]
	x = coord_king[1]
	for i in product([-1, 0, 1], repeat=2):
		if 0 <= y + i[0] < 8 and 0 <= x + i[1] < 8:
			board[y + i[0]][x + i[1]] = 3

def mark_knight(board, coord_amazon):
	y = coord_amazon[0]
	x = coord_amazon[1]
	for i in product([-2, -1, 1, 2], repeat=2):
		if 0 <= y + i[0] < 8 and 0 <= x + i[1] < 8:
			if board[y + i[0]][x + i[1]] == 0:
				board[y + i[0]][x + i[1]] = 2

def mark_queen(board, coord_amazon):
	y = coord_amazon[0]
	x = coord_amazon[1]
	while y >= 0:
		if board[y][x] == 0:
			board[y][x] = 2
		if board[y][x] == 8:
			break
		y -= 1
	y = coord_amazon[0]
	while y < 8:
		if board[y][x] == 0:
			board[y][x] = 2
		if board[y][x] == 8:
			break
		y += 1
	y = coord_amazon[0]
	while x >= 0:
		if board[y][x] == 0:
			board[y][x] = 2
		if board[y][x] == 8:
			break
		x -= 1
	x = coord_amazon[1]
	while x < 8:
		if board[y][x] == 0:
			board[y][x] = 2
		if board[y][x] == 8:
			break
		x += 1
	x = coord_amazon[1]
	while x >= 0 and y >= 0:
		if board[y][x] == 0:
			board[y][x] = 2
		if board[y][x] == 8:
			break
		x -= 1
		y -= 1
	y = coord_amazon[0]
	x = coord_amazon[1]
	while x < 8 and y >= 0:
		if board[y][x] == 0:
			board[y][x] = 2
		if board[y][x] == 8:
			break
		x += 1
		y -= 1
	y = coord_amazon[0]
	x = coord_amazon[1]
	while x >= 0 and y < 8:
		if board[y][x] == 0:
			board[y][x] = 2
		if board[y][x] == 8:
			break
		x -= 1
		y += 1
	y = coord_amazon[0]
	x = coord_amazon[1]
	while x < 8 and y < 8:
		if board[y][x] == 0:
			board[y][x] = 2
		if board[y][x] == 8:
			break
		x += 1
		y += 1
	y = coord_amazon[0]
	x = coord_amazon[1]

def check_safe(board, y, x, connected):
	for i in product([-1, 0, 1], repeat=2):
		if 0 <= y + i[0] < 8 and 0 <= x + i[1] < 8:
			if not (i[0] == 0 and i[1] == 0) and (board[y + i[0]][x + i[1]] == 0 or (connected[0] == 0 and board[y + i[0]][x + i[1]] == 9)):
				return 1
	return 0

def count_states(board, connected):
	stalemate = check = checkmate = safe = 0
	for y in range(8):
		for x in range(8):
			if board[y][x] == 0:
				if check_safe(board, y, x, connected) == 1:
					safe += 1
				else:
					stalemate += 1
			elif board[y][x] == 2:
				if check_safe(board, y, x, connected) == 0:
					checkmate += 1
				else:
					check += 1
	return [checkmate, check, stalemate, safe]

def amazon_check_mate(king, amazon):
	board = [[0 for i in range(8)] for j in range(8)]
	connected = [1]
	connected[0] = 0
	place_pieces(board, king, amazon, connected)
	return count_states(board, connected)


def isaban(i, j, ki, kj):
	if i < 0 or i > 7 or j < 0 or (j > 7):
		return True
	if ki - 2 < i and i < ki + 2 and (kj - 2 < j) and (j < kj + 2):
		return True
	return False

def ischeck(i, j, ki, kj, ai, aj):
	if i == ai and j == aj:
		return False
	if i == ai:
		if ai != ki or (kj < j and kj < aj) or (kj > j and kj > aj):
			return True
	if j == aj:
		if aj != kj or (ki < i and ki < ai) or (ki > i and ki > ai):
			return True
	if i + j == ai + aj:
		if ai + aj != ki + kj or (ki < i and ki < ai) or (ki > i and ki > ai):
			return True
	if i - j == ai - aj:
		if ai - aj != ki - kj or (ki < i and ki < ai) or (ki > i and ki > ai):
			return True
	Knight = [(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)]
	for item in Knight:
		if ai == i + item[0] and aj == j + item[1]:
			return True
	return False

def ismate(i, j, ki, kj, ai, aj):
	if i == ai and j == aj:
		return False
	move = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
	for item in move:
		(ti, tj) = (i + item[0], j + item[1])
		if not isaban(ti, tj, ki, kj):
			if ti == ai and tj == aj:
				return False
			if not ischeck(ti, tj, ki, kj, ai, aj):
				return False
	return True

def amazon_check_mate(king, amazon):
	(ki, kj) = (ord(king[0]) - ord('a'), int(king[1]) - 1)
	(ai, aj) = (ord(amazon[0]) - ord('a'), int(amazon[1]) - 1)
	ans = [0, 0, 0, 0]
	for i in range(8):
		for j in range(8):
			if not isaban(i, j, ki, kj):
				if ischeck(i, j, ki, kj, ai, aj):
					if ismate(i, j, ki, kj, ai, aj):
						if i != ai or j != aj:
							ans[0] += 1
					else:
						ans[1] += 1
				elif ismate(i, j, ki, kj, ai, aj):
					ans[2] += 1
				else:
					ans[3] += 1
	if not isaban(ai, aj, ki, kj):
		ans[3] -= 1
	return ans


from math import isclose
adjacents = [(0, 1), (0, -1), (1, 0), (-1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]
knight = [(1, -2), (1, 2), (2, -1), (2, 1), (-2, 1), (-2, -1), (-1, 2), (-1, -2)]
distance = lambda x1, y1, x2, y2: ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5
is_diagonal = lambda a, b, i, j: abs(a - i) == abs(b - j)
is_ok = lambda a, b: 0 <= a < 8 and 0 <= b < 8

def amazon_check_mate(king, amazon):
	board = [['-' for _ in range(8)] for _ in range(8)]
	king = (8 - int(king[1]), 'abcdefgh'.index(king[0]))
	amazon = (8 - int(amazon[1]), 'abcdefgh'.index(amazon[0]))
	board[king[0]][king[1]] = 'K'
	board[amazon[0]][amazon[1]] = 'A'
	get_adjacents = lambda i, j, s=0: [[board[i + k][j + l], (i + k, j + l)][s] for (k, l) in adjacents if is_ok(i + k, j + l)]

	def assign_check():
		for i in range(8):
			for j in range(8):
				if board[i][j] == '-' and (i == amazon[0] or j == amazon[1] or is_diagonal(*amazon, i, j)) and (not isclose(distance(*amazon, *king) + distance(*king, i, j), distance(*amazon, i, j), abs_tol=0.0001)):
					board[i][j] = '#'
		for (i, j) in knight:
			(ni, nj) = (amazon[0] + i, amazon[1] + j)
			if is_ok(ni, nj) and board[ni][nj] != 'K':
				board[ni][nj] = '#'

	def assign_king_check():
		for (i, j) in adjacents:
			(ni, nj) = (king[0] + i, king[1] + j)
			if is_ok(ni, nj) and board[ni][nj] != 'A':
				board[ni][nj] = '$'

	def assign_checkmates():
		exceptions = set(get_adjacents(*amazon, 1))
		for i in range(8):
			for j in range(8):
				if board[i][j] == '#' and (i, j) not in exceptions and all((n != '-' for n in get_adjacents(i, j))):
					board[i][j] = '*'

	def king_amazon_adj():
		adj = get_adjacents(*amazon)
		return adj.count('#') if 'K' in adj else 0

	def assign_safe_not_safe():
		for i in range(8):
			for j in range(8):
				if board[i][j] == '-' and all((n != '-' for n in get_adjacents(i, j))):
					board[i][j] = '@'

	def _count():
		assign_check()
		assign_king_check()
		assign_checkmates()
		assign_safe_not_safe()
		d = {'*': king_amazon_adj(), '#': -king_amazon_adj(), '@': 0, '-': 0}
		for i in board:
			for j in i:
				if j not in 'KA$':
					d[j] += 1
		return list(d.values())
	return _count()


def amazon_check_mate(king, amazon):
	ranks = '87654321'
	fyles = 'abcdefgh'
	am = (ranks.index(amazon[1]), fyles.index(amazon[0]))
	ki = (ranks.index(king[1]), fyles.index(king[0]))
	amazon_attacks = set()
	knight_moves = [(1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1), (-2, 1), (-1, 2)]
	for (dr, dc) in knight_moves:
		(row, col) = (am[0] + dr, am[1] + dc)
		if 0 <= row <= 7 and 0 <= col <= 7:
			amazon_attacks.add((row, col))
	rays = [(0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (1, -1), (-1, 1), (-1, -1)]
	for (dr, dc) in rays:
		for d in range(1, 8):
			(row, col) = (am[0] + d * dr, am[1] + d * dc)
			if not 0 <= row <= 7 or not 0 <= col <= 7:
				break
			amazon_attacks.add((row, col))
			if (row, col) == ki:
				break
	king_attacks = set()
	for (dr, dc) in rays:
		(row, col) = (ki[0] + dr, ki[1] + dc)
		if 0 <= row <= 7 and 0 <= col <= 7:
			king_attacks.add((row, col))
	attacked = amazon_attacks | king_attacks

	def has_safe_move(r, c):
		for (dr, dc) in rays:
			(row, col) = (r + dr, c + dc)
			if (0 <= row <= 7 and 0 <= col <= 7) and (row, col) not in attacked:
				return True
		return False
	(checkmates, checks, stalemates, safe) = (0, 0, 0, 0)
	for r in range(8):
		for c in range(8):
			if (r, c) in king_attacks or (r, c) == am or (r, c) == ki:
				continue
			if (r, c) in attacked:
				if has_safe_move(r, c):
					checks += 1
				else:
					checkmates += 1
			elif has_safe_move(r, c):
				safe += 1
			else:
				stalemates += 1
	return [checkmates, checks, stalemates, safe]


import numpy as np

def amazon_check_mate(king, amazon):
	nogo_board = np.full((24, 24), True)
	nogo_board[8:16, 8:16] = False
	king_sq = (ord(king[1]) - ord('1') + 8, ord(king[0]) - ord('a') + 8)
	nogo_board[king_sq[0] - 1:king_sq[0] + 2, king_sq[1] - 1:king_sq[1] + 2] = True
	amazon_sq = (ord(amazon[1]) - ord('1') + 8, ord(amazon[0]) - ord('a') + 8)
	for (di, dj) in [(2, 1), (1, 2), (-1, 2), (-2, 1), (-2, -1), (-1, -2), (1, -2), (2, -1)]:
		nogo_board[amazon_sq[0] + di, amazon_sq[1] + dj] = True
	directions = [(0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0), (-1, 1)]
	blocked = [False] * 8
	for ii in range(1, 8):
		for (index, dd) in enumerate(directions):
			if amazon_sq[0] + ii * dd[0] == king_sq[0] and amazon_sq[1] + ii * dd[1] == king_sq[1]:
				blocked[index] = True
			elif not blocked[index]:
				nogo_board[amazon_sq[0] + ii * dd[0], amazon_sq[1] + ii * dd[1]] = True
	if abs(king_sq[0] - amazon_sq[0]) <= 1 and abs(king_sq[1] - amazon_sq[1]) <= 1:
		nogo_board[amazon_sq[0], amazon_sq[1]] = True
	print(king_sq, amazon_sq)
	answer = [0, 0, 0, 0]
	for ii in range(8, 16):
		for jj in range(8, 16):
			if (abs(ii - king_sq[0]) > 1 or abs(jj - king_sq[1]) > 1) and (ii != amazon_sq[0] or jj != amazon_sq[1]):
				if nogo_board[ii - 1:ii + 2, jj - 1:jj + 2].all():
					answer[0] += 1
				elif nogo_board[ii, jj]:
					answer[1] += 1
				elif nogo_board[ii - 1:ii + 2, jj - 1:jj + 2].sum() == 8:
					answer[2] += 1
				else:
					answer[3] += 1
	return answer


d = {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6, 'h': 7}
import numpy as np

def get_area(board, x, y):
	return board[x - 1 if x - 1 >= 0 else 0:x + 2 if x + 2 <= 8 else 8, y - 1 if y - 1 >= 0 else 0:y + 2 if y + 2 <= 8 else 8]

def get_neig(board, x, y):
	rtrn = []
	if x - 1 >= 0:
		if y - 1 >= 0:
			rtrn.append(board[x - 1, y - 1])
		rtrn.append(board[x - 1, y])
		if y + 1 < 8:
			rtrn.append(board[x - 1, y + 1])
	if y - 1 >= 0:
		rtrn.append(board[x, y - 1])
	if y + 1 < 8:
		rtrn.append(board[x, y + 1])
	if x + 1 < 8:
		if y - 1 >= 0:
			rtrn.append(board[x + 1, y - 1])
		rtrn.append(board[x + 1, y])
		if y + 1 < 8:
			rtrn.append(board[x + 1, y + 1])
	return rtrn

def amazon_check_mate(king, amazon):
	print(king, amazon)
	board = np.array([['O'] * 8 for i in range(8)])
	(kingx, kingy) = (int(king[1]) - 1, d[king[0]])
	(amazonx, amazony) = (int(amazon[1]) - 1, d[amazon[0]])
	board[kingx][kingy] = 'K'
	board[amazonx][amazony] = 'A'
	subarray = get_area(board, kingx, kingy)
	for (x, y) in np.ndenumerate(subarray):
		if y == 'O':
			subarray[x] = 'N'
		if y == 'A':
			subarray[x] = 'F'
	for xy in range(amazony, 8):
		if board[amazonx, xy] == 'K':
			break
		elif board[amazonx, xy] == 'O':
			board[amazonx, xy] = 'W'
	for xy in range(amazony, -1, -1):
		if board[amazonx, xy] == 'K':
			break
		elif board[amazonx, xy] == 'O':
			board[amazonx, xy] = 'W'
	for yx in range(amazonx, 8):
		if board[yx, amazony] == 'K':
			break
		elif board[yx, amazony] == 'O':
			board[yx, amazony] = 'W'
	for yx in range(amazonx, -1, -1):
		if board[yx, amazony] == 'K':
			break
		elif board[yx, amazony] == 'O':
			board[yx, amazony] = 'W'
	diag = np.diagonal(board, amazony - amazonx)
	diag.setflags(write=True)
	if 'A' in diag:
		for di in range(np.where(diag == 'A')[0][0], len(diag)):
			if diag[di] == 'K':
				break
			if diag[di] == 'O':
				diag[di] = 'W'
		for di in range(np.where(diag == 'A')[0][0], -1, -1):
			if diag[di] == 'K':
				break
			if diag[di] == 'O':
				diag[di] = 'W'
	if 'F' in diag:
		for di in range(np.where(diag == 'F')[0][0], len(diag)):
			if diag[di] == 'K':
				break
			if diag[di] == 'O':
				diag[di] = 'W'
		for di in range(np.where(diag == 'F')[0][0], -1, -1):
			if diag[di] == 'K':
				break
			if diag[di] == 'O':
				diag[di] = 'W'
	diag2 = np.rot90(board).diagonal(-8 + amazonx + amazony + 1)
	diag2.setflags(write=True)
	if 'A' in diag2:
		for di in range(np.where(diag2 == 'A')[0][0], len(diag2)):
			if diag2[di] == 'K':
				break
			if diag2[di] == 'O':
				diag2[di] = 'W'
		for di in range(np.where(diag2 == 'A')[0][0], -1, -1):
			if diag2[di] == 'K':
				break
			if diag2[di] == 'O':
				diag2[di] = 'W'
	if 'F' in diag:
		for di in range(np.where(diag2 == 'F')[0][0], len(diag2)):
			if diag2[di] == 'K':
				break
			if diag2[di] == 'O':
				diag2[di] = 'W'
		for di in range(np.where(diag2 == 'F')[0][0], -1, -1):
			if diag2[di] == 'K':
				break
			if diag2[di] == 'O':
				diag2[di] = 'W'
	if amazonx - 2 >= 0:
		if amazony - 1 >= 0 and board[amazonx - 2, amazony - 1] == 'O':
			board[amazonx - 2, amazony - 1] = 'W'
		if amazony + 1 < 8 and board[amazonx - 2, amazony + 1] == 'O':
			board[amazonx - 2, amazony + 1] = 'W'
	if amazonx + 2 < 8:
		if amazony - 1 >= 0 and board[amazonx + 2, amazony - 1] == 'O':
			board[amazonx + 2, amazony - 1] = 'W'
		if amazony + 1 < 8 and board[amazonx + 2, amazony + 1] == 'O':
			board[amazonx + 2, amazony + 1] = 'W'
	if amazony - 2 >= 0:
		if amazonx - 1 >= 0 and board[amazonx - 1, amazony - 2] == 'O':
			board[amazonx - 1, amazony - 2] = 'W'
		if amazonx + 1 < 8 and board[amazonx + 1, amazony - 2] == 'O':
			board[amazonx + 1, amazony - 2] = 'W'
	if amazony + 2 < 8:
		if amazonx - 1 >= 0 and board[amazonx - 1, amazony + 2] == 'O':
			board[amazonx - 1, amazony + 2] = 'W'
		if amazonx + 1 < 8 and board[amazonx + 1, amazony + 2] == 'O':
			board[amazonx + 1, amazony + 2] = 'W'
	for i in range(8):
		for j in range(8):
			neigs = get_neig(board, i, j)
			if board[i, j] == 'O' and neigs.count('O') == 0:
				board[i, j] = 'S'
			if board[i, j] == 'W' and neigs.count('O') + neigs.count('A') + neigs.count('S') == 0:
				board[i, j] = 'C'
	(checkmate, warning, stalemate, safe) = (0, 0, 0, 0)
	for p in np.nditer(board):
		if p == 'C':
			checkmate += 1
		elif p == 'W':
			warning += 1
		elif p == 'S':
			stalemate += 1
		elif p == 'O':
			safe += 1
	return [checkmate, warning, stalemate, safe]