Last active
April 21, 2020 17:50
-
-
Save HarvsG/cb1020671ec9258baa94dd0f85d6d82f to your computer and use it in GitHub Desktop.
My solution to Matt Parker's 4 CARD PUZZLE (MPMP PUZZLE 4) https://www.youtube.com/watch?v=oCMVUROty0g
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import itertools | |
import sys | |
if sys.version_info[0] < 3: | |
raise Exception("Python 3 or a more recent version is required.") | |
class Deck: | |
# a class that takes an array of True/False of any length. True equals face down | |
def __init__(self,cards): | |
# make sure a list is passed, if not, convert it to one | |
if not isinstance(cards, list): | |
try: | |
cards = list(cards) | |
print('non-list object given, converted to list:') | |
print(cards) | |
except TypeError: | |
print('argument is not a list and cannot be converted to one:') | |
print(cards) | |
# check it is a list of bools | |
if not all(isinstance(x, bool) for x in cards): | |
raise Exception("The list provided contains non boolean values") | |
self.cards = cards | |
self.moves = [] | |
self.start = self._card_view(cards) | |
def check(self): | |
# A functions that checks to see if all cards are face down, if so returns the number of moves to get there | |
if all(self.cards) == True: | |
return len(self.moves) | |
else: | |
return False | |
def flip(self, index): | |
# Flip a card but if we have all cards face down then we have won, so don't make any further moves | |
if self.check() is False: | |
self.cards[index] = not self.cards[index] | |
self.moves.append(index) | |
def _card_view(self, cards): | |
# Prints the cards in a pretty emoji | |
array = [] | |
for x in cards: | |
if x == True: | |
# playing card face down emoji | |
array.append("\U0001F3B4") | |
else: | |
# playing card face up emoji | |
array.append("\U0001F0CF") | |
return array | |
def __repr__(self): | |
return str(self._card_view(self.cards)) | |
def __str__(self): | |
return ''.join(self._card_view(self.cards)) | |
def __len__(self): | |
return len(self.cards) | |
def make_decks(l): | |
# make all possible iterations of a deck of length l. return this as a list of decks | |
permutations = itertools.product([True,False],repeat=l) | |
permutations = list(permutations) # creates a list of tuples | |
for i,val in enumerate(permutations): | |
# for every permutation, create a Deck from the list(tuple) | |
permutations[i] = Deck(list(val)) | |
return permutations | |
def construct_gray_code(b): | |
# A reccursive function to construct gray code of bit length 'b'. Returns a list of strings | |
if b == 1: | |
# the simplest 1 bit gray code | |
return ['0','1'] | |
else: | |
li = construct_gray_code(b-1) # recursively get the gray code of length b - 1 | |
il = li[::-1] # creates a mirror of the list | |
for i, val in enumerate(li): | |
# prepend 0 to all the first list | |
li[i] = '0'+val | |
for i, val in enumerate(il): | |
# prepend 1 to the mirror image | |
il[i] = '1'+val | |
# concatenate and return to two lists | |
return li + il | |
def construct_moves(bits): | |
# from the generated Gray code this will generate the moves that need to be made to 'count' up through the Gray code | |
gc = construct_gray_code(bits) | |
for i, val in enumerate(gc): | |
#convert the Gray code strings to ints | |
gc[i] = int(val,base=2) | |
moves = [] | |
for i in range(len(gc)-1): | |
# since by defintion Gray code only flips one bit each time, we can find the flipped bit by subtracting between each count | |
diff = gc[i+1] - gc[i] | |
# we only care which bit is flipped, not the direction | |
diff = abs(diff) | |
# converts the difference back into a binary string that is padded | |
binary = format(diff, '#0'+str(bits+2)+'b')[2:] | |
# getting the index of the flipped bit will give us which card needs to be flipped | |
move = list(binary).index('1') | |
moves.append(move) | |
return moves | |
# construct the deck of the chosen length, and construct the moves of the corresponding length | |
num_of_cards = 4 | |
decks = make_decks(num_of_cards) | |
moves_to_make = construct_moves(num_of_cards) | |
dic = {} | |
for deck in decks: | |
# iterate through the decks | |
print(deck) | |
for move in moves_to_make: | |
# Make each move on the deck | |
deck.flip(move) | |
print(deck) | |
# show the moves made for said deck | |
print(deck.moves) | |
# fill out a dictionary of the start position of each deck and the number of moves required | |
dic[''.join(deck.start)] = len(deck.moves) | |
# print the final dictionary | |
print(dic) | |
print('The final ans is: ') | |
print(max(dic.values())) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment