import matplotlib.pyplot as plt

import numpy as np
import random

delta = 0.025
alpha = 1.5

# from: http://extremelearning.com.au/how-to-generate-uniformly-random-points-on-n-spheres-and-n-balls/
def muller(N, d):
    points = []
    while len(points) < N:
        u = np.random.normal(0, 1, d)
        norm = np.sum(u ** 2) ** (0.5)
        r = random.random() ** (1.0 / d)
        pt = r * u / norm
        points.append(tuple(pt))
    return points

def muller_tweak(point):
    change = muller(1, 2)[0]
    new_x = point[0] + delta * change[0]
    new_y = point[1] + delta * change[1]
    return (new_x, new_y)

def square_tweak(point):
    change = (2 * random.random() - 1, 2 * random.random() - 1)
    new_x = point[0] + delta * change[0]
    new_y = point[1] + delta * change[1]
    return (new_x, new_y)

def normal_tweak(point):
    change = (np.random.normal(0, delta/2), np.random.normal(0, delta/2))
    new_x = point[0] + change[0]
    new_y = point[1] + change[1]
    return (new_x, new_y)

def levy_tweak(point):
    change = (random.random()**(-1/alpha)-1, random.random()**(-1/alpha)-1)
    x_sign = random.choice([-1, 1])
    y_sign = random.choice([-1, 1])
    new_x = point[0] + x_sign * delta * change[0]
    new_y = point[1] + y_sign * delta * change[1]
    return (new_x, new_y)


plt.ion()
plt.style.use('ggplot')
fig, ax = plt.subplots()

explore_line_muller, = ax.plot([], [], linestyle='-', marker='o', markersize=1, color='red')
explore_line_square, = ax.plot([], [], linestyle='-', marker='o', markersize=1, color='blue')

explore_line_normal, = ax.plot([], [], linestyle='-', marker='o', markersize=1, color='purple')
explore_line_levy, = ax.plot([], [], linestyle='-', marker='o', markersize=1, color='orange')


muller_xline = []
muller_yline = []
square_xline = []
square_yline = []

normal_xline = []
normal_yline = []
levy_xline = []
levy_yline = []

best = 0
ax.set_xlim((-3, 3))
ax.set_ylim((-3, 3))

muller_point = (0,0)
square_point = (0,0)
normal_point = (0,0)
levy_point = (0,0)

while True:
    muller_xline.append(muller_point[0])
    muller_yline.append(muller_point[1])
    explore_line_muller.set_data(muller_xline, muller_yline)

    square_xline.append(square_point[0])
    square_yline.append(square_point[1])
    explore_line_square.set_data(square_xline, square_yline)

    normal_xline.append(normal_point[0])
    normal_yline.append(normal_point[1])
    explore_line_normal.set_data(normal_xline, normal_yline)

    levy_xline.append(levy_point[0])
    levy_yline.append(levy_point[1])
    explore_line_levy.set_data(levy_xline, levy_yline)

    plt.pause(0.0001)

    muller_point = muller_tweak(muller_point)
    square_point = square_tweak(square_point)

    normal_point = normal_tweak(normal_point)
    levy_point = levy_tweak(levy_point)