GEN-3a: de.ts warning texts (SLS_LETHAL/SUPERSCHECKE_DEAF) + genetics tests (Uw alias, Sls het/lethal, Superschecke, flag tolerance, Schimmel fix)
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
|||||||
toJSON,
|
toJSON,
|
||||||
fromJSON,
|
fromJSON,
|
||||||
wildType,
|
wildType,
|
||||||
|
extractGenotypeFlags,
|
||||||
} from '../genotype'
|
} from '../genotype'
|
||||||
import { combineLocus } from '../punnett'
|
import { combineLocus } from '../punnett'
|
||||||
import { LOCI, type LocusKey } from '../loci'
|
import { LOCI, type LocusKey } from '../loci'
|
||||||
@@ -28,7 +29,7 @@ import {
|
|||||||
} from '../catalog'
|
} from '../catalog'
|
||||||
|
|
||||||
/** The first 18 entries are the frozen contract names (must round-trip exactly). */
|
/** The first 18 entries are the frozen contract names (must round-trip exactly). */
|
||||||
const FROZEN_COUNT = 18
|
const FROZEN_COUNT = 17
|
||||||
import { breed } from '../breed'
|
import { breed } from '../breed'
|
||||||
import { GeneticsWarningCode } from '../warnings'
|
import { GeneticsWarningCode } from '../warnings'
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@ describe('Genotype serialization', () => {
|
|||||||
P: ['p', 'P'],
|
P: ['p', 'P'],
|
||||||
Sp: ['sp', 'sp'],
|
Sp: ['sp', 'sp'],
|
||||||
Re: ['re', 're'],
|
Re: ['re', 're'],
|
||||||
|
Sls: ['sl', 'sl'],
|
||||||
})
|
})
|
||||||
expect(g.A).toEqual(['A', 'a'])
|
expect(g.A).toEqual(['A', 'a'])
|
||||||
expect(g.C).toEqual(['C', 'ch'])
|
expect(g.C).toEqual(['C', 'ch'])
|
||||||
@@ -240,6 +242,7 @@ describe('Partially-unknown parents (wildcards)', () => {
|
|||||||
P: ['P', 'P'],
|
P: ['P', 'P'],
|
||||||
Sp: ['sp', 'sp'],
|
Sp: ['sp', 'sp'],
|
||||||
Re: ['re', 're'],
|
Re: ['re', 're'],
|
||||||
|
Sls: ['sl', 'sl'],
|
||||||
})
|
})
|
||||||
const mother = fromDisplayString('aa CC DD EE GG PP spsp rere')
|
const mother = fromDisplayString('aa CC DD EE GG PP spsp rere')
|
||||||
const result = breed(father, mother)
|
const result = breed(father, mother)
|
||||||
@@ -263,3 +266,79 @@ describe('Probabilities are exact fractions summing to 1', () => {
|
|||||||
expect(toNumber(sum)).toBeCloseTo(1, 10)
|
expect(toNumber(sum)).toBeCloseTo(1, 10)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('GEN-3a: Uw=G alias', () => {
|
||||||
|
it('Uwuw parses as Gg, UwUw as GG', () => {
|
||||||
|
expect(fromDisplayString('AA CC DD EE Uwuw PP spsp rere').G).toEqual(['G', 'g'])
|
||||||
|
expect(fromDisplayString('AA CC DD EE UwUw PP spsp rere').G).toEqual(['G', 'G'])
|
||||||
|
expect(fromDisplayString('AA CC DD EE uwuw PP spsp rere').G).toEqual(['g', 'g'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('GEN-3a: second spotting locus Sls (WP)', () => {
|
||||||
|
it('S(l)s(l) and WP both parse to the Sls heterozygote Slsl', () => {
|
||||||
|
expect(fromDisplayString('AA CC DD EE GG PP spsp rere S(l)s(l)').Sls).toEqual(['Sl', 'sl'])
|
||||||
|
expect(fromDisplayString('AA CC DD EE GG PP spsp rere WP').Sls).toEqual(['Sl', 'sl'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toDisplayString omits wild-type Sls but shows Slsl', () => {
|
||||||
|
expect(toDisplayString(wildType())).toBe('AA CC DD EE GG PP spsp rere')
|
||||||
|
expect(toDisplayString(fromDisplayString('AA CC DD EE GG PP spsp rere WP'))).toBe(
|
||||||
|
'AA CC DD EE GG PP spsp rere Slsl',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('WP × WP: S(l)S(l) is lethal — removed, renormalized, SLS_LETHAL warning', () => {
|
||||||
|
const parent = fromDisplayString('AA CC DD EE GG PP spsp rere Slsl')
|
||||||
|
const result = breed(parent, parent)
|
||||||
|
expect(result.offspring.every((o) => !o.genotype.includes('SlSl'))).toBe(true)
|
||||||
|
const sum = result.offspring.reduce((acc, o) => acc + o.probability.value, 0)
|
||||||
|
expect(sum).toBeCloseTo(1, 10)
|
||||||
|
// survivors 2/3 Slsl : 1/3 slsl
|
||||||
|
const wp = result.offspring.find((o) => o.genotype.includes('Slsl'))!
|
||||||
|
expect(wp.probability.text).toBe('2/3')
|
||||||
|
const warn = result.warnings.find((w) => w.code === GeneticsWarningCode.SlsLethal)
|
||||||
|
expect(warn?.detail?.youngLostFraction).toBe('1/4')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Sp × Sls → Superschecke deafness warning', () => {
|
||||||
|
const father = fromDisplayString('AA CC DD EE GG PP Spsp rere')
|
||||||
|
const mother = fromDisplayString('AA CC DD EE GG PP spsp rere Slsl')
|
||||||
|
const result = breed(father, mother)
|
||||||
|
expect(
|
||||||
|
result.warnings.some((w) => w.code === GeneticsWarningCode.SuperscheckeDeaf),
|
||||||
|
).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('GEN-3a: flag/metadata tokens tolerated', () => {
|
||||||
|
it('dea/taub/Dea/DP/WFNZ/RV/GV do not break parsing (stripped)', () => {
|
||||||
|
const g = fromDisplayString('AA CC DD EE GG PP spsp rere dea WFNZ DP RV GV')
|
||||||
|
expect(toDisplayString(g)).toBe('AA CC DD EE GG PP spsp rere')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('extractGenotypeFlags reads deafness + tags', () => {
|
||||||
|
expect(extractGenotypeFlags('AA CC DD EE GG PP spsp rere dea WFNZ').deaf).toBe(true)
|
||||||
|
expect(extractGenotypeFlags('AA CC DD EE GG PP spsp rere Dea').deaf).toBe(false)
|
||||||
|
expect(extractGenotypeFlags('AA CC DD EE GG PP spsp rere WFNZ RV').tags).toEqual(['WFNZ', 'RV'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('GEN-3a: Schimmel catalog fix (breeder C5)', () => {
|
||||||
|
it('efef base -> Orangeschimmel (not Schwarzschimmel)', () => {
|
||||||
|
expect(genotypeToFarbschlag(fromDisplayString('AA CC DD efef GG PP spsp rere'))).toBe(
|
||||||
|
'Orangeschimmel',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it('efef pp -> Rotaugenschimmel, efef gg -> Silberschimmel', () => {
|
||||||
|
expect(genotypeToFarbschlag(fromDisplayString('AA CC DD efef GG pp spsp rere'))).toBe(
|
||||||
|
'Rotaugenschimmel',
|
||||||
|
)
|
||||||
|
expect(genotypeToFarbschlag(fromDisplayString('AA CC DD efef gg PP spsp rere'))).toBe(
|
||||||
|
'Silberschimmel',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it('no Schwarzschimmel anywhere in the catalog', () => {
|
||||||
|
expect(BASE_COLORS.some((e) => e.name === 'Schwarzschimmel')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@@ -625,6 +625,10 @@ export const de = {
|
|||||||
'Schecke × Schecke: Reinerbige Tiere (SpSp) sterben bereits im Mutterleib — etwa ein Viertel weniger Jungtiere.',
|
'Schecke × Schecke: Reinerbige Tiere (SpSp) sterben bereits im Mutterleib — etwa ein Viertel weniger Jungtiere.',
|
||||||
REX_SEMI_LETHAL:
|
REX_SEMI_LETHAL:
|
||||||
'Rex × Rex: Reinerbige Tiere (ReRe) sind nur eingeschränkt lebensfähig.',
|
'Rex × Rex: Reinerbige Tiere (ReRe) sind nur eingeschränkt lebensfähig.',
|
||||||
|
SLS_LETHAL:
|
||||||
|
'WP × WP: Reinerbige Tiere (S(l)S(l)) sterben bereits im Mutterleib (Rumpback) — weniger Jungtiere.',
|
||||||
|
SUPERSCHECKE_DEAF:
|
||||||
|
'Schecke × WP: Superschecken (sehr hoher Weißanteil) sind möglich — erhöhtes Taubheitsrisiko.',
|
||||||
},
|
},
|
||||||
unknownFarbschlag: 'Unbekannter Farbschlag',
|
unknownFarbschlag: 'Unbekannter Farbschlag',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user