遺伝的プログラミング(GA)でモンスター育成

[pukiwiki]
先日 遺伝的プログラミング(GA)の話をちょろっと書きましたけれども、その続き。
DSのドラクエ9面白そうダナー と思いつつも、なんとなく波に乗りそびれた私。Pythonで、モンスターのトーナメント戦をするゲームを作って遊んでみたりしました。文字表示だけですけどね。
[/pukiwiki]

[pukiwiki]

説明
“evolution begin press enter

という表示が出るので Enterキーを押してゲーム開始。
最初の世代のモンスターはランダムに生成
16回 世代交代させて進化させた後、代表一匹をトーナメントで選出。

なんか、大量の文字が表示された後、一時停止。

“old type # 最初の世代
name:tpj power=45 speed=41 defense=61 hp=53
name:xmz power=50 speed=45 defense=53 hp=52
~中略~
name:kgk power=41 speed=55 defense=51 hp=53
 
new type #進化した個体
name:vrj power=58 speed=37 defense=52 hp=53

今回代表に選ばれたモンスターの名前は 「jqf」
このコ を 1世代目全員と戦わせて勝率を見ます。

“old type vs new type hit enter

再び、Enterキー

” new wins against old 12 / 16

今回は 16戦中 12勝4敗。 そこそこ強いモンスターに進化したみたいです。
—-
プログラム自体の説明を、ほんのちょっとだけ
-GAを使って、合計200ポイントを、モンスターの4つのパラメータに割り振り。
-DNAとして、1と0の数列を使用
-2ビットずつ 0から3の数字をあらわす
-この数字は、攻撃力、スピード、防御力、HPのいずれにポイントを割り振るかを示している。
-2ビット読み込んで数字にデコードしては、どの属性にポイントを割り振るかを決めていく。
-DNAは2*200ビット - 4*2

RPG的バトルだけど、運はパラメータにありません。
ゲームではありますが、DNAで運が決まると、ちょっとアレかな、と思ったので。

関連?
-[[ググる:ティーラ・ブラウン リング・ワールド]]
—-
[/pukiwiki]
以下ソース。 というか、また遊びたくなった時の自分用メモ。
汚くてすんません。 

import random
pointmax=200
dnamax=pointmax*2-2*4

def random_letter(a=ord("a"),z=ord("z")):
    return chr(a+random.randint(0,z-a))

class Monster :
    def __init__(self,dna=None,name=None):
        if name :
            self.name=name
        else :
            self.name=random_letter()+random_letter()+random_letter()
        if not dna :
            self.dna=[ random.randint(0,192)%2   for i in xrange(dnamax)]
        else :
            self.dna=dna[:]
        #print self.dna
        self.decode_dna()

    def decode_dna(self):
        self.power=self.speed=self.defense=self.hp=0
        param=[1 for x in range(4)]
        for i in xrange(0,dnamax,2):
            amino=self.dna[i]*2+self.dna[i+1]
            param[amino]+=1
        self.power=param[0]
        self.speed=param[1]
        self.defense=param[2]
        self.hp=param[3]
        if False :
            print "name:%s  power=%s speed=%s defense=%s hp=%s"\
                  %(self.name,self.power,self.speed,self.defense,self.hp)
            print "total %s points"%(self.power+self.speed+self.defense+self.hp)
    def pair(self,monster):
        dna1,dna2=self.cross(self.dna,monster.dna)
        dna1=self.mutate(dna1)
        dna2=self.mutate(dna2)
        #print 
        return Monster(dna1),Monster(dna2)
    def __repr__(self):
        return "name:%s  power=%s speed=%s defense=%s hp=%s"\
              %(self.name,self.power,self.speed,self.defense,self.hp)
    def cross(self,dna1,dna2):
        cross_idx=random.randint(0,10000)%dnamax
        print "cross",cross_idx
        dna1b=dna2[:cross_idx]
        dna2b=dna1[:cross_idx]
        dna1b+=dna1[cross_idx:]
        dna2b+=dna2[cross_idx:]
        return dna1b,dna2b
    def mutate(self,dna):
        mutate_idx=random.randint(0,dnamax)-1
        if mutate_idx>0:
            #print "mutate",mutate_idx
            #print dna
            dna[mutate_idx]=0 if dna[mutate_idx] else 1
            #print dna
        return dna
    def copy(self):
        return Monster(self.dna)
class Gameover(Exception):
    pass
