#%%
import collections
import math

#%%
Point = collections.namedtuple("Point", ["x", "y"])


_Hex = collections.namedtuple("Hex", ["q", "r", "s"])

def Hex(q, r, s): #creates the hexagon datatype 
    assert not (round(q + r + s) != 0), "q + r + s must be 0"
    return _Hex(q, r, s)

def eq(a,b): #Returns wether or not two hexs are equal
    return a.q==b.q and a.r==b.r and a.s==b.s

#Vector operators for hexagons
def hex_add(a, b): 
    return Hex(a.q + b.q, a.r + b.r, a.s + b.s)

def hex_subtract(a, b):
    return Hex(a.q - b.q, a.r - b.r, a.s - b.s)

def hex_scale(a, k:int):
    return Hex(a.q * k, a.r * k, a.s * k)


def hex_length(hex): #length of the line from 0,0 to the hexagon
    return (abs(hex.q) + abs(hex.r) + abs(hex.s)) // 2

def hex_distance(a, b): #distance between two hexagons
    return hex_length(hex_subtract(a, b))

#Functions to move directionly in the lattice or aquire neibhbors
hex_directions = [Hex(1, 0, -1), Hex(1, -1, 0), Hex(0, -1, 1), Hex(-1, 0, 1), Hex(-1, 1, 0), Hex(0, 1, -1)]
def hex_direction(direction):
    return hex_directions[direction]

def hex_neighbor(hex, direction):
    return hex_add(hex, hex_direction(direction))


# Datatypes for pointy top or flattop lattices
Orientation = collections.namedtuple("Orientation", ["f0", "f1", "f2", "f3", "b0", "b1", "b2", "b3", "start_angle"])

Layout = collections.namedtuple("Layout", ["orientation", "size", "origin"])

layout_pointy = Orientation(math.sqrt(3.0), math.sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0, math.sqrt(3.0) / 3.0, -1.0 / 3.0, 0.0, 2.0 / 3.0, 0.5)
layout_flat = Orientation(3.0 / 2.0, 0.0, math.sqrt(3.0) / 2.0, math.sqrt(3.0), 2.0 / 3.0, 0.0, -1.0 / 3.0, math.sqrt(3.0) / 3.0, 0.0)



def hex_to_pixel(layout, h): #returns the x,y coordinate of the center of the hexagon
    M = layout.orientation
    x = (M.f0*h.q+M.f1*h.r)*layout.size.x
    y = (M.f2*h.q+M.f3*h.r)*layout.size.y
    return Point(x + layout.origin.x , y + layout.origin.y)


def hex_corner_offset(layout, corner): #Returns how far off each corner is from the center point
    M = layout.orientation
    size = layout.size
    angle = 2.0 * math.pi * (M.start_angle - corner) / 6.0
    return Point(size.x * math.cos(angle), size.y * math.sin(angle))

def polygon_corners(layout, h): #Creates an array of corners by applying the corner offset method to the center six times
    corners = []
    center = hex_to_pixel(layout, h)
    for i in range(0, 6):
        offset = hex_corner_offset(layout, i)
        corners.append(Point(center.x + offset.x, center.y + offset.y))
    return corners


#%%
#Test functions
def complain(name):
    print("FAIL {0}".format(name))

def equal_hex(name, a, b):
    if not (a.q == b.q and a.s == b.s and a.r == b.r):
        complain(name)

def equal_int(name, a, b):
    if not (a == b):
        complain(name)

def equal_hex_array(name, a, b):
    equal_int(name, len(a), len(b))
    for i in range(0, len(a)):
        equal_hex(name, a[i], b[i])

def test_hex_arithmetic():
    equal_hex("hex_add", Hex(4, -10, 6), hex_add(Hex(1, -3, 2), Hex(3, -7, 4)))
    equal_hex("hex_subtract", Hex(-2, 4, -2), hex_subtract(Hex(1, -3, 2), Hex(3, -7, 4)))

def test_hex_direction():
    equal_hex("hex_direction", Hex(0, -1, 1), hex_direction(2))

def test_hex_neighbor():
    equal_hex("hex_neighbor", Hex(1, -3, 2), hex_neighbor(Hex(1, -2, 1), 2))


def test_hex_distance():
    equal_int("hex_distance", 7, hex_distance(Hex(3, -7, 4), Hex(0, 0, 0)))





def test_all():
    test_hex_arithmetic()
    test_hex_direction()
    test_hex_neighbor()
    test_hex_distance()




