# Magpies are my favourite birds

Baby ones even more so...



It is a little known fact^ that the black & white colours of baby magpies differ by **at least** one place and **at most** two places from the colours of the mother magpie.

So now you can work out if any two magpies may be related. 

*...and Quardle oodle ardle wardle doodle the magpies said*

# Kata Task

Given the colours of two magpies, determine if one is a possible **child** or **grand-child** of the other.


# Notes

* Each pair of birds being compared will have same number of colour areas
* `B` = Black
* `W` = White

# Example

Given these three magpies


Magpie 1  BWBWBW
Magpie 2  BWBWBB
Magpie 3  WWWWBB


You can see:
* Magpie 2 may be a child of Magpie 1 because there is only one difference
* Magpie 3 may be child of Magpie 2 because there are two differences
* So Magpie 3 may be a grand-child of Magpie 1
* On the other hand, Magpie 3 cannot be a child of Magpie 1 because there are three differences


---

DM :-)


^ *This fact is little known because I just made it up*

Difficulty: EASY

Starter code:
def child(bird1, bird2):
	

Solutions:
def diffs(bird1, bird2):
	return sum((c1 != c2 for (c1, c2) in zip(bird1, bird2)))

def child(bird1, bird2):
	return diffs(bird1, bird2) in [1, 2]

def grandchild(bird1, bird2):
	return diffs(bird1, bird2) in [0, 1, 2, 3, 4] if len(bird1) > 1 else bird1 == bird2


def child(bird1, bird2):
	return 1 <= sum((i != j for (i, j) in zip(bird1, bird2))) <= 2

def grandchild(bird1, bird2):
	return bird1 == bird2 or (sum((i != j for (i, j) in zip(bird1, bird2))) <= 4 and len(bird1) > 1)


from itertools import islice
from operator import ne

def child(bird1, bird2):
	return 0 < sum(islice(filter(None, map(ne, bird1, bird2)), 3)) < 3

def grandchild(bird1, bird2):
	if len(bird1) == 1:
		return bird1 == bird2
	return sum(islice(filter(None, map(ne, bird1, bird2)), 5)) < 5


def diffs(a, b):
	return sum((1 for (aa, bb) in zip(a, b) if aa != bb))

def child(bird1, bird2):
	return diffs(bird1, bird2) in [1, 2]

def grandchild(bird1, bird2):
	l = len(bird1)
	d = diffs(bird1, bird2)
	return d < 5 and l > 1 or (d == 0 and l == 1)


child = lambda b1, b2: 0 < sum([i != j for (i, j) in zip(b1, b2)]) < 3
grandchild = lambda b1, b2: sum([i != j for (i, j) in zip(b1, b2)]) < 5 and b1 + b2 not in ['WB', 'BW']


def hamming(a, b):
	return sum((c1 != c2 for (c1, c2) in zip(a, b)))

def child(bird1, bird2):
	return 1 <= hamming(bird1, bird2) <= 2

def grandchild(bird1, bird2):
	if len(bird1) == 1:
		return bird1 == bird2
	return 0 <= hamming(bird1, bird2) <= 4


def diff(bird1, bird2):
	return sum((c1 != c2 for (c1, c2) in zip(bird1, bird2)))

def child(bird1, bird2):
	return 1 <= diff(bird1, bird2) <= 2

def grandchild(bird1, bird2):
	d = diff(bird1, bird2)
	if len(bird1) == 1:
		return d == 0
	return 0 <= d <= 4


from operator import ne

def child(bird1, bird2):
	return 1 <= sum(map(ne, bird1, bird2)) <= 2

def grandchild(bird1, bird2):
	return sum(map(ne, bird1, bird2)) <= 4 and bird1 + bird2 not in 'BWB'


def mutations(s):

	def flip(c):
		return {'B': 'W', 'W': 'B'}[c]

	def recurse(ss, n, acc):
		if not ss:
			if n > 0:
				yield ''.join(acc)
		else:
			yield from recurse(ss[1:], n, acc + [ss[0]])
			if n < 2:
				yield from recurse(ss[1:], n + 1, acc + [flip(ss[0])])
	yield from recurse(s, 0, [])

def child(bird1, bird2):
	possible = set(mutations(bird1))
	return bird2 in possible

def grandchild(bird1, bird2):
	possible = set((grand_child for child in mutations(bird1) for grand_child in mutations(child)))
	return bird2 in possible


from itertools import product

def child(bird1, bird2):
	return sum([b[0] != b[1] for b in zip(bird1, bird2)]) in (1, 2)

def grandchild(bird1, bird2):
	for mother in product('BW', repeat=len(bird1)):
		if child(bird1, mother) and child(bird2, mother):
			return True
	return False