三体宇宙文明模型

Settings.py

'''Define Settings'''
# 定义宇宙尺寸
CosmicWidth = 1300
CosmicHeight = 900
# 定义恒星系资源的上下限
ResouceLowLimit = 30
ResouceUpperLimit =50
# 定义资源和诞生概率的关系
BirthFactor = 0.5
# 定义无效ID
InvalidCivilID = -1
InvalidGalaxyID = -1
# 定义监测间隔
BirthTimeInterval = 10
ExplusionTimeInterval = 5
RegressTimeInterval = 15
# 定义文明性格
KindCharacter = 1
VagueCharacter = 0
FerociousCharacter = -1
CharacterType = [KindCharacter, VagueCharacter, FerociousCharacter]
# 定义广播概率
KindPoints = 70
VaguePoints = 10
# 定义恒星系数量
GalaxysNum = 400
# 定义模拟时长
TimeEnd = 1000
# 定义文明等级的上限
RankUperLimit = 150
# 颜色定义
BackGrand = (33, 36, 29)
WhiteColor = (255, 255, 255)
# 废弃参数
DetectPoints = 90

main.py

from CosmicModel import *
from Settings import *
from threading import Thread
import sys
import pygame
import time, math

Galaxys = []
CivilMap = {}
ExposedCividIDs = []
Timer = 0
CivilRegion ={}
StopAsyncRun = False
Line0 = 1#MAX 12
Line1 = 1#MAX 15
Offset = 500
Log = []




def DrawCivil(screen, Slow, CID = InvalidCivilID):
    if Slow == False:
        for galaxy in Galaxys:
            if galaxy.CivilID != InvalidCivilID and galaxy.Civilization.alive:
                civil = galaxy.Civilization
                pos = galaxy.pos
                pos = [pos[0] + 100, pos[1] + 100]
                pygame.draw.circle(screen, civil.Color, pos, galaxy.Rank / 10)
                pygame.display.update()

    else:
        if(CID == InvalidCivilID):
            return
        civil = CivilMap[CID]
        Gids = civil.GalaxyIDs
        for Gid in Gids:
            pos = Galaxys[Gid-1].pos
            pos = [pos[0] + 100, pos[1] + 100]
            for i in range(20, 10, -1):
                pygame.draw.circle(screen, civil.Color, pos, max(Galaxys[Gid-1].Rank / i, 3))
                time.sleep(0.03)
                pygame.display.update()

def DrawBroadcast(screen, pos, rank, color, cid):
    global CivilMap
    if CivilMap[cid].StopDrawBroadcast:
        return
    pos = [pos[0] + 100, pos[1] + 100]
    Radius = CivilMap[cid].Rank / 10
    LastRadius = Radius
    Draw = 1
    while True:
        if Draw:
            Color = color
        else:
            Color = BackGrand
        pygame.draw.circle(screen, color, pos, rank / 10)
        pygame.draw.circle(screen, Color, pos, Radius, 1)
        pygame.display.update()
        time.sleep(0.2)
        Radius = Radius + 10
        if Radius >= max(rank+10, 30):
            Draw = (Draw + 1) % 2
            if Draw == 0:
                Radius = LastRadius
            else:
                if CivilMap.get(cid) == None:
                    Radius = LastRadius
                else:
                    Radius = CivilMap[cid].Rank / 10
                    LastRadius = Radius
            if (CivilMap.get(cid) == None or CivilMap[cid].StopDrawBroadcast) and Draw:
                break


def DrawRegion(screen, cid):
    global CivilRegion, Galaxy, CivilMap
    if CivilMap.get(cid) == None:
        return
    pos0 = Galaxys[CivilMap[cid].GalaxyIDs[0]-1].pos
    pos0 = [pos0[0] + 100, pos0[1] + 100]
    if CivilRegion.get(cid) == None:
        CivilRegion[cid] = []
    for gid in CivilMap[cid].GalaxyIDs[1:-1]:
        pos1 = Galaxys[gid-1].pos
        pos1 = [pos1[0] + 100, pos1[1] + 100]
        if [pos0, pos1] in CivilRegion[cid]:
            continue
        CivilRegion[cid].append([pos0, pos1])
        pygame.draw.line(screen, CivilMap[cid].Color, pos0, pos1, 1)
        pygame.display.update()