class Battle:
    flag_over="flag over"
    def __init__(self,mon1,mon2):
        self.mon_list=[mon1,mon2]
        self.mon1=mon1.copy()
        self.mon2=mon2.copy()
        self.mon1.idx,self.mon2.idx=0,1
        self.mon1.hit_ratio=hit_miss_ratio(mon1,mon2)
        self.mon1.critical_ratio=critical_ratio(mon1,mon2)

        self.mon2.hit_ratio=hit_miss_ratio(mon2,mon1)
        self.mon2.critical_ratio=critical_ratio(mon2,mon1)
        self.mon1.name=mon1.name
        self.mon2.name=mon2.name
    def turn (self):
        mon1,mon2=self.mon1,self.mon2
        
        m1,m2=self.speed()
        
        for i in xrange(2):
            print "atacker : %s"%m1.name
            hr=hit_miss_ratio(m1,m2)
            
            ap=self.damage(m1,m2)
            if ap<=0: ap=1
            hitflag = hr>100.33*random.random()%1.0
            if hitflag :
                cr=critical_ratio(m1,m2)
                if cr>100.33*random.random()%1.0:
                    print "critical hit"
                    ap=int(ap*2.5)
                
                
                if m2.hp>1 and m2.hp-ap<=0 :ap=m2.hp-1
                m2.hp-=ap

                if m2.hp<0 : m2.hp=0
                print "    damage=%s   %s : hp=%s"%(ap,m2.name,m2.hp)

                
                if m2.hp<=0 :
                    raise Gameover
            else :
                print "miss"
            m1,m2=m2,m1
        #print mon1
        #print mon2
    def battle(self):
        try:        
            for i in xrange(100):
                self.turn()
        except Gameover:
            pass
        if self.mon1.hp>self.mon2.hp :
            return self.mon_list[0]
        else :
            return self.mon_list[1]



    def damage(self,atacker,defender):
        atack_rate=1.-float(defender.defense)/pointmax
        atack_rate=atack_rate*float(atacker.power)/defender.defense
        atack_rate=atack_rate*float(atacker.power)/pointmax
        
        ap=0.5*pointmax*atack_rate*(0.75 + 0.25*(random.random()-0.5))
        

        if ap<=0 : ap=1

        return int(ap)
    
        
    def speed(self):
        mon1,mon2=self.mon1,self.mon2
        sp1=mon1.speed+random.randint(0,mon1.defense)//2
        sp2=mon2.speed+random.randint(0,mon2.defense)//2
        if sp1>sp2 :
            return mon1,mon2
        else :
            return mon2,mon1
    

def critical_ratio(atacker,defender):
        weakpoint=pointmax-defender.defense
        weakratio=float(weakpoint)/pointmax
        #print "original weakpoint",weakpoint
        powerratio=float(atacker.power)/defender.defense
        speedratio=float(atacker.speed)/defender.speed

        #print "original weakratio",weakratio
        #print "powerration",powerratio
        #print "speedratio",speedratio
        weakratio=weakratio*powerratio*speedratio
        if weakratio >0.9 : weakratio=0.9
        weakratio*=0.2
        #print "final weakpoint ratio",weakratio
        return  weakratio
def hit_miss(atacker,defender):
    """ """
    
    s=defender.speed/float(atacker.speed)
    if  s< random.randint(0,1000)%pointmax+0.5*(random.random()-.5):
        hit1=True
    else:
        hit1=False

    d=defender.defense/float(atacker.power)
    if  d< random.randint(0,1000)%pointmax+0.5*(random.random()-0.5):
        hit2=True
    else:
        hit2=False
    
    return hit1 and hit2    
def hit_miss_ratio(atacker,defender):
    """ """
    
    miss1=float(defender.speed)/pointmax/float(atacker.speed)

    miss2=float(defender.defense)/pointmax/float(atacker.power)
    
    return 1.0- (miss1+miss2)
        

        

def stat(atacker,defender):
    fmax=float(pointmax)
    r1=defender.speed/fmax/atacker.speed
    r2=defender.defense/fmax/atacker.power
    
    print "calc ratio" , (1.0-(r1+r2))*100,"%"

    print "hit miss ratio",hit_miss_ratio(atacker,defender)*100,"%"  

    print "calc critical ratio",
    print critical_ratio(atacker,defender)*100 ,"%"



        

def vs(monlist):
    newlist=[]
    for i in monlist:
        print i
    print
    for i in xrange(0,len(monlist),2):
        print "vs",monlist[i].name,monlist[i+1].name
        battle=Battle(monlist[i],monlist[i+1])
        winner=battle.battle()
        print "winner :",winner.name
        print
        newlist.append(winner)
    return newlist

def pair(monlist):
    newlist=[]
    monlist=monlist[:]
    random.shuffle(monlist)
    for i in xrange(0,len(monlist),2):
        newlist+=monlist[i].pair(monlist[i+1])        
        newlist+=monlist[i].pair(monlist[i+1])        

    return newlist

monlist0=monlist=[ Monster() for x in xrange(16)]



raw_input ("evolution begin    press enter")

for i in xrange(16):
    monlist=vs(monlist)
    monlist=pair(monlist)

monlist0=monlist0
monlist=vs(monlist)
monlist=vs(monlist)
monlist=vs(monlist)
monlist=vs(monlist)



print "old type"
for i in monlist0:
    print i
    i.tp="old"

print "new type"
i=mon_new=monlist[0]
print i
i.tp="new"    

raw_input("old type vs new type    hit enter ")
counter=0
for i in monlist0:
    battle=Battle(mon_new,i)
    winner=battle.battle()
    if winner==mon_new : counter+=1

print " new wins against old %s / %s" % (counter,len(monlist0))


if False :
    mon1=Monster()
    mon2=Monster()
    print mon1
    #print mon1.dna

    print mon2
    #print mon2.dna

    #stat(mon1,mon2)
    #stat(mon2,mon1)

    battle=Battle(mon1,mon2)
    winner=battle.battle()
    print

    print "winner :" ,winner

コメントを残す

メールアドレスが公開されることはありません。