test_all()


#%%
# Creates rectangular map of hexagons
def rect_map(map_height,map_width):
    map=[]
    for r in range(map_height):
        r_offset=math.floor(r/2)
        for q in range(-r_offset,map_width-r_offset):
            map.append(Hex(q,r,-q-r))
    return map


#%%
map=rect_map(6,8)
map
#%%
layout= Layout(layout_pointy,Point(10,10),Point(0,0))


#%%

fd8e7dc8404c25a7dd5ed605ca9095cf722981a9
<mask token>


def Hex(q, r, s):
    assert not round(q + r + s) != 0, 'q + r + s must be 0'
    return _Hex(q, r, s)


def eq(a, b):
    return a.q == b.q and a.r == b.r and a.s == b.s


def hex_add(a, b):
    return Hex(a.q + b.q, a.r + b.r, a.s + b.s)


<mask token>


def hex_scale(a, k: int):
    return Hex(a.q * k, a.r * k, a.s * k)


def hex_length(hex):
    return (abs(hex.q) + abs(hex.r) + abs(hex.s)) // 2


def hex_distance(a, b):
    return hex_length(hex_subtract(a, b))


<mask token>


def hex_to_pixel(layout, h):
    M = layout.orientation
    x = (M.f0 * h.q + M.f1 * h.r) * layout.size.x
    y = (M.f2 * h.q + M.f3 * h.r) * layout.size.y
    return Point(x + layout.origin.x, y + layout.origin.y)


def hex_corner_offset(layout, corner):
    M = layout.orientation
    size = layout.size
    angle = 2.0 * math.pi * (M.start_angle - corner) / 6.0
    return Point(size.x * math.cos(angle), size.y * math.sin(angle))


def polygon_corners(layout, h):
    corners = []
    center = hex_to_pixel(layout, h)
    for i in range(0, 6):
        offset = hex_corner_offset(layout, i)
        corners.append(Point(center.x + offset.x, center.y + offset.y))
    return corners


def complain(name):
    print('FAIL {0}'.format(name))


def equal_hex(name, a, b):
    if not (a.q == b.q and a.s == b.s and a.r == b.r):
        complain(name)


<mask token>


def equal_hex_array(name, a, b):
    equal_int(name, len(a), len(b))
    for i in range(0, len(a)):
        equal_hex(name, a[i], b[i])


def test_hex_arithmetic():
    equal_hex('hex_add', Hex(4, -10, 6), hex_add(Hex(1, -3, 2), Hex(3, -7, 4)))
    equal_hex('hex_subtract', Hex(-2, 4, -2), hex_subtract(Hex(1, -3, 2),
        Hex(3, -7, 4)))


def test_hex_direction():
    equal_hex('hex_direction', Hex(0, -1, 1), hex_direction(2))


def test_hex_neighbor():
    equal_hex('hex_neighbor', Hex(1, -3, 2), hex_neighbor(Hex(1, -2, 1), 2))


<mask token>


def test_all():
    test_hex_arithmetic()
    test_hex_direction()
    test_hex_neighbor()
    test_hex_distance()


<mask token>


def rect_map(map_height, map_width):
    map = []
    for r in range(map_height):
        r_offset = math.floor(r / 2)
        for q in range(-r_offset, map_width - r_offset):
            map.append(Hex(q, r, -q - r))
    return map


<mask token>

<mask token>


def Hex(q, r, s):
    assert not round(q + r + s) != 0, 'q + r + s must be 0'
    return _Hex(q, r, s)


def eq(a, b):
    return a.q == b.q and a.r == b.r and a.s == b.s


def hex_add(a, b):
    return Hex(a.q + b.q, a.r + b.r, a.s + b.s)


def hex_subtract(a, b):
    return Hex(a.q - b.q, a.r - b.r, a.s - b.s)


def hex_scale(a, k: int):
    return Hex(a.q * k, a.r * k, a.s * k)


def hex_length(hex):
    return (abs(hex.q) + abs(hex.r) + abs(hex.s)) // 2


def hex_distance(a, b):
    return hex_length(hex_subtract(a, b))


<mask token>


def hex_to_pixel(layout, h):
    M = layout.orientation
    x = (M.f0 * h.q + M.f1 * h.r) * layout.size.x
    y = (M.f2 * h.q + M.f3 * h.r) * layout.size.y
    return Point(x + layout.origin.x, y + layout.origin.y)