def DrawLog(screen, font, txt, lines=0, offset=0):
    Sw = CosmicWidth+205
    Sh = (lines+1)*40+offset
    pygame.draw.rect(screen, BackGrand, ([Sw, Sh-40], [CosmicWidth+600, Sh]))
    pygame.draw.line(screen, WhiteColor, (CosmicWidth + 200, 500), (CosmicWidth + 600, 500), 3)
    # pygame.display.update()
    if lines != 0:
        Sw += 5
    text = font.render(txt, True, WhiteColor, BackGrand)
    # 获得显示对象的 rect区域大小
    textRect = text.get_rect()
    # 设置显示对象左下居中
    textRect.bottomleft = (Sw, Sh)
    screen.blit(text, textRect)
    pygame.display.update()
    return

def DrawCivilLog(screen, font):
    global Log, Galaxys, CivilMap
    while len(Log) >= 13:
        Log.pop(0)
    pygame.draw.rect(screen, BackGrand, ([CosmicWidth+205, Offset+80], [CosmicWidth+600, CosmicHeight + 200]))
    DrawLog(screen, font, "文明观测日志:", offset=Offset)
    for index, log in enumerate(Log):
        DrawLog(screen, font, log, lines=index+1, offset=Offset)
    time.sleep(0.5)


def ClearRegion(screen, cid):
    if CivilRegion.get(cid) == None:
        return False
    for pos0, pos1 in CivilRegion[cid]:
        pygame.draw.line(screen, BackGrand, pos0, pos1, 1)
        pygame.display.update()
    return True

def CalcDistance(spos, dpos):
    return math.pow((math.pow(spos[1]-dpos[1], 2)+math.pow(spos[0]-dpos[0], 2)), 0.5)

def DetectPos(SrcCID, DesCID):
    for SrcGalaxyid in CivilMap[SrcCID].GalaxyIDs:
        spos = Galaxys[SrcGalaxyid-1].pos
        for DesGalaxyid in CivilMap[DesCID].GalaxyIDs:
            dpos = Galaxys[DesGalaxyid-1].pos
            distance = CalcDistance(spos, dpos)
            if CivilMap[SrcCID].Rank > distance/5:
                if randint(0, 100) <= 90:
                    CivilMap[SrcCID].DetectedCivil.append(DesCID)
                    #print("Civil[{}]{} detect Civil[{}]{}".format(SrcCID, spos, DesCID, dpos))
                    return True

def GalaxysInit():
    global Galaxys
    Galaxys = [Galaxy() for cnt in range(GalaxysNum)]



def GalaxysCheckBirth(screen, font):
    global Galaxys, CivilMap, ExposedCividIDs, Log
    for Galaxy in Galaxys:
        if(Galaxy.CheckBirth(Timer)):
            CivilMap[Galaxy.CivilID] = Galaxy.Civilization
            DrawCivil(screen, True, CID=Galaxy.CivilID)
            if Galaxy.Civilization.isBroadcast:
                ExposedCividIDs.append(Galaxy.CivilID)
                Thread(target=DrawBroadcast,
                                      args=(screen,
                                            Galaxy.pos,
                                            Galaxy.Civilization.Rank,
                                            Galaxy.Civilization.Color,
                                            Galaxy.CivilID)).start()
                Log.append("{}[{}]向宇宙广播其自身存在".format(GetDescription(Galaxy.CivilID), Galaxy.CivilID))
                DrawCivilLog(screen, font)


def CivilCheckBroadcast(screen, font):
    global Galaxys, CivilMap, ExposedCividIDs, Log
    if(len(ExposedCividIDs)==0 or len(CivilMap)==0):
        return
    shuffle(ExposedCividIDs)
    for did in ExposedCividIDs:
        keys = list(CivilMap.keys())
        shuffle(keys)
        for sid in keys:
            if CivilMap[did].alive == False:
                break
            if sid == did :
                continue
            if(DetectPos(sid, did)):
                # CivilMap[sid].StopDrawBroadcast = True
                Galaxys, Log = CivilMap[sid].DetectedCivilHandle(CivilMap[did], Galaxys, Log)
                DrawRegion(screen, Galaxys[CivilMap[sid].GalaxyIDs[0]-1].CivilID)
                DrawCivilLog(screen, font)
                break



def DetectDieCivil(screen):
    global Galaxys, CivilMap, ExposedCividIDs, CivilRegion
    ExposedCividIDs = [did for did in ExposedCividIDs if CivilMap[did].alive]
    tmp = {}
    for key in CivilMap.keys():
        if CivilMap[key].alive:
            tmp.update({key: CivilMap[key]})
        else:
            if ClearRegion(screen, key):
                CivilRegion.pop(key)
    CivilMap = tmp

def CivilDetermine(Time):
    global CivilMap
    for cid in CivilMap.keys():
        CivilMap[cid].TechExplosion(Time)
        CivilMap[cid].TechRegress(Time)

