// 크리처의 게놈과 드리프트를 계산합니다. 게놈은 계통시드에서 유도한 다섯 개의 작은 정수이고 드리프트는 성장으로 천천히 쌓이는 카운터입니다.
// 계통시드는 월드시드와 크리처의 기원으로 만든 문자열입니다. 표현형은 원본 게놈에 드리프트 단계만큼 흔들림을 더한 값이고, 드리프트가 진화임계에 닿으면 진화 준비입니다.
// 게놈과 흔들림은 mutation 도메인 난수만 쓰므로 조우와 전투 난수가 게놈을 바꿀 수 없고, 같은 시드와 플레이는 같은 게놈을 만듭니다.
// 상태, 데이터, 전투 모듈을 가져오지 않습니다. 모든 값을 가둬 손상된 저장이 정수 넘침이나 느린 그리기를 일으키지 않게 합니다.
// Genome and drift: a five-trait genome derived from a lineage seed, slow drift, phenotype nudges and evolution readiness.

import core.rng { 난수 }
import core.util { 범위가두기 }

// 색소, 막, 크기, 대사, 촉수는 연못 미생물의 형질이고 값은 [0, 강도상한) 입니다.
export record 게놈 {
  색소: int,
  막: int,
  크기: int,
  대사: int,
  촉수: int,
}

// 토파즈 과제 '구조 타입 주석 속 다른 모듈 레코드' 때문에 모듈째 가져오며 TPZ3017 때문에 레코드를 따로 가져올 수 없어 이 함수로 만듭니다.
// 5.21 에서 그 과제가 고쳐지면 제거 여부를 다시 봅니다.
export function 게놈만들기(색소: int, 막: int, 크기: int, 대사: int, 촉수: int) -> 게놈 {
  게놈 { 색소: 색소, 막: 막, 크기: 크기, 대사: 대사, 촉수: 촉수 }
}

export let 트레이트수 = 5
export let 강도상한 = 8

// 10 이면 유한한 야생을 다 비울 때 3~4 세대까지 진화해 자동 진행이 보스를 이길 수 있습니다.
export let 진화임계 = 10
export let 드리프트상한 = 1000000

// 흔들림 계산이 단계에 비례하므로 단계를 상수로 가둡니다. 진화임계 / 8 = 3 까지만 미리 보여 줍니다.
export let 단계상한 = 3

enum 기원 {
  시조,
  야생(string),
  모름,
}

export function 시작계통시드(월드시드: string) -> string {
  "{월드시드}#starter"
}

// 같은 시드에서 같은 스텝, 칸, 종으로 잡으면 같은 계통시드입니다.
export function 야생계통시드(월드시드: string, 스텝: int, x: int, y: int, 종아이디: int) -> string {
  "{월드시드}#wild@{스텝}:{x}:{y}:{종아이디}"
}

// 진화는 계통시드 뒤에 ">분기" 를 붙이므로 맨 앞의 기원 표시는 어느 노드에도 남습니다.
function 기원읽기(계통시드: string) -> 기원 {
  let 해시조각들 = 계통시드.split("#")
  if 해시조각들.length < 2 { return 기원.모름 }
  let 핵심 = 해시조각들[1].split(">")[0]
  if 핵심 == "starter" { return 기원.시조 }
  let 야생조각들 = 핵심.split("@")
  if 야생조각들.length < 2 { return 기원.모름 }
  기원.야생(야생조각들[1].split(":")[0])
}

export function 출처(계통시드: string) -> string {
  match 기원읽기(계통시드) {
    case 시조 => "시조"
    case 야생(스텝) => "야생 #{스텝}"
    case 모름 => "야생"
  }
}

// 숲 공유 코드에 쓰는 짧은 기원 키입니다.
export function 출처키(계통시드: string) -> string {
  match 기원읽기(계통시드) {
    case 시조 => "S"
    case 야생(스텝) => "W{스텝}"
    case 모름 => "W"
  }
}

// 시드 문자열이 크리처마다 게놈 스트림을 가르므로 엔티티 인자는 0 입니다.
export function 계통엔티티(계통시드: string) -> int {
  난수("mutation", 0, 0, 계통시드)
}

