import pygame
import math
import random
import sys
from array import array

# 初始化
pygame.init()
WIDTH, HEIGHT = 800, 500
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("78x78 Maze - Assassinate the King")
clock = pygame.time.Clock()

# ---------- 地图 (78 x 78) ----------
MAP_SIZE = 78
world = array('B', [0]) * (MAP_SIZE * MAP_SIZE)

# 随机生成墙壁 (25% 几率)
random.seed(42)  # 可删除以获得完全随机地图
WALL_CHANCE = 0.25
for i in range(MAP_SIZE * MAP_SIZE):
    if random.random() < WALL_CHANCE:
        world[i] = 1

def is_wall(x, y):
    ix, iy = int(x), int(y)
    if 0 <= ix < MAP_SIZE and 0 <= iy < MAP_SIZE:
        return world[iy * MAP_SIZE + ix] == 1
    return True

def set_wall(x, y, val):
    if 0 <= x < MAP_SIZE and 0 <= y < MAP_SIZE:
        world[y * MAP_SIZE + x] = 1 if val else 0

def clear_area(cx, cy, r=6):
    for dx in range(-r, r+1):
        for dy in range(-r, r+1):
            nx, ny = cx+dx, cy+dy
            if 0 <= nx < MAP_SIZE and 0 <= ny < MAP_SIZE:
                set_wall(nx, ny, False)

def random_empty_pos():
    while True:
        x = random.randint(5, MAP_SIZE-6)
        y = random.randint(5, MAP_SIZE-6)
        if not is_wall(x, y):
            return x + 0.5, y + 0.5