def Detect(screen, font):
    global Galaxys, CivilMap, Log
    keys = list(CivilMap.keys())
    shuffle(keys)
    for key0 in keys:
        for key1 in keys:
            if CivilMap[key0].alive == False:
                break
            if CivilMap[key1].alive==False or key0 == key1 :
                continue
            DetectFlag = False
            for Galaxy0 in CivilMap[key0].GalaxyIDs:
                if DetectFlag:
                    break
                for Galaxy1 in CivilMap[key1].GalaxyIDs:
                    if DetectFlag:
                        break
                    distance = CalcDistance(Galaxys[Galaxy0-1].pos, Galaxys[Galaxy1-1].pos)
                    if distance < max(Galaxys[Galaxy0-1].Rank, 30):
                        GalaxyTemp = Galaxys[Galaxy0-1]
                        Galaxys,Log = CivilMap[key0].DetectedCivilHandle(CivilMap[key1], Galaxys, Log)
                        DrawCivilLog(screen, font)
                        DrawRegion(screen, GalaxyTemp.CivilID)
                        DetectFlag = True
                        break

def GetEvent():
    global StopAsyncRun
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    StopAsyncRun = (StopAsyncRun+1)%2
                    return
def GetDescription(id):
    if id == -1:
        return "恶意文明"
    elif id == 0:
        return "混沌文明"
    else:
        return "善意文明"

def GetSummary(screen, font):
    global Galaxys, CivilMap, Line0, Timer
    Tmp = sorted(CivilMap.items(), key= lambda item:item[1].Rank, reverse=True)
    if len(Tmp)==0:
        return
    DrawLog(screen, font, "第{}个时间刻度的报告如下:".format(Timer))
    for i in range(min(3, len(Tmp))):
        DrawLog(screen, font, "{}、 {}号文明[{}]:".format(i, Tmp[i][1].CivilID, GetDescription(Tmp[i][1].Character)), Line0)
        Line0 += 1
        DrawLog(screen, font, "文明等级[{}],资源点[{}]".format(int(Tmp[i][1].Rank),
                                                                Tmp[i][1].ResourcePoints), Line0)
        Line0 += 1
        Str = ""
        if not Tmp[i][1].isBroadcast:
            Str = "不"
        DrawLog(screen, font, "恒星系[{}]个,{}向宇宙广播自身存在".format(len(Tmp[i][1].GalaxyIDs), Str), Line0)
        Line0 += 1
    DrawLog(screen, font, "共有{}个文明".format(len(Tmp)), Line0)
    Line0 += 1
    KindCnt = 0
    VagueCnt = 0
    for item in CivilMap.values():
        if item.Character == KindCharacter:
            KindCnt+=1
        elif item.Character == VagueCharacter:
            VagueCnt += 1
    DrawLog(screen, font, "善意[{}],混沌[{}],恶意[{}]".format(KindCnt, VagueCnt, len(Tmp)-KindCnt-VagueCnt), Line0)
    Line0 += 1
    DrawCivilLog(screen, font)


    Line0 = 1


    # DrawLog(screen, font, "共有宇宙文明{}个.".format(len(CivilMap)), lines=1)
    # DrawLog(screen, font, "其中善意文明含{}个".format(len(CivilMap)), lines=1)

def main():
    global Timer,StopAsyncRun
    # Pygame 初始化
    pygame.init()
    # 创建屏幕对象
    screen = pygame.display.set_mode((CosmicWidth+600, CosmicHeight+200))
    # 创造字体对象(需要注意的是,字体应选择中文字体)
    font = pygame.font.Font("PingFang SC.ttc", 20)
    # 标题
    pygame.display.set_caption("宇宙模型模拟v1.0")
    # 填充背景
    screen.fill(BackGrand)
    # 分割出日志区
    pygame.draw.line(screen, WhiteColor, (CosmicWidth + 200, 0), (CosmicWidth + 200, CosmicHeight + 200), 3)
    pygame.draw.line(screen, WhiteColor, (CosmicWidth + 200, 500), (CosmicWidth + 600, 500), 3)
    # 进行渲染
    pygame.display.update()
    # 创造规定数量的恒星系
    GalaxysInit()
    # 按空格开始(为录屏提供等待时间)
    GetEvent()
    while Timer < TimeEnd:
        # 异步停止(如果开GetEvent线程的话)
        if StopAsyncRun:
            continue
        pygame.draw.line(screen, WhiteColor, (CosmicWidth + 200, 0), (CosmicWidth + 200, CosmicHeight + 200), 3)
        pygame.draw.line(screen, WhiteColor, (CosmicWidth + 200, 500), (CosmicWidth + 600, 500), 3)
        # 监测各行星是否诞生文明
        GalaxysCheckBirth(screen, font)
        # 尝试绘画出已诞生文明的恒星系
        DrawCivil(screen, False)
        # 检测是否探测到其他文明
        Detect(screen, font)
        # 移除死亡文明
        DetectDieCivil(screen)
        # 监听广播,破解位置,触发事件
        CivilCheckBroadcast(screen, font)
        # 移除死亡文明
        DetectDieCivil(screen)
        # 画出文明势力范围
        for cid in CivilMap.keys():
            DrawRegion(screen, cid)
        # 监测各文明是否有事件发生(如技术爆炸)
        CivilDetermine(Timer)
        # 做出该时刻的总结汇报
        GetSummary(screen, font)
        # 渲染
        pygame.display.update()
        Timer += 1

