slow.monster

STUDIO HAZE · jeniapp

Topaz source

Open by house policy. Topaz is our own language.

Topaz 5.20.2 · 63 files

apps/slow-monster/core/evolution.tpz

Topaz · 10948 B · Original file

1// 진화 준비가 된 크리처가 전투에서 레벨업하면 분기 진화합니다. 부모 게놈, 계통시드, 진화카운터에서 후보 돌연변이 2~3 개를 유도하고 고른 분기를 영구 형태로 고정합니다.
2// 후보는 분기 번호, 파생 형태이름, 돌연변이 게놈, 스탯부스트, 지배트레이트입니다. 고정 결과는 새 전투원과 계통 노드 재료입니다.
3// 모든 진화 난수는 mutation 도메인의 1,000,000 부터 시작하는 카운터 띠에서 뽑아 게놈(0~4)과 흔들림(19 이하) 카운터와 겹치지 않습니다. 같은 시드와 같은 분기 선택은 같은 생명의 나무입니다.
4// 형태 이름은 손으로 쓴 진화 사슬이 아니라 게놈에서 유도합니다. 세대, 진화카운터, 분기 번호를 가둬 조작된 저장이 넘침을 일으키지 않게 합니다.
5// Branch evolution: derives two or three seeded mutation candidates from the parent and locks the chosen branch.
7import core.rng { 난수 }
8import core.genome
9import core.creature
10import core.util { 범위가두기 }
11import data.species
13// 진화카운터마다 1,000 칸의 띠를 씁니다. 진화카운터 상한 1,000,000 이라 띠의 끝도 2^31 아래입니다.
14export let 진화시작 = 1000000
15export let 카운터스텝 = 1000
16export let 최대후보수 = 3
17export let 최소후보수 = 2
19export type 후보 = {
20 분기: int,
21 형태이름: string,
22 게놈: genome.게놈,
23 스탯부스트: int,
24 지배트레이트: int,
27// 시드지문은 새 계통시드이며 같은 시드와 분기면 같은 지문이 나오는 재현의 증거입니다.
28export type 진화결과 = {
29 전투원: creature.전투원,
30 형태이름: string,
31 분기: int,
32 세대: int,
33 게놈요약후: string,
34 시드지문: string,
37// 도감에 그릴 발견 기록입니다. 고유번호는 계통시드의 해시입니다.
38export record 계통노드 {
39 고유번호: int,
40 부모고유번호: int,
41 형태이름: string,
42 게놈요약: string,
43 세대: int,
44 발견스텝: int,
45 시드지문: string,
48// 선형합동 난수의 아래 비트는 규칙적이라 위 비트를 씁니다.
49function 진화굴림(엔티티: int, 지역번호: int, 계통시드: string, 진화카운터: int, 폭: int) -> int {
50 let 띠 = 진화시작 + creature.진화카운터가두기(진화카운터) * 카운터스텝 + 지역번호
51 (난수("mutation", 엔티티, 띠, 계통시드) / 21474836) % 폭.atLeast(1)
54export function 후보수(엔티티: int, 계통시드: string, 진화카운터: int) -> int {
55 최소후보수 + 진화굴림(엔티티, 0, 계통시드, 진화카운터, 최대후보수 - 최소후보수 + 1)
58function 트레이트가두기(값: int) -> int {
59 범위가두기(값, 0, genome.강도상한 - 1)
62function 트레이트들(대상: genome.게놈) -> Array<int> {
63 [대상.색소, 대상.막, 대상.크기, 대상.대사, 대상.촉수]
66// 값이 같으면 앞 번호가 이깁니다.
67export function 지배트레이트(대상: genome.게놈) -> int {
68 let 값들 = 트레이트들(대상)
69 reduce(1..<genome.트레이트수, 0, (최선, i) => if 값들[i] > 값들[최선] { i } else { 최선 })
72// 진화는 늘 성장으로 보이도록 형질을 위로만 올리고 상한에서 멈춥니다.
73function 너지(값: int, 세기: int) -> int {
74 (값 + 세기).atMost(genome.강도상한 - 1)
77// 첫째 형질은 (지배 + 1 + 분기) 로 분기마다 다르게 고정해 분기마다 다른 형질이 자랍니다. 둘째 형질은 첫째와 겹치면 하나 옆으로 옮깁니다.
78function 돌연변이게놈(부모: genome.게놈, 엔티티: int, 분기: int, 계통시드: string, 진화카운터: int) -> genome.게놈 {
79 let 기준 = 10 + 분기 * 5
80 let 첫형질 = (지배트레이트(부모) + 1 + 분기) % genome.트레이트수
81 let 뽑은형질 = 진화굴림(엔티티, 기준 + 1, 계통시드, 진화카운터, genome.트레이트수)
82 let 둘째형질 = if 뽑은형질 == 첫형질 { (첫형질 + 1) % genome.트레이트수 } else { 뽑은형질 }
83 let 첫세기 = 진화굴림(엔티티, 기준 + 2, 계통시드, 진화카운터, 3) + 1
84 let 둘째세기 = 진화굴림(엔티티, 기준 + 3, 계통시드, 진화카운터, 3) + 1
85 let 부모값들 = 트레이트들(부모)
86 let 값들 = [
87 for i in 0..<genome.트레이트수 => 트레이트가두기(if i == 첫형질 { 너지(부모값들[i], 첫세기) } else if i == 둘째형질 { 너지(부모값들[i], 둘째세기) } else { 부모값들[i] })
88 ]
89 genome.게놈만들기(값들[0], 값들[1], 값들[2], 값들[3], 값들[4])
92function 어간(지배: int) -> string {
93 match 지배 {
94 case 0 => "빛무리"
95 case 1 => "갑각"
96 case 2 => "거대"
97 case 3 => "열소"
98 case _ => "촉수"
99 }
102// 후보는 2~3 개라 분기 번호로 고른 접미는 늘 서로 달라 어간이 같아도 이름이 겹치지 않습니다.
103function 접미(분기: int) -> string {
104 match 분기 % 6 {
105 case 0 => "체"
106 case 1 => "충"
107 case 2 => "포자"
108 case 3 => "군체"
109 case 4 => "정령"
110 case _ => "변이체"
111 }
114// 세대는 노드가 따로 들고 있어 이름에 넣지 않습니다.
115function 형태이름(돌연변이: genome.게놈, 분기: int) -> string {
116 "{어간(지배트레이트(돌연변이))}{접미(분기)}"
119function 후보유도(부모: genome.게놈, 엔티티: int, 분기: int, 계통시드: string, 진화카운터: int) -> 후보 {
120 let 돌연변이 = 돌연변이게놈(부모, 엔티티, 분기, 계통시드, 진화카운터)
121 {
122 분기: 분기,
123 형태이름: 형태이름(돌연변이, 분기),
124 게놈: 돌연변이,
125 스탯부스트: 진화굴림(엔티티, 40 + 분기, 계통시드, 진화카운터, 4) + 2,
126 지배트레이트: 지배트레이트(돌연변이),
127 }
130export function 후보들유도(부모: genome.게놈, 엔티티: int, 계통시드: string, 진화카운터: int) -> Array<후보> {
131 map(0..<후보수(엔티티, 계통시드, 진화카운터), k => 후보유도(부모, 엔티티, k, 계통시드, 진화카운터))
134// 정확한 형태와 이름은 고정한 뒤에 보여 주고 고르기 전에는 돌연변이게놈의 첫째 형질만 귀띔합니다.
135export function 후보힌트(후보입력: 후보) -> string {
136 match (후보입력.지배트레이트 + 1 + 후보입력.분기) % genome.트레이트수 {
137 case 0 => "빛을 머금는 색소가 짙어지는 기운"
138 case 1 => "막이 단단해지는 기운"
139 case 2 => "몸집이 불어나는 기운"
140 case 3 => "대사가 빨라지는 기운"
141 case _ => "촉수가 뻗어 나가는 기운"
142 }
145// 부모에서 후보를 다시 유도해 실제로 고른 게놈을 뺀 나머지, 가지 않은 길의 힌트를 모읍니다.
146export function 형제힌트들(부모게놈: genome.게놈, 부모시드: string, 부모세대: int, 선택게놈요약: string) -> string {
147 let 후보들 = 후보들유도(부모게놈, 계통고유번호(부모시드), 부모시드, 부모세대)
148 후보들.filter(후 => genome.게놈요약(후.게놈) != 선택게놈요약).map(후보힌트).join(" · ")
151// 상한에 막힌 형질 때문에 두 분기의 게놈이 같을 수 있어 마지막으로 일치한 분기를 씁니다. 없으면 -1 입니다.
152export function 노드분기(부모게놈: genome.게놈, 부모시드: string, 부모세대: int, 자식게놈요약: string) -> int {
153 let 후보들 = 후보들유도(부모게놈, 계통고유번호(부모시드), 부모시드, 부모세대)
154 let 일치들 = 후보들.filter(후 => genome.게놈요약(후.게놈) == 자식게놈요약)
155 if 일치들.length == 0 { -1 } else { 일치들[일치들.length - 1].분기 }
158// 종아이디, 스프라이트, 속성, 기술은 그대로 두고 돌연변이 게놈이 새 기준 게놈이 됩니다. 진화는 체력을 새 최대로 채우고 드리프트를 0 으로 되돌립니다.
159// 범위 밖 분기 선택은 0 번 분기로 돌아갑니다.
160export function 분기진화(부모: creature.전투원, 분기선택: int, 부모세대: int, 부모진화카운터: int) -> 진화결과 {
161 let 카운터 = creature.진화카운터가두기(부모진화카운터)
162 let 후보들 = 후보들유도(부모.게놈, 계통고유번호(부모.계통시드), 부모.계통시드, 카운터)
163 let 분기 = 범위가두기(분기선택, 0, 후보들.length - 1)
164 let 고른후보 = 후보들[분기]
165 let 시드 = "{부모.계통시드}>{분기}"
166 let 새세대 = creature.세대가두기(부모세대 + 1)
167 let 종정보 = species.종찾기(부모.종아이디)
168 let 부스트 = 고른후보.스탯부스트
169 let 새최대 = creature.최대체력(종정보, 부모.레벨) + 부스트 * 2
170 let 새전투원 = 부모{
171 현재체력: 새최대,
172 최대체력: 새최대,
173 공격: 종정보.기본공격 + 부모.레벨 + 부스트,
174 방어: 종정보.기본방어 + 부모.레벨 + 부스트,
175 속도: 종정보.기본속도 + 부모.레벨 + 부스트,
176 계통시드: 시드,
177 드리프트: 0,
178 게놈: 고른후보.게놈,
179 세대: 새세대,
180 진화카운터: creature.진화카운터가두기(카운터 + 1),
181 형태이름: 고른후보.형태이름,
182 스탯부스트: 부모.스탯부스트 + 부스트,
183 }
184 {
185 전투원: 새전투원,
186 형태이름: 고른후보.형태이름,
187 분기: 분기,
188 세대: 새세대,
189 게놈요약후: genome.게놈요약(고른후보.게놈),
190 시드지문: 시드,
191 }
194export function 진화발사(드리프트: int, 오른레벨: int) -> bool {
195 genome.진화준비(드리프트) && 오른레벨 > 0
198// 후보 메뉴가 이 값으로 분기진화와 똑같은 후보를 다시 유도하므로 메뉴에서 본 후보가 곧 고정되는 후보입니다.
199export function 계통고유번호(계통시드: string) -> int {
200 난수("mutation", 0, 0, 계통시드)
203export function 노드빌드(결과: 진화결과, 부모고유번호: int, 스텝: int) -> 계통노드 {
204 노드생성(계통고유번호(결과.시드지문), 부모고유번호, 결과.형태이름, 결과.게놈요약후, 결과.세대, 스텝, 결과.시드지문)
207// 토파즈 과제 '구조 타입 주석 속 다른 모듈 레코드' 때문에 모듈째 가져오며 TPZ3017 때문에 레코드를 따로 가져올 수 없어 이 함수로 만듭니다.
208// 5.21 에서 그 과제가 고쳐지면 제거 여부를 다시 봅니다.
209export function 노드생성(고유번호: int, 부모고유번호: int, 형태이름: string, 게놈요약: string, 세대: int, 발견스텝: int, 시드지문: string) -> 계통노드 {
210 계통노드 {
211 고유번호: 고유번호,
212 부모고유번호: 부모고유번호,
213 형태이름: 형태이름,
214 게놈요약: 게놈요약,
215 세대: 세대,
216 발견스텝: 발견스텝,
217 시드지문: 시드지문,
218 }

SHA256 1bd267c038ff762be2b2bf8903e64348f89e764b808534606da68c74a54f0806