# Task
 Imagine a standard chess board with only two white and two black knights placed in their standard starting positions: the white knights on b1 and g1; the black knights on b8 and g8.

 There are two players: one plays for `white`, the other for `black`. During each move, the player picks one of his knights and moves it to an unoccupied square according to standard chess rules. Thus, a knight on d5 can move to any of the following squares: b6, c7, e7, f6, f4, e3, c3, and b4, as long as it is not occupied by either a friendly or an enemy knight.

 The players take turns in making moves, starting with the white player. Given the configuration `positions` of the knights after an unspecified number of moves, determine whose turn it is.

# Example

 For `positions = "b1;g1;b8;g8"`, the output should be `true`.

 The configuration corresponds to the initial state of the game. Thus, it's white's turn.

# Input/Output


 - `[input]` string `positions`

    The positions of the four knights, starting with white knights, separated by a semicolon, in the chess notation.


 - `[output]` a boolean value

    `true` if white is to move, `false` otherwise.

Source: codewars | Difficulty: EASY

Starter code: def whose_turn(positions):
	

Solutions:
def whose_turn(positions):
	return sum((ord(c) for c in positions.replace(';', ''))) % 2 == 0


def whose_turn(positions):
	return sum((ord(c) for c in positions)) & 1 == 1


tbl = str.maketrans('', '', 'aceg1357;')

def whose_turn(positions):
	return not len(positions.translate(tbl)) % 2


def whose_turn(positions):
	(wk1, wk2, bk1, bk2) = positions.split(';')
	cciw = cell_color_is_white
	return not cciw(wk1) ^ cciw(wk2) ^ cciw(bk1) ^ cciw(bk2)

def cell_color_is_white(cell):
	return (ord(cell[0]) + ord(cell[1])) % 2 == 1


import re

def whose_turn(positions):
	return len(re.sub('[1357;aceg]', '', positions)) % 2 == 0


def whose_turn(positions):
	a = [int(p, 19) & 1 for p in positions.split(';')]
	return len(set(a[:2])) == len(set(a[2:]))


def whose_turn(positions):
	positions = positions.split(';')
	on_white_squares = [is_white_square(pos) for pos in positions]
	white_pieces_match = on_white_squares[0] == on_white_squares[1]
	black_pieces_match = on_white_squares[2] == on_white_squares[3]
	return white_pieces_match == black_pieces_match

def is_white_square(pos):
	(x, y) = (ord(pos[0]) - 97, int(pos[1]))
	return x % 2 == y % 2


def whose_turn(positions):
	return sum((ord(file) + ord(rank) for (file, rank) in positions.split(';'))) % 2 == 0


from typing import Dict, Generator, List, Tuple
K_MOVES = [(a, b) for a in [-2, -1, 1, 2] for b in [-2, -1, 1, 2] if abs(a) != abs(b)]

def gen_k_moves(k: Tuple[int, int]) -> Generator[Tuple[int, int], None, None]:
	(kc, kr) = k
	for (dc, dr) in K_MOVES:
		(c, r) = (kc + dc, kr + dr)
		if 1 <= r <= 8 and 1 <= c <= 8:
			yield (c, r)

def solve(k: Tuple[int, int]) -> Dict[Tuple[int, int], int]:
	solutions = {}
	edges = [k]
	visited = set()
	steps = 0
	while edges:
		visited.update(edges)
		for edge in edges:
			solutions[edge] = steps
		steps += 1
		new_edges = []
		for edge in edges:
			for nk in gen_k_moves(edge):
				if nk in visited:
					continue
				new_edges.append(nk)
				visited.add(nk)
		edges = new_edges
	return solutions
w1_solutions = solve((2, 1))
w2_solutions = solve((7, 1))
b1_solutions = solve((2, 8))
b2_solutions = solve((7, 8))

def whose_turn(positions):
	positions = [(ord(s[0]) - ord('a') + 1, int(s[1])) for s in positions.split(';')]
	w1 = (w1_solutions[positions[0]], w2_solutions[positions[0]])
	w2 = (w1_solutions[positions[1]], w2_solutions[positions[1]])
	b1 = (b1_solutions[positions[2]], b2_solutions[positions[2]])
	b2 = (b1_solutions[positions[3]], b2_solutions[positions[3]])
	whites = min(w1[0] + w2[1], w1[1] + w2[0])
	blacks = min(b1[0] + b2[1], b1[1] + b2[0])
	return whites % 2 == blacks % 2


def whose_turn(positions):
	return sum((ord(c) for c in positions.replace(';', ''))) & 1 == 0