if __name__ == '__main__':
    main()

 CosmicModel.py

from Settings import *
from random import *
import numpy as np


class GlobalVariables():
    __CivilID = 0
    __GalaxyID = 0
    __NormalDistribution = np.random.normal(30, 10, 10000)
    __StandardNormalDistribution = np.random.normal(0, 1, 10000)

    @property
    def CivilUID(self):
        GlobalVariables.__CivilID += 1
        return GlobalVariables.__CivilID

    @property
    def NormalValue(self):
        num = choice(self.__NormalDistribution)
        while num <= 0 or num >= 100:
            num = choice(self.__NormalDistribution)
        return num

    @property
    def StandardNormalValue(self):
        return choice(self.__StandardNormalDistribution)

    @property
    def GalaxyUID(self):
        GlobalVariables.__GalaxyID += 1
        return GlobalVariables.__GalaxyID


class Galaxy():
    def __init__(self):
        self.pos = [randint(0, CosmicWidth), randint(0, CosmicHeight)]
        self.ResourcePoints = randint(ResouceLowLimit, ResouceUpperLimit)
        self.BirthPoints = self.ResourcePoints * BirthFactor
        self.GalaxyID = GlobalVariables().GalaxyUID
        self.CivilID = InvalidCivilID
        self.HitTime = randint(0, 10)
        self.Rank = 0
        self.Civilization = None


    def CheckBirth(self, time):
        if(self.CivilID != InvalidCivilID) or (time-self.HitTime < BirthTimeInterval):
            return False
        if randint(0, 100) <= self.BirthPoints:
            return False
        self.Civilization = Civilization()
        self.Civilization.BirthInit(self.GalaxyID)
        self.Civilization.ResourcePoints = self.ResourcePoints
        self.Civilization.ParentStar = self
        self.CivilID = self.Civilization.CivilID
        self.Rank = self.Civilization.Rank
        self.HitTime = time
        #print("Time[{}]:Galaxy[{}] Birth Civilization[{}]".format(time, self.GalaxyID, self.CivilID))
        # self.Civilization.PrintCivilInfo()
        return True





