본문 바로가기
Project/[Unity] 지진 시뮬레이션

[Unity] 지진 시뮬레이션 12 - 유전 알고리즘

by ruki 2024. 4. 4.

시뮬레이션의 핵심인 유전 알고리즘을 작성할 것이다.선택, 교차, 변이, 대치 과정을 거쳐 자녀에게 유전정보를 물려주는 코드이다.


1. 부모 DNA 선택

모든 플레이어 중에서 살아남지 못한 플레이어들을 자식 플레이어로 재활용할 것이다.

DNA를 물려받게 하기 위해 생존 리스트에 있는 부모 중 무작위의 부모로부터 DNA를 추출하였다.

 

살아남지 못한 플레이어를 검사한다.

for (int i = 0; i < Count; i++)
{
    if(i == Survivor[Survivor_Count_Num]) Survivor_Count_Num++;
    else
    {
    	
    }
}

 

무작위의 부모 DNA를 추출한다.

for (int i = 0; i < Count; i++)
{
    if(i == Survivor[Survivor_Count_Num]) Survivor_Count_Num++;
    else
    {
        int ParentDNA_num1 = Random.Range(0, Survivor.Count);
        int ParentDNA_num2 = Random.Range(0, Survivor.Count);

        int[] ParentDNA_1 = ObjectList[Survivor[ParentDNA_num1]].GetComponent<playerMove>().DNA;
        int[] ParentDNA_2 = ObjectList[Survivor[ParentDNA_num2]].GetComponent<playerMove>().DNA;
    }
}

2. 교차

부모로부터 무작위의 유전자를 받을 것이다.

DNA 하나하나 당 무작위로 부모한테 받으면 부모의 행동들이 조금만 달라도 학습이 크게 더뎌지기 때문에 유전자 단위체로 나누어 무작위 하게 할당해줘야 한다.

 

함수를 생성한다.

int[] Heredity((int[],int[]) DNA)
{
}

 

유전자가 저장될 공간을 생성하고 유전자 하나 당 DNA의 양을 선택하는 변수를 만든다.

public int GeneSize;
int[] childDNA = new int[DNA.Item1.Length];

 

유전자의 양만큼 반복하여 랜덤하게 유전자를 할당한다.

for(int i = 0; i < DNA.Item1.Length / GeneSize; i++)
{
    bool randomBool = (Random.value > 0.5f);

    if(randomBool)  for (int n = 0; n < GeneSize; n++) childDNA[i*GeneSize + n] = DNA.Item1[i*GeneSize + n];
    else            for (int n = 0; n < GeneSize; n++) childDNA[i*GeneSize + n] = DNA.Item2[i*GeneSize + n];
}

 

전체 코드

public int GeneSize;
int[] Heredity((int[],int[]) DNA)
{
    int[] childDNA = new int[DNA.Item1.Length];
    for(int i = 0; i < DNA.Item1.Length / GeneSize; i++)
    {
        bool randomBool = (Random.value > 0.5f);
        if(randomBool)  for (int n = 0; n < GeneSize; n++) childDNA[i*GeneSize + n] = DNA.Item1[i*GeneSize + n];
        else            for (int n = 0; n < GeneSize; n++) childDNA[i*GeneSize + n] = DNA.Item2[i*GeneSize + n];
    }
    return childDNA;
}

 


3. 변이

변이를 추가할 것이다.

public int MutationSize;

    int[] Mutation(int[] TargetDNA)
    {
        int M_size = Random.Range(0, MutationSize);

        for (int i = 0; i < M_size; i++)
        {
            TargetDNA[Random.Range(0, TargetDNA.Length)] = Random.Range(0, 6);
        }
        
        return TargetDNA;
    }

변이의 최대값을 설정하여 무작위로 변이가 이루어지도록 하였다.


3. 대치

만들어진 유전자를 탈락한 유전자에 덮어씌웠다.

ObjectList[i].GetComponent<playerMove>().DNA = Mutation(Heredity((ParentDNA_1, ParentDNA_2)));

 

전체 코드

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class gameManager : MonoBehaviour
{
    public static gameManager instance;
    
    private void Awake() 
    {
        instance = this;
    } 
    public int Count; 
    public GameObject BaseObject; 
    public GameObject ParentObject; 
    public List<GameObject> ObjectList = new List<GameObject>(); 

    public int LiveObjectNumber;

    void Start()
    {
        LiveObjectNumber = Count;

        SetObject();
    }

    void Update()
    {
        if(LiveObjectNumber <= Count/2) {Reset(); EditorApplication.isPaused = true;}
    }

    void Reset()
    {
        float AllScore = 0;

        List<int> Survivor  = new List<int>();
        int[] PlayerScores  = new int[Count];

        for (int N = 0; N < Count; N++)
        {
            if(ObjectList[N].gameObject.activeSelf) 
            {
                PlayerScores[N] = ObjectList[N].GetComponent<playerScore>().score;
                AllScore += PlayerScores[N];

                Survivor.Add(N);
            }
        }

        float Survivor_Count = Survivor.Count;

        for (int i = 0; i < Survivor.Count; i++)
        {
            if(PlayerScores[Survivor[i]] < AllScore / Survivor_Count) 
            {
                Survivor.RemoveAt(i);
                i -= 1;
            }
        }

        for (int i = 0; i < Count; i++)         ObjectList[i].SetActive(false);
        for (int i = 0; i < Survivor.Count; i++)ObjectList[Survivor[i]].SetActive(true);

        int Survivor_Count_Num = 0;

        for (int i = 0; i < Count; i++)
        {
            if(i == Survivor[Survivor_Count_Num]) 
            {
                if(Survivor_Count_Num < Survivor.Count-1) Survivor_Count_Num++;
            }
            else
            {
                int ParentDNA_num1 = Random.Range(0, Survivor.Count);
                int ParentDNA_num2 = Random.Range(0, Survivor.Count);

                int[] ParentDNA_1 = ObjectList[Survivor[ParentDNA_num1]].GetComponent<playerMove>().DNA;
                int[] ParentDNA_2 = ObjectList[Survivor[ParentDNA_num2]].GetComponent<playerMove>().DNA;

                ObjectList[i].GetComponent<playerMove>().DNA = Mutation(Heredity((ParentDNA_1, ParentDNA_2)));
            }
        }
    }
    
    public int GeneSize;

    int[] Heredity((int[],int[]) DNA)
    {
        int[] childDNA = new int[DNA.Item1.Length];

        for(int i = 0; i < DNA.Item1.Length / GeneSize; i++)
        {
            bool randomBool = (Random.value > 0.5f);

            if(randomBool)  for (int n = 0; n < GeneSize; n++) childDNA[i*GeneSize + n] = DNA.Item1[i*GeneSize + n];
            else            for (int n = 0; n < GeneSize; n++) childDNA[i*GeneSize + n] = DNA.Item2[i*GeneSize + n];
        }

        return childDNA;
    }

    public int MutationSize;

    int[] Mutation(int[] TargetDNA)
    {
        int M_size = Random.Range(0, MutationSize);

        for (int i = 0; i < M_size; i++)
        {
            TargetDNA[Random.Range(0, TargetDNA.Length)] = Random.Range(0, 6);
        }
        
        return TargetDNA;
    }

    void SetObject()
    {
        for (int i = 0; i < Count; i++) 
        {
            ObjectList.Add(Instantiate(BaseObject, ParentObject.transform));
            ObjectList[i].name = i.ToString();
            ObjectList[i].SetActive(true);
        } 
    }
}

가장 중요한 알고리즘을 제작하였다.

다음부터는 마무리 작업들을 시작할 것이다.