# 玩家初始位置
player_x, player_y = MAP_SIZE//2 + 0.5, MAP_SIZE//2 + 0.5
clear_area(MAP_SIZE//2, MAP_SIZE//2, 8)

# 国王位置
king_x, king_y = random_empty_pos()
clear_area(int(king_x), int(king_y), 8)

# 视角设置
player_angle = 0.0
FOV = math.pi / 3
MOVE_SPEED = 0.05
MOUSE_SENSITIVITY = 0.002
KILL_DISTANCE = 1.5

font = pygame.font.Font(None, 30)
MAX_DEPTH = 30

# ---------- DDA 光线投射 ----------
def cast_ray(sx, sy, angle):
    ray_dir_x = math.cos(angle)
    ray_dir_y = math.sin(angle)
    map_x, map_y = int(sx), int(sy)
    delta_dist_x = abs(1/ray_dir_x) if ray_dir_x else 1e30
    delta_dist_y = abs(1/ray_dir_y) if ray_dir_y else 1e30

    if ray_dir_x < 0:
        step_x = -1
        side_dist_x = (sx - map_x) * delta_dist_x
    else:
        step_x = 1
        side_dist_x = (map_x + 1.0 - sx) * delta_dist_x

    if ray_dir_y < 0:
        step_y = -1
        side_dist_y = (sy - map_y) * delta_dist_y
    else:
        step_y = 1
        side_dist_y = (map_y + 1.0 - sy) * delta_dist_y

    side = 0
    for _ in range(MAX_DEPTH * 10):
        if side_dist_x < side_dist_y:
            side_dist_x += delta_dist_x
            map_x += step_x
            side = 0
        else:
            side_dist_y += delta_dist_y
            map_y += step_y
            side = 1
        if is_wall(map_x, map_y):
            if side == 0:
                dist = side_dist_x - delta_dist_x
            else:
                dist = side_dist_y - delta_dist_y
            return dist, True
    return MAX_DEPTH, False

# 方向箭头绘制
def draw_arrow(surf, player_x, player_y, target_x, target_y, player_angle):
    angle_to = math.atan2(target_y - player_y, target_x - player_x)
    diff = (angle_to - player_angle + math.pi) % (2*math.pi) - math.pi
    arrow_x = WIDTH // 2
    arrow_y = HEIGHT // 2 - 40
    arrow_len = 25
    end_x = arrow_x + arrow_len * math.sin(diff)
    end_y = arrow_y - arrow_len * math.cos(diff)
    pygame.draw.line(surf, (255, 215, 0), (arrow_x, arrow_y), (end_x, end_y), 4)
    pygame.draw.circle(surf, (255, 215, 0), (end_x, end_y), 6)

# ---------- 游戏循环 ----------
running = True
pygame.mouse.set_visible(False)
pygame.event.set_grab(True)

while running:
    clock.tick(60)
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
            running = False

    mouse_dx, _ = pygame.mouse.get_rel()
    player_angle += mouse_dx * MOUSE_SENSITIVITY

    keys = pygame.key.get_pressed()
    if keys[pygame.K_w]:
        nx = player_x + MOVE_SPEED * math.cos(player_angle)
        ny = player_y + MOVE_SPEED * math.sin(player_angle)
        if not is_wall(nx, ny): player_x, player_y = nx, ny
    if keys[pygame.K_s]:
        nx = player_x - MOVE_SPEED * math.cos(player_angle)
        ny = player_y - MOVE_SPEED * math.sin(player_angle)
        if not is_wall(nx, ny): player_x, player_y = nx, ny
    if keys[pygame.K_a]:
        nx = player_x - MOVE_SPEED * math.sin(player_angle)
        ny = player_y + MOVE_SPEED * math.cos(player_angle)
        if not is_wall(nx, ny): player_x, player_y = nx, ny
    if keys[pygame.K_d]:
        nx = player_x + MOVE_SPEED * math.sin(player_angle)
        ny = player_y - MOVE_SPEED * math.cos(player_angle)
        if not is_wall(nx, ny): player_x, player_y = nx, ny

    # 刺杀
    if keys[pygame.K_SPACE]:
        if math.hypot(player_x - king_x, player_y - king_y) < KILL_DISTANCE:
            king_x, king_y = random_empty_pos()
            clear_area(int(king_x), int(king_y), 8)
            for _ in range(100):
                px = random.randint(5, MAP_SIZE-6)
                py = random.randint(5, MAP_SIZE-6)
                if not is_wall(px, py):
                    player_x, player_y = px+0.5, py+0.5
                    break

    # 渲染
    screen.fill((20, 20, 30))
    pygame.draw.rect(screen, (40, 40, 40), (0, HEIGHT//2, WIDTH, HEIGHT//2))

    num_rays = WIDTH // 2
    for i in range(num_rays):
        ray_angle = player_angle - FOV/2 + (i / num_rays) * FOV
        dist, hit = cast_ray(player_x, player_y, ray_angle)
        dist *= math.cos(ray_angle - player_angle)
        h = HEIGHT / (dist + 0.01)
        top = HEIGHT/2 - h/2
        shade = max(30, 200 - int(dist*12))
        color = (shade, shade, shade) if hit else (0,0,0)
        sw = WIDTH / num_rays
        pygame.draw.rect(screen, color, (i*sw, top, sw+1, h))

    # 国王精灵
    dx = king_x - player_x
    dy = king_y - player_y
    dist = math.hypot(dx, dy)
    angle_to = math.atan2(dy, dx)
    diff = (angle_to - player_angle + math.pi) % (2*math.pi) - math.pi
    if abs(diff) < FOV/2 and dist > 0.1:
        sx = WIDTH/2 + (diff / (FOV/2)) * (WIDTH/2)
        sh = min(HEIGHT, HEIGHT / dist)
        sw = sh
        red = max(80, 255 - int(dist*15))
        rect = pygame.Rect(sx - sw/2, HEIGHT/2 - sh/2, sw, sh)
        pygame.draw.rect(screen, (red, 20, 20), rect)
        pygame.draw.rect(screen, (255,215,0), rect, 2)

    # 方向箭头
    draw_arrow(screen, player_x, player_y, king_x, king_y, player_angle)

    # 提示
    d = math.hypot(player_x - king_x, player_y - king_y)
    if d < KILL_DISTANCE:
        txt = font.render("Press SPACE to assassinate the King!", True, (255,255,0))
        screen.blit(txt, (WIDTH//2 - txt.get_width()//2, HEIGHT-60))
    else:
        txt = font.render("Follow the golden arrow to the red King", True, (200,200,200))
        screen.blit(txt, (WIDTH//2 - txt.get_width()//2, HEIGHT-30))

    # 小地图 (20x20 视野)
    mini_scale = 6
    mini_r = 20
    mx0 = int(player_x) - mini_r//2
    my0 = int(player_y) - mini_r//2
    for y in range(mini_r):
        for x in range(mini_r):
            wx = mx0 + x
            wy = my0 + y
            if 0 <= wx < MAP_SIZE and 0 <= wy < MAP_SIZE:
                col = (100,100,100) if is_wall(wx, wy) else (30,30,30)
                pygame.draw.rect(screen, col, (x*mini_scale, y*mini_scale, mini_scale, mini_scale))
    if abs(king_x - player_x) < mini_r/2 and abs(king_y - player_y) < mini_r/2:
        kx = int((king_x - mx0) * mini_scale)
        ky = int((king_y - my0) * mini_scale)
        pygame.draw.circle(screen, (255,0,0), (kx, ky), 5)
    pygame.draw.circle(screen, (0,255,0), (mini_r//2 * mini_scale, mini_r//2 * mini_scale), 5)

    pygame.display.flip()

pygame.quit()
sys.exit()