Natural gene / no coat phase
Sex
The sex chromosome pair, registered as an ordinary locus. A mare is
X/X, a stallion is X/Y — a real
X and a real Y, both slots occupied. It is the
first gene in the order and the only gene that paints
nothing.
Crossing two of them
It paints nothing
Both outcomes are wild types: this is the first gene in the model whose every combination changes nothing about the coat. That is what keeps it free.
- The catalogue does not widen. Both combinations land on a wild
type, and
GenotypeCatalogcollapses every wild type into one group, so the whole locus is one pen. The catalogue did not move; onlytotalGenotypes()doubled, because every genotype now genuinely comes in two sexes. (Both numbers have since grown again with dun’s white-pattern rewrite — 2,064,387 pens of 55,099,802,880 genotypes.) - The texture cache does not double. A derived
Gene.affectsCoat()is false when every one of a gene’s outcomes is a wild type, andGenotype.coatCode()— whatCoatData.textureKey()keys on — leaves those genes out. So a mare and a stallion of the same colour share one baked texture, the same reasoning that already keeps invisible epigenetics out of the key. - The short display form ignores it. A plain horse still reads
eeaa; the info panel and the paper dump say “Mare” / “Stallion” in words, as they always did.
Sexual dimorphism (a stallion’s crest, a heavier build) is not modelled. If it ever is, it belongs to a separate gene that reads this one, not to this one.
Y/Y is not a horse
Nothing that can actually happen produces one: a founder draws from a table with two
entries, and a foal always takes an X from its dam. So the sex locus is
the first gene to override Gene.canOccur(AllelePair), which
GenotypeCatalog.allPairsOf filters on — no catalogue entry, and not
counted in totalGenotypes().
@Override
public Expression expressionOf(AllelePair pair) {
return pair.homozygousFor(X) ? MARE : STALLION;
}
@Override
public boolean canOccur(AllelePair pair) {
return !pair.homozygousFor(Y);
}
expressionOf still has to answer for it: parsing is deliberately
tolerant, so a hand-written code string can name Y/Y, and reading it as
a stallion beats throwing.
Its gene carrot
- Gene key
- horsegenetics.sex
- Alleles
- X Y
- Priority
- 1 — ahead of every coat gene
- Outcomes
mare(X/X),stallion(X/Y) — both wild types- Cannot occur
Y/Y— see below- Default allele
- X (so a code with no sex segment reads as a mare)
- Wild frequency
- 50%
X/X, 50%X/Y - Founder draws
- 1 × nextFloat()
- Coat effect
- none, ever
Reading the sex
Sex stays the enum the rest of the code reads — it is now
derived rather than stored. HorseRecord has no
sex field and no "sex" codec entry;
HorseRecord.sex() reads the locus out of the genetic code it already
carries, and so does StoredGenome.sex() on a filled seed jar.
public Sex sex() {
return Genes.SEX.sexOf(pair(Genes.SEX));
}
/** The sex segment alone, without parsing the rest - this is asked per GUI frame. */
public static Sex sexOf(String code) { ... }
The one place sex is still chosen rather than inherited is a founder:
Genotype.withSex(Sex) rewrites the locus and nothing else. The horse
dimension uses it to stock each pen with one mare and one stallion of the same
colour, and the custom spawn egg uses it for the Mare / Stallion button. A foal never
goes through it.
Why sex is a gene
Sex used to be a field on the horse record, rolled by a coin flip at birth. That made it a second source of truth beside the genotype, and it meant a foal’s sex was invented rather than inherited. As a locus it costs one allele pair and buys three things:
- Inheritance falls out of the ordinary Mendelian draw. A mare
is
X/Xand can only pass anX; a stallion isX/Yand passes one or the other 50/50 — exactly the twonextBoolean()per gene that breeding already draws. There is no special case anywhere: the sire decides the foal’s sex because that is what the alleles say, not because a handler was told to. - It is resolved before every other gene. At priority
1the sex locus is the first entry inGenes.codeOrder(), so a future X-linked or Y-linked gene (roadmap §5.3, brindle is the driving case) can segregate against a sex that is already decided. - Sex travels with the genome — into the genotype code, the stallion seed jar’s stored sample, and the custom spawn egg — instead of needing its own field in each of them.
common/genetics/genes/SexGene.java