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.
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.
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
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)
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
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