def hex_corner_offset(layout, corner):
    M = layout.orientation
    size = layout.size
    angle = 2.0 * math.pi * (M.start_angle - corner) / 6.0
    return Point(size.x * math.cos(angle), size.y * math.sin(angle))


def polygon_corners(layout, h):
    corners = []
    center = hex_to_pixel(layout, h)
    for i in range(0, 6):
        offset = hex_corner_offset(layout, i)
        corners.append(Point(center.x + offset.x, center.y + offset.y))
    return corners


def complain(name):
    print('FAIL {0}'.format(name))


def equal_hex(name, a, b):
    if not (a.q == b.q and a.s == b.s and a.r == b.r):
        complain(name)


<mask token>


def equal_hex_array(name, a, b):
    equal_int(name, len(a), len(b))
    for i in range(0, len(a)):
        equal_hex(name, a[i], b[i])


def test_hex_arithmetic():
    equal_hex('hex_add', Hex(4, -10, 6), hex_add(Hex(1, -3, 2), Hex(3, -7, 4)))
    equal_hex('hex_subtract', Hex(-2, 4, -2), hex_subtract(Hex(1, -3, 2),
        Hex(3, -7, 4)))


def test_hex_direction():
    equal_hex('hex_direction', Hex(0, -1, 1), hex_direction(2))


def test_hex_neighbor():
    equal_hex('hex_neighbor', Hex(1, -3, 2), hex_neighbor(Hex(1, -2, 1), 2))


def test_hex_distance():
    equal_int('hex_distance', 7, hex_distance(Hex(3, -7, 4), Hex(0, 0, 0)))


def test_all():
    test_hex_arithmetic()
    test_hex_direction()
    test_hex_neighbor()
    test_hex_distance()


<mask token>


def rect_map(map_height, map_width):
    map = []
    for r in range(map_height):
        r_offset = math.floor(r / 2)
        for q in range(-r_offset, map_width - r_offset):
            map.append(Hex(q, r, -q - r))
    return map


<mask token>

<mask token>


def Hex(q, r, s):
    assert not round(q + r + s) != 0, 'q + r + s must be 0'
    return _Hex(q, r, s)


def eq(a, b):
    return a.q == b.q and a.r == b.r and a.s == b.s


def hex_add(a, b):
    return Hex(a.q + b.q, a.r + b.r, a.s + b.s)


def hex_subtract(a, b):
    return Hex(a.q - b.q, a.r - b.r, a.s - b.s)


def hex_scale(a, k: int):
    return Hex(a.q * k, a.r * k, a.s * k)


def hex_length(hex):
    return (abs(hex.q) + abs(hex.r) + abs(hex.s)) // 2


def hex_distance(a, b):
    return hex_length(hex_subtract(a, b))


<mask token>


def hex_neighbor(hex, direction):
    return hex_add(hex, hex_direction(direction))


<mask token>


def hex_to_pixel(layout, h):
    M = layout.orientation
    x = (M.f0 * h.q + M.f1 * h.r) * layout.size.x
    y = (M.f2 * h.q + M.f3 * h.r) * layout.size.y
    return Point(x + layout.origin.x, y + layout.origin.y)


def hex_corner_offset(layout, corner):
    M = layout.orientation
    size = layout.size
    angle = 2.0 * math.pi * (M.start_angle - corner) / 6.0
    return Point(size.x * math.cos(angle), size.y * math.sin(angle))


def polygon_corners(layout, h):
    corners = []
    center = hex_to_pixel(layout, h)
    for i in range(0, 6):
        offset = hex_corner_offset(layout, i)
        corners.append(Point(center.x + offset.x, center.y + offset.y))
    return corners


def complain(name):
    print('FAIL {0}'.format(name))


def equal_hex(name, a, b):
    if not (a.q == b.q and a.s == b.s and a.r == b.r):
        complain(name)


def equal_int(name, a, b):
    if not a == b:
        complain(name)


def equal_hex_array(name, a, b):
    equal_int(name, len(a), len(b))
    for i in range(0, len(a)):
        equal_hex(name, a[i], b[i])


def test_hex_arithmetic():
    equal_hex('hex_add', Hex(4, -10, 6), hex_add(Hex(1, -3, 2), Hex(3, -7, 4)))
    equal_hex('hex_subtract', Hex(-2, 4, -2), hex_subtract(Hex(1, -3, 2),
        Hex(3, -7, 4)))


def test_hex_direction():
    equal_hex('hex_direction', Hex(0, -1, 1), hex_direction(2))