// 선형합동 난수의 아래 비트는 규칙적이라 작은 폭으로 줄이면 치우치므로 위 비트를 씁니다.
function 트레이트유도(엔티티: int, 트레이트번호: int, 계통시드: string) -> int {
  (난수("mutation", 엔티티, 트레이트번호, 계통시드) / 21474836) % 강도상한
}

export function 게놈유도(계통시드: string) -> 게놈 {
  let 엔티티 = 계통엔티티(계통시드)
  게놈 {
    색소: 트레이트유도(엔티티, 0, 계통시드),
    막: 트레이트유도(엔티티, 1, 계통시드),
    크기: 트레이트유도(엔티티, 2, 계통시드),
    대사: 트레이트유도(엔티티, 3, 계통시드),
    촉수: 트레이트유도(엔티티, 4, 계통시드),
  }
}

// 기본은 레벨업 하나에 3, 레벨업 없는 승리에 2 입니다. 데모는 어떤 승리든 두 번이면 임계를 넘도록 14 와 12 를 씁니다.
export function 드리프트증가(오른레벨: int, 승리: bool, 데모: bool) -> int {
  let 레벨당 = if 데모 { 14 } else { 3 }
  let 승리몫 = if 데모 { 12 } else { 2 }
  if 오른레벨 > 0 { 오른레벨 * 레벨당 } else if 승리 { 승리몫 } else { 0 }
}

export function 드리프트가두기(드리프트: int) -> int {
  범위가두기(드리프트, 0, 드리프트상한)
}

// 여덟마다 한 단계라 흔들림이 드물고 느리게 보입니다.
function 드리프트단계(드리프트: int) -> int {
  (드리프트가두기(드리프트) / 8).atMost(단계상한)
}

// 단계마다 -1, 0, +1 중 하나를 더합니다. 카운터를 트레이트수만큼 띄워 게놈유도의 카운터 0~4 와 겹치지 않게 합니다.
function 너지(엔티티: int, 트레이트번호: int, 단계: int, 계통시드: string) -> int {
  let 방향들 = map(1..<(단계 + 1), k => (난수("mutation", 엔티티, 트레이트번호 + k * 트레이트수, 계통시드) / 21474836) % 3 - 1)
  방향들.reduce(0, (합, 방향) => 합 + 방향)
}

// 페이지가 표현형으로 블롭의 색조와 모양을 조금 흔들어 다가올 진화를 미리 보여 줍니다.
export function 표현형(원본: 게놈, 계통시드: string, 드리프트: int) -> 게놈 {
  let 단계 = 드리프트단계(드리프트)
  if 단계 <= 0 { return 원본 }
  let 엔티티 = 계통엔티티(계통시드)
  게놈 {
    색소: 원본.색소 + 너지(엔티티, 0, 단계, 계통시드),
    막: 원본.막 + 너지(엔티티, 1, 단계, 계통시드),
    크기: 원본.크기 + 너지(엔티티, 2, 단계, 계통시드),
    대사: 원본.대사 + 너지(엔티티, 3, 단계, 계통시드),
    촉수: 원본.촉수 + 너지(엔티티, 4, 단계, 계통시드),
  }
}

export function 진화준비(드리프트: int) -> bool {
  드리프트가두기(드리프트) >= 진화임계
}

export function 드리프트퍼센트(드리프트: int) -> int {
  범위가두기(드리프트가두기(드리프트) * 100 / 진화임계, 0, 100)
}

export function 게놈요약(대상: 게놈) -> string {
  "{대상.색소},{대상.막},{대상.크기},{대상.대사},{대상.촉수}"
}

function 트레이트칸(칸들: Array<string>, i: int) -> int {
  범위가두기(if 칸들.length > i { toInt(칸들[i]) ?? 0 } else { 0 }, 0, 강도상한 - 1)
}

// 게놈요약의 역입니다. 도감이 가지 않은 형제 후보를 다시 유도할 때 씁니다.
export function 게놈요약파싱(요약: string) -> 게놈 {
  let 칸들 = 요약.split(",")
  게놈 {
    색소: 트레이트칸(칸들, 0),
    막: 트레이트칸(칸들, 1),
    크기: 트레이트칸(칸들, 2),
    대사: 트레이트칸(칸들, 3),
    촉수: 트레이트칸(칸들, 4),
  }
}
