Golden Spirals

Steve loves nature. He likes looking at the pretty patterns in nature like the one below.

The spiral in the picture above have a special name - it is called a Golden Spiral. The Golden Spiral is found everywhere in nature - in flowers, in pinecones, in hurricanes, in galaxies, DNA molecules and many more.

The Golden Spiral is related to a special number in math called the Golden Ratio = 1.618. In this adventure we will build natural looking spirals using the Golden Ratio that makes Steve happy as he explores the Minecraft world.

Fibonacci series

Lets look at a few number series: The series of natural numbers is - 1,2,3,4,5,...

The series of even numbers is - 2,4,6,8,...

The series of odd numbers is - 1,3,5,7,...

There is a special sequence of numbers called the Fibonacci Series that helps us calculate the golden ratio.

The series of Fibonacci series = 1,1,2,3,5,8,...

It turns out that the Fibonacci numbers approximate the Golden Ratio.

Task 1: Calculating Fibonacci series

Your first task is to calculate the first 10 numbers in the Fibonacci series

Fibonacci series = 1,1,2,3,5,8,...

Fill in the rest of the series below:

1, 1, 2, __ , __ , __ , __ , __ , __ , __ , __

Task 2: Verify the Fibonacci series in Task 1

Run the code block below that defineds a function fib_list for the Fibonacci series.


In [1]:
# generate a list of Fibonacci series starting with 1
import itertools
import math
import numpy as np
def fib_function():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

def fib_list(n):
    fib = fib_function()
    return list(itertools.islice(fib,n+2))[2:]

Verify that your Fibonacci series is correct by using the function _fiblist

fib_list(10)

In [ ]:
# Task 2 program

Task 3: Functions to build Fibonacci and Log Spirals

Nice job on calculating the Fibonacci series. Lets define some more functions to build sprials using the Fibonacci series and the Spirals from the picture.

Just run all the cells below until you reach Task 4.


In [2]:
# return a range if the numbers are different
def ordered_range(a,b):
    if (a == b):
        return [a]
    elif (a < b):
        return range(a,b+1)
    else:
        return range(a,b-1,-1)

# replace None with previous value of coordinate
def replace_null(acc,p):
    null_replacer = lambda (a,b): b if (a is None) else a
    if not acc:
        return [p]
    else:
        previous_point = acc[-1]
        next_point = map(null_replacer, zip(p,previous_point))
        return acc + [next_point]

# a point p has (x,y,z) coordinates
def line_coordinates(p1,p2):
    x1,y1,z1 = p1[0],p1[1],p1[2]
    x2,y2,z2 = p2[0],p2[1],p2[2]
    points = reduce(replace_null,itertools.izip_longest(ordered_range(x1,x2),ordered_range(y1,y2),ordered_range(z1,z2)),[])
    return points

In [3]:
# generate the sequence [(1,0),(0,1),(-1,0),(0,-1),(1,0),...]
def next_mult((m,n)):
    return (-1*n,m)

# return a new sequence = previous sequence + new segment
# and new start position for next segment new_start
def fib_segment((prev_sequence,(xm,zm)),segment_length):
    start = prev_sequence[-1]
    x1,y1,z1 = start[0],start[1],start[2]
    x2,y2,z2 = x1 + (xm * segment_length), y1, z1 + (zm * segment_length)
    new_segment = line_coordinates((x1,y1,z1),(x2,y2,z2))[1:]
    new_sequence = prev_sequence + new_segment
    return (new_sequence,next_mult((xm,zm)))
    
# fibonacci coordinates
# alternating x,z using next_mult
def fib_coords(start, n):
    fib_series = fib_list(n)
    fib_series.reverse()
    fib_points = reduce(fib_segment,fib_series,([start],(1,0)))
    return fib_points[0]

#logarithmic spiral functions
def log_spiral_xy(theta, theta_transform):
    a = 1
    b = 0.3
    x = a*math.exp(b*theta)*math.cos(theta_transform(theta))
    z = a*math.exp(b*theta)*math.sin(theta_transform(theta))
    return (x,z)

def theta_to_xyz((x,y,z),t, theta_transform):
    x1,z1 = log_spiral_xy(t, theta_transform)
    return (x1,y,z1)

# log spiral coordinates
def log_sequence((x,y,z),rads, theta_transform = lambda t: t):
    logseq = map(lambda t: tuple(map(lambda x, y: int(round(x + y)), (x,y,z), theta_to_xyz((x,y,z),t, theta_transform))) ,rads)
    loglines = reduce(lambda acc,s: acc + line_coordinates(s[0],s[1]), zip(logseq,logseq[1:]),[])
    return loglines

In [4]:
# Minecraft initialization
import sys
#sys.path.append('/Users/esumitra/workspaces/mc/mcpipy')
import mcpi.minecraft as minecraft
import mcpi.block as block
import time
mc = minecraft.Minecraft.create()

In [9]:
# build in Minecraft

# common minecraft builder function
def mc_builder(blockid, point_function):
    pos = mc.player.getTilePos()
    start_point = (pos.x,pos.y,pos.z)
    mc.postToChat("building in 5 seconds ...")
    time.sleep(4)
    mc.postToChat("building in 1 seconds ...")
    time.sleep(1)
    points = point_function(start_point)
    for p in points:
        mc.setBlock(p[0], p[1], p[2], blockid)

# builder for fibonacci
def build_fib(blockid,n=4):
    mc_builder(blockid, lambda (x,y,z): fib_coords((x,y,z),n))

# builder for spiral with 1 arm
def build_spiral_arm(blockid, rads = np.arange(0,2*math.pi,0.25)):
    mc_builder(blockid, lambda (x,y,z): log_sequence((x,y,z),rads))

# builder for spiral with 7 arms
def build_spiral(blockid, rads = np.arange(0,2*math.pi*2,0.1)):
    spiral_points = (lambda p0:
                     reduce(lambda acc,x: acc + log_sequence(p0,rads,lambda t: t + x),
                            np.arange(0,2*math.pi,math.pi/3.0),
                            []))
    mc_builder(blockid, spiral_points)

def build_reverse_spiral(blockid, rads = np.arange(0,2*math.pi*2,0.1)):
    spiral_points = (lambda p0:
                     reduce(lambda acc,x: acc + log_sequence(p0,rads,lambda t: -1.0 * (t + x)),
                            np.arange(0,2*math.pi,math.pi/3.0),
                            []))
    mc_builder(blockid, spiral_points)

Task 4: Fibonacci and Log Spirals

Ready for some fun? Find a location in Minecraft where you want to generate a spiral and run the programs below. Use the program build_fib to build a Fibonacci spiral arm and build_spiral_arm to build a golden spiral arm.

build_fib(block.DIAMOND_BLOCK.id)
build_spiral_arm(block.STONE.id)

Trying using different numbers and building blocks in the building functions.


In [41]:
# Task 4

Task 5: Spiral Landscaping

For your last task, lets shape the landscape using the build_spiral and build_reverse_spiral functions with different blocks. Try using natural blocks like block.FLOWER_YELLOW.id or block.TORCH.id at night in you program. Move to different locations in Minecraft and run the function to build a spiral at that location.

build_spiral(block.FLOWER_YELLOW.id)

Have fun!


In [29]:
# Task 5