# 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: introductory

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)])<5and 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'

# Ever so slightly computationally inefficient method :)

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)):
        # check if both birds are related to the 'created' mother magpie
        if child(bird1, mother) and child(bird2, mother): 
            return True
    return False