GA - 미로 속 슬라임 - 흠뻑 젖은 슬라임

8. 젖은 슬라임
앞과 같은 미로를 통과하기 위해서는, 슬라임이 과거에 어디를 통과했는지에 관한 기억이 필요합니다. 그래서 왔던길로 돌아가지 말아야 하는 것이죠.

내가 어디서 왔는지를 표시하기 위해 슬라임이 수액을 분비한다고 해 봅시다. 슬라임은 이동할 때마다 자신이 있는 위치를 수액으로 흠뻑 적십니다. 슬라임의 이동경로를 따라 수액의 흔적이 남을 겁니다. 이 수액은 시간이 갈수록 조금씩 말라갑니다.
그와 함께 슬라임은 자기 주위의 습기를 알 수 있는 감지기를 가지고 있습니다. 이것으로 자기가 어디서 왔는지 알 수 있겠죠.

이 젖은슬라임을 만들기 위해서는 약간의 변형이 필요합니다. 먼저 미로에 습기를 저장할 수 있도록 해야 합니다.

procedure MazeProcess(slime, maze)
   // 슬라임을 출발점에 세움
   slime.LocX := 1
   slime.LocY := 1
   maze.Clear() // 미로 안의 모든 습기 제거

   // 미로 안에서 슬라임 움직임
   for k := 0, 900 do // 미로 안에서 슬라임이 움직이는 횟수
                     // 미로가 클수록 커져야 함
      // 미로 안의 습기 말리기
      for x := 0, 15 do
         for y := 0, 15 do
            if maze.Wet[x][y] > 0 then
               maze.Wet[x][y] := maze.Wet[x][y] - 1
            end
         end
      end

      // 슬라임이 수액 분비
      maze.Wet[slime.LocX][slime.LocY] := 100

      // 어디로 움직일지 생각
      movedirect = SlimeThink(slime, maze)

      // 이동할 위치
      if movedirect == 'E' then
         newx := slime.LocX + 1
         newy := slime.LocY
      elseif movedirect == 'W' then
         newx := slime.LocX - 1
         newy := slime.LocY
      elseif movedirect == 'S' then
         newx := slime.LocX
         newy := slime.LocY + 1
      elseif movedirect == 'N' then
         newx := slime.LocX
         newy := slime.LocY - 1
      end

      // 이동한 결과 계산
      if maze.IsBlock(newx, newy) then // 블럭에 충돌
         slime.Fitness := slime.Fitness - 10 // 적응도 감소
      else // 충돌하지 않을 경우
         slime.LocX := newx
         slime.LocY := newy
         slime.Fitness := slime.Fitness - 1 // 적응도 감소

         // 목적지에 도착했나?
         if maze.IsGoal(slime.LocX, slime.LocY) then
            slime.Fitness := slime.Fitness + 1000 // 적응도 대폭 증가
            break // for 루프 탈출
         end
      end
   end
end


즉 슬라임이 있는 위치의 습도는 100, 슬라임이 떠난 후 1씩 감소합니다.

function SlimeThink(slime, maze)
   // 슬라임 동서남북의 블럭정보 얻기
   curstate.detect.EastBlock := maze.IsBlock(slime.LocX + 1, slime.LocY)
   curstate.detect.WestBlock := maze.IsBlock(slime.LocX - 1, slime.LocY)
   curstate.detect.SouthBlock := maze.IsBlock(slime.LocX, slime.LocY + 1)
   curstate.detect.NorthBlock := maze.IsBlock(slime.LocX, slime.LocY - 1)

   // 사방의 습기 찾기
   curstate.detect.EastWet := maze.WetOrder(slime.LocX + 1, slime.LocY)
   curstate.detect.WestWet := maze.WetOrder(slime.LocX - 1, slime.LocY)
   curstate.detect.SouthWet := maze.WetOrder(slime.LocX, slime.LocY + 1)
   curstate.detect.NorthWet := maze.WetOrder(slime.LocX, slime.LocY - 1)

   // 나갈 입구의 방향 찾기
   curstate.detect.ExitEast := maze.IsExitEast(slime.LocX, slime.LocY)
   curstate.detect.ExitWest := maze.IsExitWest(slime.LocX, slime.LocY)
   curstate.detect.ExitSouth := maze.IsExitSouth(slime.LocX, slime.LocY)
   curstate.detect.ExitNorth := maze.IsExitNorth(slime.LocX, slime.LocY)
   // GeneList(유전자들의 묶음)에서 curstate 찾기
   fnd := slimt.GeneList.Find(curstat)
   if fnd == nil then // 처음 만나는 상황
      // effect부분을 랜덤으로 만듦
      for k := 0, 10 do
         curstate.effect[k] = Random("EWSN") // EWSN 중에서 하나 선택
      end
      slime.GeneList.Append(curstate) // 현재 상황 추가
      fnd = curstate
   end
   // 여기까지 해서 fnd에는 현재상황에 맞는 유전자가 들어있음

   return fnd.effect[Random(0, 9)]
end

이때 EastWet~NorthWet을 설정하는 데는 각 방향의 습기를 그대로 저장할 수도 있지만, 그렇게 된다면 유전자 수가 너무 많아질 수 있습니다. 동서남북 최대 100단계이니 100000000개 가까이 유전자가 늘어날 수 있죠(물론 불가능한 조합도 있습니다만)
그러므로 여기서는 각 방향의 습기 등수를 가져오도록 했습니다(가장 많이 젖은 곳이 0, 그다음 젖은 순서대로 1, 2, 3). 그 외는 앞의 슬라임과 동일합니다.

9. 젖은 슬라임의 결과
'수액에 젖은' 슬라임으로 100세대를 진화시킨 결과입니다.


그리고 앞의 슬라임이 통과하지 못했던 미로를 통과한 결과입니다.


두 개의 미로 모두 최적의 슬라임이 탄생했습니다. 단, 두개의 미로를 모두 통과할 수 있는 슬라임이 태어난 것은 아닙니다. 첫째 미로를 돌파하는 슬라임을 둘째 미로에 넣는다면 제대로 못찾을 것입니다.
이러한 현상을 막기 위해서는 미로 자체를 변화시켜야겠죠.

댓글 없음:

댓글 쓰기