class Civilization():
    def __init__(self):
        self.alive = True
        self.Rank = 0
        self.ResourcePoints = 0
        self.CivilID = GlobalVariables().CivilUID
        self.GalaxyIDs = []
        self.Character = choice(CharacterType)
        self.isBroadcast = False
        self.StopDrawBroadcast = False
        self.DetectedCivil = []
        self.LastExplusionTime = 0
        self.LastRegressTime = 0
        self.ParentStar = 0
        self.Color = (randint(0, 255), randint(0, 255), randint(0, 255))
        while self.Color == BackGrand:
            self.Color = (randint(0, 255), randint(0, 255), randint(0, 255))


    def BirthInit(self, GalaxyID):
        self.Rank = GlobalVariables().NormalValue
        self.GalaxyIDs.append(GalaxyID)
        if self.Character == KindCharacter:
            if randint(0, 100) <= KindPoints:
                self.isBroadcast = True
        elif self.Character == VagueCharacter:
            if randint(0, 100) <= VaguePoints:
                self.isBroadcast = True
        else:
            self.isBroadcast = False




    def PrintCivilInfo(self):
        print("\nCivil ID is {}".format(self.CivilID))
        print("Rank is {}".format(self.Rank))
        print("ResourcePoints is {}".format(self.ResourcePoints))
        print("Character is {}".format(self.Character))
        print("Broadcast is {}".format(self.isBroadcast))
        print("Have Galaxy:".format(self.isBroadcast))
        for GalaxyID in self.GalaxyIDs:
            print("\t{}".format(GalaxyID), end=',')
        print('\n')

    def DetectedCivilHandle(self, Civil, Galaxys, Log):
        if self.Character == KindCharacter:
            Log.append("善意文明[{}]发现文明[{}],选择联合.".format(self.CivilID, Civil.CivilID))
            Galaxys = self.Combine(Civil, Galaxys)
        elif self.Character == VagueCharacter:
            tmp = GlobalVariables().StandardNormalValue
            if tmp <= -1:
                Log.append("混沌文明[{}]发现文明[{}],选择攻击.".format(self.CivilID, Civil.CivilID))
                self.Character = KindCharacter
                Galaxys, Log=self.Attack(Civil, Galaxys, Log)
            elif tmp >= 1:
                Log.append("混沌文明[{}]发现文明[{}],选择联合.".format(self.CivilID, Civil.CivilID))
                self.Character = FerociousCharacter
                Galaxys=self.Combine(Civil, Galaxys)
        else:
            Log.append("恶意文明[{}]发现文明[{}],选择攻击.".format(self.CivilID, Civil.CivilID))
            Galaxys, Log=self.Attack(Civil, Galaxys, Log)
        return [Galaxys, Log]

    def Attack(self,  Civil, Galaxys, Log):
        # print("civil[{}] Attack civil [{}]".format(self.CivilID, Civil.CivilID))
        DiffRank = self.Rank - Civil.Rank
        DiffResouce = self.ResourcePoints - Civil.ResourcePoints

        self.Character = FerociousCharacter
        Civil.Character = FerociousCharacter
        self.StopDrawBroadcast = True
        Civil.StopDrawBroadcast = True
        self.isBroadcast = False
        Civil.isBroadcast = False
        if(DiffRank > 5):
            Log.append("文明[{}]摧毁文明[{}]".format(self.CivilID, Civil.CivilID))
            Galaxys = self.Absorb(Civil, Galaxys)
        elif DiffRank < -5:
            Log.append("文明[{}]摧毁文明[{}]".format(Civil.CivilID, self.CivilID))
            Galaxys = Civil.Absorb(self, Galaxys)
        else:
            self.StopDrawBroadcast = True
            Civil.StopDrawBroadcast = True
            self.isBroadcast = False
            Civil.isBroadcast = False

            Points = 50 + DiffRank + 5 + min(25, DiffResouce)
            if randint(0, 100) <= Points:
                Log.append("文明[{}]战胜文明[{}]".format(self.CivilID, Civil.CivilID))
                Galaxys = self.Absorb(Civil, Galaxys)
            else:
                Log.append("文明[{}]战胜文明[{}]".format(Civil.CivilID, self.CivilID))
                Galaxys = Civil.Absorb(self, Galaxys)
        return [Galaxys, Log]

    def Combine(self,  Civil, Galaxys):
        if self.Rank > Civil.Rank:
            # print("civil[{}] Combine civil [{}]".format(self.CivilID, Civil.CivilID))
            Galaxys = self.Absorb(Civil, Galaxys)
        else:
            # print("civil[{}] Combine civil [{}]".format(Civil.CivilID, self.CivilID))
            self.alive = False
            Galaxys = Civil.Absorb(self, Galaxys)
        return Galaxys

    def TechExplosion(self, Time, IgnoreTime = False):
        if Time - self.LastExplusionTime < ExplusionTimeInterval:
            if not IgnoreTime:
                return
        self.LastExplusionTime = Time
        if self.ResourcePoints >= self.Rank:
            self.Rank += randint(5, 10)
            self.ResourcePoints = max(10, self.ResourcePoints - randint(0, 5))
        else:
            self.Rank += randint(0, 5)
        self.Rank = min(RankUperLimit, self.Rank)
        self.ParentStar.Rank = self.Rank


    def TechRegress(self, Time):
        if self.ResourcePoints >= self.Rank or self.Rank <= 30 or Time - self.LastRegressTime < RegressTimeInterval:
            return
        self.LastRegressTime = Time
        self.Rank -= randint(0, 5)
        self.ParentStar.Rank = self.Rank

    def Absorb(self, Civil, Galaxys):
        Civil.alive = False
        self.ResourcePoints += Civil.ResourcePoints
        self.GalaxyIDs += Civil.GalaxyIDs
        for id in Civil.GalaxyIDs:
            Galaxys[id - 1].Civil = self.CivilID
            Galaxys[id - 1].Civilization = self
        self.TechExplosion(0, True)
        return Galaxys

代码资源如下:

链接:https://pan.baidu.com/s/1z959FbaUvth2ZCu4VI99Bw 
提取码:2333

代码效果如下:

【三体】用程序来模拟黑暗森林法则...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值