# 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.

Difficulty: introductory

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(r"[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]

    # Player's knights on black-black or white-white => odd number of moves
    white_pieces_match = on_white_squares[0] == on_white_squares[1]
    black_pieces_match = on_white_squares[2] == on_white_squares[3]

    # Equal number of moves => white to move
    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]:
    """
    Generates all valid knight moved from the given point
    """
    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]:
    """
    Given the starting point of a knight k, return a dict of postions to minimum moves to that position
    """
    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