def test_hex_neighbor():
    equal_hex('hex_neighbor', Hex(1, -3, 2), hex_neighbor(Hex(1, -2, 1), 2))


def test_hex_distance():
    equal_int('hex_distance', 7, hex_distance(Hex(3, -7, 4), Hex(0, 0, 0)))


def test_all():
    test_hex_arithmetic()
    test_hex_direction()
    test_hex_neighbor()
    test_hex_distance()


<mask token>


def rect_map(map_height, map_width):
    map = []
    for r in range(map_height):
        r_offset = math.floor(r / 2)
        for q in range(-r_offset, map_width - r_offset):
            map.append(Hex(q, r, -q - r))
    return map


<mask token>

<mask token>


def Hex(q, r, s):
    assert not round(q + r + s) != 0, 'q + r + s must be 0'
    return _Hex(q, r, s)


def eq(a, b):
    return a.q == b.q and a.r == b.r and a.s == b.s


def hex_add(a, b):
    return Hex(a.q + b.q, a.r + b.r, a.s + b.s)


def hex_subtract(a, b):
    return Hex(a.q - b.q, a.r - b.r, a.s - b.s)


def hex_scale(a, k: int):
    return Hex(a.q * k, a.r * k, a.s * k)


def hex_length(hex):
    return (abs(hex.q) + abs(hex.r) + abs(hex.s)) // 2


def hex_distance(a, b):
    return hex_length(hex_subtract(a, b))


<mask token>


def hex_direction(direction):
    return hex_directions[direction]


def hex_neighbor(hex, direction):
    return hex_add(hex, hex_direction(direction))


<mask token>


def hex_to_pixel(layout, h):
    M = layout.orientation
    x = (M.f0 * h.q + M.f1 * h.r) * layout.size.x
    y = (M.f2 * h.q + M.f3 * h.r) * layout.size.y
    return Point(x + layout.origin.x, y + layout.origin.y)


def hex_corner_offset(layout, corner):
    M = layout.orientation
    size = layout.size
    angle = 2.0 * math.pi * (M.start_angle - corner) / 6.0
    return Point(size.x * math.cos(angle), size.y * math.sin(angle))


def polygon_corners(layout, h):
    corners = []
    center = hex_to_pixel(layout, h)
    for i in range(0, 6):
        offset = hex_corner_offset(layout, i)
        corners.append(Point(center.x + offset.x, center.y + offset.y))
    return corners


def complain(name):
    print('FAIL {0}'.format(name))


def equal_hex(name, a, b):
    if not (a.q == b.q and a.s == b.s and a.r == b.r):
        complain(name)


def equal_int(name, a, b):
    if not a == b:
        complain(name)


def equal_hex_array(name, a, b):
    equal_int(name, len(a), len(b))
    for i in range(0, len(a)):
        equal_hex(name, a[i], b[i])


def test_hex_arithmetic():
    equal_hex('hex_add', Hex(4, -10, 6), hex_add(Hex(1, -3, 2), Hex(3, -7, 4)))
    equal_hex('hex_subtract', Hex(-2, 4, -2), hex_subtract(Hex(1, -3, 2),
        Hex(3, -7, 4)))


def test_hex_direction():
    equal_hex('hex_direction', Hex(0, -1, 1), hex_direction(2))


def test_hex_neighbor():
    equal_hex('hex_neighbor', Hex(1, -3, 2), hex_neighbor(Hex(1, -2, 1), 2))


def test_hex_distance():
    equal_int('hex_distance', 7, hex_distance(Hex(3, -7, 4), Hex(0, 0, 0)))


def test_all():
    test_hex_arithmetic()
    test_hex_direction()
    test_hex_neighbor()
    test_hex_distance()


<mask token>


def rect_map(map_height, map_width):
    map = []
    for r in range(map_height):
        r_offset = math.floor(r / 2)
        for q in range(-r_offset, map_width - r_offset):
            map.append(Hex(q, r, -q - r))
    return map


<mask token>

#%%
import collections
import math

#%%
Point = collections.namedtuple("Point", ["x", "y"])


_Hex = collections.namedtuple("Hex", ["q", "r", "s"])

def Hex(q, r, s): #creates the hexagon datatype 
    assert not (round(q + r + s) != 0), "q + r + s must be 0"
    return _Hex(q, r, s)

def eq(a,b): #Returns wether or not two hexs are equal
    return a.q==b.q and a.r==b.r and a.s==b.s

