Sunday, August 21, 2016

Pickover's OR Game

In his book Mazes for the Mind: Computers and the Unexpected, Clifford Pickover explores tons of algorithms that produce surprising results, including this one: take all the points on an x-y grid and convert the coordinates to binary. Taken digit by digit, if there's a 1 in either x OR y, the result will have a 1 in that digit. Color that x-y location according to the base 10 value of that number, mod 255 of course. (Since RGB values typically have a range of 0 - 255).

For example, for the point at (10, 45) you'd convert the x and y coordinates to binary. 10 is 1010 in binary, and 45 is 101101. Comparing the digits and filling in 1's if there's a 1 in that place in either number, you get 101111 or 47. You'd color that pixel 47, on the darker end of the 0-255 grayscale.

I have a binary converter program in my book Hacking Math Class with Python:

def convToBinary(num):
    '''converts decimal number to binary'''
    exponent = 0
    binary_number = 0
    while num >= 2 ** exponent: #Find the lowest power of 2
        exponent += 1           #the number is less than
    exponent -= 1
    for i in range(exponent + 1):
        #if num contains that power of 2
        if num - 2**exponent > -1: 
            #add that power of 10
            binary_number += 10**exponent 
            num -= 2**exponent
        exponent -= 1
    return binary_number

I created an "addZeros" function to make sure each binary number was 8 digits.

def addZeros(x):
    '''converts a binary number to
    8-digit form'''
    x = str(convToBinary(x))
    length = len(x)
    x = (8-length)*'0'+ x
    return x

In the next step I'll want to convert the number back to decimal so I wrote this:

def convToDec(num):
    '''converts num to decimal'''
    decnum = 0
    num = str(num)
    num = num[::-1] #reverse digits
    for i,v in enumerate(num):
        if v == '1':
            decnum += 2 ** i
    return decnum

Now we can make the comparison I described above, and return the decimal number that we'll give to the color argument.

def compareOr(a,b):
    '''converts 2 numbers to binary and
    compares their digits, returns a 1 in
    a place if it's 1 in a OR b'''
    result = 0
    a,b = addZeros(a),addZeros(b)
    for i in range(8):
        if a[-(1+i)] == '1' or b[-(1+i)] == '1':
            result += 10**i
    return convToDec(result)

Finally we'll draw a tiny 2x2 pixel rectangle of the calculated color. I used the bluescale:

def displayOr(x,y):
    '''displays the result of the "Or" comparison'''
    pygame.draw.rect(screen,compareOr(x,y) %254,[x,y,2,2])

I first did this in Pygame. All the code so far will run in the Python mode of Processing except the very last line. In Processing it needs to be replaced by these two lines.

    fill(0,0,compareOr(x,y) %254)
    rect(x,y,2,2)

Run the displayOr function using this nested loop for every other pixel on the screen:

for x in range(0,WIDTH,2):
    for y in range(0,HEIGHT,2):
        displayOr(x,y)

Guess what it makes?


If you guessed the Sierpinski Triangle you're right about a ton of seemingly unrelated afternoons spent playing around with math, Python, Pygame, Processing and Pickover. Pete out!

No comments:

Post a Comment