Puzzle

From miki
Jump to navigation Jump to search

Prisoner and othello board

Reference:

#!/usr/bin/python3

# Problem:
#
# Two prisoners are given the chance to be free if they can solve the following problem.
#
# The guardian takes the first prisoner to a room where there is a 8x8 chess board.
# On the board, there are 64 tokens that are white on one side and black on the other side.
# The tokens are flipped at random with no apparent pattern.
# The guardian chooses one of the cell, and hide the prison key in the cell (say in some hidden compartment under the cell).
# The guardian says that the 2nd prisoner will have to guess where the key is hidden by looking at the board.
# The only thing that the 1st prisoner can (and must) do is to flip one of the 64 tokens on the board.
# What strategy the prisoners must follow in order to guarantee that the 2nd prisoner finds the key?
# We assume that the prisoner were aware of the problem of the guardian and could have agreed on a given strategy beforehand.
#
# Solution:
# * We split the problem into 2 sub-problems: find which row and find which column.
# * To find the row, we use the parity of the columns. To find the colum, we use the parity of the rows.
# * The parity of a column is given by the number of (say) white token. 
#   If this number is odd, parity of the column is odd, and even otherwise.
# * By flipping a token, the prisoner can flip independently the parity of one column, and of one row.
# * Prisoner will use these column (resp. row) parities to encode the index of the row (resp. colum):
#   * Prisoner sets index to 0.
#   * Prisoner looks at first column.   If the parity is odd, the prisoner compute index ^= 0b001 (binary notation)
#   * Prisoner looks at second column.  If the parity is odd, the prisoner compute index ^= 0b010 (binary notation)
#   * Prisoner looks at third column.   If the parity is odd, the prisoner compute index ^= 0b100 (binary notation)
#   * Prisoner looks at fourth column.  If the parity is odd, the prisoner compute index ^= 0b011 (binary notation)
#   * Prisoner looks at fifth column.   If the parity is odd, the prisoner compute index ^= 0b101 (binary notation)
#   * Prisoner looks at sixth column.   If the parity is odd, the prisoner compute index ^= 0b110 (binary notation)
#   * Prisoner looks at seventh column. If the parity is odd, the prisoner compute index ^= 0b111 (binary notation)
#   * Prisoner ignores the eighth column.
# * To communicate the index of the secret cell, the prisoner computes the index given the current parities.
#   * If the index is correct, prisoner will flip the parity of the eighth column.
#   * Otherwise, by construction, there is one and only one column that must be flipped in order to obtain the correct parity.
#     This is because the current index and the desired index differ by either one bit (one of the column 1 to 3 must be flipped), 
#     or two bits (column 4 to 6 must be flipped) or three bits (column 7 must be flipped).

def eval(parities):
    "Return the value from an array of 8 parities"
    value = 0
    if parities & 1 != 0:
        value ^= 1 # toggle 001
    if parities & 2 != 0:
        value ^= 2 # toggle 010
    if parities & 4 != 0:
        value ^= 4 # toggle 100
    if parities & 8 != 0:
        value ^= 3 # toggle 011
    if parities & 16 != 0:
        value ^= 5 # toggle 101
    if parities & 32 != 0:
        value ^= 6 # toggle 110
    if parities & 64 != 0:
        value ^= 7 # toggle 111
    # last parity, 128, toggles nothing
    return value

def flip(parity,bit_idx):
    "Flip a bit in the given parity"
    assert((bit_idx >= 0) and (bit_idx < 8))
    return parity ^ (2 ** bit_idx)

def find_flip(parity,value):
    "Find which bit in the parity must be flipped to get value"
    assert((value >= 0) and (value < 8))
    current = eval(parity)
    if current ^ value == 1:
        return 0
    if current ^ value == 2:
        return 1
    if current ^ value == 4:
        return 2
    if current ^ value == 2+1:
        return 3
    if current ^ value == 4+1:
        return 4
    if current ^ value == 4+2:
        return 5
    if current ^ value == 4+2+1:
        return 6
    assert(current == value)
    return 7

for parity in range(256):
    for value in range(8):
        bit_idx = find_flip(parity,value)
        new_parity = flip(parity,bit_idx)
        if eval(new_parity) != value:
            print(f"Wrong flip for parity {parity} value {value} bit_idx {bit_idx} new_parity {new_parity} new_value {eval(new_parity)}")
            assert(eval(new_parity) == value)

print("We are FREE!")