#Vector operators for hexagons
def hex_add(a, b): 
    return Hex(a.q + b.q, a.r + b.r, a.s + b.s)

def hex_subtract(a, b):
    return Hex(a.q - b.q, a.r - b.r, a.s - b.s)

def hex_scale(a, k:int):
    return Hex(a.q * k, a.r * k, a.s * k)


def hex_length(hex): #length of the line from 0,0 to the hexagon
    return (abs(hex.q) + abs(hex.r) + abs(hex.s)) // 2

def hex_distance(a, b): #distance between two hexagons
    return hex_length(hex_subtract(a, b))

#Functions to move directionly in the lattice or aquire neibhbors
hex_directions = [Hex(1, 0, -1), Hex(1, -1, 0), Hex(0, -1, 1), Hex(-1, 0, 1), Hex(-1, 1, 0), Hex(0, 1, -1)]
def hex_direction(direction):
    return hex_directions[direction]

def hex_neighbor(hex, direction):
    return hex_add(hex, hex_direction(direction))


# Datatypes for pointy top or flattop lattices
Orientation = collections.namedtuple("Orientation", ["f0", "f1", "f2", "f3", "b0", "b1", "b2", "b3", "start_angle"])

Layout = collections.namedtuple("Layout", ["orientation", "size", "origin"])

layout_pointy = Orientation(math.sqrt(3.0), math.sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0, math.sqrt(3.0) / 3.0, -1.0 / 3.0, 0.0, 2.0 / 3.0, 0.5)
layout_flat = Orientation(3.0 / 2.0, 0.0, math.sqrt(3.0) / 2.0, math.sqrt(3.0), 2.0 / 3.0, 0.0, -1.0 / 3.0, math.sqrt(3.0) / 3.0, 0.0)



def hex_to_pixel(layout, h): #returns the x,y coordinate of the center of the hexagon
    M = layout.orientation
    x = (M.f0*h.q+M.f1*h.r)*layout.size.x
    y = (M.f2*h.q+M.f3*h.r)*layout.size.y
    return Point(x + layout.origin.x , y + layout.origin.y)


def hex_corner_offset(layout, corner): #Returns how far off each corner is from the center point
    M = layout.orientation
    size = layout.size
    angle = 2.0 * math.pi * (M.start_angle - corner) / 6.0
    return Point(size.x * math.cos(angle), size.y * math.sin(angle))

def polygon_corners(layout, h): #Creates an array of corners by applying the corner offset method to the center six times
    corners = []
    center = hex_to_pixel(layout, h)
    for i in range(0, 6):
        offset = hex_corner_offset(layout, i)
        corners.append(Point(center.x + offset.x, center.y + offset.y))
    return corners


#%%
#Test functions
def complain(name):
    print("FAIL {0}".format(name))

def equal_hex(name, a, b):
    if not (a.q == b.q and a.s == b.s and a.r == b.r):
        complain(name)

def equal_int(name, a, b):
    if not (a == b):
        complain(name)

def equal_hex_array(name, a, b):
    equal_int(name, len(a), len(b))
    for i in range(0, len(a)):
        equal_hex(name, a[i], b[i])

def test_hex_arithmetic():
    equal_hex("hex_add", Hex(4, -10, 6), hex_add(Hex(1, -3, 2), Hex(3, -7, 4)))
    equal_hex("hex_subtract", Hex(-2, 4, -2), hex_subtract(Hex(1, -3, 2), Hex(3, -7, 4)))

def test_hex_direction():
    equal_hex("hex_direction", Hex(0, -1, 1), hex_direction(2))

def test_hex_neighbor():
    equal_hex("hex_neighbor", Hex(1, -3, 2), hex_neighbor(Hex(1, -2, 1), 2))


def test_hex_distance():
    equal_int("hex_distance", 7, hex_distance(Hex(3, -7, 4), Hex(0, 0, 0)))





def test_all():
    test_hex_arithmetic()
    test_hex_direction()
    test_hex_neighbor()
    test_hex_distance()




test_all()


#%%
# Creates rectangular map of hexagons
def rect_map(map_height,map_width):
    map=[]
    for r in range(map_height):
        r_offset=math.floor(r/2)
        for q in range(-r_offset,map_width-r_offset):
            map.append(Hex(q,r,-q-r))
    return map


#%%
map=rect_map(6,8)
map
#%%
layout= Layout(layout_pointy,Point(10,10),Point(0,0))


#%%
