import pygame
import random
import math
import numpy as np


def y():
    # 参数
    screen_width = 1500  # 屏幕宽度
    screen_height = 900  # 屏幕高度
    map_width = screen_width * 4  # 地图的大小
    map_height = screen_height * 4  # 地图的大小
    number_enemy = map_width * map_height / 500000  # 敌人的数量
    number_dots = map_width * map_height / 50  # 点点的数量
    max_show_size = 100  # 球显示的最大半径（屏幕有限，球再增大时，改变的地图比例尺寸）

    my_value = 1000  # 我的初始值
    enemy_value_low = 500  # 敌人的初始值（最低）
    enemy_value_high = 1500  # 敌人的初始值（最高）
    dot_value = 30  # 点点的值（地上的豆豆/食物值）
    my_speed = 10  # 我的球运动的速度
    speed_up = 20  # 按下鼠标时加速
    speed_enemy = 10  # 敌人球正常运动速度
    speed_enemy_anomaly = 20  # 敌人突然加速时的速度（速度异常时的速度）
    anomaly_pro = 0.5  # 敌人加速的概率
    change_pro = 0.05  # 敌人移动路径变化的概率，也就是1/change_pro左右会变化一次
    eat_percent = 0.9  # 吃掉敌人的球，按多少比例并入自己的体积，1对应的是100%
    loss = 0.001  # 按比例减小体重（此外越重的减少越多，10万体积损失值为loss的一倍）
    enemy_bigger_pro = 0.0005  # 敌人的值增加了我的球的值的enemy_bigger_rate倍的几率
    enemy_bigger_rate = 0.1  # 增加我的球的体积的enemy_bigger_rate倍

    class Color(object):  # 定义颜色的类
        @classmethod  # 加了这个可以不需要把实例化，能直接调用类的方法
        def random_color(cls):  # cls, 即class，表示可以通过类名直接调用
            red = random.randint(0, 255)
            green = random.randint(0, 255)
            blue = random.randint(0, 255)
            return red, green, blue

    class Ball(object):  # 定义球
        def __init__(self, x, y, sx, sy, color, value):  # 初始化
            self.x = x  # 球的地图位置参数
            self.y = y
            self.sx = sx  # 速度参数
            self.sy = sy
            self.color = color  # 颜色
            self.value = value  # 球的值，也就是球的大小（不是显示的大小）
            self.is_alive = True  # 球默认是存活状态

    class My_Ball(Ball):  # 定义我的球，继承了Ball类的方法
        def __init__(self, x, y, sx, sy, color, value):
            # 注意：如果重写了__init__() 时，实例化子类，就不会调用父类已经定义的__init__()
            # 如果子类不重写__init__()方法，实例化子类后，会自动调用父类的__init__()的方法
            # 如果子类重写__init__()方法又需要调用父类的方法，则要使用super关键词。
            super().__init__(x, y, sx, sy, color, value)  # 调用父类Ball的初始化方法__init__()
            self.radius = int(self.value ** 0.5)  # 我的球的半径（不考虑系数pi）
            if self.radius >= max_show_size:  # 如果半径比规定的最大半径还大，则显示最大半径
                self.show_radius = max_show_size  # 我的球显示的半径
            else:
                self.show_radius = self.radius  # 如果半径没有超过规定最大的半径，则显示原来实际大小的半径
            self.position_x = int(screen_width / 2)  # 把我的球固定在屏幕中间position_x，是屏幕显示的位置
            self.position_y = int(screen_height / 2)  # 把我的球固定在屏幕中间position_y，是屏幕显示的位置

        def draw(self, window):  # 把我的球画出来
            self.radius = int(self.value ** 0.5)  # 这里重复上面的，因为除了初始化之后，还要更新
            if self.radius >= max_show_size:
                self.show_radius = max_show_size
            else:
                self.show_radius = self.radius
            self.position_x = int(screen_width / 2)
            self.position_y = int(screen_height / 2)
            pygame.draw.circle(window, self.color, (self.position_x, self.position_y), self.show_radius)

        def eat_ball(self, other):  # 吃别的球（包括小点点和敌人）
            if self != other and self.is_alive and other.is_alive:  # 如果other不是自身，自身和对方也都是存活状态，则执行下面动作
                distance = ((self.position_x - other.position_x) ** 2 + (
                            self.position_y - other.position_y) ** 2) ** 0.5  # 两个球之间的距离
                if distance < self.show_radius and (self.show_radius > other.show_radius or (
                        self.show_radius == other.show_radius and self.value > other.value)):  # 如果自身半径比别人大，而且两者距离小于自身半径，那么可以吃掉。
                    other.is_alive = False  # 吃球（敌方已死）
                    self.value += other.value * eat_percent  # 自己的值增大（体量增大）
                    self.radius = int(self.value ** 0.5)  # 计算出半径
                    if self.radius >= max_show_size:  # 我的球的显示半径
                        self.show_radius = max_show_size
                    else:
                        self.show_radius = self.radius

        def move(self):  # 移动规则
            self.x += self.sx  # 地图位置加上速度
            self.y += self.sy
            # 横向出界
            if self.x < 0:  # 离开了地图左边
                self.x = 0
            if self.x > map_width:  # 离开了地图右边
                self.x = map_width
            # 纵向出界
            if self.y <= 0:  # 离开了地图下边
                self.y = 0
            if self.y >= map_height:  # 离开了地图上边
                self.y = map_height

    class Enemy_Ball(Ball):  # 定义敌人的球，继承了Ball类的方法
        def __init__(self, x, y, sx, sy, color, value, host_ball):  # 初始化带上host_ball，也就是我的球
            super().__init__(x, y, sx, sy, color, value)
            self.host_ball = host_ball
            self.radius = int(self.value ** 0.5)
            if self.host_ball.radius >= max_show_size:  # 如果我的球比规定的最大尺寸还大，则敌人的球显示的比例要减小
                self.show_radius = max(10,
                                       int(self.radius / (self.host_ball.radius / max_show_size)))  # 敌人的球也不能太小，最小半径为10
                self.position_x = int((self.x - self.host_ball.x) / (self.host_ball.radius / max_show_size)) + int(
                    screen_width / 2)  # 计算出敌人的球和我的球的相对位置，并且按比例减小
                self.position_y = int((self.y - self.host_ball.y) / (self.host_ball.radius / max_show_size)) + int(
                    screen_height / 2)  # 计算出敌人的球和我的球的相对位置，并且按比例减小
            else:
                self.show_radius = self.radius  # 正常显示
                self.position_x = (self.x - self.host_ball.x) + int(screen_width / 2)  # 敌人和我的球的相对位置
                self.position_y = (self.y - self.host_ball.y) + int(screen_height / 2)  # 敌人和我的球的相对位置

        # 画出球
        def draw(self, window):
            self.radius = int(self.value ** 0.5)
            if self.host_ball.radius >= max_show_size:  # 这边把初始化的内容再写一遍，因为敌人的球初始化之后还要根据我的球而动态改变
                self.show_radius = max(10, int(self.radius / (self.host_ball.radius / max_show_size)))
                self.position_x = int((self.x - self.host_ball.x) / (self.host_ball.radius / max_show_size)) + int(
                    screen_width / 2)
                self.position_y = int((self.y - self.host_ball.y) / (self.host_ball.radius / max_show_size)) + int(
                    screen_height / 2)
            else:
                self.show_radius = self.radius
                self.position_x = (self.x - self.host_ball.x) + int(screen_width / 2)
                self.position_y = (self.y - self.host_ball.y) + int(screen_height / 2)
            pygame.draw.circle(window, self.color, (self.position_x, self.position_y), self.show_radius)

        def eat_ball(self, other):
            if self != other and self.is_alive and other.is_alive:
                distance = ((self.position_x - other.position_x) ** 2 + (
                            self.position_y - other.position_y) ** 2) ** 0.5
                if distance < self.show_radius and (self.show_radius > other.show_radius or (
                        self.show_radius == other.show_radius and self.value > other.value)):
                    other.is_alive = False  # 吃球
                    self.value += other.value * eat_percent
                    self.radius = int(self.value ** 0.5)

        def move(self):  # 移动规则
            self.x += self.sx  # 地图位置加上速度
            self.y += self.sy
            # 横向出界
            if self.x < 0:  # 离开了地图左边
                self.sx = -self.sx
                self.x = 0
            if self.x > map_width:  # 离开了地图右边
                self.sx = -self.sx
                self.x = map_width
            # 纵向出界
            if self.y <= 0:  # 离开了地图下边
                self.sy = -self.sy
                self.y = 0
            if self.y >= map_height:  # 离开了地图上边
                self.sy = -self.sy
                self.y = map_height

    class Dot_Ball(Ball):  # 定义地上的小点点，供自己的球和敌人的球吃，继承了Ball类的方法
        def __init__(self, x, y, sx, sy, color, value, host_ball):
            super().__init__(x, y, sx, sy, color, value)
            self.host_ball = host_ball
            self.radius = 8  # 初始小点点大小
            if self.host_ball.radius >= max_show_size:
                self.show_radius = max(3, int(
                    self.radius / (self.host_ball.radius / max_show_size)))  # 小点点显示也不能太小，最小显示半径为3
                self.position_x = int((self.x - self.host_ball.x) / (self.host_ball.radius / max_show_size)) + int(
                    screen_width / 2)
                self.position_y = int((self.y - self.host_ball.y) / (self.host_ball.radius / max_show_size)) + int(
                    screen_height / 2)
            else:
                self.show_radius = self.radius
                self.position_x = (self.x - self.host_ball.x) + int(screen_width / 2)
                self.position_y = (self.y - self.host_ball.y) + int(screen_height / 2)

        # 画出球
        def draw(self, window):
            if self.host_ball.radius >= max_show_size:  # 这边把初始化的内容再写一遍，因为小点点初始化之后还要根据我的球而动态改变
                self.show_radius = max(3, int(self.radius / (self.host_ball.radius / max_show_size)))
                self.position_x = int((self.x - self.host_ball.x) / (self.host_ball.radius / max_show_size)) + int(
                    screen_width / 2)
                self.position_y = int((self.y - self.host_ball.y) / (self.host_ball.radius / max_show_size)) + int(
                    screen_height / 2)
            else:
                self.show_radius = self.radius
                self.position_x = (self.x - self.host_ball.x) + int(screen_width / 2)
                self.position_y = (self.y - self.host_ball.y) + int(screen_height / 2)
            pygame.draw.circle(window, self.color, (self.position_x, self.position_y), self.show_radius)

    def creat_my_ball():  # 产生我的球
        x = random.randint(0, map_width)  # 我的球在地图中的位置，随机生成
        y = random.randint(0, map_height)
        value = my_value  # 我的球的初始值
        color = 255, 255, 255  # 我的球的颜色
        sx = 0  # 速度默认为0
        sy = 0
        host_ball = My_Ball(x, y, sx, sy, color, value)  # 调用My_Ball类
        return host_ball  # 返回我的球

    def auto_creat_ball(balls, host_ball):  # 自动产生敌人的球
        if len(balls) <= number_enemy:  # 控制敌人的数量，如果个数够了，就不再生成
            x = random.randint(0, map_width)  # 敌人球在地图中的位置，随机生成
            y = random.randint(0, map_height)
            value = random.randint(enemy_value_low, enemy_value_high)  # 敌人的球初始值
            sx = random.randint(-speed_enemy, speed_enemy)  # 敌人的球移动速度
            i2 = random.randint(0, 1)  # y的移动方向
            if i2 == 0:
                sy = int((speed_enemy ** 2 - sx ** 2) ** 0.5)
            else:
                sy = -int((speed_enemy ** 2 - sx ** 2) ** 0.5)
            color = Color.random_color()  # 敌人的颜色随机生成
            enemy = Enemy_Ball(x, y, sx, sy, color, value, host_ball)
            balls.append(enemy)

    def auto_creat_dots(dots, host_ball):  # 自动生成点点
        if len(dots) <= number_dots:  # 控制点点的数量
            x = random.randint(0, map_width)  # 随机生成点点的位置
            y = random.randint(0, map_height)
            value = dot_value  # 点点的值
            sx = 0  # 点点速度为0
            sy = 0
            color = Color.random_color()  # 颜色
            dot = Dot_Ball(x, y, sx, sy, color, value, host_ball)
            dots.append(dot)

    def control_my_ball(host_ball):  # 控制我的球
        host_ball.move()
        host_ball.value = host_ball.value * (1 - loss * host_ball.value / 100000)
        for event in pygame.event.get():  # 监控事件（鼠标移动）
            # print(event)
            if event.type == pygame.MOUSEBUTTONDOWN:
                pos = event.pos
                speed = speed_up
            elif event.type == pygame.MOUSEMOTION:
                pos = event.pos
                if event.buttons[0] == 1:
                    speed = speed_up
                if event.buttons[0] == 0:
                    speed = my_speed
            elif event.type == pygame.MOUSEBUTTONUP:
                pos = event.pos
                speed = my_speed
            else:
                pos = [screen_width / 2, screen_height / 2]
                speed = my_speed
            if abs(pos[0] - screen_width / 2) < 30 and abs(pos[1] - screen_height / 2) < 30:
                host_ball.sx = 0
                host_ball.sy = 0
            elif pos[0] > screen_width / 2 and pos[1] >= screen_height / 2:
                angle = abs(math.atan((pos[1] - screen_height / 2) / (pos[0] - screen_width / 2)))
                host_ball.sx = int(speed * math.cos(angle))
                host_ball.sy = int(speed * math.sin(angle))
            elif pos[0] > screen_width / 2 and pos[1] < screen_height / 2:
                angle = abs(math.atan((pos[1] - screen_height / 2) / (pos[0] - screen_width / 2)))
                host_ball.sx = int(speed * math.cos(angle))
                host_ball.sy = -int(speed * math.sin(angle))
            elif pos[0] < screen_width / 2 and pos[1] >= screen_height / 2:
                angle = abs(math.atan((pos[1] - screen_height / 2) / (pos[0] - screen_width / 2)))
                host_ball.sx = -int(speed * math.cos(angle))
                host_ball.sy = int(speed * math.sin(angle))
            elif pos[0] < screen_width / 2 and pos[1] < screen_height / 2:
                angle = abs(math.atan((pos[1] - screen_height / 2) / (pos[0] - screen_width / 2)))
                host_ball.sx = -int(speed * math.cos(angle))
                host_ball.sy = -int(speed * math.sin(angle))
            elif pos[0] == screen_width / 2:
                host_ball.sx = 0
                if pos[1] >= 0:
                    host_ball.sy = speed
                else:
                    host_ball.sy = -speed

    def enemy_move(balls, host_ball):  # 敌人移动
        for enemy in balls:
            enemy.move()  # 移动
            enemy.value = enemy.value * (1 - loss * enemy.value / 100000)
            if random.randint(1, int(1 / enemy_bigger_pro)) == 1:
                enemy.value += host_ball.value * enemy_bigger_rate
            if random.randint(1, int(1 / anomaly_pro)) == 1:
                speed_enemy0 = speed_enemy_anomaly  # 敌人异常速度
            else:
                speed_enemy0 = speed_enemy  # 敌人正常速度
            i = random.randint(1, int(1 / change_pro))  # 一定的概率改变轨迹
            if i == 1:
                enemy.sx = random.randint(-speed_enemy0, speed_enemy0)
                i2 = random.randint(0, 1)
                if i2 == 0:
                    enemy.sy = int((speed_enemy0 ** 2 - enemy.sx ** 2) ** 0.5)
                else:
                    enemy.sy = -int((speed_enemy0 ** 2 - enemy.sx ** 2) ** 0.5)

    def eat_each_other(host_ball, balls, dots):  # 吃球
        for enemy in balls:
            for enemy2 in balls:
                enemy.eat_ball(enemy2)  # 敌人互吃
            for food in dots:
                enemy.eat_ball(food)  # 敌人吃点点
        for enemy in balls:
            host_ball.eat_ball(enemy)  # 我吃敌人
            enemy.eat_ball(host_ball)  # 敌人吃我
        for food in dots:
            host_ball.eat_ball(food)  # 我吃点点

    def paint(host_ball, balls, dots, screen):
        screen.fill((0, 0, 0))  # 刷漆
        if host_ball.is_alive:
            host_ball.draw(screen)
        for enemy in balls:  # 遍历容器
            if enemy.is_alive:
                enemy.draw(screen)
            else:
                balls.remove(enemy)
        for food in dots:  # 遍历容器
            if food.is_alive:
                food.draw(screen)
            else:
                dots.remove(food)

    def main():
        pygame.init()  # 初始化
        screen = pygame.display.set_mode((screen_width, screen_height))  # 设置屏幕
        pygame.display.set_caption("球球大作战")  # 设置屏幕标题
        balls = []  # 定义一容器  存放所有的敌方球
        dots = []  # 定义一容器 存放所有的点点
        is_running = True  # 默认运行状态
        host_ball = creat_my_ball()  # 产生我的球
        i00 = 0  # 一个参数
        while is_running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    is_running = False
            auto_creat_dots(dots, host_ball)  # 自动生成点点
            auto_creat_ball(balls, host_ball)  # 自动生成敌人
            paint(host_ball, balls, dots, screen)  # 把所有的都画出来 调用draw方法
            pygame.display.flip()  # 渲染
            pygame.time.delay(30)  # 设置动画的时间延迟

            control_my_ball(host_ball)  # 移动我的球
            enemy_move(balls, host_ball)  # 敌人的球随机运动
            eat_each_other(host_ball, balls, dots)  # 吃球 调用eat_ball方法
            i00 += 1
            if np.mod(i00, 50) == 0:
                print(host_ball.value)

    if __name__ == '__main__':
        main()